Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi...
authorJohn W. Linville <linville@tuxdriver.com>
Tue, 5 Nov 2013 20:53:10 +0000 (15:53 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 5 Nov 2013 20:53:10 +0000 (15:53 -0500)
258 files changed:
Documentation/DocBook/80211.tmpl
drivers/bcma/driver_pci.c
drivers/bluetooth/ath3k.c
drivers/bluetooth/bfusb.c
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/bpa10x.c
drivers/bluetooth/bt3c_cs.c
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btsdio.c
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/btusb.c
drivers/bluetooth/btwilink.c
drivers/bluetooth/dtl1_cs.c
drivers/bluetooth/hci_bcsp.c
drivers/bluetooth/hci_h4.c
drivers/bluetooth/hci_h5.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_ll.c
drivers/bluetooth/hci_vhci.c
drivers/net/wireless/ath/Kconfig
drivers/net/wireless/ath/Makefile
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/ce.h
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/debug.h
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/mac.h
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/ath10k/wmi.h
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath6kl/htc.h
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ani.c
drivers/net/wireless/ath/ath9k/ar5008_phy.c
drivers/net/wireless/ath/ath9k/ar9002_calib.c
drivers/net/wireless/ath/ath9k/ar9002_hw.c
drivers/net/wireless/ath/ath9k/ar9002_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/calib.c
drivers/net/wireless/ath/ath9k/calib.h
drivers/net/wireless/ath/ath9k/common.c
drivers/net/wireless/ath/ath9k/common.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/dfs.h
drivers/net/wireless/ath/ath9k/dfs_debug.c
drivers/net/wireless/ath/ath9k/dfs_debug.h
drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pri_detector.c [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pri_detector.h [deleted file]
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw-ops.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/link.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/dfs_pattern_detector.c [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pattern_detector.h [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pri_detector.c [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pri_detector.h [new file with mode: 0644]
drivers/net/wireless/ath/regd.c
drivers/net/wireless/ath/wcn36xx/debug.c
drivers/net/wireless/atmel.c
drivers/net/wireless/b43/phy_n.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/cw1200/cw1200_spi.c
drivers/net/wireless/cw1200/fwio.c
drivers/net/wireless/cw1200/hwbus.h
drivers/net/wireless/cw1200/hwio.c
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/libertas/firmware.c
drivers/net/wireless/libertas/if_cs.c
drivers/net/wireless/libertas/if_sdio.c
drivers/net/wireless/libertas/if_spi.c
drivers/net/wireless/libertas/if_usb.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/11n_aggr.h
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/p54/p54usb.c
drivers/net/wireless/rt2x00/Kconfig
drivers/net/wireless/rt2x00/Makefile
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2800.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800mmio.c [new file with mode: 0644]
drivers/net/wireless/rt2x00/rt2800mmio.h [new file with mode: 0644]
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800pci.h
drivers/net/wireless/rt2x00/rt2800soc.c [new file with mode: 0644]
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00crypto.c
drivers/net/wireless/rt2x00/rt2x00debug.c
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00link.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00pci.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
drivers/net/wireless/rtlwifi/wifi.h
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/conf.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/io.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/scan.c
drivers/net/wireless/ti/wlcore/testmode.c
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/mei_phy.c
drivers/nfc/microread/i2c.c
drivers/nfc/microread/mei.c
drivers/nfc/microread/microread.c
drivers/nfc/nfcsim.c
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544/i2c.c
drivers/nfc/pn544/pn544.c
drivers/nfc/port100.c [new file with mode: 0644]
include/linux/bcma/bcma_driver_pci.h
include/linux/ieee80211.h
include/net/bluetooth/a2mp.h [deleted file]
include/net/bluetooth/amp.h [deleted file]
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
include/net/bluetooth/rfcomm.h
include/net/bluetooth/sco.h
include/net/bluetooth/smp.h [deleted file]
include/net/cfg80211.h
include/net/mac80211.h
include/net/nfc/digital.h [new file with mode: 0644]
include/net/nfc/hci.h
include/net/nfc/nci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
net/bluetooth/a2mp.c
net/bluetooth/a2mp.h [new file with mode: 0644]
net/bluetooth/af_bluetooth.c
net/bluetooth/amp.c
net/bluetooth/amp.h [new file with mode: 0644]
net/bluetooth/bnep/core.c
net/bluetooth/cmtp/core.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/hci_sysfs.c
net/bluetooth/hidp/core.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/sco.c
net/bluetooth/smp.c
net/bluetooth/smp.h [new file with mode: 0644]
net/mac80211/Kconfig
net/mac80211/aes_ccm.c
net/mac80211/aes_ccm.h
net/mac80211/cfg.c
net/mac80211/debug.h
net/mac80211/debugfs_netdev.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.h
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/mesh_ps.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/rate.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/spectmgmt.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wpa.c
net/nfc/Kconfig
net/nfc/Makefile
net/nfc/core.c
net/nfc/digital.h [new file with mode: 0644]
net/nfc/digital_core.c [new file with mode: 0644]
net/nfc/digital_dep.c [new file with mode: 0644]
net/nfc/digital_technology.c [new file with mode: 0644]
net/nfc/nci/spi.c
net/nfc/netlink.c
net/nfc/rawsock.c
net/rfkill/Kconfig
net/rfkill/rfkill-gpio.c
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/radiotap.c
net/wireless/reg.c
net/wireless/reg.h
net/wireless/scan.c
net/wireless/sme.c
net/wireless/util.c

index f403ec3c5c9a4a17cf6c534f2da8590144faf766..46ad6faee9ab73dfae2fa3e2b00bb6fb7f1c63a1 100644 (file)
 !Finclude/net/cfg80211.h cfg80211_scan_request
 !Finclude/net/cfg80211.h cfg80211_scan_done
 !Finclude/net/cfg80211.h cfg80211_bss
-!Finclude/net/cfg80211.h cfg80211_inform_bss_frame
-!Finclude/net/cfg80211.h cfg80211_inform_bss
+!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
+!Finclude/net/cfg80211.h cfg80211_inform_bss_width
 !Finclude/net/cfg80211.h cfg80211_unlink_bss
 !Finclude/net/cfg80211.h cfg80211_find_ie
 !Finclude/net/cfg80211.h ieee80211_bss_get_ie
index c9fd6943ce456a1123e58106b0f028418d535fdd..50329d1057ed5dc5c929fde89237add36c55ea48 100644 (file)
@@ -210,25 +210,6 @@ static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
        }
 }
 
-static void bcma_core_pci_power_save(struct bcma_drv_pci *pc, bool up)
-{
-       u16 data;
-
-       if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) {
-               data = up ? 0x74 : 0x7C;
-               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
-                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64);
-               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
-                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
-       } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) {
-               data = up ? 0x75 : 0x7D;
-               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
-                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65);
-               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
-                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
-       }
-}
-
 /**************************************************
  * Init.
  **************************************************/
@@ -255,6 +236,32 @@ void bcma_core_pci_init(struct bcma_drv_pci *pc)
                bcma_core_pci_clientmode_init(pc);
 }
 
+void bcma_core_pci_power_save(struct bcma_bus *bus, bool up)
+{
+       struct bcma_drv_pci *pc;
+       u16 data;
+
+       if (bus->hosttype != BCMA_HOSTTYPE_PCI)
+               return;
+
+       pc = &bus->drv_pci[0];
+
+       if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) {
+               data = up ? 0x74 : 0x7C;
+               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64);
+               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
+       } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) {
+               data = up ? 0x75 : 0x7D;
+               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65);
+               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
+       }
+}
+EXPORT_SYMBOL_GPL(bcma_core_pci_power_save);
+
 int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
                          bool enable)
 {
@@ -310,8 +317,6 @@ void bcma_core_pci_up(struct bcma_bus *bus)
 
        pc = &bus->drv_pci[0];
 
-       bcma_core_pci_power_save(pc, true);
-
        bcma_core_pci_extend_L1timer(pc, true);
 }
 EXPORT_SYMBOL_GPL(bcma_core_pci_up);
@@ -326,7 +331,5 @@ void bcma_core_pci_down(struct bcma_bus *bus)
        pc = &bus->drv_pci[0];
 
        bcma_core_pci_extend_L1timer(pc, false);
-
-       bcma_core_pci_power_save(pc, false);
 }
 EXPORT_SYMBOL_GPL(bcma_core_pci_down);
index 0a327f4154a2b2039ad4ae0e50b7195dfa2cb5f0..6bfc1bb318f6399397ca8f169cc07fd98b46256d 100644 (file)
@@ -57,7 +57,7 @@ struct ath3k_version {
        unsigned char   reserved[0x07];
 };
 
-static struct usb_device_id ath3k_table[] = {
+static const struct usb_device_id ath3k_table[] = {
        /* Atheros AR3011 */
        { USB_DEVICE(0x0CF3, 0x3000) },
 
@@ -112,7 +112,7 @@ MODULE_DEVICE_TABLE(usb, ath3k_table);
 #define BTUSB_ATH3012          0x80
 /* This table is to load patch and sysconfig files
  * for AR3012 */
-static struct usb_device_id ath3k_blist_tbl[] = {
+static const struct usb_device_id ath3k_blist_tbl[] = {
 
        /* Atheros AR3012 with sflash firmware*/
        { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
index 995aee9cba22a8d871289004a4cef5ec60cdeaa2..31386998c9a7b4159f04194f2125ee10d73cc920 100644 (file)
@@ -42,7 +42,7 @@
 
 static struct usb_driver bfusb_driver;
 
-static struct usb_device_id bfusb_table[] = {
+static const struct usb_device_id bfusb_table[] = {
        /* AVM BlueFRITZ! USB */
        { USB_DEVICE(0x057c, 0x2200) },
 
@@ -318,7 +318,6 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
                        return -ENOMEM;
                }
 
-               skb->dev = (void *) data->hdev;
                bt_cb(skb)->pkt_type = pkt_type;
 
                data->reassembly = skb;
@@ -333,7 +332,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
                memcpy(skb_put(data->reassembly, len), buf, len);
 
        if (hdr & 0x08) {
-               hci_recv_frame(data->reassembly);
+               hci_recv_frame(data->hdev, data->reassembly);
                data->reassembly = NULL;
        }
 
@@ -465,26 +464,18 @@ static int bfusb_close(struct hci_dev *hdev)
        return 0;
 }
 
-static int bfusb_send_frame(struct sk_buff *skb)
+static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-       struct bfusb_data *data;
+       struct bfusb_data *data = hci_get_drvdata(hdev);
        struct sk_buff *nskb;
        unsigned char buf[3];
        int sent = 0, size, count;
 
        BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
 
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
-       data = hci_get_drvdata(hdev);
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
@@ -544,11 +535,6 @@ static int bfusb_send_frame(struct sk_buff *skb)
        return 0;
 }
 
-static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
 static int bfusb_load_firmware(struct bfusb_data *data,
                               const unsigned char *firmware, int count)
 {
@@ -699,11 +685,10 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
        hci_set_drvdata(hdev, data);
        SET_HCIDEV_DEV(hdev, &intf->dev);
 
-       hdev->open     = bfusb_open;
-       hdev->close    = bfusb_close;
-       hdev->flush    = bfusb_flush;
-       hdev->send     = bfusb_send_frame;
-       hdev->ioctl    = bfusb_ioctl;
+       hdev->open  = bfusb_open;
+       hdev->close = bfusb_close;
+       hdev->flush = bfusb_flush;
+       hdev->send  = bfusb_send_frame;
 
        if (hci_register_dev(hdev) < 0) {
                BT_ERR("Can't register HCI device");
index 6c3e3d43c718a1d756c9308a8a135bd9e64a2e32..57427de864a657530ec92f1a54fe9e0d5c52938e 100644 (file)
@@ -399,7 +399,6 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
 
                if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
 
-                       info->rx_skb->dev = (void *) info->hdev;
                        bt_cb(info->rx_skb)->pkt_type = buf[i];
 
                        switch (bt_cb(info->rx_skb)->pkt_type) {
@@ -477,7 +476,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
                                        break;
 
                                case RECV_WAIT_DATA:
-                                       hci_recv_frame(info->rx_skb);
+                                       hci_recv_frame(info->hdev, info->rx_skb);
                                        info->rx_skb = NULL;
                                        break;
 
@@ -659,17 +658,9 @@ static int bluecard_hci_close(struct hci_dev *hdev)
 }
 
 
-static int bluecard_hci_send_frame(struct sk_buff *skb)
+static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       bluecard_info_t *info;
-       struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
-
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
-       info = hci_get_drvdata(hdev);
+       bluecard_info_t *info = hci_get_drvdata(hdev);
 
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
@@ -693,12 +684,6 @@ static int bluecard_hci_send_frame(struct sk_buff *skb)
 }
 
 
-static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-
 
 /* ======================== Card services HCI interaction ======================== */
 
@@ -734,11 +719,10 @@ static int bluecard_open(bluecard_info_t *info)
        hci_set_drvdata(hdev, info);
        SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
-       hdev->open     = bluecard_hci_open;
-       hdev->close    = bluecard_hci_close;
-       hdev->flush    = bluecard_hci_flush;
-       hdev->send     = bluecard_hci_send_frame;
-       hdev->ioctl    = bluecard_hci_ioctl;
+       hdev->open  = bluecard_hci_open;
+       hdev->close = bluecard_hci_close;
+       hdev->flush = bluecard_hci_flush;
+       hdev->send  = bluecard_hci_send_frame;
 
        id = inb(iobase + 0x30);
 
index 2fe4a8031348f0c8b05074eb9889a1d2e02a7a91..8a319913c9a96fd60c6d021c5edd055b83ef0aad 100644 (file)
@@ -37,7 +37,7 @@
 
 #define VERSION "0.10"
 
-static struct usb_device_id bpa10x_table[] = {
+static const struct usb_device_id bpa10x_table[] = {
        /* Tektronix BPA 100/105 (Digianswer) */
        { USB_DEVICE(0x08fd, 0x0002) },
 
@@ -129,8 +129,6 @@ static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
                                return -ENOMEM;
                        }
 
-                       skb->dev = (void *) hdev;
-
                        data->rx_skb[queue] = skb;
 
                        scb = (void *) skb->cb;
@@ -155,7 +153,7 @@ static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
                        data->rx_skb[queue] = NULL;
 
                        bt_cb(skb)->pkt_type = scb->type;
-                       hci_recv_frame(skb);
+                       hci_recv_frame(hdev, skb);
                }
 
                count -= len; buf += len;
@@ -352,9 +350,8 @@ static int bpa10x_flush(struct hci_dev *hdev)
        return 0;
 }
 
-static int bpa10x_send_frame(struct sk_buff *skb)
+static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        struct bpa10x_data *data = hci_get_drvdata(hdev);
        struct usb_ctrlrequest *dr;
        struct urb *urb;
@@ -366,6 +363,8 @@ static int bpa10x_send_frame(struct sk_buff *skb)
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
+       skb->dev = (void *) hdev;
+
        urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!urb)
                return -ENOMEM;
index a1aaa3ba2a4bafed0e2c4573b1614e24ddb84306..73d87994d028ad3e64397a61e05a2e8c15f78321 100644 (file)
@@ -247,7 +247,6 @@ static void bt3c_receive(bt3c_info_t *info)
 
                if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
 
-                       info->rx_skb->dev = (void *) info->hdev;
                        bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
                        inb(iobase + DATA_H);
                        //printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
@@ -318,7 +317,7 @@ static void bt3c_receive(bt3c_info_t *info)
                                        break;
 
                                case RECV_WAIT_DATA:
-                                       hci_recv_frame(info->rx_skb);
+                                       hci_recv_frame(info->hdev, info->rx_skb);
                                        info->rx_skb = NULL;
                                        break;
 
@@ -416,19 +415,11 @@ static int bt3c_hci_close(struct hci_dev *hdev)
 }
 
 
-static int bt3c_hci_send_frame(struct sk_buff *skb)
+static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       bt3c_info_t *info;
-       struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+       bt3c_info_t *info = hci_get_drvdata(hdev);
        unsigned long flags;
 
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
-       info = hci_get_drvdata(hdev);
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
@@ -455,12 +446,6 @@ static int bt3c_hci_send_frame(struct sk_buff *skb)
 }
 
 
-static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-
 
 /* ======================== Card services HCI interaction ======================== */
 
@@ -577,11 +562,10 @@ static int bt3c_open(bt3c_info_t *info)
        hci_set_drvdata(hdev, info);
        SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
-       hdev->open     = bt3c_hci_open;
-       hdev->close    = bt3c_hci_close;
-       hdev->flush    = bt3c_hci_flush;
-       hdev->send     = bt3c_hci_send_frame;
-       hdev->ioctl    = bt3c_hci_ioctl;
+       hdev->open  = bt3c_hci_open;
+       hdev->close = bt3c_hci_close;
+       hdev->flush = bt3c_hci_flush;
+       hdev->send  = bt3c_hci_send_frame;
 
        /* Load firmware */
        err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
index 6e7bd4e4adbb29f5b3d43ea0f4fcacf2a67a3c3f..5cf31c4fe6d1cf552c9846c8fe7a79857851c9b6 100644 (file)
@@ -187,7 +187,6 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 cmd_no,
 
        bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
 
-       skb->dev = (void *) priv->btmrvl_dev.hcidev;
        skb_queue_head(&priv->adapter->tx_queue, skb);
 
        priv->btmrvl_dev.sendcmdflag = true;
@@ -356,26 +355,12 @@ static void btmrvl_free_adapter(struct btmrvl_private *priv)
        priv->adapter = NULL;
 }
 
-static int btmrvl_ioctl(struct hci_dev *hdev,
-                               unsigned int cmd, unsigned long arg)
+static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       return -ENOIOCTLCMD;
-}
-
-static int btmrvl_send_frame(struct sk_buff *skb)
-{
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-       struct btmrvl_private *priv = NULL;
+       struct btmrvl_private *priv = hci_get_drvdata(hdev);
 
        BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
 
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device");
-               return -ENODEV;
-       }
-
-       priv = hci_get_drvdata(hdev);
-
        if (!test_bit(HCI_RUNNING, &hdev->flags)) {
                BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
                print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
@@ -650,12 +635,11 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
        priv->btmrvl_dev.hcidev = hdev;
        hci_set_drvdata(hdev, priv);
 
-       hdev->bus = HCI_SDIO;
-       hdev->open = btmrvl_open;
+       hdev->bus   = HCI_SDIO;
+       hdev->open  = btmrvl_open;
        hdev->close = btmrvl_close;
        hdev->flush = btmrvl_flush;
-       hdev->send = btmrvl_send_frame;
-       hdev->ioctl = btmrvl_ioctl;
+       hdev->send  = btmrvl_send_frame;
        hdev->setup = btmrvl_setup;
 
        hdev->dev_type = priv->btmrvl_dev.dev_type;
index 332475e400cff2f2234c401fb4f1d1a50ba66e0c..fabcf5bb48afbb1c62cf5145d5a12f60eb3a05a8 100644 (file)
@@ -600,15 +600,14 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
        case HCI_SCODATA_PKT:
        case HCI_EVENT_PKT:
                bt_cb(skb)->pkt_type = type;
-               skb->dev = (void *)hdev;
                skb_put(skb, buf_len);
                skb_pull(skb, SDIO_HEADER_LEN);
 
                if (type == HCI_EVENT_PKT) {
                        if (btmrvl_check_evtpkt(priv, skb))
-                               hci_recv_frame(skb);
+                               hci_recv_frame(hdev, skb);
                } else {
-                       hci_recv_frame(skb);
+                       hci_recv_frame(hdev, skb);
                }
 
                hdev->stat.byte_rx += buf_len;
@@ -616,12 +615,11 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
 
        case MRVL_VENDOR_PKT:
                bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
-               skb->dev = (void *)hdev;
                skb_put(skb, buf_len);
                skb_pull(skb, SDIO_HEADER_LEN);
 
                if (btmrvl_process_event(priv, skb))
-                       hci_recv_frame(skb);
+                       hci_recv_frame(hdev, skb);
 
                hdev->stat.byte_rx += buf_len;
                break;
index 4a9909713874dd03eb52240b148ed83b9a22b9d8..b61440aaee658210143f435a872ed51e649485e6 100644 (file)
@@ -157,10 +157,9 @@ static int btsdio_rx_packet(struct btsdio_data *data)
 
        data->hdev->stat.byte_rx += len;
 
-       skb->dev = (void *) data->hdev;
        bt_cb(skb)->pkt_type = hdr[3];
 
-       err = hci_recv_frame(skb);
+       err = hci_recv_frame(data->hdev, skb);
        if (err < 0)
                return err;
 
@@ -255,9 +254,8 @@ static int btsdio_flush(struct hci_dev *hdev)
        return 0;
 }
 
-static int btsdio_send_frame(struct sk_buff *skb)
+static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        struct btsdio_data *data = hci_get_drvdata(hdev);
 
        BT_DBG("%s", hdev->name);
index beb262f2dc4d087432f6e18ab0862888683d0c6d..a03ecc22a561caf4c3d1bdc53489a5b9612745d8 100644 (file)
@@ -198,7 +198,6 @@ static void btuart_receive(btuart_info_t *info)
 
                if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
 
-                       info->rx_skb->dev = (void *) info->hdev;
                        bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX);
 
                        switch (bt_cb(info->rx_skb)->pkt_type) {
@@ -265,7 +264,7 @@ static void btuart_receive(btuart_info_t *info)
                                        break;
 
                                case RECV_WAIT_DATA:
-                                       hci_recv_frame(info->rx_skb);
+                                       hci_recv_frame(info->hdev, info->rx_skb);
                                        info->rx_skb = NULL;
                                        break;
 
@@ -424,17 +423,9 @@ static int btuart_hci_close(struct hci_dev *hdev)
 }
 
 
-static int btuart_hci_send_frame(struct sk_buff *skb)
+static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       btuart_info_t *info;
-       struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
-
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
-       info = hci_get_drvdata(hdev);
+       btuart_info_t *info = hci_get_drvdata(hdev);
 
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
@@ -458,12 +449,6 @@ static int btuart_hci_send_frame(struct sk_buff *skb)
 }
 
 
-static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-
 
 /* ======================== Card services HCI interaction ======================== */
 
@@ -495,11 +480,10 @@ static int btuart_open(btuart_info_t *info)
        hci_set_drvdata(hdev, info);
        SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
-       hdev->open     = btuart_hci_open;
-       hdev->close    = btuart_hci_close;
-       hdev->flush    = btuart_hci_flush;
-       hdev->send     = btuart_hci_send_frame;
-       hdev->ioctl    = btuart_hci_ioctl;
+       hdev->open  = btuart_hci_open;
+       hdev->close = btuart_hci_close;
+       hdev->flush = btuart_hci_flush;
+       hdev->send  = btuart_hci_send_frame;
 
        spin_lock_irqsave(&(info->lock), flags);
 
index f3dfc0a88fdcb95e25647caa6cfc14a1d9a5a484..30868fa870f6086d614184a389feef3267619eef 100644 (file)
@@ -50,7 +50,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_ATH3012          0x80
 #define BTUSB_INTEL            0x100
 
-static struct usb_device_id btusb_table[] = {
+static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
        { USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
 
@@ -121,7 +121,7 @@ static struct usb_device_id btusb_table[] = {
 
 MODULE_DEVICE_TABLE(usb, btusb_table);
 
-static struct usb_device_id blacklist_table[] = {
+static const struct usb_device_id blacklist_table[] = {
        /* CSR BlueCore devices */
        { USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
 
@@ -716,9 +716,8 @@ static int btusb_flush(struct hci_dev *hdev)
        return 0;
 }
 
-static int btusb_send_frame(struct sk_buff *skb)
+static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        struct btusb_data *data = hci_get_drvdata(hdev);
        struct usb_ctrlrequest *dr;
        struct urb *urb;
@@ -730,6 +729,8 @@ static int btusb_send_frame(struct sk_buff *skb)
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
+       skb->dev = (void *) hdev;
+
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                urb = usb_alloc_urb(0, GFP_ATOMIC);
@@ -774,7 +775,7 @@ static int btusb_send_frame(struct sk_buff *skb)
                break;
 
        case HCI_SCODATA_PKT:
-               if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
+               if (!data->isoc_tx_ep || hci_conn_num(hdev, SCO_LINK) < 1)
                        return -ENODEV;
 
                urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
@@ -833,8 +834,8 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
 
        BT_DBG("%s evt %d", hdev->name, evt);
 
-       if (hdev->conn_hash.sco_num != data->sco_num) {
-               data->sco_num = hdev->conn_hash.sco_num;
+       if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) {
+               data->sco_num = hci_conn_num(hdev, SCO_LINK);
                schedule_work(&data->work);
        }
 }
@@ -889,7 +890,7 @@ static void btusb_work(struct work_struct *work)
        int new_alts;
        int err;
 
-       if (hdev->conn_hash.sco_num > 0) {
+       if (data->sco_num > 0) {
                if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
                        err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
                        if (err < 0) {
@@ -903,9 +904,9 @@ static void btusb_work(struct work_struct *work)
 
                if (hdev->voice_setting & 0x0020) {
                        static const int alts[3] = { 2, 4, 5 };
-                       new_alts = alts[hdev->conn_hash.sco_num - 1];
+                       new_alts = alts[data->sco_num - 1];
                } else {
-                       new_alts = hdev->conn_hash.sco_num;
+                       new_alts = data->sco_num;
                }
 
                if (data->isoc_altsetting != new_alts) {
index 60abf596f60ea21f9354ae1b3d3608bb04e4142e..f038dba19e36b77973da26ee9688ed7791126d2f 100644 (file)
@@ -108,10 +108,8 @@ static long st_receive(void *priv_data, struct sk_buff *skb)
                return -EFAULT;
        }
 
-       skb->dev = (void *) lhst->hdev;
-
        /* Forward skb to HCI core layer */
-       err = hci_recv_frame(skb);
+       err = hci_recv_frame(lhst->hdev, skb);
        if (err < 0) {
                BT_ERR("Unable to push skb to HCI core(%d)", err);
                return err;
@@ -253,14 +251,11 @@ static int ti_st_close(struct hci_dev *hdev)
        return err;
 }
 
-static int ti_st_send_frame(struct sk_buff *skb)
+static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev;
        struct ti_st *hst;
        long len;
 
-       hdev = (struct hci_dev *)skb->dev;
-
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
index 33f3a6950c0e85e852766d41bc80db4cd986d98f..52eed1f3565dbf5868c094d246cf7dee4dcc86a4 100644 (file)
@@ -256,9 +256,8 @@ static void dtl1_receive(dtl1_info_t *info)
                                case 0x83:
                                case 0x84:
                                        /* send frame to the HCI layer */
-                                       info->rx_skb->dev = (void *) info->hdev;
                                        bt_cb(info->rx_skb)->pkt_type &= 0x0f;
-                                       hci_recv_frame(info->rx_skb);
+                                       hci_recv_frame(info->hdev, info->rx_skb);
                                        break;
                                default:
                                        /* unknown packet */
@@ -383,20 +382,12 @@ static int dtl1_hci_close(struct hci_dev *hdev)
 }
 
 
-static int dtl1_hci_send_frame(struct sk_buff *skb)
+static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       dtl1_info_t *info;
-       struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+       dtl1_info_t *info = hci_get_drvdata(hdev);
        struct sk_buff *s;
        nsh_t nsh;
 
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
-       info = hci_get_drvdata(hdev);
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
@@ -438,12 +429,6 @@ static int dtl1_hci_send_frame(struct sk_buff *skb)
 }
 
 
-static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,  unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-
 
 /* ======================== Card services HCI interaction ======================== */
 
@@ -477,11 +462,10 @@ static int dtl1_open(dtl1_info_t *info)
        hci_set_drvdata(hdev, info);
        SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
-       hdev->open     = dtl1_hci_open;
-       hdev->close    = dtl1_hci_close;
-       hdev->flush    = dtl1_hci_flush;
-       hdev->send     = dtl1_hci_send_frame;
-       hdev->ioctl    = dtl1_hci_ioctl;
+       hdev->open  = dtl1_hci_open;
+       hdev->close = dtl1_hci_close;
+       hdev->flush = dtl1_hci_flush;
+       hdev->send  = dtl1_hci_send_frame;
 
        spin_lock_irqsave(&(info->lock), flags);
 
index 57e502e0608058ca9943b7cf5c14613339562729..0bc87f7abd958a262028cdd1f0cb7cb0b624c45e 100644 (file)
@@ -522,7 +522,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
                                memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE);
                                bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
 
-                               hci_recv_frame(bcsp->rx_skb);
+                               hci_recv_frame(hu->hdev, bcsp->rx_skb);
                        } else {
                                BT_ERR ("Packet for unknown channel (%u %s)",
                                        bcsp->rx_skb->data[1] & 0x0f,
@@ -536,7 +536,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
                /* Pull out BCSP hdr */
                skb_pull(bcsp->rx_skb, 4);
 
-               hci_recv_frame(bcsp->rx_skb);
+               hci_recv_frame(hu->hdev, bcsp->rx_skb);
        }
 
        bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
@@ -655,7 +655,6 @@ static int bcsp_recv(struct hci_uart *hu, void *data, int count)
                                        bcsp->rx_count = 0;
                                        return 0;
                                }
-                               bcsp->rx_skb->dev = (void *) hu->hdev;
                                break;
                        }
                        break;
index 8ae9f1ea2bb5e59dd40f5c905c3057b5f0e35417..7048a583fe51a695a044ad541a0f894e3bf7c510 100644 (file)
@@ -124,30 +124,6 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
        return 0;
 }
 
-static inline int h4_check_data_len(struct h4_struct *h4, int len)
-{
-       int room = skb_tailroom(h4->rx_skb);
-
-       BT_DBG("len %d room %d", len, room);
-
-       if (!len) {
-               hci_recv_frame(h4->rx_skb);
-       } else if (len > room) {
-               BT_ERR("Data length is too large");
-               kfree_skb(h4->rx_skb);
-       } else {
-               h4->rx_state = H4_W4_DATA;
-               h4->rx_count = len;
-               return len;
-       }
-
-       h4->rx_state = H4_W4_PACKET_TYPE;
-       h4->rx_skb   = NULL;
-       h4->rx_count = 0;
-
-       return 0;
-}
-
 /* Recv data */
 static int h4_recv(struct hci_uart *hu, void *data, int count)
 {
index b6154d5a07a51cf954b1e34d869cb6d3feb75f53..f6f4974505600a2884f56c01be22bd6a79492f04 100644 (file)
@@ -340,7 +340,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
                /* Remove Three-wire header */
                skb_pull(h5->rx_skb, 4);
 
-               hci_recv_frame(h5->rx_skb);
+               hci_recv_frame(hu->hdev, h5->rx_skb);
                h5->rx_skb = NULL;
 
                break;
index bc68a440d432cd21b2ee10885e7106cfe2852b79..6e06f6f6915296ad08d7da394175da19250364db 100644 (file)
@@ -234,21 +234,13 @@ static int hci_uart_close(struct hci_dev *hdev)
 }
 
 /* Send frames from HCI layer */
-static int hci_uart_send_frame(struct sk_buff *skb)
+static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev* hdev = (struct hci_dev *) skb->dev;
-       struct hci_uart *hu;
-
-       if (!hdev) {
-               BT_ERR("Frame for unknown device (hdev=NULL)");
-               return -ENODEV;
-       }
+       struct hci_uart *hu = hci_get_drvdata(hdev);
 
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
-       hu = hci_get_drvdata(hdev);
-
        BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 
        hu->proto->enqueue(hu, skb);
index cfc7679385890b6e0feaadee1462a1e85330cc59..69a90b1b5ff56131aa7a2e51b7d18aabba28b560 100644 (file)
@@ -110,7 +110,6 @@ static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
        /* prepare packet */
        hcill_packet = (struct hcill_cmd *) skb_put(skb, 1);
        hcill_packet->cmd = cmd;
-       skb->dev = (void *) hu->hdev;
 
        /* send packet */
        skb_queue_tail(&ll->txq, skb);
@@ -346,14 +345,14 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
        return 0;
 }
 
-static inline int ll_check_data_len(struct ll_struct *ll, int len)
+static inline int ll_check_data_len(struct hci_dev *hdev, struct ll_struct *ll, int len)
 {
        int room = skb_tailroom(ll->rx_skb);
 
        BT_DBG("len %d room %d", len, room);
 
        if (!len) {
-               hci_recv_frame(ll->rx_skb);
+               hci_recv_frame(hdev, ll->rx_skb);
        } else if (len > room) {
                BT_ERR("Data length is too large");
                kfree_skb(ll->rx_skb);
@@ -395,7 +394,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
                        switch (ll->rx_state) {
                        case HCILL_W4_DATA:
                                BT_DBG("Complete data");
-                               hci_recv_frame(ll->rx_skb);
+                               hci_recv_frame(hu->hdev, ll->rx_skb);
 
                                ll->rx_state = HCILL_W4_PACKET_TYPE;
                                ll->rx_skb = NULL;
@@ -406,7 +405,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
 
                                BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
 
-                               ll_check_data_len(ll, eh->plen);
+                               ll_check_data_len(hu->hdev, ll, eh->plen);
                                continue;
 
                        case HCILL_W4_ACL_HDR:
@@ -415,7 +414,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
 
                                BT_DBG("ACL header: dlen %d", dlen);
 
-                               ll_check_data_len(ll, dlen);
+                               ll_check_data_len(hu->hdev, ll, dlen);
                                continue;
 
                        case HCILL_W4_SCO_HDR:
@@ -423,7 +422,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
 
                                BT_DBG("SCO header: dlen %d", sh->dlen);
 
-                               ll_check_data_len(ll, sh->dlen);
+                               ll_check_data_len(hu->hdev, ll, sh->dlen);
                                continue;
                        }
                }
@@ -494,7 +493,6 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
                        return -ENOMEM;
                }
 
-               ll->rx_skb->dev = (void *) hu->hdev;
                bt_cb(ll->rx_skb)->pkt_type = type;
        }
 
index c04a3e6fb37c32f5e8ee4006246567024e2b9017..7b167385a1c4e8bdf7d0974707f0b0b43ea21f43 100644 (file)
@@ -81,21 +81,13 @@ static int vhci_flush(struct hci_dev *hdev)
        return 0;
 }
 
-static int vhci_send_frame(struct sk_buff *skb)
+static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev* hdev = (struct hci_dev *) skb->dev;
-       struct vhci_data *data;
-
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
+       struct vhci_data *data = hci_get_drvdata(hdev);
 
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
-       data = hci_get_drvdata(hdev);
-
        memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
        skb_queue_tail(&data->readq, skb);
 
@@ -179,10 +171,9 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
                        return -ENODEV;
                }
 
-               skb->dev = (void *) data->hdev;
                bt_cb(skb)->pkt_type = pkt_type;
 
-               ret = hci_recv_frame(skb);
+               ret = hci_recv_frame(data->hdev, skb);
                break;
 
        case HCI_VENDOR_PKT:
index ba81d6292eebd9bd0d34033125349ca90d83bef6..c63d1159db5ca939718f4c34385347865b53ef67 100644 (file)
@@ -25,6 +25,23 @@ config ATH_DEBUG
          Say Y, if you want to debug atheros wireless drivers.
          Right now only ath9k makes use of this.
 
+config ATH_REG_DYNAMIC_USER_REG_HINTS
+       bool "Atheros dynamic user regulatory hints"
+       depends on CFG80211_CERTIFICATION_ONUS
+       default n
+       ---help---
+         Say N. This should only be enabled in countries where
+         this feature is explicitly allowed and only on cards that
+         specifically have been tested for this.
+
+config ATH_REG_DYNAMIC_USER_CERT_TESTING
+       bool "Atheros dynamic user regulatory testing"
+       depends on ATH_REG_DYNAMIC_USER_REG_HINTS && CFG80211_CERTIFICATION_ONUS
+       default n
+       ---help---
+         Say N. This should only be enabled on systems
+         undergoing certification testing.
+
 source "drivers/net/wireless/ath/ath5k/Kconfig"
 source "drivers/net/wireless/ath/ath9k/Kconfig"
 source "drivers/net/wireless/ath/carl9170/Kconfig"
index 363b05653c7e92682046df548819d72092845e16..7d023b0f13b47d064cd2db064c58690e2da8e1ea 100644 (file)
@@ -12,7 +12,9 @@ obj-$(CONFIG_ATH_COMMON)      += ath.o
 ath-objs :=    main.o \
                regd.o \
                hw.o \
-               key.o
+               key.o \
+               dfs_pattern_detector.o \
+               dfs_pri_detector.o
 
 ath-$(CONFIG_ATH_DEBUG) += debug.o
 ccflags-y += -D__CHECK_ENDIAN__
index 834e29ea236c5a9c78f195ff589801d15f998950..e46951b8fb925df8a4cd4bb4ed14a738871175b2 100644 (file)
@@ -283,7 +283,7 @@ static int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
 
        if (unlikely(CE_RING_DELTA(nentries_mask,
                                   write_index, sw_index - 1) <= 0)) {
-               ret = -EIO;
+               ret = -ENOSR;
                goto exit;
        }
 
@@ -338,38 +338,19 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
        return ret;
 }
 
-int ath10k_ce_sendlist_send(struct ath10k_ce_pipe *ce_state,
-                           void *per_transfer_context,
-                           unsigned int transfer_id,
-                           u32 paddr, unsigned int nbytes,
-                           u32 flags)
+int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
 {
-       struct ath10k_ce_ring *src_ring = ce_state->src_ring;
-       struct ath10k *ar = ce_state->ar;
+       struct ath10k *ar = pipe->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       unsigned int nentries_mask = src_ring->nentries_mask;
-       unsigned int sw_index;
-       unsigned int write_index;
-       int delta, ret = -ENOMEM;
+       int delta;
 
        spin_lock_bh(&ar_pci->ce_lock);
-
-       sw_index = src_ring->sw_index;
-       write_index = src_ring->write_index;
-
-       delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
-
-       if (delta >= 1) {
-               ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
-                                           paddr, nbytes,
-                                           transfer_id, flags);
-               if (ret)
-                       ath10k_warn("CE send failed: %d\n", ret);
-       }
-
+       delta = CE_RING_DELTA(pipe->src_ring->nentries_mask,
+                             pipe->src_ring->write_index,
+                             pipe->src_ring->sw_index - 1);
        spin_unlock_bh(&ar_pci->ce_lock);
 
-       return ret;
+       return delta;
 }
 
 int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
index aec802868341b42fe86411258640086d7fd585e7..15d45b5b76153b60133502be39b1803eb86c8517 100644 (file)
@@ -156,21 +156,7 @@ void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
                                void (*send_cb)(struct ath10k_ce_pipe *),
                                int disable_interrupts);
 
-/*
- * Queue a "sendlist" of buffers to be sent using gather to a single
- * anonymous destination buffer
- *   ce         - which copy engine to use
- *   sendlist        - list of simple buffers to send using gather
- *   transfer_id     - arbitrary ID; reflected to destination
- * Returns 0 on success; otherwise an error status.
- *
- * Implemenation note: Pushes multiple buffers with Gather to Source ring.
- */
-int ath10k_ce_sendlist_send(struct ath10k_ce_pipe *ce_state,
-                           void *per_transfer_context,
-                           unsigned int transfer_id,
-                           u32 paddr, unsigned int nbytes,
-                           u32 flags);
+int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe);
 
 /*==================Recv=======================*/
 
index 76906d5a082e00e9ebbb4fe967b430668646fe54..1129994fb10505864c57e1025d95cec8aa009093 100644 (file)
@@ -59,27 +59,6 @@ static void ath10k_send_suspend_complete(struct ath10k *ar)
        wake_up(&ar->event_queue);
 }
 
-static int ath10k_check_fw_version(struct ath10k *ar)
-{
-       char version[32];
-
-       if (ar->fw_version_major >= SUPPORTED_FW_MAJOR &&
-           ar->fw_version_minor >= SUPPORTED_FW_MINOR &&
-           ar->fw_version_release >= SUPPORTED_FW_RELEASE &&
-           ar->fw_version_build >= SUPPORTED_FW_BUILD)
-               return 0;
-
-       snprintf(version, sizeof(version), "%u.%u.%u.%u",
-                SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR,
-                SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD);
-
-       ath10k_warn("WARNING: Firmware version %s is not officially supported.\n",
-                   ar->hw->wiphy->fw_version);
-       ath10k_warn("Please upgrade to version %s (or newer)\n", version);
-
-       return 0;
-}
-
 static int ath10k_init_connect_htc(struct ath10k *ar)
 {
        int status;
@@ -189,8 +168,7 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
        return fw;
 }
 
-static int ath10k_push_board_ext_data(struct ath10k *ar,
-                                     const struct firmware *fw)
+static int ath10k_push_board_ext_data(struct ath10k *ar)
 {
        u32 board_data_size = QCA988X_BOARD_DATA_SZ;
        u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
@@ -210,14 +188,14 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
        if (board_ext_data_addr == 0)
                return 0;
 
-       if (fw->size != (board_data_size + board_ext_data_size)) {
+       if (ar->board_len != (board_data_size + board_ext_data_size)) {
                ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n",
-                          fw->size, board_data_size, board_ext_data_size);
+                          ar->board_len, board_data_size, board_ext_data_size);
                return -EINVAL;
        }
 
        ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
-                                     fw->data + board_data_size,
+                                     ar->board_data + board_data_size,
                                      board_ext_data_size);
        if (ret) {
                ath10k_err("could not write board ext data (%d)\n", ret);
@@ -236,12 +214,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
-       const struct firmware *fw = ar->board_data;
        u32 board_data_size = QCA988X_BOARD_DATA_SZ;
        u32 address;
        int ret;
 
-       ret = ath10k_push_board_ext_data(ar, fw);
+       ret = ath10k_push_board_ext_data(ar);
        if (ret) {
                ath10k_err("could not push board ext data (%d)\n", ret);
                goto exit;
@@ -253,8 +230,9 @@ static int ath10k_download_board_data(struct ath10k *ar)
                goto exit;
        }
 
-       ret = ath10k_bmi_write_memory(ar, address, fw->data,
-                                     min_t(u32, board_data_size, fw->size));
+       ret = ath10k_bmi_write_memory(ar, address, ar->board_data,
+                                     min_t(u32, board_data_size,
+                                           ar->board_len));
        if (ret) {
                ath10k_err("could not write board data (%d)\n", ret);
                goto exit;
@@ -272,17 +250,16 @@ exit:
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-       const struct firmware *fw = ar->otp;
        u32 address = ar->hw_params.patch_load_addr;
        u32 exec_param;
        int ret;
 
        /* OTP is optional */
 
-       if (!ar->otp)
+       if (!ar->otp_data || !ar->otp_len)
                return 0;
 
-       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len);
        if (ret) {
                ath10k_err("could not write otp (%d)\n", ret);
                goto exit;
@@ -301,13 +278,13 @@ exit:
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-       const struct firmware *fw = ar->firmware;
        u32 address;
        int ret;
 
        address = ar->hw_params.patch_load_addr;
 
-       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       ret = ath10k_bmi_fast_download(ar, address, ar->firmware_data,
+                                      ar->firmware_len);
        if (ret) {
                ath10k_err("could not write fw (%d)\n", ret);
                goto exit;
@@ -319,8 +296,8 @@ exit:
 
 static void ath10k_core_free_firmware_files(struct ath10k *ar)
 {
-       if (ar->board_data && !IS_ERR(ar->board_data))
-               release_firmware(ar->board_data);
+       if (ar->board && !IS_ERR(ar->board))
+               release_firmware(ar->board);
 
        if (ar->otp && !IS_ERR(ar->otp))
                release_firmware(ar->otp);
@@ -328,12 +305,20 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar)
        if (ar->firmware && !IS_ERR(ar->firmware))
                release_firmware(ar->firmware);
 
+       ar->board = NULL;
        ar->board_data = NULL;
+       ar->board_len = 0;
+
        ar->otp = NULL;
+       ar->otp_data = NULL;
+       ar->otp_len = 0;
+
        ar->firmware = NULL;
+       ar->firmware_data = NULL;
+       ar->firmware_len = 0;
 }
 
-static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar)
 {
        int ret = 0;
 
@@ -347,15 +332,18 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
                return -EINVAL;
        }
 
-       ar->board_data = ath10k_fetch_fw_file(ar,
-                                             ar->hw_params.fw.dir,
-                                             ar->hw_params.fw.board);
-       if (IS_ERR(ar->board_data)) {
-               ret = PTR_ERR(ar->board_data);
+       ar->board = ath10k_fetch_fw_file(ar,
+                                        ar->hw_params.fw.dir,
+                                        ar->hw_params.fw.board);
+       if (IS_ERR(ar->board)) {
+               ret = PTR_ERR(ar->board);
                ath10k_err("could not fetch board data (%d)\n", ret);
                goto err;
        }
 
+       ar->board_data = ar->board->data;
+       ar->board_len = ar->board->size;
+
        ar->firmware = ath10k_fetch_fw_file(ar,
                                            ar->hw_params.fw.dir,
                                            ar->hw_params.fw.fw);
@@ -365,6 +353,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
                goto err;
        }
 
+       ar->firmware_data = ar->firmware->data;
+       ar->firmware_len = ar->firmware->size;
+
        /* OTP may be undefined. If so, don't fetch it at all */
        if (ar->hw_params.fw.otp == NULL)
                return 0;
@@ -378,6 +369,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
                goto err;
        }
 
+       ar->otp_data = ar->otp->data;
+       ar->otp_len = ar->otp->size;
+
        return 0;
 
 err:
@@ -385,6 +379,191 @@ err:
        return ret;
 }
 
+static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
+{
+       size_t magic_len, len, ie_len;
+       int ie_id, i, index, bit, ret;
+       struct ath10k_fw_ie *hdr;
+       const u8 *data;
+       __le32 *timestamp;
+
+       /* first fetch the firmware file (firmware-*.bin) */
+       ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name);
+       if (IS_ERR(ar->firmware)) {
+               ath10k_err("Could not fetch firmware file '%s': %ld\n",
+                          name, PTR_ERR(ar->firmware));
+               return PTR_ERR(ar->firmware);
+       }
+
+       data = ar->firmware->data;
+       len = ar->firmware->size;
+
+       /* magic also includes the null byte, check that as well */
+       magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;
+
+       if (len < magic_len) {
+               ath10k_err("firmware image too small to contain magic: %zu\n",
+                          len);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
+               ath10k_err("Invalid firmware magic\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* jump over the padding */
+       magic_len = ALIGN(magic_len, 4);
+
+       len -= magic_len;
+       data += magic_len;
+
+       /* loop elements */
+       while (len > sizeof(struct ath10k_fw_ie)) {
+               hdr = (struct ath10k_fw_ie *)data;
+
+               ie_id = le32_to_cpu(hdr->id);
+               ie_len = le32_to_cpu(hdr->len);
+
+               len -= sizeof(*hdr);
+               data += sizeof(*hdr);
+
+               if (len < ie_len) {
+                       ath10k_err("Invalid length for FW IE %d (%zu < %zu)\n",
+                                  ie_id, len, ie_len);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               switch (ie_id) {
+               case ATH10K_FW_IE_FW_VERSION:
+                       if (ie_len > sizeof(ar->hw->wiphy->fw_version) - 1)
+                               break;
+
+                       memcpy(ar->hw->wiphy->fw_version, data, ie_len);
+                       ar->hw->wiphy->fw_version[ie_len] = '\0';
+
+                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                  "found fw version %s\n",
+                                   ar->hw->wiphy->fw_version);
+                       break;
+               case ATH10K_FW_IE_TIMESTAMP:
+                       if (ie_len != sizeof(u32))
+                               break;
+
+                       timestamp = (__le32 *)data;
+
+                       ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n",
+                                  le32_to_cpup(timestamp));
+                       break;
+               case ATH10K_FW_IE_FEATURES:
+                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                  "found firmware features ie (%zd B)\n",
+                                  ie_len);
+
+                       for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) {
+                               index = i / 8;
+                               bit = i % 8;
+
+                               if (index == ie_len)
+                                       break;
+
+                               if (data[index] & (1 << bit))
+                                       __set_bit(i, ar->fw_features);
+                       }
+
+                       ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "",
+                                       ar->fw_features,
+                                       sizeof(ar->fw_features));
+                       break;
+               case ATH10K_FW_IE_FW_IMAGE:
+                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                  "found fw image ie (%zd B)\n",
+                                  ie_len);
+
+                       ar->firmware_data = data;
+                       ar->firmware_len = ie_len;
+
+                       break;
+               case ATH10K_FW_IE_OTP_IMAGE:
+                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                  "found otp image ie (%zd B)\n",
+                                  ie_len);
+
+                       ar->otp_data = data;
+                       ar->otp_len = ie_len;
+
+                       break;
+               default:
+                       ath10k_warn("Unknown FW IE: %u\n",
+                                   le32_to_cpu(hdr->id));
+                       break;
+               }
+
+               /* jump over the padding */
+               ie_len = ALIGN(ie_len, 4);
+
+               len -= ie_len;
+               data += ie_len;
+       }
+
+       if (!ar->firmware_data || !ar->firmware_len) {
+               ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from %s, skipping\n",
+                           name);
+               ret = -ENOMEDIUM;
+               goto err;
+       }
+
+       /* now fetch the board file */
+       if (ar->hw_params.fw.board == NULL) {
+               ath10k_err("board data file not defined");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ar->board = ath10k_fetch_fw_file(ar,
+                                        ar->hw_params.fw.dir,
+                                        ar->hw_params.fw.board);
+       if (IS_ERR(ar->board)) {
+               ret = PTR_ERR(ar->board);
+               ath10k_err("could not fetch board data (%d)\n", ret);
+               goto err;
+       }
+
+       ar->board_data = ar->board->data;
+       ar->board_len = ar->board->size;
+
+       return 0;
+
+err:
+       ath10k_core_free_firmware_files(ar);
+       return ret;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+       int ret;
+
+       ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE);
+       if (ret == 0) {
+               ar->fw_api = 2;
+               goto out;
+       }
+
+       ret = ath10k_core_fetch_firmware_api_1(ar);
+       if (ret)
+               return ret;
+
+       ar->fw_api = 1;
+
+out:
+       ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api);
+
+       return 0;
+}
+
 static int ath10k_init_download_firmware(struct ath10k *ar)
 {
        int ret;
@@ -541,6 +720,9 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
        INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
        skb_queue_head_init(&ar->offchan_tx_queue);
 
+       INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
+       skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
+
        init_waitqueue_head(&ar->event_queue);
 
        INIT_WORK(&ar->restart_work, ath10k_core_restart);
@@ -555,6 +737,8 @@ EXPORT_SYMBOL(ath10k_core_create);
 
 void ath10k_core_destroy(struct ath10k *ar)
 {
+       ath10k_debug_destroy(ar);
+
        flush_workqueue(ar->workqueue);
        destroy_workqueue(ar->workqueue);
 
@@ -566,6 +750,8 @@ int ath10k_core_start(struct ath10k *ar)
 {
        int status;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ath10k_bmi_start(ar);
 
        if (ath10k_init_configure_target(ar)) {
@@ -616,10 +802,6 @@ int ath10k_core_start(struct ath10k *ar)
 
        ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version);
 
-       status = ath10k_check_fw_version(ar);
-       if (status)
-               goto err_disconnect_htc;
-
        status = ath10k_wmi_cmd_init(ar);
        if (status) {
                ath10k_err("could not send WMI init command (%d)\n", status);
@@ -642,6 +824,7 @@ int ath10k_core_start(struct ath10k *ar)
                goto err_disconnect_htc;
 
        ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+       INIT_LIST_HEAD(&ar->arvifs);
 
        return 0;
 
@@ -658,6 +841,8 @@ EXPORT_SYMBOL(ath10k_core_start);
 
 void ath10k_core_stop(struct ath10k *ar)
 {
+       lockdep_assert_held(&ar->conf_mutex);
+
        ath10k_debug_stop(ar);
        ath10k_htc_stop(&ar->htc);
        ath10k_htt_detach(&ar->htt);
@@ -705,15 +890,21 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
                return ret;
        }
 
+       mutex_lock(&ar->conf_mutex);
+
        ret = ath10k_core_start(ar);
        if (ret) {
                ath10k_err("could not init core (%d)\n", ret);
                ath10k_core_free_firmware_files(ar);
                ath10k_hif_power_down(ar);
+               mutex_unlock(&ar->conf_mutex);
                return ret;
        }
 
        ath10k_core_stop(ar);
+
+       mutex_unlock(&ar->conf_mutex);
+
        ath10k_hif_power_down(ar);
        return 0;
 }
index 292ad4577c986aa3efc9c4c377307005fcdddecf..0934f7633de399df82df8ab74a9e0f6ef64f5b82 100644 (file)
 /* Antenna noise floor */
 #define ATH10K_DEFAULT_NOISE_FLOOR -95
 
+#define ATH10K_MAX_NUM_MGMT_PENDING 16
+
 struct ath10k;
 
 struct ath10k_skb_cb {
        dma_addr_t paddr;
        bool is_mapped;
        bool is_aborted;
+       u8 vdev_id;
 
        struct {
-               u8 vdev_id;
                u8 tid;
                bool is_offchan;
 
@@ -102,11 +104,26 @@ struct ath10k_bmi {
        bool done_sent;
 };
 
+#define ATH10K_MAX_MEM_REQS 16
+
+struct ath10k_mem_chunk {
+       void *vaddr;
+       dma_addr_t paddr;
+       u32 len;
+       u32 req_id;
+};
+
 struct ath10k_wmi {
        enum ath10k_htc_ep_id eid;
        struct completion service_ready;
        struct completion unified_ready;
        wait_queue_head_t tx_credits_wq;
+       struct wmi_cmd_map *cmd;
+       struct wmi_vdev_param_map *vdev_param;
+       struct wmi_pdev_param_map *pdev_param;
+
+       u32 num_mem_chunks;
+       struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS];
 };
 
 struct ath10k_peer_stat {
@@ -188,6 +205,8 @@ struct ath10k_peer {
 #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
 
 struct ath10k_vif {
+       struct list_head list;
+
        u32 vdev_id;
        enum wmi_vdev_type vdev_type;
        enum wmi_vdev_subtype vdev_subtype;
@@ -198,8 +217,10 @@ struct ath10k_vif {
        struct ath10k *ar;
        struct ieee80211_vif *vif;
 
+       struct work_struct wep_key_work;
        struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
-       u8 def_wep_key_index;
+       u8 def_wep_key_idx;
+       u8 def_wep_key_newidx;
 
        u16 tx_seq_no;
 
@@ -268,6 +289,12 @@ enum ath10k_fw_features {
        /* wmi_mgmt_rx_hdr contains extra RSSI information */
        ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0,
 
+       /* firmware from 10X branch */
+       ATH10K_FW_FEATURE_WMI_10X = 1,
+
+       /* firmware support tx frame management over WMI, otherwise it's HTT */
+       ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2,
+
        /* keep last */
        ATH10K_FW_FEATURE_COUNT,
 };
@@ -324,9 +351,19 @@ struct ath10k {
                } fw;
        } hw_params;
 
-       const struct firmware *board_data;
+       const struct firmware *board;
+       const void *board_data;
+       size_t board_len;
+
        const struct firmware *otp;
+       const void *otp_data;
+       size_t otp_len;
+
        const struct firmware *firmware;
+       const void *firmware_data;
+       size_t firmware_len;
+
+       int fw_api;
 
        struct {
                struct completion started;
@@ -369,6 +406,7 @@ struct ath10k {
        /* protects shared structure data */
        spinlock_t data_lock;
 
+       struct list_head arvifs;
        struct list_head peers;
        wait_queue_head_t peer_mapping_wq;
 
@@ -377,6 +415,9 @@ struct ath10k {
        struct completion offchan_tx_completed;
        struct sk_buff *offchan_tx_skb;
 
+       struct work_struct wmi_mgmt_tx_work;
+       struct sk_buff_head wmi_mgmt_tx_queue;
+
        enum ath10k_state state;
 
        struct work_struct restart_work;
index 59615c7f217e408ee48669f6d5d949ae8fef1ada..760ff2289e3cf2903773544d3349cab7bde1a852 100644 (file)
@@ -618,6 +618,8 @@ int ath10k_debug_start(struct ath10k *ar)
 {
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_debug_htt_stats_req(ar);
        if (ret)
                /* continue normally anyway, this isn't serious */
@@ -628,7 +630,13 @@ int ath10k_debug_start(struct ath10k *ar)
 
 void ath10k_debug_stop(struct ath10k *ar)
 {
-       cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* Must not use _sync to avoid deadlock, we do that in
+        * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid
+        * warning from del_timer(). */
+       if (ar->debug.htt_stats_mask != 0)
+               cancel_delayed_work(&ar->debug.htt_stats_dwork);
 }
 
 int ath10k_debug_create(struct ath10k *ar)
@@ -662,6 +670,11 @@ int ath10k_debug_create(struct ath10k *ar)
        return 0;
 }
 
+void ath10k_debug_destroy(struct ath10k *ar)
+{
+       cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);
+}
+
 #endif /* CONFIG_ATH10K_DEBUGFS */
 
 #ifdef CONFIG_ATH10K_DEBUG
index fa581486626f7e6d8a320b6f9daa8cab87c42b23..46e640a6968d0cd90c05f2d09b696844d348ef81 100644 (file)
@@ -46,6 +46,7 @@ extern __printf(1, 2) int ath10k_warn(const char *fmt, ...);
 int ath10k_debug_start(struct ath10k *ar);
 void ath10k_debug_stop(struct ath10k *ar);
 int ath10k_debug_create(struct ath10k *ar);
+void ath10k_debug_destroy(struct ath10k *ar);
 void ath10k_debug_read_service_map(struct ath10k *ar,
                                   void *service_map,
                                   size_t map_size);
@@ -67,6 +68,10 @@ static inline int ath10k_debug_create(struct ath10k *ar)
        return 0;
 }
 
+static inline void ath10k_debug_destroy(struct ath10k *ar)
+{
+}
+
 static inline void ath10k_debug_read_service_map(struct ath10k *ar,
                                                 void *service_map,
                                                 size_t map_size)
index 3b93c6a01c6c618a7e2ae4a194a226be769984aa..d9335e9d0d04d247e868e465323037e4f9f78d9e 100644 (file)
@@ -308,7 +308,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        struct sk_buff *txdesc = NULL;
        struct htt_cmd *cmd;
        struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
-       u8 vdev_id = skb_cb->htt.vdev_id;
+       u8 vdev_id = skb_cb->vdev_id;
        int len = 0;
        int msdu_id = -1;
        int res;
@@ -384,7 +384,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
        struct sk_buff *txdesc = NULL;
        bool use_frags;
-       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       u8 vdev_id = ATH10K_SKB_CB(msdu)->vdev_id;
        u8 tid;
        int prefetch_len, desc_len;
        int msdu_id = -1;
index 8c1be76859220fa1b57d2c9965bbdbb2fc749222..8aeb46d9b53409da2ced923d49780b763105a5d6 100644 (file)
 
 #include "targaddrs.h"
 
-/* Supported FW version */
-#define SUPPORTED_FW_MAJOR     1
-#define SUPPORTED_FW_MINOR     0
-#define SUPPORTED_FW_RELEASE   0
-#define SUPPORTED_FW_BUILD     636
-
 /* QCA988X 1.0 definitions (unsupported) */
 #define QCA988X_HW_1_0_CHIP_ID_REV     0x0
 
 #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
 #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
 
+#define ATH10K_FW_API2_FILE            "firmware-2.bin"
+
+/* includes also the null byte */
+#define ATH10K_FIRMWARE_MAGIC               "QCA-ATH10K"
+
+struct ath10k_fw_ie {
+       __le32 id;
+       __le32 len;
+       u8 data[0];
+};
+
+enum ath10k_fw_ie_type {
+       ATH10K_FW_IE_FW_VERSION = 0,
+       ATH10K_FW_IE_TIMESTAMP = 1,
+       ATH10K_FW_IE_FEATURES = 2,
+       ATH10K_FW_IE_FW_IMAGE = 3,
+       ATH10K_FW_IE_OTP_IMAGE = 4,
+};
+
 /* Known pecularities:
  *  - current FW doesn't support raw rx mode (last tested v599)
  *  - current FW dumps upon raw tx mode (last tested v599)
@@ -59,6 +72,7 @@ enum ath10k_mcast2ucast_mode {
        ATH10K_MCAST2UCAST_ENABLED = 1,
 };
 
+/* Target specific defines for MAIN firmware */
 #define TARGET_NUM_VDEVS                       8
 #define TARGET_NUM_PEER_AST                    2
 #define TARGET_NUM_WDS_ENTRIES                 32
@@ -93,6 +107,36 @@ enum ath10k_mcast2ucast_mode {
 #define TARGET_NUM_MSDU_DESC                   (1024 + 400)
 #define TARGET_MAX_FRAG_ENTRIES                        0
 
+/* Target specific defines for 10.X firmware */
+#define TARGET_10X_NUM_VDEVS                   16
+#define TARGET_10X_NUM_PEER_AST                        2
+#define TARGET_10X_NUM_WDS_ENTRIES             32
+#define TARGET_10X_DMA_BURST_SIZE              0
+#define TARGET_10X_MAC_AGGR_DELIM              0
+#define TARGET_10X_AST_SKID_LIMIT              16
+#define TARGET_10X_NUM_PEERS                   (128 + (TARGET_10X_NUM_VDEVS))
+#define TARGET_10X_NUM_OFFLOAD_PEERS           0
+#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS    0
+#define TARGET_10X_NUM_PEER_KEYS               2
+#define TARGET_10X_NUM_TIDS                    256
+#define TARGET_10X_TX_CHAIN_MASK               (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_10X_RX_CHAIN_MASK               (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_10X_RX_TIMEOUT_LO_PRI           100
+#define TARGET_10X_RX_TIMEOUT_HI_PRI           40
+#define TARGET_10X_RX_DECAP_MODE               ATH10K_HW_TXRX_NATIVE_WIFI
+#define TARGET_10X_SCAN_MAX_PENDING_REQS       4
+#define TARGET_10X_BMISS_OFFLOAD_MAX_VDEV      2
+#define TARGET_10X_ROAM_OFFLOAD_MAX_VDEV       2
+#define TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES        8
+#define TARGET_10X_GTK_OFFLOAD_MAX_VDEV                3
+#define TARGET_10X_NUM_MCAST_GROUPS            0
+#define TARGET_10X_NUM_MCAST_TABLE_ELEMS       0
+#define TARGET_10X_MCAST2UCAST_MODE            ATH10K_MCAST2UCAST_DISABLED
+#define TARGET_10X_TX_DBG_LOG_SIZE             1024
+#define TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1
+#define TARGET_10X_VOW_CONFIG                  0
+#define TARGET_10X_NUM_MSDU_DESC               (1024 + 400)
+#define TARGET_10X_MAX_FRAG_ENTRIES            0
 
 /* Number of Copy Engines supported */
 #define CE_COUNT 8
index 99a9bad3f398cc87238b1d1c37868b7585a795b3..0b1cc516e778c912e77d341a90f4921e5227a258 100644 (file)
@@ -334,25 +334,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 
 static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
 {
+       struct ath10k *ar = arvif->ar;
+       u32 vdev_param;
+
        if (value != 0xFFFFFFFF)
                value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
                              ATH10K_RTS_MAX);
 
-       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
-                                        WMI_VDEV_PARAM_RTS_THRESHOLD,
-                                        value);
+       vdev_param = ar->wmi.vdev_param->rts_threshold;
+       return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
 }
 
 static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
 {
+       struct ath10k *ar = arvif->ar;
+       u32 vdev_param;
+
        if (value != 0xFFFFFFFF)
                value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold,
                                ATH10K_FRAGMT_THRESHOLD_MIN,
                                ATH10K_FRAGMT_THRESHOLD_MAX);
 
-       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
-                                        WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
-                                        value);
+       vdev_param = ar->wmi.vdev_param->fragmentation_threshold;
+       return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
 }
 
 static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
@@ -562,12 +566,9 @@ static int ath10k_monitor_stop(struct ath10k *ar)
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       /* For some reasons, ath10k_wmi_vdev_down() here couse
-        * often ath10k_wmi_vdev_stop() to fail. Next we could
-        * not run monitor vdev and driver reload
-        * required. Don't see such problems we skip
-        * ath10k_wmi_vdev_down() here.
-        */
+       ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
+       if (ret)
+               ath10k_warn("Monitor vdev down failed: %d\n", ret);
 
        ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
        if (ret)
@@ -677,6 +678,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
                                struct ieee80211_bss_conf *info,
                                const u8 self_peer[ETH_ALEN])
 {
+       u32 vdev_param;
        int ret = 0;
 
        lockdep_assert_held(&arvif->ar->conf_mutex);
@@ -710,8 +712,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
                return;
        }
 
-       ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_ATIM_WINDOW,
+       vdev_param = arvif->ar->wmi.vdev_param->atim_window;
+       ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
                                        ATH10K_DEFAULT_ATIM);
        if (ret)
                ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
@@ -721,35 +723,30 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
 /*
  * Review this when mac80211 gains per-interface powersave support.
  */
-static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
 {
-       struct ath10k_generic_iter *ar_iter = data;
-       struct ieee80211_conf *conf = &ar_iter->ar->hw->conf;
-       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_conf *conf = &ar->hw->conf;
        enum wmi_sta_powersave_param param;
        enum wmi_sta_ps_mode psmode;
        int ret;
 
        lockdep_assert_held(&arvif->ar->conf_mutex);
 
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
+       if (arvif->vif->type != NL80211_IFTYPE_STATION)
+               return 0;
 
        if (conf->flags & IEEE80211_CONF_PS) {
                psmode = WMI_STA_PS_MODE_ENABLED;
                param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
 
-               ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar,
-                                                 arvif->vdev_id,
-                                                 param,
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
                                                  conf->dynamic_ps_timeout);
                if (ret) {
                        ath10k_warn("Failed to set inactivity time for VDEV: %d\n",
                                    arvif->vdev_id);
-                       return;
+                       return ret;
                }
-
-               ar_iter->ret = ret;
        } else {
                psmode = WMI_STA_PS_MODE_DISABLED;
        }
@@ -757,11 +754,14 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
                   arvif->vdev_id, psmode ? "enable" : "disable");
 
-       ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
-                                            psmode);
-       if (ar_iter->ret)
+       ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
+       if (ret) {
                ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
                            psmode, arvif->vdev_id);
+               return ret;
+       }
+
+       return 0;
 }
 
 /**********************/
@@ -1031,14 +1031,27 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
                                    struct wmi_peer_assoc_complete_arg *arg)
 {
        const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+       u8 ampdu_factor;
 
        if (!vht_cap->vht_supported)
                return;
 
        arg->peer_flags |= WMI_PEER_VHT;
-
        arg->peer_vht_caps = vht_cap->cap;
 
+
+       ampdu_factor = (vht_cap->cap &
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+                      IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+       /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to
+        * zero in VHT IE. Using it would result in degraded throughput.
+        * arg->peer_max_mpdu at this point contains HT max_mpdu so keep
+        * it if VHT max_mpdu is smaller. */
+       arg->peer_max_mpdu = max(arg->peer_max_mpdu,
+                                (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+                                       ampdu_factor)) - 1);
+
        if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
                arg->peer_flags |= WMI_PEER_80MHZ;
 
@@ -1124,26 +1137,25 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
        WARN_ON(phymode == MODE_UNKNOWN);
 }
 
-static int ath10k_peer_assoc(struct ath10k *ar,
-                            struct ath10k_vif *arvif,
-                            struct ieee80211_sta *sta,
-                            struct ieee80211_bss_conf *bss_conf)
+static int ath10k_peer_assoc_prepare(struct ath10k *ar,
+                                    struct ath10k_vif *arvif,
+                                    struct ieee80211_sta *sta,
+                                    struct ieee80211_bss_conf *bss_conf,
+                                    struct wmi_peer_assoc_complete_arg *arg)
 {
-       struct wmi_peer_assoc_complete_arg arg;
-
        lockdep_assert_held(&ar->conf_mutex);
 
-       memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
+       memset(arg, 0, sizeof(*arg));
 
-       ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
-       ath10k_peer_assoc_h_crypto(ar, arvif, &arg);
-       ath10k_peer_assoc_h_rates(ar, sta, &arg);
-       ath10k_peer_assoc_h_ht(ar, sta, &arg);
-       ath10k_peer_assoc_h_vht(ar, sta, &arg);
-       ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg);
-       ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg);
+       ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg);
+       ath10k_peer_assoc_h_crypto(ar, arvif, arg);
+       ath10k_peer_assoc_h_rates(ar, sta, arg);
+       ath10k_peer_assoc_h_ht(ar, sta, arg);
+       ath10k_peer_assoc_h_vht(ar, sta, arg);
+       ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg);
+       ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg);
 
-       return ath10k_wmi_peer_assoc(ar, &arg);
+       return 0;
 }
 
 /* can be called only in mac80211 callbacks due to `key_count` usage */
@@ -1153,6 +1165,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_peer_assoc_complete_arg peer_arg;
        struct ieee80211_sta *ap_sta;
        int ret;
 
@@ -1168,15 +1181,24 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
                return;
        }
 
-       ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf);
+       ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
+                                       bss_conf, &peer_arg);
        if (ret) {
-               ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid);
+               ath10k_warn("Peer assoc prepare failed for %pM\n: %d",
+                           bss_conf->bssid, ret);
                rcu_read_unlock();
                return;
        }
 
        rcu_read_unlock();
 
+       ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
+       if (ret) {
+               ath10k_warn("Peer assoc failed for %pM\n: %d",
+                           bss_conf->bssid, ret);
+               return;
+       }
+
        ath10k_dbg(ATH10K_DBG_MAC,
                   "mac vdev %d up (associated) bssid %pM aid %d\n",
                   arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
@@ -1224,19 +1246,28 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
        /* FIXME: why don't we print error if wmi call fails? */
        ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
 
-       arvif->def_wep_key_index = 0;
+       arvif->def_wep_key_idx = 0;
 }
 
 static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
                                struct ieee80211_sta *sta)
 {
+       struct wmi_peer_assoc_complete_arg peer_arg;
        int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
+       ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
        if (ret) {
-               ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
+               ath10k_warn("WMI peer assoc prepare failed for %pM\n",
+                           sta->addr);
+               return ret;
+       }
+
+       ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
+       if (ret) {
+               ath10k_warn("Peer assoc failed for STA %pM\n: %d",
+                           sta->addr, ret);
                return ret;
        }
 
@@ -1405,6 +1436,33 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 /* TX handlers */
 /***************/
 
+static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr)
+{
+       if (ieee80211_is_mgmt(hdr->frame_control))
+               return HTT_DATA_TX_EXT_TID_MGMT;
+
+       if (!ieee80211_is_data_qos(hdr->frame_control))
+               return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+
+       if (!is_unicast_ether_addr(ieee80211_get_DA(hdr)))
+               return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+
+       return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
+}
+
+static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar,
+                                 struct ieee80211_tx_info *info)
+{
+       if (info->control.vif)
+               return ath10k_vif_to_arvif(info->control.vif)->vdev_id;
+
+       if (ar->monitor_enabled)
+               return ar->monitor_vdev_id;
+
+       ath10k_warn("could not resolve vdev id\n");
+       return 0;
+}
+
 /*
  * Frames sent to the FW have to be in "Native Wifi" format.
  * Strip the QoS field from the 802.11 header.
@@ -1425,6 +1483,30 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
        skb_pull(skb, IEEE80211_QOS_CTL_LEN);
 }
 
+static void ath10k_tx_wep_key_work(struct work_struct *work)
+{
+       struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
+                                               wep_key_work);
+       int ret, keyidx = arvif->def_wep_key_newidx;
+
+       if (arvif->def_wep_key_idx == keyidx)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
+                  arvif->vdev_id, keyidx);
+
+       ret = ath10k_wmi_vdev_set_param(arvif->ar,
+                                       arvif->vdev_id,
+                                       arvif->ar->wmi.vdev_param->def_keyid,
+                                       keyidx);
+       if (ret) {
+               ath10k_warn("could not update wep keyidx (%d)\n", ret);
+               return;
+       }
+
+       arvif->def_wep_key_idx = keyidx;
+}
+
 static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1433,7 +1515,6 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
        struct ath10k *ar = arvif->ar;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ieee80211_key_conf *key = info->control.hw_key;
-       int ret;
 
        if (!ieee80211_has_protected(hdr->frame_control))
                return;
@@ -1445,21 +1526,14 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
            key->cipher != WLAN_CIPHER_SUITE_WEP104)
                return;
 
-       if (key->keyidx == arvif->def_wep_key_index)
-               return;
-
-       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d keyidx %d\n",
-                  arvif->vdev_id, key->keyidx);
-
-       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_DEF_KEYID,
-                                       key->keyidx);
-       if (ret) {
-               ath10k_warn("could not update wep keyidx (%d)\n", ret);
+       if (key->keyidx == arvif->def_wep_key_idx)
                return;
-       }
 
-       arvif->def_wep_key_index = key->keyidx;
+       /* FIXME: Most likely a few frames will be TXed with an old key. Simply
+        * queueing frames until key index is updated is not an option because
+        * sk_buff may need more processing to be done, e.g. offchannel */
+       arvif->def_wep_key_newidx = key->keyidx;
+       ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
 }
 
 static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
@@ -1489,7 +1563,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
 static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       int ret;
+       int ret = 0;
 
        if (ar->htt.target_version_major >= 3) {
                /* Since HTT 3.0 there is no separate mgmt tx command */
@@ -1497,16 +1571,32 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
                goto exit;
        }
 
-       if (ieee80211_is_mgmt(hdr->frame_control))
-               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
-       else if (ieee80211_is_nullfunc(hdr->frame_control))
+       if (ieee80211_is_mgmt(hdr->frame_control)) {
+               if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+                            ar->fw_features)) {
+                       if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >=
+                           ATH10K_MAX_NUM_MGMT_PENDING) {
+                               ath10k_warn("wmi mgmt_tx queue limit reached\n");
+                               ret = -EBUSY;
+                               goto exit;
+                       }
+
+                       skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb);
+                       ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
+               } else {
+                       ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
+               }
+       } else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+                            ar->fw_features) &&
+                  ieee80211_is_nullfunc(hdr->frame_control)) {
                /* FW does not report tx status properly for NullFunc frames
                 * unless they are sent through mgmt tx path. mac80211 sends
-                * those frames when it detects link/beacon loss and depends on
-                * the tx status to be correct. */
+                * those frames when it detects link/beacon loss and depends
+                * on the tx status to be correct. */
                ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
-       else
+       } else {
                ret = ath10k_htt_tx(&ar->htt, skb);
+       }
 
 exit:
        if (ret) {
@@ -1557,7 +1647,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
 
                hdr = (struct ieee80211_hdr *)skb->data;
                peer_addr = ieee80211_get_DA(hdr);
-               vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id;
+               vdev_id = ATH10K_SKB_CB(skb)->vdev_id;
 
                spin_lock_bh(&ar->data_lock);
                peer = ath10k_peer_find(ar, vdev_id, peer_addr);
@@ -1599,6 +1689,36 @@ void ath10k_offchan_tx_work(struct work_struct *work)
        }
 }
 
+void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar)
+{
+       struct sk_buff *skb;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
+               if (!skb)
+                       break;
+
+               ieee80211_free_txskb(ar->hw, skb);
+       }
+}
+
+void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work);
+       struct sk_buff *skb;
+       int ret;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
+               if (!skb)
+                       break;
+
+               ret = ath10k_wmi_mgmt_tx(ar, skb);
+               if (ret)
+                       ath10k_warn("wmi mgmt_tx failed (%d)\n", ret);
+       }
+}
+
 /************/
 /* Scanning */
 /************/
@@ -1722,16 +1842,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ath10k *ar = hw->priv;
-       struct ath10k_vif *arvif = NULL;
-       u32 vdev_id = 0;
-       u8 tid;
-
-       if (info->control.vif) {
-               arvif = ath10k_vif_to_arvif(info->control.vif);
-               vdev_id = arvif->vdev_id;
-       } else if (ar->monitor_enabled) {
-               vdev_id = ar->monitor_vdev_id;
-       }
+       u8 tid, vdev_id;
 
        /* We should disable CCK RATE due to P2P */
        if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
@@ -1739,14 +1850,8 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 
        /* we must calculate tid before we apply qos workaround
         * as we'd lose the qos control field */
-       tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
-       if (ieee80211_is_mgmt(hdr->frame_control)) {
-               tid = HTT_DATA_TX_EXT_TID_MGMT;
-       } else if (ieee80211_is_data_qos(hdr->frame_control) &&
-                  is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
-               u8 *qc = ieee80211_get_qos_ctl(hdr);
-               tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
-       }
+       tid = ath10k_tx_h_get_tid(hdr);
+       vdev_id = ath10k_tx_h_get_vdev_id(ar, info);
 
        /* it makes no sense to process injected frames like that */
        if (info->control.vif &&
@@ -1757,14 +1862,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
                ath10k_tx_h_seq_no(skb);
        }
 
+       ATH10K_SKB_CB(skb)->vdev_id = vdev_id;
        ATH10K_SKB_CB(skb)->htt.is_offchan = false;
-       ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
        ATH10K_SKB_CB(skb)->htt.tid = tid;
 
        if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
                spin_lock_bh(&ar->data_lock);
                ATH10K_SKB_CB(skb)->htt.is_offchan = true;
-               ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id;
+               ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
                spin_unlock_bh(&ar->data_lock);
 
                ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
@@ -1786,6 +1891,7 @@ void ath10k_halt(struct ath10k *ar)
 
        del_timer_sync(&ar->scan.timeout);
        ath10k_offchan_tx_purge(ar);
+       ath10k_mgmt_over_wmi_tx_purge(ar);
        ath10k_peer_cleanup_all(ar);
        ath10k_core_stop(ar);
        ath10k_hif_power_down(ar);
@@ -1832,12 +1938,12 @@ static int ath10k_start(struct ieee80211_hw *hw)
        else if (ar->state == ATH10K_STATE_RESTARTING)
                ar->state = ATH10K_STATE_RESTARTED;
 
-       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
+       ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1);
        if (ret)
                ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
                            ret);
 
-       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0);
+       ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 0);
        if (ret)
                ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
                            ret);
@@ -1862,32 +1968,29 @@ static void ath10k_stop(struct ieee80211_hw *hw)
        ar->state = ATH10K_STATE_OFF;
        mutex_unlock(&ar->conf_mutex);
 
+       ath10k_mgmt_over_wmi_tx_purge(ar);
+
        cancel_work_sync(&ar->offchan_tx_work);
+       cancel_work_sync(&ar->wmi_mgmt_tx_work);
        cancel_work_sync(&ar->restart_work);
 }
 
-static void ath10k_config_ps(struct ath10k *ar)
+static int ath10k_config_ps(struct ath10k *ar)
 {
-       struct ath10k_generic_iter ar_iter;
+       struct ath10k_vif *arvif;
+       int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       /* During HW reconfiguration mac80211 reports all interfaces that were
-        * running until reconfiguration was started. Since FW doesn't have any
-        * vdevs at this point we must not iterate over this interface list.
-        * This setting will be updated upon add_interface(). */
-       if (ar->state == ATH10K_STATE_RESTARTED)
-               return;
-
-       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-       ar_iter.ar = ar;
-
-       ieee80211_iterate_active_interfaces_atomic(
-               ar->hw, IEEE80211_IFACE_ITER_NORMAL,
-               ath10k_ps_iter, &ar_iter);
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               ret = ath10k_mac_vif_setup_ps(arvif);
+               if (ret) {
+                       ath10k_warn("could not setup powersave (%d)\n", ret);
+                       break;
+               }
+       }
 
-       if (ar_iter.ret)
-               ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+       return ret;
 }
 
 static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
@@ -1936,6 +2039,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        int ret = 0;
        u32 value;
        int bit;
+       u32 vdev_param;
 
        mutex_lock(&ar->conf_mutex);
 
@@ -1944,21 +2048,22 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        arvif->ar = ar;
        arvif->vif = vif;
 
+       INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
+
        if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
                ath10k_warn("Only one monitor interface allowed\n");
                ret = -EBUSY;
-               goto exit;
+               goto err;
        }
 
        bit = ffs(ar->free_vdev_map);
        if (bit == 0) {
                ret = -EBUSY;
-               goto exit;
+               goto err;
        }
 
        arvif->vdev_id = bit - 1;
        arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
-       ar->free_vdev_map &= ~(1 << arvif->vdev_id);
 
        if (ar->p2p)
                arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
@@ -1994,25 +2099,34 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                                     arvif->vdev_subtype, vif->addr);
        if (ret) {
                ath10k_warn("WMI vdev create failed: ret %d\n", ret);
-               goto exit;
+               goto err;
        }
 
-       ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID,
-                                       arvif->def_wep_key_index);
-       if (ret)
+       ar->free_vdev_map &= ~BIT(arvif->vdev_id);
+       list_add(&arvif->list, &ar->arvifs);
+
+       vdev_param = ar->wmi.vdev_param->def_keyid;
+       ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
+                                       arvif->def_wep_key_idx);
+       if (ret) {
                ath10k_warn("Failed to set default keyid: %d\n", ret);
+               goto err_vdev_delete;
+       }
 
-       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+       vdev_param = ar->wmi.vdev_param->tx_encap_type;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                        ATH10K_HW_TXRX_NATIVE_WIFI);
-       if (ret)
+       /* 10.X firmware does not support this VDEV parameter. Do not warn */
+       if (ret && ret != -EOPNOTSUPP) {
                ath10k_warn("Failed to set TX encap: %d\n", ret);
+               goto err_vdev_delete;
+       }
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
                if (ret) {
                        ath10k_warn("Failed to create peer for AP: %d\n", ret);
-                       goto exit;
+                       goto err_vdev_delete;
                }
        }
 
@@ -2021,39 +2135,62 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
-               if (ret)
+               if (ret) {
                        ath10k_warn("Failed to set RX wake policy: %d\n", ret);
+                       goto err_peer_delete;
+               }
 
                param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
                value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
-               if (ret)
+               if (ret) {
                        ath10k_warn("Failed to set TX wake thresh: %d\n", ret);
+                       goto err_peer_delete;
+               }
 
                param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
                value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
-               if (ret)
+               if (ret) {
                        ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
+                       goto err_peer_delete;
+               }
        }
 
        ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
-       if (ret)
+       if (ret) {
                ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
                            arvif->vdev_id, ret);
+               goto err_peer_delete;
+       }
 
        ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
-       if (ret)
+       if (ret) {
                ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
                            arvif->vdev_id, ret);
+               goto err_peer_delete;
+       }
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
                ar->monitor_present = true;
 
-exit:
        mutex_unlock(&ar->conf_mutex);
+       return 0;
+
+err_peer_delete:
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP)
+               ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr);
+
+err_vdev_delete:
+       ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+       ar->free_vdev_map &= ~BIT(arvif->vdev_id);
+       list_del(&arvif->list);
+
+err:
+       mutex_unlock(&ar->conf_mutex);
+
        return ret;
 }
 
@@ -2066,6 +2203,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&ar->conf_mutex);
 
+       cancel_work_sync(&arvif->wep_key_work);
+
        spin_lock_bh(&ar->data_lock);
        if (arvif->beacon) {
                dev_kfree_skb_any(arvif->beacon);
@@ -2074,6 +2213,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
        spin_unlock_bh(&ar->data_lock);
 
        ar->free_vdev_map |= 1 << (arvif->vdev_id);
+       list_del(&arvif->list);
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
@@ -2154,6 +2294,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret = 0;
+       u32 vdev_param, pdev_param;
 
        mutex_lock(&ar->conf_mutex);
 
@@ -2162,8 +2303,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BEACON_INT) {
                arvif->beacon_interval = info->beacon_int;
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_BEACON_INTERVAL,
+               vdev_param = ar->wmi.vdev_param->beacon_interval;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                arvif->beacon_interval);
                ath10k_dbg(ATH10K_DBG_MAC,
                           "mac vdev %d beacon_interval %d\n",
@@ -2179,8 +2320,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                           "vdev %d set beacon tx mode to staggered\n",
                           arvif->vdev_id);
 
-               ret = ath10k_wmi_pdev_set_param(ar,
-                                               WMI_PDEV_PARAM_BEACON_TX_MODE,
+               pdev_param = ar->wmi.pdev_param->beacon_tx_mode;
+               ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
                                                WMI_BEACON_STAGGERED_MODE);
                if (ret)
                        ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
@@ -2194,8 +2335,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                           "mac vdev %d dtim_period %d\n",
                           arvif->vdev_id, arvif->dtim_period);
 
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_DTIM_PERIOD,
+               vdev_param = ar->wmi.vdev_param->dtim_period;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                arvif->dtim_period);
                if (ret)
                        ath10k_warn("Failed to set dtim period for VDEV: %d\n",
@@ -2262,8 +2403,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
                           arvif->vdev_id, cts_prot);
 
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_ENABLE_RTSCTS,
+               vdev_param = ar->wmi.vdev_param->enable_rtscts;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                cts_prot);
                if (ret)
                        ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
@@ -2281,8 +2422,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n",
                           arvif->vdev_id, slottime);
 
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_SLOT_TIME,
+               vdev_param = ar->wmi.vdev_param->slot_time;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                slottime);
                if (ret)
                        ath10k_warn("Failed to set erp slot for VDEV: %d\n",
@@ -2300,8 +2441,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                           "mac vdev %d preamble %dn",
                           arvif->vdev_id, preamble);
 
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_PREAMBLE,
+               vdev_param = ar->wmi.vdev_param->preamble;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                preamble);
                if (ret)
                        ath10k_warn("Failed to set preamble for VDEV: %d\n",
@@ -2751,86 +2892,51 @@ static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw)
  * Both RTS and Fragmentation threshold are interface-specific
  * in ath10k, but device-specific in mac80211.
  */
-static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct ath10k_generic_iter *ar_iter = data;
-       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-       u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
-
-       lockdep_assert_held(&arvif->ar->conf_mutex);
-
-       /* During HW reconfiguration mac80211 reports all interfaces that were
-        * running until reconfiguration was started. Since FW doesn't have any
-        * vdevs at this point we must not iterate over this interface list.
-        * This setting will be updated upon add_interface(). */
-       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
-               return;
-
-       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts_threshold %d\n",
-                  arvif->vdev_id, rts);
-
-       ar_iter->ret = ath10k_mac_set_rts(arvif, rts);
-       if (ar_iter->ret)
-               ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
-                           arvif->vdev_id);
-}
 
 static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 {
-       struct ath10k_generic_iter ar_iter;
        struct ath10k *ar = hw->priv;
-
-       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-       ar_iter.ar = ar;
+       struct ath10k_vif *arvif;
+       int ret = 0;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces_atomic(
-               hw, IEEE80211_IFACE_ITER_NORMAL,
-               ath10k_set_rts_iter, &ar_iter);
-       mutex_unlock(&ar->conf_mutex);
-
-       return ar_iter.ret;
-}
-
-static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct ath10k_generic_iter *ar_iter = data;
-       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-       u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
-
-       lockdep_assert_held(&arvif->ar->conf_mutex);
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n",
+                          arvif->vdev_id, value);
 
-       /* During HW reconfiguration mac80211 reports all interfaces that were
-        * running until reconfiguration was started. Since FW doesn't have any
-        * vdevs at this point we must not iterate over this interface list.
-        * This setting will be updated upon add_interface(). */
-       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
-               return;
-
-       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation_threshold %d\n",
-                  arvif->vdev_id, frag);
+               ret = ath10k_mac_set_rts(arvif, value);
+               if (ret) {
+                       ath10k_warn("could not set rts threshold for vdev %d (%d)\n",
+                                   arvif->vdev_id, ret);
+                       break;
+               }
+       }
+       mutex_unlock(&ar->conf_mutex);
 
-       ar_iter->ret = ath10k_mac_set_frag(arvif, frag);
-       if (ar_iter->ret)
-               ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
-                           arvif->vdev_id);
+       return ret;
 }
 
 static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 {
-       struct ath10k_generic_iter ar_iter;
        struct ath10k *ar = hw->priv;
-
-       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-       ar_iter.ar = ar;
+       struct ath10k_vif *arvif;
+       int ret = 0;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces_atomic(
-               hw, IEEE80211_IFACE_ITER_NORMAL,
-               ath10k_set_frag_iter, &ar_iter);
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n",
+                          arvif->vdev_id, value);
+
+               ret = ath10k_mac_set_rts(arvif, value);
+               if (ret) {
+                       ath10k_warn("could not set fragmentation threshold for vdev %d (%d)\n",
+                                   arvif->vdev_id, ret);
+                       break;
+               }
+       }
        mutex_unlock(&ar->conf_mutex);
 
-       return ar_iter.ret;
+       return ret;
 }
 
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
index 6fce9bfb19a5f2340d2e41fe2f668b7e8c9f4dce..ba1021997b8fc8c9604884604c2facd57ab0a8d1 100644 (file)
@@ -34,6 +34,8 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
 void ath10k_reset_scan(unsigned long ptr);
 void ath10k_offchan_tx_purge(struct ath10k *ar);
 void ath10k_offchan_tx_work(struct work_struct *work);
+void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
+void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
 void ath10k_halt(struct ath10k *ar);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
index dff23d97bed065d984efac3c481ce2b06fa6e675..f8d59c7b90821a69b3401a7ec143bd6d8e0fc367 100644 (file)
@@ -720,18 +720,8 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
                        "ath10k tx: data: ",
                        nbuf->data, nbuf->len);
 
-       /* Make sure we have resources to handle this request */
-       spin_lock_bh(&pipe_info->pipe_lock);
-       if (!pipe_info->num_sends_allowed) {
-               ath10k_warn("Pipe: %d is full\n", pipe_id);
-               spin_unlock_bh(&pipe_info->pipe_lock);
-               return -ENOSR;
-       }
-       pipe_info->num_sends_allowed--;
-       spin_unlock_bh(&pipe_info->pipe_lock);
-
-       ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, transfer_id,
-                                     skb_cb->paddr, len, flags);
+       ret = ath10k_ce_send(ce_hdl, nbuf, skb_cb->paddr, len, transfer_id,
+                            flags);
        if (ret)
                ath10k_warn("CE send failed: %p\n", nbuf);
 
@@ -741,14 +731,7 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
 static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ath10k_pci_pipe *pipe_info = &(ar_pci->pipe_info[pipe]);
-       int ret;
-
-       spin_lock_bh(&pipe_info->pipe_lock);
-       ret = pipe_info->num_sends_allowed;
-       spin_unlock_bh(&pipe_info->pipe_lock);
-
-       return ret;
+       return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl);
 }
 
 static void ath10k_pci_hif_dump_area(struct ath10k *ar)
@@ -863,7 +846,6 @@ static int ath10k_pci_start_ce(struct ath10k *ar)
                                                   ath10k_pci_ce_send_done,
                                                   disable_interrupts);
                        completions += attr->src_nentries;
-                       pipe_info->num_sends_allowed = attr->src_nentries - 1;
                }
 
                if (attr->dest_nentries) {
@@ -1033,7 +1015,6 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
                 */
                spin_lock_bh(&compl->pipe_info->pipe_lock);
                list_add_tail(&compl->list, &compl->pipe_info->compl_free);
-               compl->pipe_info->num_sends_allowed += send_done;
                spin_unlock_bh(&compl->pipe_info->pipe_lock);
        }
 
index 7c49f6f96f703993b45188171b82200a5792d7a4..52fb7b9735714ea3f2dbf3ed03936ac3ae11f5c7 100644 (file)
@@ -178,9 +178,6 @@ struct ath10k_pci_pipe {
        /* List of free CE completion slots */
        struct list_head compl_free;
 
-       /* Limit the number of outstanding send requests. */
-       int num_sends_allowed;
-
        struct ath10k_pci *ar_pci;
        struct tasklet_struct intr;
 };
index 6803ead9b9cfd0c6f0be623e025e11f869394a4b..77238afbed759ae11baaaae0d8f168af16251625 100644 (file)
 #include "wmi.h"
 #include "mac.h"
 
+/* MAIN WMI cmd track */
+static struct wmi_cmd_map wmi_cmd_map = {
+       .init_cmdid = WMI_INIT_CMDID,
+       .start_scan_cmdid = WMI_START_SCAN_CMDID,
+       .stop_scan_cmdid = WMI_STOP_SCAN_CMDID,
+       .scan_chan_list_cmdid = WMI_SCAN_CHAN_LIST_CMDID,
+       .scan_sch_prio_tbl_cmdid = WMI_SCAN_SCH_PRIO_TBL_CMDID,
+       .pdev_set_regdomain_cmdid = WMI_PDEV_SET_REGDOMAIN_CMDID,
+       .pdev_set_channel_cmdid = WMI_PDEV_SET_CHANNEL_CMDID,
+       .pdev_set_param_cmdid = WMI_PDEV_SET_PARAM_CMDID,
+       .pdev_pktlog_enable_cmdid = WMI_PDEV_PKTLOG_ENABLE_CMDID,
+       .pdev_pktlog_disable_cmdid = WMI_PDEV_PKTLOG_DISABLE_CMDID,
+       .pdev_set_wmm_params_cmdid = WMI_PDEV_SET_WMM_PARAMS_CMDID,
+       .pdev_set_ht_cap_ie_cmdid = WMI_PDEV_SET_HT_CAP_IE_CMDID,
+       .pdev_set_vht_cap_ie_cmdid = WMI_PDEV_SET_VHT_CAP_IE_CMDID,
+       .pdev_set_dscp_tid_map_cmdid = WMI_PDEV_SET_DSCP_TID_MAP_CMDID,
+       .pdev_set_quiet_mode_cmdid = WMI_PDEV_SET_QUIET_MODE_CMDID,
+       .pdev_green_ap_ps_enable_cmdid = WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       .pdev_get_tpc_config_cmdid = WMI_PDEV_GET_TPC_CONFIG_CMDID,
+       .pdev_set_base_macaddr_cmdid = WMI_PDEV_SET_BASE_MACADDR_CMDID,
+       .vdev_create_cmdid = WMI_VDEV_CREATE_CMDID,
+       .vdev_delete_cmdid = WMI_VDEV_DELETE_CMDID,
+       .vdev_start_request_cmdid = WMI_VDEV_START_REQUEST_CMDID,
+       .vdev_restart_request_cmdid = WMI_VDEV_RESTART_REQUEST_CMDID,
+       .vdev_up_cmdid = WMI_VDEV_UP_CMDID,
+       .vdev_stop_cmdid = WMI_VDEV_STOP_CMDID,
+       .vdev_down_cmdid = WMI_VDEV_DOWN_CMDID,
+       .vdev_set_param_cmdid = WMI_VDEV_SET_PARAM_CMDID,
+       .vdev_install_key_cmdid = WMI_VDEV_INSTALL_KEY_CMDID,
+       .peer_create_cmdid = WMI_PEER_CREATE_CMDID,
+       .peer_delete_cmdid = WMI_PEER_DELETE_CMDID,
+       .peer_flush_tids_cmdid = WMI_PEER_FLUSH_TIDS_CMDID,
+       .peer_set_param_cmdid = WMI_PEER_SET_PARAM_CMDID,
+       .peer_assoc_cmdid = WMI_PEER_ASSOC_CMDID,
+       .peer_add_wds_entry_cmdid = WMI_PEER_ADD_WDS_ENTRY_CMDID,
+       .peer_remove_wds_entry_cmdid = WMI_PEER_REMOVE_WDS_ENTRY_CMDID,
+       .peer_mcast_group_cmdid = WMI_PEER_MCAST_GROUP_CMDID,
+       .bcn_tx_cmdid = WMI_BCN_TX_CMDID,
+       .pdev_send_bcn_cmdid = WMI_PDEV_SEND_BCN_CMDID,
+       .bcn_tmpl_cmdid = WMI_BCN_TMPL_CMDID,
+       .bcn_filter_rx_cmdid = WMI_BCN_FILTER_RX_CMDID,
+       .prb_req_filter_rx_cmdid = WMI_PRB_REQ_FILTER_RX_CMDID,
+       .mgmt_tx_cmdid = WMI_MGMT_TX_CMDID,
+       .prb_tmpl_cmdid = WMI_PRB_TMPL_CMDID,
+       .addba_clear_resp_cmdid = WMI_ADDBA_CLEAR_RESP_CMDID,
+       .addba_send_cmdid = WMI_ADDBA_SEND_CMDID,
+       .addba_status_cmdid = WMI_ADDBA_STATUS_CMDID,
+       .delba_send_cmdid = WMI_DELBA_SEND_CMDID,
+       .addba_set_resp_cmdid = WMI_ADDBA_SET_RESP_CMDID,
+       .send_singleamsdu_cmdid = WMI_SEND_SINGLEAMSDU_CMDID,
+       .sta_powersave_mode_cmdid = WMI_STA_POWERSAVE_MODE_CMDID,
+       .sta_powersave_param_cmdid = WMI_STA_POWERSAVE_PARAM_CMDID,
+       .sta_mimo_ps_mode_cmdid = WMI_STA_MIMO_PS_MODE_CMDID,
+       .pdev_dfs_enable_cmdid = WMI_PDEV_DFS_ENABLE_CMDID,
+       .pdev_dfs_disable_cmdid = WMI_PDEV_DFS_DISABLE_CMDID,
+       .roam_scan_mode = WMI_ROAM_SCAN_MODE,
+       .roam_scan_rssi_threshold = WMI_ROAM_SCAN_RSSI_THRESHOLD,
+       .roam_scan_period = WMI_ROAM_SCAN_PERIOD,
+       .roam_scan_rssi_change_threshold = WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       .roam_ap_profile = WMI_ROAM_AP_PROFILE,
+       .ofl_scan_add_ap_profile = WMI_ROAM_AP_PROFILE,
+       .ofl_scan_remove_ap_profile = WMI_OFL_SCAN_REMOVE_AP_PROFILE,
+       .ofl_scan_period = WMI_OFL_SCAN_PERIOD,
+       .p2p_dev_set_device_info = WMI_P2P_DEV_SET_DEVICE_INFO,
+       .p2p_dev_set_discoverability = WMI_P2P_DEV_SET_DISCOVERABILITY,
+       .p2p_go_set_beacon_ie = WMI_P2P_GO_SET_BEACON_IE,
+       .p2p_go_set_probe_resp_ie = WMI_P2P_GO_SET_PROBE_RESP_IE,
+       .p2p_set_vendor_ie_data_cmdid = WMI_P2P_SET_VENDOR_IE_DATA_CMDID,
+       .ap_ps_peer_param_cmdid = WMI_AP_PS_PEER_PARAM_CMDID,
+       .ap_ps_peer_uapsd_coex_cmdid = WMI_AP_PS_PEER_UAPSD_COEX_CMDID,
+       .peer_rate_retry_sched_cmdid = WMI_PEER_RATE_RETRY_SCHED_CMDID,
+       .wlan_profile_trigger_cmdid = WMI_WLAN_PROFILE_TRIGGER_CMDID,
+       .wlan_profile_set_hist_intvl_cmdid =
+                               WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       .wlan_profile_get_profile_data_cmdid =
+                               WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       .wlan_profile_enable_profile_id_cmdid =
+                               WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       .wlan_profile_list_profile_id_cmdid =
+                               WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+       .pdev_suspend_cmdid = WMI_PDEV_SUSPEND_CMDID,
+       .pdev_resume_cmdid = WMI_PDEV_RESUME_CMDID,
+       .add_bcn_filter_cmdid = WMI_ADD_BCN_FILTER_CMDID,
+       .rmv_bcn_filter_cmdid = WMI_RMV_BCN_FILTER_CMDID,
+       .wow_add_wake_pattern_cmdid = WMI_WOW_ADD_WAKE_PATTERN_CMDID,
+       .wow_del_wake_pattern_cmdid = WMI_WOW_DEL_WAKE_PATTERN_CMDID,
+       .wow_enable_disable_wake_event_cmdid =
+                               WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       .wow_enable_cmdid = WMI_WOW_ENABLE_CMDID,
+       .wow_hostwakeup_from_sleep_cmdid = WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+       .rtt_measreq_cmdid = WMI_RTT_MEASREQ_CMDID,
+       .rtt_tsf_cmdid = WMI_RTT_TSF_CMDID,
+       .vdev_spectral_scan_configure_cmdid =
+                               WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+       .vdev_spectral_scan_enable_cmdid = WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+       .request_stats_cmdid = WMI_REQUEST_STATS_CMDID,
+       .set_arp_ns_offload_cmdid = WMI_SET_ARP_NS_OFFLOAD_CMDID,
+       .network_list_offload_config_cmdid =
+                               WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID,
+       .gtk_offload_cmdid = WMI_GTK_OFFLOAD_CMDID,
+       .csa_offload_enable_cmdid = WMI_CSA_OFFLOAD_ENABLE_CMDID,
+       .csa_offload_chanswitch_cmdid = WMI_CSA_OFFLOAD_CHANSWITCH_CMDID,
+       .chatter_set_mode_cmdid = WMI_CHATTER_SET_MODE_CMDID,
+       .peer_tid_addba_cmdid = WMI_PEER_TID_ADDBA_CMDID,
+       .peer_tid_delba_cmdid = WMI_PEER_TID_DELBA_CMDID,
+       .sta_dtim_ps_method_cmdid = WMI_STA_DTIM_PS_METHOD_CMDID,
+       .sta_uapsd_auto_trig_cmdid = WMI_STA_UAPSD_AUTO_TRIG_CMDID,
+       .sta_keepalive_cmd = WMI_STA_KEEPALIVE_CMD,
+       .echo_cmdid = WMI_ECHO_CMDID,
+       .pdev_utf_cmdid = WMI_PDEV_UTF_CMDID,
+       .dbglog_cfg_cmdid = WMI_DBGLOG_CFG_CMDID,
+       .pdev_qvit_cmdid = WMI_PDEV_QVIT_CMDID,
+       .pdev_ftm_intg_cmdid = WMI_PDEV_FTM_INTG_CMDID,
+       .vdev_set_keepalive_cmdid = WMI_VDEV_SET_KEEPALIVE_CMDID,
+       .vdev_get_keepalive_cmdid = WMI_VDEV_GET_KEEPALIVE_CMDID,
+       .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID,
+       .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID,
+       .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID,
+};
+
+/* 10.X WMI cmd track */
+static struct wmi_cmd_map wmi_10x_cmd_map = {
+       .init_cmdid = WMI_10X_INIT_CMDID,
+       .start_scan_cmdid = WMI_10X_START_SCAN_CMDID,
+       .stop_scan_cmdid = WMI_10X_STOP_SCAN_CMDID,
+       .scan_chan_list_cmdid = WMI_10X_SCAN_CHAN_LIST_CMDID,
+       .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
+       .pdev_set_regdomain_cmdid = WMI_10X_PDEV_SET_REGDOMAIN_CMDID,
+       .pdev_set_channel_cmdid = WMI_10X_PDEV_SET_CHANNEL_CMDID,
+       .pdev_set_param_cmdid = WMI_10X_PDEV_SET_PARAM_CMDID,
+       .pdev_pktlog_enable_cmdid = WMI_10X_PDEV_PKTLOG_ENABLE_CMDID,
+       .pdev_pktlog_disable_cmdid = WMI_10X_PDEV_PKTLOG_DISABLE_CMDID,
+       .pdev_set_wmm_params_cmdid = WMI_10X_PDEV_SET_WMM_PARAMS_CMDID,
+       .pdev_set_ht_cap_ie_cmdid = WMI_10X_PDEV_SET_HT_CAP_IE_CMDID,
+       .pdev_set_vht_cap_ie_cmdid = WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID,
+       .pdev_set_dscp_tid_map_cmdid = WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID,
+       .pdev_set_quiet_mode_cmdid = WMI_10X_PDEV_SET_QUIET_MODE_CMDID,
+       .pdev_green_ap_ps_enable_cmdid = WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       .pdev_get_tpc_config_cmdid = WMI_10X_PDEV_GET_TPC_CONFIG_CMDID,
+       .pdev_set_base_macaddr_cmdid = WMI_10X_PDEV_SET_BASE_MACADDR_CMDID,
+       .vdev_create_cmdid = WMI_10X_VDEV_CREATE_CMDID,
+       .vdev_delete_cmdid = WMI_10X_VDEV_DELETE_CMDID,
+       .vdev_start_request_cmdid = WMI_10X_VDEV_START_REQUEST_CMDID,
+       .vdev_restart_request_cmdid = WMI_10X_VDEV_RESTART_REQUEST_CMDID,
+       .vdev_up_cmdid = WMI_10X_VDEV_UP_CMDID,
+       .vdev_stop_cmdid = WMI_10X_VDEV_STOP_CMDID,
+       .vdev_down_cmdid = WMI_10X_VDEV_DOWN_CMDID,
+       .vdev_set_param_cmdid = WMI_10X_VDEV_SET_PARAM_CMDID,
+       .vdev_install_key_cmdid = WMI_10X_VDEV_INSTALL_KEY_CMDID,
+       .peer_create_cmdid = WMI_10X_PEER_CREATE_CMDID,
+       .peer_delete_cmdid = WMI_10X_PEER_DELETE_CMDID,
+       .peer_flush_tids_cmdid = WMI_10X_PEER_FLUSH_TIDS_CMDID,
+       .peer_set_param_cmdid = WMI_10X_PEER_SET_PARAM_CMDID,
+       .peer_assoc_cmdid = WMI_10X_PEER_ASSOC_CMDID,
+       .peer_add_wds_entry_cmdid = WMI_10X_PEER_ADD_WDS_ENTRY_CMDID,
+       .peer_remove_wds_entry_cmdid = WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID,
+       .peer_mcast_group_cmdid = WMI_10X_PEER_MCAST_GROUP_CMDID,
+       .bcn_tx_cmdid = WMI_10X_BCN_TX_CMDID,
+       .pdev_send_bcn_cmdid = WMI_10X_PDEV_SEND_BCN_CMDID,
+       .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
+       .bcn_filter_rx_cmdid = WMI_10X_BCN_FILTER_RX_CMDID,
+       .prb_req_filter_rx_cmdid = WMI_10X_PRB_REQ_FILTER_RX_CMDID,
+       .mgmt_tx_cmdid = WMI_10X_MGMT_TX_CMDID,
+       .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
+       .addba_clear_resp_cmdid = WMI_10X_ADDBA_CLEAR_RESP_CMDID,
+       .addba_send_cmdid = WMI_10X_ADDBA_SEND_CMDID,
+       .addba_status_cmdid = WMI_10X_ADDBA_STATUS_CMDID,
+       .delba_send_cmdid = WMI_10X_DELBA_SEND_CMDID,
+       .addba_set_resp_cmdid = WMI_10X_ADDBA_SET_RESP_CMDID,
+       .send_singleamsdu_cmdid = WMI_10X_SEND_SINGLEAMSDU_CMDID,
+       .sta_powersave_mode_cmdid = WMI_10X_STA_POWERSAVE_MODE_CMDID,
+       .sta_powersave_param_cmdid = WMI_10X_STA_POWERSAVE_PARAM_CMDID,
+       .sta_mimo_ps_mode_cmdid = WMI_10X_STA_MIMO_PS_MODE_CMDID,
+       .pdev_dfs_enable_cmdid = WMI_10X_PDEV_DFS_ENABLE_CMDID,
+       .pdev_dfs_disable_cmdid = WMI_10X_PDEV_DFS_DISABLE_CMDID,
+       .roam_scan_mode = WMI_10X_ROAM_SCAN_MODE,
+       .roam_scan_rssi_threshold = WMI_10X_ROAM_SCAN_RSSI_THRESHOLD,
+       .roam_scan_period = WMI_10X_ROAM_SCAN_PERIOD,
+       .roam_scan_rssi_change_threshold =
+                               WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       .roam_ap_profile = WMI_10X_ROAM_AP_PROFILE,
+       .ofl_scan_add_ap_profile = WMI_10X_OFL_SCAN_ADD_AP_PROFILE,
+       .ofl_scan_remove_ap_profile = WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE,
+       .ofl_scan_period = WMI_10X_OFL_SCAN_PERIOD,
+       .p2p_dev_set_device_info = WMI_10X_P2P_DEV_SET_DEVICE_INFO,
+       .p2p_dev_set_discoverability = WMI_10X_P2P_DEV_SET_DISCOVERABILITY,
+       .p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE,
+       .p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE,
+       .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
+       .ap_ps_peer_param_cmdid = WMI_CMD_UNSUPPORTED,
+       .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
+       .peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID,
+       .wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID,
+       .wlan_profile_set_hist_intvl_cmdid =
+                               WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       .wlan_profile_get_profile_data_cmdid =
+                               WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       .wlan_profile_enable_profile_id_cmdid =
+                               WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       .wlan_profile_list_profile_id_cmdid =
+                               WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+       .pdev_suspend_cmdid = WMI_10X_PDEV_SUSPEND_CMDID,
+       .pdev_resume_cmdid = WMI_10X_PDEV_RESUME_CMDID,
+       .add_bcn_filter_cmdid = WMI_10X_ADD_BCN_FILTER_CMDID,
+       .rmv_bcn_filter_cmdid = WMI_10X_RMV_BCN_FILTER_CMDID,
+       .wow_add_wake_pattern_cmdid = WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID,
+       .wow_del_wake_pattern_cmdid = WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID,
+       .wow_enable_disable_wake_event_cmdid =
+                               WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       .wow_enable_cmdid = WMI_10X_WOW_ENABLE_CMDID,
+       .wow_hostwakeup_from_sleep_cmdid =
+                               WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+       .rtt_measreq_cmdid = WMI_10X_RTT_MEASREQ_CMDID,
+       .rtt_tsf_cmdid = WMI_10X_RTT_TSF_CMDID,
+       .vdev_spectral_scan_configure_cmdid =
+                               WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+       .vdev_spectral_scan_enable_cmdid =
+                               WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+       .request_stats_cmdid = WMI_10X_REQUEST_STATS_CMDID,
+       .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED,
+       .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED,
+       .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED,
+       .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED,
+       .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED,
+       .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED,
+       .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED,
+       .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED,
+       .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED,
+       .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED,
+       .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED,
+       .echo_cmdid = WMI_10X_ECHO_CMDID,
+       .pdev_utf_cmdid = WMI_10X_PDEV_UTF_CMDID,
+       .dbglog_cfg_cmdid = WMI_10X_DBGLOG_CFG_CMDID,
+       .pdev_qvit_cmdid = WMI_10X_PDEV_QVIT_CMDID,
+       .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED,
+       .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
+       .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
+       .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
+       .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID,
+       .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID,
+};
+
+/* MAIN WMI VDEV param map */
+static struct wmi_vdev_param_map wmi_vdev_param_map = {
+       .rts_threshold = WMI_VDEV_PARAM_RTS_THRESHOLD,
+       .fragmentation_threshold = WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       .beacon_interval = WMI_VDEV_PARAM_BEACON_INTERVAL,
+       .listen_interval = WMI_VDEV_PARAM_LISTEN_INTERVAL,
+       .multicast_rate = WMI_VDEV_PARAM_MULTICAST_RATE,
+       .mgmt_tx_rate = WMI_VDEV_PARAM_MGMT_TX_RATE,
+       .slot_time = WMI_VDEV_PARAM_SLOT_TIME,
+       .preamble = WMI_VDEV_PARAM_PREAMBLE,
+       .swba_time = WMI_VDEV_PARAM_SWBA_TIME,
+       .wmi_vdev_stats_update_period = WMI_VDEV_STATS_UPDATE_PERIOD,
+       .wmi_vdev_pwrsave_ageout_time = WMI_VDEV_PWRSAVE_AGEOUT_TIME,
+       .wmi_vdev_host_swba_interval = WMI_VDEV_HOST_SWBA_INTERVAL,
+       .dtim_period = WMI_VDEV_PARAM_DTIM_PERIOD,
+       .wmi_vdev_oc_scheduler_air_time_limit =
+                                       WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       .wds = WMI_VDEV_PARAM_WDS,
+       .atim_window = WMI_VDEV_PARAM_ATIM_WINDOW,
+       .bmiss_count_max = WMI_VDEV_PARAM_BMISS_COUNT_MAX,
+       .bmiss_first_bcnt = WMI_VDEV_PARAM_BMISS_FIRST_BCNT,
+       .bmiss_final_bcnt = WMI_VDEV_PARAM_BMISS_FINAL_BCNT,
+       .feature_wmm = WMI_VDEV_PARAM_FEATURE_WMM,
+       .chwidth = WMI_VDEV_PARAM_CHWIDTH,
+       .chextoffset = WMI_VDEV_PARAM_CHEXTOFFSET,
+       .disable_htprotection = WMI_VDEV_PARAM_DISABLE_HTPROTECTION,
+       .sta_quickkickout = WMI_VDEV_PARAM_STA_QUICKKICKOUT,
+       .mgmt_rate = WMI_VDEV_PARAM_MGMT_RATE,
+       .protection_mode = WMI_VDEV_PARAM_PROTECTION_MODE,
+       .fixed_rate = WMI_VDEV_PARAM_FIXED_RATE,
+       .sgi = WMI_VDEV_PARAM_SGI,
+       .ldpc = WMI_VDEV_PARAM_LDPC,
+       .tx_stbc = WMI_VDEV_PARAM_TX_STBC,
+       .rx_stbc = WMI_VDEV_PARAM_RX_STBC,
+       .intra_bss_fwd = WMI_VDEV_PARAM_INTRA_BSS_FWD,
+       .def_keyid = WMI_VDEV_PARAM_DEF_KEYID,
+       .nss = WMI_VDEV_PARAM_NSS,
+       .bcast_data_rate = WMI_VDEV_PARAM_BCAST_DATA_RATE,
+       .mcast_data_rate = WMI_VDEV_PARAM_MCAST_DATA_RATE,
+       .mcast_indicate = WMI_VDEV_PARAM_MCAST_INDICATE,
+       .dhcp_indicate = WMI_VDEV_PARAM_DHCP_INDICATE,
+       .unknown_dest_indicate = WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+       .ap_keepalive_min_idle_inactive_time_secs =
+                       WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+       .ap_keepalive_max_idle_inactive_time_secs =
+                       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+       .ap_keepalive_max_unresponsive_time_secs =
+                       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+       .ap_enable_nawds = WMI_VDEV_PARAM_AP_ENABLE_NAWDS,
+       .mcast2ucast_set = WMI_VDEV_PARAM_UNSUPPORTED,
+       .enable_rtscts = WMI_VDEV_PARAM_ENABLE_RTSCTS,
+       .txbf = WMI_VDEV_PARAM_TXBF,
+       .packet_powersave = WMI_VDEV_PARAM_PACKET_POWERSAVE,
+       .drop_unencry = WMI_VDEV_PARAM_DROP_UNENCRY,
+       .tx_encap_type = WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+       .ap_detect_out_of_sync_sleeping_sta_time_secs =
+                                       WMI_VDEV_PARAM_UNSUPPORTED,
+};
+
+/* 10.X WMI VDEV param map */
+static struct wmi_vdev_param_map wmi_10x_vdev_param_map = {
+       .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD,
+       .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
+       .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
+       .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE,
+       .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
+       .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME,
+       .preamble = WMI_10X_VDEV_PARAM_PREAMBLE,
+       .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME,
+       .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD,
+       .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
+       .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL,
+       .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD,
+       .wmi_vdev_oc_scheduler_air_time_limit =
+                               WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       .wds = WMI_10X_VDEV_PARAM_WDS,
+       .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW,
+       .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
+       .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
+       .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
+       .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM,
+       .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH,
+       .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET,
+       .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
+       .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
+       .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE,
+       .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE,
+       .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE,
+       .sgi = WMI_10X_VDEV_PARAM_SGI,
+       .ldpc = WMI_10X_VDEV_PARAM_LDPC,
+       .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC,
+       .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC,
+       .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
+       .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID,
+       .nss = WMI_10X_VDEV_PARAM_NSS,
+       .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
+       .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
+       .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE,
+       .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE,
+       .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+       .ap_keepalive_min_idle_inactive_time_secs =
+               WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+       .ap_keepalive_max_idle_inactive_time_secs =
+               WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+       .ap_keepalive_max_unresponsive_time_secs =
+               WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+       .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
+       .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
+       .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
+       .txbf = WMI_VDEV_PARAM_UNSUPPORTED,
+       .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED,
+       .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED,
+       .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED,
+       .ap_detect_out_of_sync_sleeping_sta_time_secs =
+               WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+};
+
+static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK,
+       .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK,
+       .txpower_limit2g = WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
+       .txpower_limit5g = WMI_PDEV_PARAM_TXPOWER_LIMIT5G,
+       .txpower_scale = WMI_PDEV_PARAM_TXPOWER_SCALE,
+       .beacon_gen_mode = WMI_PDEV_PARAM_BEACON_GEN_MODE,
+       .beacon_tx_mode = WMI_PDEV_PARAM_BEACON_TX_MODE,
+       .resmgr_offchan_mode = WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       .protection_mode = WMI_PDEV_PARAM_PROTECTION_MODE,
+       .dynamic_bw = WMI_PDEV_PARAM_DYNAMIC_BW,
+       .non_agg_sw_retry_th = WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       .agg_sw_retry_th = WMI_PDEV_PARAM_AGG_SW_RETRY_TH,
+       .sta_kickout_th = WMI_PDEV_PARAM_STA_KICKOUT_TH,
+       .ac_aggrsize_scaling = WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       .ltr_enable = WMI_PDEV_PARAM_LTR_ENABLE,
+       .ltr_ac_latency_be = WMI_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       .ltr_ac_latency_bk = WMI_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       .ltr_ac_latency_vi = WMI_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       .ltr_ac_latency_vo = WMI_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       .ltr_ac_latency_timeout = WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       .ltr_sleep_override = WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       .ltr_rx_override = WMI_PDEV_PARAM_LTR_RX_OVERRIDE,
+       .ltr_tx_activity_timeout = WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       .l1ss_enable = WMI_PDEV_PARAM_L1SS_ENABLE,
+       .dsleep_enable = WMI_PDEV_PARAM_DSLEEP_ENABLE,
+       .pcielp_txbuf_flush = WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
+       .pcielp_txbuf_watermark = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+       .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+       .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
+       .pdev_stats_update_period = WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       .vdev_stats_update_period = WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       .peer_stats_update_period = WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       .bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       .pmf_qos = WMI_PDEV_PARAM_PMF_QOS,
+       .arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
+       .arpdhcp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED,
+       .dcs = WMI_PDEV_PARAM_DCS,
+       .ani_enable = WMI_PDEV_PARAM_ANI_ENABLE,
+       .ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD,
+       .ani_listen_period = WMI_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       .ani_ofdm_level = WMI_PDEV_PARAM_ANI_OFDM_LEVEL,
+       .ani_cck_level = WMI_PDEV_PARAM_ANI_CCK_LEVEL,
+       .dyntxchain = WMI_PDEV_PARAM_DYNTXCHAIN,
+       .proxy_sta = WMI_PDEV_PARAM_PROXY_STA,
+       .idle_ps_config = WMI_PDEV_PARAM_IDLE_PS_CONFIG,
+       .power_gating_sleep = WMI_PDEV_PARAM_POWER_GATING_SLEEP,
+       .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED,
+       .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED,
+       .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED,
+};
+
+static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK,
+       .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
+       .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
+       .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
+       .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
+       .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
+       .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
+       .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE,
+       .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW,
+       .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
+       .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
+       .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE,
+       .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
+       .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE,
+       .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
+       .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED,
+       .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED,
+       .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED,
+       .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED,
+       .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       .bcnflt_stats_update_period =
+                               WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS,
+       .arp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arpdhcp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+       .dcs = WMI_10X_PDEV_PARAM_DCS,
+       .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE,
+       .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
+       .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
+       .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
+       .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN,
+       .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED,
+       .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED,
+       .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED,
+       .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
+       .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
+       .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
+};
+
 int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
 {
        int ret;
@@ -64,7 +529,7 @@ static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 }
 
 static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
-                                     enum wmi_cmd_id cmd_id)
+                                     u32 cmd_id)
 {
        struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
        struct wmi_cmd_hdr *cmd_hdr;
@@ -144,9 +609,17 @@ static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar)
 }
 
 static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
-                              enum wmi_cmd_id cmd_id)
+                              u32 cmd_id)
 {
-       int ret = -EINVAL;
+       int ret = -EOPNOTSUPP;
+
+       might_sleep();
+
+       if (cmd_id == WMI_CMD_UNSUPPORTED) {
+               ath10k_warn("wmi command %d is not supported by firmware\n",
+                           cmd_id);
+               return ret;
+       }
 
        wait_event_timeout(ar->wmi.tx_credits_wq, ({
                /* try to send pending beacons first. they take priority */
@@ -162,6 +635,57 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
        return ret;
 }
 
+int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
+{
+       int ret = 0;
+       struct wmi_mgmt_tx_cmd *cmd;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *wmi_skb;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       int len;
+       u16 fc;
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       fc = le16_to_cpu(hdr->frame_control);
+
+       if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
+               return -EINVAL;
+
+       len = sizeof(cmd->hdr) + skb->len;
+       len = round_up(len, 4);
+
+       wmi_skb = ath10k_wmi_alloc_skb(len);
+       if (!wmi_skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data;
+
+       cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id);
+       cmd->hdr.tx_rate = 0;
+       cmd->hdr.tx_power = 0;
+       cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len));
+
+       memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN);
+       memcpy(cmd->buf, skb->data, skb->len);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
+                  wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
+                  fc & IEEE80211_FCTL_STYPE);
+
+       /* Send the management frame buffer to the target */
+       ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       /* TODO: report tx status to mac80211 - temporary just ACK */
+       info->flags |= IEEE80211_TX_STAT_ACK;
+       ieee80211_tx_status_irqsafe(ar->hw, skb);
+
+       return ret;
+}
+
 static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data;
@@ -964,6 +1488,55 @@ static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
        ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
 }
 
+static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
+}
+
+static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
+                                     u32 num_units, u32 unit_len)
+{
+       dma_addr_t paddr;
+       u32 pool_size;
+       int idx = ar->wmi.num_mem_chunks;
+
+       pool_size = num_units * round_up(unit_len, 4);
+
+       if (!pool_size)
+               return -EINVAL;
+
+       ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev,
+                                                          pool_size,
+                                                          &paddr,
+                                                          GFP_ATOMIC);
+       if (!ar->wmi.mem_chunks[idx].vaddr) {
+               ath10k_warn("failed to allocate memory chunk\n");
+               return -ENOMEM;
+       }
+
+       memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size);
+
+       ar->wmi.mem_chunks[idx].paddr = paddr;
+       ar->wmi.mem_chunks[idx].len = pool_size;
+       ar->wmi.mem_chunks[idx].req_id = req_id;
+       ar->wmi.num_mem_chunks++;
+
+       return 0;
+}
+
 static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
                                              struct sk_buff *skb)
 {
@@ -988,7 +1561,8 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
        ar->phy_capability = __le32_to_cpu(ev->phy_capability);
        ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
 
-       if (ar->fw_version_build > 636)
+       /* only manually set fw features when not using FW IE format */
+       if (ar->fw_api == 1 && ar->fw_version_build > 636)
                set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features);
 
        if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
@@ -1035,6 +1609,108 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
        complete(&ar->wmi.service_ready);
 }
 
+static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar,
+                                                 struct sk_buff *skb)
+{
+       u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
+       int ret;
+       struct wmi_service_ready_event_10x *ev = (void *)skb->data;
+
+       if (skb->len < sizeof(*ev)) {
+               ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
+                           skb->len, sizeof(*ev));
+               return;
+       }
+
+       ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
+       ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
+       ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
+       ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+       ar->fw_version_major =
+               (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
+       ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+       ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+       ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
+
+       if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
+               ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n",
+                           ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
+               ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
+       }
+
+       ar->ath_common.regulatory.current_rd =
+               __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
+
+       ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap,
+                                     sizeof(ev->wmi_service_bitmap));
+
+       if (strlen(ar->hw->wiphy->fw_version) == 0) {
+               snprintf(ar->hw->wiphy->fw_version,
+                        sizeof(ar->hw->wiphy->fw_version),
+                        "%u.%u",
+                        ar->fw_version_major,
+                        ar->fw_version_minor);
+       }
+
+       num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs);
+
+       if (num_mem_reqs > ATH10K_MAX_MEM_REQS) {
+               ath10k_warn("requested memory chunks number (%d) exceeds the limit\n",
+                           num_mem_reqs);
+               return;
+       }
+
+       if (!num_mem_reqs)
+               goto exit;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n",
+                  num_mem_reqs);
+
+       for (i = 0; i < num_mem_reqs; ++i) {
+               req_id = __le32_to_cpu(ev->mem_reqs[i].req_id);
+               num_units = __le32_to_cpu(ev->mem_reqs[i].num_units);
+               unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size);
+               num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info);
+
+               if (num_unit_info & NUM_UNITS_IS_NUM_PEERS)
+                       /* number of units to allocate is number of
+                        * peers, 1 extra for self peer on target */
+                       /* this needs to be tied, host and target
+                        * can get out of sync */
+                       num_units = TARGET_10X_NUM_PEERS + 1;
+               else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS)
+                       num_units = TARGET_10X_NUM_VDEVS + 1;
+
+               ath10k_dbg(ATH10K_DBG_WMI,
+                          "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n",
+                          req_id,
+                          __le32_to_cpu(ev->mem_reqs[i].num_units),
+                          num_unit_info,
+                          unit_size,
+                          num_units);
+
+               ret = ath10k_wmi_alloc_host_mem(ar, req_id, num_units,
+                                               unit_size);
+               if (ret)
+                       return;
+       }
+
+exit:
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
+                  __le32_to_cpu(ev->sw_version),
+                  __le32_to_cpu(ev->abi_version),
+                  __le32_to_cpu(ev->phy_capability),
+                  __le32_to_cpu(ev->ht_cap_info),
+                  __le32_to_cpu(ev->vht_cap_info),
+                  __le32_to_cpu(ev->vht_supp_mcs),
+                  __le32_to_cpu(ev->sys_cap_info),
+                  __le32_to_cpu(ev->num_mem_reqs),
+                  __le32_to_cpu(ev->num_rf_chains));
+
+       complete(&ar->wmi.service_ready);
+}
+
 static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
@@ -1055,7 +1731,7 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
        return 0;
 }
 
-static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
+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;
@@ -1174,9 +1850,138 @@ static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
        dev_kfree_skb(skb);
 }
 
+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);
+
+       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) {
+       case WMI_10X_MGMT_RX_EVENTID:
+               ath10k_wmi_event_mgmt_rx(ar, skb);
+               /* mgmt_rx() owns the skb now! */
+               return;
+       case WMI_10X_SCAN_EVENTID:
+               ath10k_wmi_event_scan(ar, skb);
+               break;
+       case WMI_10X_CHAN_INFO_EVENTID:
+               ath10k_wmi_event_chan_info(ar, skb);
+               break;
+       case WMI_10X_ECHO_EVENTID:
+               ath10k_wmi_event_echo(ar, skb);
+               break;
+       case WMI_10X_DEBUG_MESG_EVENTID:
+               ath10k_wmi_event_debug_mesg(ar, skb);
+               break;
+       case WMI_10X_UPDATE_STATS_EVENTID:
+               ath10k_wmi_event_update_stats(ar, skb);
+               break;
+       case WMI_10X_VDEV_START_RESP_EVENTID:
+               ath10k_wmi_event_vdev_start_resp(ar, skb);
+               break;
+       case WMI_10X_VDEV_STOPPED_EVENTID:
+               ath10k_wmi_event_vdev_stopped(ar, skb);
+               break;
+       case WMI_10X_PEER_STA_KICKOUT_EVENTID:
+               ath10k_wmi_event_peer_sta_kickout(ar, skb);
+               break;
+       case WMI_10X_HOST_SWBA_EVENTID:
+               ath10k_wmi_event_host_swba(ar, skb);
+               break;
+       case WMI_10X_TBTTOFFSET_UPDATE_EVENTID:
+               ath10k_wmi_event_tbttoffset_update(ar, skb);
+               break;
+       case WMI_10X_PHYERR_EVENTID:
+               ath10k_wmi_event_phyerr(ar, skb);
+               break;
+       case WMI_10X_ROAM_EVENTID:
+               ath10k_wmi_event_roam(ar, skb);
+               break;
+       case WMI_10X_PROFILE_MATCH:
+               ath10k_wmi_event_profile_match(ar, skb);
+               break;
+       case WMI_10X_DEBUG_PRINT_EVENTID:
+               ath10k_wmi_event_debug_print(ar, skb);
+               break;
+       case WMI_10X_PDEV_QVIT_EVENTID:
+               ath10k_wmi_event_pdev_qvit(ar, skb);
+               break;
+       case WMI_10X_WLAN_PROFILE_DATA_EVENTID:
+               ath10k_wmi_event_wlan_profile_data(ar, skb);
+               break;
+       case WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_measurement_report(ar, skb);
+               break;
+       case WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_tsf_measurement_report(ar, skb);
+               break;
+       case WMI_10X_RTT_ERROR_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_error_report(ar, skb);
+               break;
+       case WMI_10X_WOW_WAKEUP_HOST_EVENTID:
+               ath10k_wmi_event_wow_wakeup_host(ar, skb);
+               break;
+       case WMI_10X_DCS_INTERFERENCE_EVENTID:
+               ath10k_wmi_event_dcs_interference(ar, skb);
+               break;
+       case WMI_10X_PDEV_TPC_CONFIG_EVENTID:
+               ath10k_wmi_event_pdev_tpc_config(ar, skb);
+               break;
+       case WMI_10X_INST_RSSI_STATS_EVENTID:
+               ath10k_wmi_event_inst_rssi_stats(ar, skb);
+               break;
+       case WMI_10X_VDEV_STANDBY_REQ_EVENTID:
+               ath10k_wmi_event_vdev_standby_req(ar, skb);
+               break;
+       case WMI_10X_VDEV_RESUME_REQ_EVENTID:
+               ath10k_wmi_event_vdev_resume_req(ar, skb);
+               break;
+       case WMI_10X_SERVICE_READY_EVENTID:
+               ath10k_wmi_10x_service_ready_event_rx(ar, skb);
+               break;
+       case WMI_10X_READY_EVENTID:
+               ath10k_wmi_ready_event_rx(ar, skb);
+               break;
+       default:
+               ath10k_warn("Unknown eventid: %d\n", id);
+               break;
+       }
+
+       dev_kfree_skb(skb);
+}
+
+
+static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               ath10k_wmi_10x_process_rx(ar, skb);
+       else
+               ath10k_wmi_main_process_rx(ar, skb);
+}
+
 /* WMI Initialization functions */
 int ath10k_wmi_attach(struct ath10k *ar)
 {
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+               ar->wmi.cmd = &wmi_10x_cmd_map;
+               ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
+               ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+       } else {
+               ar->wmi.cmd = &wmi_cmd_map;
+               ar->wmi.vdev_param = &wmi_vdev_param_map;
+               ar->wmi.pdev_param = &wmi_pdev_param_map;
+       }
+
        init_completion(&ar->wmi.service_ready);
        init_completion(&ar->wmi.unified_ready);
        init_waitqueue_head(&ar->wmi.tx_credits_wq);
@@ -1186,6 +1991,17 @@ int ath10k_wmi_attach(struct ath10k *ar)
 
 void ath10k_wmi_detach(struct ath10k *ar)
 {
+       int i;
+
+       /* free the host memory chunks requested by firmware */
+       for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+               dma_free_coherent(ar->dev,
+                                 ar->wmi.mem_chunks[i].len,
+                                 ar->wmi.mem_chunks[i].vaddr,
+                                 ar->wmi.mem_chunks[i].paddr);
+       }
+
+       ar->wmi.num_mem_chunks = 0;
 }
 
 int ath10k_wmi_connect_htc_service(struct ath10k *ar)
@@ -1237,7 +2053,8 @@ int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
                   "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
                   rd, rd2g, rd5g, ctl2g, ctl5g);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_set_regdomain_cmdid);
 }
 
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
@@ -1267,7 +2084,8 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
                   "wmi set channel mode %d freq %d\n",
                   arg->mode, arg->freq);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_set_channel_cmdid);
 }
 
 int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
@@ -1282,7 +2100,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
        cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
        cmd->suspend_opt = WMI_PDEV_SUSPEND;
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
 }
 
 int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
@@ -1293,15 +2111,19 @@ int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
        if (skb == NULL)
                return -ENOMEM;
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid);
 }
 
-int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
-                             u32 value)
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
 {
        struct wmi_pdev_set_param_cmd *cmd;
        struct sk_buff *skb;
 
+       if (id == WMI_PDEV_PARAM_UNSUPPORTED) {
+               ath10k_warn("pdev param %d not supported by firmware\n", id);
+               return -EOPNOTSUPP;
+       }
+
        skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
        if (!skb)
                return -ENOMEM;
@@ -1312,15 +2134,16 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
                   id, value);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
 }
 
-int ath10k_wmi_cmd_init(struct ath10k *ar)
+static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
 {
        struct wmi_init_cmd *cmd;
        struct sk_buff *buf;
        struct wmi_resource_config config = {};
-       u32 val;
+       u32 len, val;
+       int i;
 
        config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
        config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
@@ -1373,23 +2196,158 @@ int ath10k_wmi_cmd_init(struct ath10k *ar)
        config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC);
        config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES);
 
-       buf = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       len = sizeof(*cmd) +
+             (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
+
+       buf = ath10k_wmi_alloc_skb(len);
        if (!buf)
                return -ENOMEM;
 
        cmd = (struct wmi_init_cmd *)buf->data;
-       cmd->num_host_mem_chunks = 0;
+
+       if (ar->wmi.num_mem_chunks == 0) {
+               cmd->num_host_mem_chunks = 0;
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
+                  __cpu_to_le32(ar->wmi.num_mem_chunks));
+
+       cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+       for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+               cmd->host_mem_chunks[i].ptr =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+               cmd->host_mem_chunks[i].size =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].len);
+               cmd->host_mem_chunks[i].req_id =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+
+               ath10k_dbg(ATH10K_DBG_WMI,
+                          "wmi chunk %d len %d requested, addr 0x%x\n",
+                          i,
+                          cmd->host_mem_chunks[i].size,
+                          cmd->host_mem_chunks[i].ptr);
+       }
+out:
        memcpy(&cmd->resource_config, &config, sizeof(config));
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n");
-       return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID);
+       return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
+}
+
+static int ath10k_wmi_10x_cmd_init(struct ath10k *ar)
+{
+       struct wmi_init_cmd_10x *cmd;
+       struct sk_buff *buf;
+       struct wmi_resource_config_10x config = {};
+       u32 len, val;
+       int i;
+
+       config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
+       config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
+       config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS);
+       config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS);
+       config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT);
+       config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK);
+       config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK);
+       config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI);
+       config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE);
+
+       config.scan_max_pending_reqs =
+               __cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS);
+
+       config.bmiss_offload_max_vdev =
+               __cpu_to_le32(TARGET_10X_BMISS_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_vdev =
+               __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_ap_profiles =
+               __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES);
+
+       config.num_mcast_groups = __cpu_to_le32(TARGET_10X_NUM_MCAST_GROUPS);
+       config.num_mcast_table_elems =
+               __cpu_to_le32(TARGET_10X_NUM_MCAST_TABLE_ELEMS);
+
+       config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE);
+       config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE);
+       config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES);
+       config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE);
+       config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM);
+
+       val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
+       config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val);
+
+       config.vow_config = __cpu_to_le32(TARGET_10X_VOW_CONFIG);
+
+       config.num_msdu_desc = __cpu_to_le32(TARGET_10X_NUM_MSDU_DESC);
+       config.max_frag_entries = __cpu_to_le32(TARGET_10X_MAX_FRAG_ENTRIES);
+
+       len = sizeof(*cmd) +
+             (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
+
+       buf = ath10k_wmi_alloc_skb(len);
+       if (!buf)
+               return -ENOMEM;
+
+       cmd = (struct wmi_init_cmd_10x *)buf->data;
+
+       if (ar->wmi.num_mem_chunks == 0) {
+               cmd->num_host_mem_chunks = 0;
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
+                  __cpu_to_le32(ar->wmi.num_mem_chunks));
+
+       cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+       for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+               cmd->host_mem_chunks[i].ptr =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+               cmd->host_mem_chunks[i].size =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].len);
+               cmd->host_mem_chunks[i].req_id =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+
+               ath10k_dbg(ATH10K_DBG_WMI,
+                          "wmi chunk %d len %d requested, addr 0x%x\n",
+                          i,
+                          cmd->host_mem_chunks[i].size,
+                          cmd->host_mem_chunks[i].ptr);
+       }
+out:
+       memcpy(&cmd->resource_config, &config, sizeof(config));
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi init 10x\n");
+       return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
+}
+
+int ath10k_wmi_cmd_init(struct ath10k *ar)
+{
+       int ret;
+
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               ret = ath10k_wmi_10x_cmd_init(ar);
+       else
+               ret = ath10k_wmi_main_cmd_init(ar);
+
+       return ret;
 }
 
-static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg)
+static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar,
+                                         const struct wmi_start_scan_arg *arg)
 {
        int len;
 
-       len = sizeof(struct wmi_start_scan_cmd);
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               len = sizeof(struct wmi_start_scan_cmd_10x);
+       else
+               len = sizeof(struct wmi_start_scan_cmd);
 
        if (arg->ie_len) {
                if (!arg->ie)
@@ -1449,7 +2407,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
        int len = 0;
        int i;
 
-       len = ath10k_wmi_start_scan_calc_len(arg);
+       len = ath10k_wmi_start_scan_calc_len(ar, arg);
        if (len < 0)
                return len; /* len contains error code here */
 
@@ -1481,7 +2439,14 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
        cmd->scan_ctrl_flags    = __cpu_to_le32(arg->scan_ctrl_flags);
 
        /* TLV list starts after fields included in the struct */
-       off = sizeof(*cmd);
+       /* There's just one filed that differes the two start_scan
+        * structures - burst_duration, which we are not using btw,
+          no point to make the split here, just shift the buffer to fit with
+          given FW */
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               off = sizeof(struct wmi_start_scan_cmd_10x);
+       else
+               off = sizeof(struct wmi_start_scan_cmd);
 
        if (arg->n_channels) {
                channels = (void *)skb->data + off;
@@ -1543,7 +2508,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
        }
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n");
-       return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid);
 }
 
 void ath10k_wmi_start_scan_init(struct ath10k *ar,
@@ -1559,7 +2524,7 @@ void ath10k_wmi_start_scan_init(struct ath10k *ar,
        arg->repeat_probe_time = 0;
        arg->probe_spacing_time = 0;
        arg->idle_time = 0;
-       arg->max_scan_time = 5000;
+       arg->max_scan_time = 20000;
        arg->probe_delay = 5;
        arg->notify_scan_events = WMI_SCAN_EVENT_STARTED
                | WMI_SCAN_EVENT_COMPLETED
@@ -1603,7 +2568,7 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
                   arg->req_id, arg->req_type, arg->u.scan_id);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid);
 }
 
 int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
@@ -1628,7 +2593,7 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
                   "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
                   vdev_id, type, subtype, macaddr);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid);
 }
 
 int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
@@ -1646,20 +2611,20 @@ int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
        ath10k_dbg(ATH10K_DBG_WMI,
                   "WMI vdev delete id %d\n", vdev_id);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid);
 }
 
 static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
                                const struct wmi_vdev_start_request_arg *arg,
-                               enum wmi_cmd_id cmd_id)
+                               u32 cmd_id)
 {
        struct wmi_vdev_start_request_cmd *cmd;
        struct sk_buff *skb;
        const char *cmdname;
        u32 flags = 0;
 
-       if (cmd_id != WMI_VDEV_START_REQUEST_CMDID &&
-           cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID)
+       if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
+           cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
                return -EINVAL;
        if (WARN_ON(arg->ssid && arg->ssid_len == 0))
                return -EINVAL;
@@ -1668,9 +2633,9 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
        if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
                return -EINVAL;
 
-       if (cmd_id == WMI_VDEV_START_REQUEST_CMDID)
+       if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid)
                cmdname = "start";
-       else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID)
+       else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid)
                cmdname = "restart";
        else
                return -EINVAL; /* should not happen, we already check cmd_id */
@@ -1721,15 +2686,17 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
 int ath10k_wmi_vdev_start(struct ath10k *ar,
                          const struct wmi_vdev_start_request_arg *arg)
 {
-       return ath10k_wmi_vdev_start_restart(ar, arg,
-                                            WMI_VDEV_START_REQUEST_CMDID);
+       u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid;
+
+       return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
 }
 
 int ath10k_wmi_vdev_restart(struct ath10k *ar,
                     const struct wmi_vdev_start_request_arg *arg)
 {
-       return ath10k_wmi_vdev_start_restart(ar, arg,
-                                            WMI_VDEV_RESTART_REQUEST_CMDID);
+       u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid;
+
+       return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
 }
 
 int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
@@ -1746,7 +2713,7 @@ int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid);
 }
 
 int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
@@ -1767,7 +2734,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
                   "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
                   vdev_id, aid, bssid);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid);
 }
 
 int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
@@ -1785,15 +2752,22 @@ int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi mgmt vdev down id 0x%x\n", vdev_id);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid);
 }
 
 int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
-                             enum wmi_vdev_param param_id, u32 param_value)
+                             u32 param_id, u32 param_value)
 {
        struct wmi_vdev_set_param_cmd *cmd;
        struct sk_buff *skb;
 
+       if (param_id == WMI_VDEV_PARAM_UNSUPPORTED) {
+               ath10k_dbg(ATH10K_DBG_WMI,
+                          "vdev param %d not supported by firmware\n",
+                           param_id);
+               return -EOPNOTSUPP;
+       }
+
        skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
        if (!skb)
                return -ENOMEM;
@@ -1807,7 +2781,7 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
                   "wmi vdev id 0x%x set param %d value %d\n",
                   vdev_id, param_id, param_value);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid);
 }
 
 int ath10k_wmi_vdev_install_key(struct ath10k *ar,
@@ -1842,7 +2816,8 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi vdev install key idx %d cipher %d len %d\n",
                   arg->key_idx, arg->key_cipher, arg->key_len);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->vdev_install_key_cmdid);
 }
 
 int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
@@ -1862,7 +2837,7 @@ int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi peer create vdev_id %d peer_addr %pM\n",
                   vdev_id, peer_addr);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid);
 }
 
 int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
@@ -1882,7 +2857,7 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi peer delete vdev_id %d peer_addr %pM\n",
                   vdev_id, peer_addr);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid);
 }
 
 int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
@@ -1903,7 +2878,7 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
                   vdev_id, peer_addr, tid_bitmap);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid);
 }
 
 int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
@@ -1927,7 +2902,7 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
                   "wmi vdev %d peer 0x%pM set param %d value %d\n",
                   vdev_id, peer_addr, param_id, param_value);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid);
 }
 
 int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
@@ -1948,7 +2923,8 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
                   "wmi set powersave id 0x%x mode %d\n",
                   vdev_id, psmode);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->sta_powersave_mode_cmdid);
 }
 
 int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
@@ -1970,7 +2946,8 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi sta ps param vdev_id 0x%x param %d value %d\n",
                   vdev_id, param_id, value);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->sta_powersave_param_cmdid);
 }
 
 int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
@@ -1996,7 +2973,8 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
                   "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
                   vdev_id, param_id, value, mac);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->ap_ps_peer_param_cmdid);
 }
 
 int ath10k_wmi_scan_chan_list(struct ath10k *ar,
@@ -2049,7 +3027,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar,
                ci->flags            |= __cpu_to_le32(flags);
        }
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
 }
 
 int ath10k_wmi_peer_assoc(struct ath10k *ar,
@@ -2108,7 +3086,7 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi peer assoc vdev %d addr %pM\n",
                   arg->vdev_id, arg->addr);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
 }
 
 int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
@@ -2128,7 +3106,7 @@ int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
        cmd->hdr.bcn_len  = __cpu_to_le32(arg->bcn_len);
        memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
 
-       return ath10k_wmi_cmd_send_nowait(ar, skb, WMI_BCN_TX_CMDID);
+       return ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid);
 }
 
 static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
@@ -2159,7 +3137,8 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
        ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_set_wmm_params_cmdid);
 }
 
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
@@ -2175,7 +3154,7 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
        cmd->stats_id = __cpu_to_le32(stats_id);
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid);
 }
 
 int ath10k_wmi_force_fw_hang(struct ath10k *ar,
@@ -2194,5 +3173,5 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar,
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
                   type, delay_ms);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
 }
index 2c52c23107dde732b8285791e249cd6bb9a7f74d..78c991aec7f93d6419cc62d1b09ea2f63033b627 100644 (file)
@@ -208,6 +208,118 @@ struct wmi_mac_addr {
        (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \
        } while (0)
 
+struct wmi_cmd_map {
+       u32 init_cmdid;
+       u32 start_scan_cmdid;
+       u32 stop_scan_cmdid;
+       u32 scan_chan_list_cmdid;
+       u32 scan_sch_prio_tbl_cmdid;
+       u32 pdev_set_regdomain_cmdid;
+       u32 pdev_set_channel_cmdid;
+       u32 pdev_set_param_cmdid;
+       u32 pdev_pktlog_enable_cmdid;
+       u32 pdev_pktlog_disable_cmdid;
+       u32 pdev_set_wmm_params_cmdid;
+       u32 pdev_set_ht_cap_ie_cmdid;
+       u32 pdev_set_vht_cap_ie_cmdid;
+       u32 pdev_set_dscp_tid_map_cmdid;
+       u32 pdev_set_quiet_mode_cmdid;
+       u32 pdev_green_ap_ps_enable_cmdid;
+       u32 pdev_get_tpc_config_cmdid;
+       u32 pdev_set_base_macaddr_cmdid;
+       u32 vdev_create_cmdid;
+       u32 vdev_delete_cmdid;
+       u32 vdev_start_request_cmdid;
+       u32 vdev_restart_request_cmdid;
+       u32 vdev_up_cmdid;
+       u32 vdev_stop_cmdid;
+       u32 vdev_down_cmdid;
+       u32 vdev_set_param_cmdid;
+       u32 vdev_install_key_cmdid;
+       u32 peer_create_cmdid;
+       u32 peer_delete_cmdid;
+       u32 peer_flush_tids_cmdid;
+       u32 peer_set_param_cmdid;
+       u32 peer_assoc_cmdid;
+       u32 peer_add_wds_entry_cmdid;
+       u32 peer_remove_wds_entry_cmdid;
+       u32 peer_mcast_group_cmdid;
+       u32 bcn_tx_cmdid;
+       u32 pdev_send_bcn_cmdid;
+       u32 bcn_tmpl_cmdid;
+       u32 bcn_filter_rx_cmdid;
+       u32 prb_req_filter_rx_cmdid;
+       u32 mgmt_tx_cmdid;
+       u32 prb_tmpl_cmdid;
+       u32 addba_clear_resp_cmdid;
+       u32 addba_send_cmdid;
+       u32 addba_status_cmdid;
+       u32 delba_send_cmdid;
+       u32 addba_set_resp_cmdid;
+       u32 send_singleamsdu_cmdid;
+       u32 sta_powersave_mode_cmdid;
+       u32 sta_powersave_param_cmdid;
+       u32 sta_mimo_ps_mode_cmdid;
+       u32 pdev_dfs_enable_cmdid;
+       u32 pdev_dfs_disable_cmdid;
+       u32 roam_scan_mode;
+       u32 roam_scan_rssi_threshold;
+       u32 roam_scan_period;
+       u32 roam_scan_rssi_change_threshold;
+       u32 roam_ap_profile;
+       u32 ofl_scan_add_ap_profile;
+       u32 ofl_scan_remove_ap_profile;
+       u32 ofl_scan_period;
+       u32 p2p_dev_set_device_info;
+       u32 p2p_dev_set_discoverability;
+       u32 p2p_go_set_beacon_ie;
+       u32 p2p_go_set_probe_resp_ie;
+       u32 p2p_set_vendor_ie_data_cmdid;
+       u32 ap_ps_peer_param_cmdid;
+       u32 ap_ps_peer_uapsd_coex_cmdid;
+       u32 peer_rate_retry_sched_cmdid;
+       u32 wlan_profile_trigger_cmdid;
+       u32 wlan_profile_set_hist_intvl_cmdid;
+       u32 wlan_profile_get_profile_data_cmdid;
+       u32 wlan_profile_enable_profile_id_cmdid;
+       u32 wlan_profile_list_profile_id_cmdid;
+       u32 pdev_suspend_cmdid;
+       u32 pdev_resume_cmdid;
+       u32 add_bcn_filter_cmdid;
+       u32 rmv_bcn_filter_cmdid;
+       u32 wow_add_wake_pattern_cmdid;
+       u32 wow_del_wake_pattern_cmdid;
+       u32 wow_enable_disable_wake_event_cmdid;
+       u32 wow_enable_cmdid;
+       u32 wow_hostwakeup_from_sleep_cmdid;
+       u32 rtt_measreq_cmdid;
+       u32 rtt_tsf_cmdid;
+       u32 vdev_spectral_scan_configure_cmdid;
+       u32 vdev_spectral_scan_enable_cmdid;
+       u32 request_stats_cmdid;
+       u32 set_arp_ns_offload_cmdid;
+       u32 network_list_offload_config_cmdid;
+       u32 gtk_offload_cmdid;
+       u32 csa_offload_enable_cmdid;
+       u32 csa_offload_chanswitch_cmdid;
+       u32 chatter_set_mode_cmdid;
+       u32 peer_tid_addba_cmdid;
+       u32 peer_tid_delba_cmdid;
+       u32 sta_dtim_ps_method_cmdid;
+       u32 sta_uapsd_auto_trig_cmdid;
+       u32 sta_keepalive_cmd;
+       u32 echo_cmdid;
+       u32 pdev_utf_cmdid;
+       u32 dbglog_cfg_cmdid;
+       u32 pdev_qvit_cmdid;
+       u32 pdev_ftm_intg_cmdid;
+       u32 vdev_set_keepalive_cmdid;
+       u32 vdev_get_keepalive_cmdid;
+       u32 force_fw_hang_cmdid;
+       u32 gpio_config_cmdid;
+       u32 gpio_output_cmdid;
+};
+
 /*
  * wmi command groups.
  */
@@ -247,7 +359,9 @@ enum wmi_cmd_group {
 #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1)
 #define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1)
 
-/* Command IDs and commande events. */
+#define WMI_CMD_UNSUPPORTED 0
+
+/* Command IDs and command events for MAIN FW. */
 enum wmi_cmd_id {
        WMI_INIT_CMDID = 0x1,
 
@@ -488,6 +602,217 @@ enum wmi_event_id {
        WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO),
 };
 
+/* Command IDs and command events for 10.X firmware */
+enum wmi_10x_cmd_id {
+       WMI_10X_START_CMDID = 0x9000,
+       WMI_10X_END_CMDID = 0x9FFF,
+
+       /* initialize the wlan sub system */
+       WMI_10X_INIT_CMDID,
+
+       /* Scan specific commands */
+
+       WMI_10X_START_SCAN_CMDID = WMI_10X_START_CMDID,
+       WMI_10X_STOP_SCAN_CMDID,
+       WMI_10X_SCAN_CHAN_LIST_CMDID,
+       WMI_10X_ECHO_CMDID,
+
+       /* PDEV(physical device) specific commands */
+       WMI_10X_PDEV_SET_REGDOMAIN_CMDID,
+       WMI_10X_PDEV_SET_CHANNEL_CMDID,
+       WMI_10X_PDEV_SET_PARAM_CMDID,
+       WMI_10X_PDEV_PKTLOG_ENABLE_CMDID,
+       WMI_10X_PDEV_PKTLOG_DISABLE_CMDID,
+       WMI_10X_PDEV_SET_WMM_PARAMS_CMDID,
+       WMI_10X_PDEV_SET_HT_CAP_IE_CMDID,
+       WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID,
+       WMI_10X_PDEV_SET_BASE_MACADDR_CMDID,
+       WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID,
+       WMI_10X_PDEV_SET_QUIET_MODE_CMDID,
+       WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       WMI_10X_PDEV_GET_TPC_CONFIG_CMDID,
+
+       /* VDEV(virtual device) specific commands */
+       WMI_10X_VDEV_CREATE_CMDID,
+       WMI_10X_VDEV_DELETE_CMDID,
+       WMI_10X_VDEV_START_REQUEST_CMDID,
+       WMI_10X_VDEV_RESTART_REQUEST_CMDID,
+       WMI_10X_VDEV_UP_CMDID,
+       WMI_10X_VDEV_STOP_CMDID,
+       WMI_10X_VDEV_DOWN_CMDID,
+       WMI_10X_VDEV_STANDBY_RESPONSE_CMDID,
+       WMI_10X_VDEV_RESUME_RESPONSE_CMDID,
+       WMI_10X_VDEV_SET_PARAM_CMDID,
+       WMI_10X_VDEV_INSTALL_KEY_CMDID,
+
+       /* peer specific commands */
+       WMI_10X_PEER_CREATE_CMDID,
+       WMI_10X_PEER_DELETE_CMDID,
+       WMI_10X_PEER_FLUSH_TIDS_CMDID,
+       WMI_10X_PEER_SET_PARAM_CMDID,
+       WMI_10X_PEER_ASSOC_CMDID,
+       WMI_10X_PEER_ADD_WDS_ENTRY_CMDID,
+       WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID,
+       WMI_10X_PEER_MCAST_GROUP_CMDID,
+
+       /* beacon/management specific commands */
+
+       WMI_10X_BCN_TX_CMDID,
+       WMI_10X_BCN_PRB_TMPL_CMDID,
+       WMI_10X_BCN_FILTER_RX_CMDID,
+       WMI_10X_PRB_REQ_FILTER_RX_CMDID,
+       WMI_10X_MGMT_TX_CMDID,
+
+       /* commands to directly control ba negotiation directly from host. */
+       WMI_10X_ADDBA_CLEAR_RESP_CMDID,
+       WMI_10X_ADDBA_SEND_CMDID,
+       WMI_10X_ADDBA_STATUS_CMDID,
+       WMI_10X_DELBA_SEND_CMDID,
+       WMI_10X_ADDBA_SET_RESP_CMDID,
+       WMI_10X_SEND_SINGLEAMSDU_CMDID,
+
+       /* Station power save specific config */
+       WMI_10X_STA_POWERSAVE_MODE_CMDID,
+       WMI_10X_STA_POWERSAVE_PARAM_CMDID,
+       WMI_10X_STA_MIMO_PS_MODE_CMDID,
+
+       /* set debug log config */
+       WMI_10X_DBGLOG_CFG_CMDID,
+
+       /* DFS-specific commands */
+       WMI_10X_PDEV_DFS_ENABLE_CMDID,
+       WMI_10X_PDEV_DFS_DISABLE_CMDID,
+
+       /* QVIT specific command id */
+       WMI_10X_PDEV_QVIT_CMDID,
+
+       /* Offload Scan and Roaming related  commands */
+       WMI_10X_ROAM_SCAN_MODE,
+       WMI_10X_ROAM_SCAN_RSSI_THRESHOLD,
+       WMI_10X_ROAM_SCAN_PERIOD,
+       WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       WMI_10X_ROAM_AP_PROFILE,
+       WMI_10X_OFL_SCAN_ADD_AP_PROFILE,
+       WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE,
+       WMI_10X_OFL_SCAN_PERIOD,
+
+       /* P2P specific commands */
+       WMI_10X_P2P_DEV_SET_DEVICE_INFO,
+       WMI_10X_P2P_DEV_SET_DISCOVERABILITY,
+       WMI_10X_P2P_GO_SET_BEACON_IE,
+       WMI_10X_P2P_GO_SET_PROBE_RESP_IE,
+
+       /* AP power save specific config */
+       WMI_10X_AP_PS_PEER_PARAM_CMDID,
+       WMI_10X_AP_PS_PEER_UAPSD_COEX_CMDID,
+
+       /* Rate-control specific commands */
+       WMI_10X_PEER_RATE_RETRY_SCHED_CMDID,
+
+       /* WLAN Profiling commands. */
+       WMI_10X_WLAN_PROFILE_TRIGGER_CMDID,
+       WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+
+       /* Suspend resume command Ids */
+       WMI_10X_PDEV_SUSPEND_CMDID,
+       WMI_10X_PDEV_RESUME_CMDID,
+
+       /* Beacon filter commands */
+       WMI_10X_ADD_BCN_FILTER_CMDID,
+       WMI_10X_RMV_BCN_FILTER_CMDID,
+
+       /* WOW Specific WMI commands*/
+       WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID,
+       WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID,
+       WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       WMI_10X_WOW_ENABLE_CMDID,
+       WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+
+       /* RTT measurement related cmd */
+       WMI_10X_RTT_MEASREQ_CMDID,
+       WMI_10X_RTT_TSF_CMDID,
+
+       /* transmit beacon by value */
+       WMI_10X_PDEV_SEND_BCN_CMDID,
+
+       /* F/W stats */
+       WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+       WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+       WMI_10X_REQUEST_STATS_CMDID,
+
+       /* GPIO Configuration */
+       WMI_10X_GPIO_CONFIG_CMDID,
+       WMI_10X_GPIO_OUTPUT_CMDID,
+
+       WMI_10X_PDEV_UTF_CMDID = WMI_10X_END_CMDID - 1,
+};
+
+enum wmi_10x_event_id {
+       WMI_10X_SERVICE_READY_EVENTID = 0x8000,
+       WMI_10X_READY_EVENTID,
+       WMI_10X_START_EVENTID = 0x9000,
+       WMI_10X_END_EVENTID = 0x9FFF,
+
+       /* Scan specific events */
+       WMI_10X_SCAN_EVENTID = WMI_10X_START_EVENTID,
+       WMI_10X_ECHO_EVENTID,
+       WMI_10X_DEBUG_MESG_EVENTID,
+       WMI_10X_UPDATE_STATS_EVENTID,
+
+       /* Instantaneous RSSI event */
+       WMI_10X_INST_RSSI_STATS_EVENTID,
+
+       /* VDEV specific events */
+       WMI_10X_VDEV_START_RESP_EVENTID,
+       WMI_10X_VDEV_STANDBY_REQ_EVENTID,
+       WMI_10X_VDEV_RESUME_REQ_EVENTID,
+       WMI_10X_VDEV_STOPPED_EVENTID,
+
+       /* peer  specific events */
+       WMI_10X_PEER_STA_KICKOUT_EVENTID,
+
+       /* beacon/mgmt specific events */
+       WMI_10X_HOST_SWBA_EVENTID,
+       WMI_10X_TBTTOFFSET_UPDATE_EVENTID,
+       WMI_10X_MGMT_RX_EVENTID,
+
+       /* Channel stats event */
+       WMI_10X_CHAN_INFO_EVENTID,
+
+       /* PHY Error specific WMI event */
+       WMI_10X_PHYERR_EVENTID,
+
+       /* Roam event to trigger roaming on host */
+       WMI_10X_ROAM_EVENTID,
+
+       /* matching AP found from list of profiles */
+       WMI_10X_PROFILE_MATCH,
+
+       /* debug print message used for tracing FW code while debugging */
+       WMI_10X_DEBUG_PRINT_EVENTID,
+       /* VI spoecific event */
+       WMI_10X_PDEV_QVIT_EVENTID,
+       /* FW code profile data in response to profile request */
+       WMI_10X_WLAN_PROFILE_DATA_EVENTID,
+
+       /*RTT related event ID*/
+       WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID,
+       WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID,
+       WMI_10X_RTT_ERROR_REPORT_EVENTID,
+
+       WMI_10X_WOW_WAKEUP_HOST_EVENTID,
+       WMI_10X_DCS_INTERFERENCE_EVENTID,
+
+       /* TPC config for the current operating channel */
+       WMI_10X_PDEV_TPC_CONFIG_EVENTID,
+
+       WMI_10X_GPIO_INPUT_EVENTID,
+       WMI_10X_PDEV_UTF_EVENTID = WMI_10X_END_EVENTID-1,
+};
+
 enum wmi_phy_mode {
        MODE_11A        = 0,   /* 11a Mode */
        MODE_11G        = 1,   /* 11b/g Mode */
@@ -805,6 +1130,46 @@ struct wmi_service_ready_event {
        struct wlan_host_mem_req mem_reqs[1];
 } __packed;
 
+/* This is the definition from 10.X firmware branch */
+struct wmi_service_ready_event_10x {
+       __le32 sw_version;
+       __le32 abi_version;
+
+       /* WMI_PHY_CAPABILITY */
+       __le32 phy_capability;
+
+       /* Maximum number of frag table entries that SW will populate less 1 */
+       __le32 max_frag_entry;
+       __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+       __le32 num_rf_chains;
+
+       /*
+        * The following field is only valid for service type
+        * WMI_SERVICE_11AC
+        */
+       __le32 ht_cap_info; /* WMI HT Capability */
+       __le32 vht_cap_info; /* VHT capability info field of 802.11ac */
+       __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */
+       __le32 hw_min_tx_power;
+       __le32 hw_max_tx_power;
+
+       struct hal_reg_capabilities hal_reg_capabilities;
+
+       __le32 sys_cap_info;
+       __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */
+
+       /*
+        * request to host to allocate a chuck of memory and pss it down to FW
+        * via WM_INIT. FW uses this as FW extesnsion memory for saving its
+        * data structures. Only valid for low latency interfaces like PCIE
+        * where FW can access this memory directly (or) by DMA.
+        */
+       __le32 num_mem_reqs;
+
+       struct wlan_host_mem_req mem_reqs[1];
+} __packed;
+
+
 #define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
 #define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
 
@@ -1012,6 +1377,192 @@ struct wmi_resource_config {
        __le32 max_frag_entries;
 } __packed;
 
+struct wmi_resource_config_10x {
+       /* number of virtual devices (VAPs) to support */
+       __le32 num_vdevs;
+
+       /* number of peer nodes to support */
+       __le32 num_peers;
+
+       /* number of keys per peer */
+       __le32 num_peer_keys;
+
+       /* total number of TX/RX data TIDs */
+       __le32 num_tids;
+
+       /*
+        * max skid for resolving hash collisions
+        *
+        *   The address search table is sparse, so that if two MAC addresses
+        *   result in the same hash value, the second of these conflicting
+        *   entries can slide to the next index in the address search table,
+        *   and use it, if it is unoccupied.  This ast_skid_limit parameter
+        *   specifies the upper bound on how many subsequent indices to search
+        *   over to find an unoccupied space.
+        */
+       __le32 ast_skid_limit;
+
+       /*
+        * the nominal chain mask for transmit
+        *
+        *   The chain mask may be modified dynamically, e.g. to operate AP
+        *   tx with a reduced number of chains if no clients are associated.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of tx chains.
+        */
+       __le32 tx_chain_mask;
+
+       /*
+        * the nominal chain mask for receive
+        *
+        *   The chain mask may be modified dynamically, e.g. for a client
+        *   to use a reduced number of chains for receive if the traffic to
+        *   the client is low enough that it doesn't require downlink MIMO
+        *   or antenna diversity.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of rx chains.
+        */
+       __le32 rx_chain_mask;
+
+       /*
+        * what rx reorder timeout (ms) to use for the AC
+        *
+        *   Each WMM access class (voice, video, best-effort, background) will
+        *   have its own timeout value to dictate how long to wait for missing
+        *   rx MPDUs to arrive before flushing subsequent MPDUs that have
+        *   already been received.
+        *   This parameter specifies the timeout in milliseconds for each
+        *   class.
+        */
+       __le32 rx_timeout_pri_vi;
+       __le32 rx_timeout_pri_vo;
+       __le32 rx_timeout_pri_be;
+       __le32 rx_timeout_pri_bk;
+
+       /*
+        * what mode the rx should decap packets to
+        *
+        *   MAC can decap to RAW (no decap), native wifi or Ethernet types
+        *   THis setting also determines the default TX behavior, however TX
+        *   behavior can be modified on a per VAP basis during VAP init
+        */
+       __le32 rx_decap_mode;
+
+       /* what is the maximum scan requests than can be queued */
+       __le32 scan_max_pending_reqs;
+
+       /* maximum VDEV that could use BMISS offload */
+       __le32 bmiss_offload_max_vdev;
+
+       /* maximum VDEV that could use offload roaming */
+       __le32 roam_offload_max_vdev;
+
+       /* maximum AP profiles that would push to offload roaming */
+       __le32 roam_offload_max_ap_profiles;
+
+       /*
+        * how many groups to use for mcast->ucast conversion
+        *
+        *   The target's WAL maintains a table to hold information regarding
+        *   which peers belong to a given multicast group, so that if
+        *   multicast->unicast conversion is enabled, the target can convert
+        *   multicast tx frames to a series of unicast tx frames, to each
+        *   peer within the multicast group.
+            This num_mcast_groups configuration parameter tells the target how
+        *   many multicast groups to provide storage for within its multicast
+        *   group membership table.
+        */
+       __le32 num_mcast_groups;
+
+       /*
+        * size to alloc for the mcast membership table
+        *
+        *   This num_mcast_table_elems configuration parameter tells the
+        *   target how many peer elements it needs to provide storage for in
+        *   its multicast group membership table.
+        *   These multicast group membership table elements are shared by the
+        *   multicast groups stored within the table.
+        */
+       __le32 num_mcast_table_elems;
+
+       /*
+        * whether/how to do multicast->unicast conversion
+        *
+        *   This configuration parameter specifies whether the target should
+        *   perform multicast --> unicast conversion on transmit, and if so,
+        *   what to do if it finds no entries in its multicast group
+        *   membership table for the multicast IP address in the tx frame.
+        *   Configuration value:
+        *   0 -> Do not perform multicast to unicast conversion.
+        *   1 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        drop the frame.
+        *   2 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        transmit the frame as multicast.
+        */
+       __le32 mcast2ucast_mode;
+
+       /*
+        * how much memory to allocate for a tx PPDU dbg log
+        *
+        *   This parameter controls how much memory the target will allocate
+        *   to store a log of tx PPDU meta-information (how large the PPDU
+        *   was, when it was sent, whether it was successful, etc.)
+        */
+       __le32 tx_dbg_log_size;
+
+       /* how many AST entries to be allocated for WDS */
+       __le32 num_wds_entries;
+
+       /*
+        * MAC DMA burst size, e.g., For target PCI limit can be
+        * 0 -default, 1 256B
+        */
+       __le32 dma_burst_size;
+
+       /*
+        * Fixed delimiters to be inserted after every MPDU to
+        * account for interface latency to avoid underrun.
+        */
+       __le32 mac_aggr_delim;
+
+       /*
+        *   determine whether target is responsible for detecting duplicate
+        *   non-aggregate MPDU and timing out stale fragments.
+        *
+        *   A-MPDU reordering is always performed on the target.
+        *
+        *   0: target responsible for frag timeout and dup checking
+        *   1: host responsible for frag timeout and dup checking
+        */
+       __le32 rx_skip_defrag_timeout_dup_detection_check;
+
+       /*
+        * Configuration for VoW :
+        * No of Video Nodes to be supported
+        * and Max no of descriptors for each Video link (node).
+        */
+       __le32 vow_config;
+
+       /* Number of msdu descriptors target should use */
+       __le32 num_msdu_desc;
+
+       /*
+        * Max. number of Tx fragments per MSDU
+        *  This parameter controls the max number of Tx fragments per MSDU.
+        *  This is sent by the target as part of the WMI_SERVICE_READY event
+        *  and is overriden by the OS shim as required.
+        */
+       __le32 max_frag_entries;
+} __packed;
+
+
+#define NUM_UNITS_IS_NUM_VDEVS   0x1
+#define NUM_UNITS_IS_NUM_PEERS   0x2
+
 /* strucutre describing host memory chunk. */
 struct host_memory_chunk {
        /* id of the request that is passed up in service ready */
@@ -1033,6 +1584,18 @@ struct wmi_init_cmd {
        struct host_memory_chunk host_mem_chunks[1];
 } __packed;
 
+/* _10x stucture is from 10.X FW API */
+struct wmi_init_cmd_10x {
+       struct wmi_resource_config_10x resource_config;
+       __le32 num_host_mem_chunks;
+
+       /*
+        * variable number of host memory chunks.
+        * This should be the last element in the structure
+        */
+       struct host_memory_chunk host_mem_chunks[1];
+} __packed;
+
 /* TLV for channel list */
 struct wmi_chan_list {
        __le32 tag; /* WMI_CHAN_LIST_TAG */
@@ -1152,6 +1715,88 @@ struct wmi_start_scan_cmd {
         */
 } __packed;
 
+/* This is the definition from 10.X firmware branch */
+struct wmi_start_scan_cmd_10x {
+       /* Scan ID */
+       __le32 scan_id;
+
+       /* Scan requestor ID */
+       __le32 scan_req_id;
+
+       /* VDEV id(interface) that is requesting scan */
+       __le32 vdev_id;
+
+       /* Scan Priority, input to scan scheduler */
+       __le32 scan_priority;
+
+       /* Scan events subscription */
+       __le32 notify_scan_events;
+
+       /* dwell time in msec on active channels */
+       __le32 dwell_time_active;
+
+       /* dwell time in msec on passive channels */
+       __le32 dwell_time_passive;
+
+       /*
+        * min time in msec on the BSS channel,only valid if atleast one
+        * VDEV is active
+        */
+       __le32 min_rest_time;
+
+       /*
+        * max rest time in msec on the BSS channel,only valid if at least
+        * one VDEV is active
+        */
+       /*
+        * the scanner will rest on the bss channel at least min_rest_time
+        * after min_rest_time the scanner will start checking for tx/rx
+        * activity on all VDEVs. if there is no activity the scanner will
+        * switch to off channel. if there is activity the scanner will let
+        * the radio on the bss channel until max_rest_time expires.at
+        * max_rest_time scanner will switch to off channel irrespective of
+        * activity. activity is determined by the idle_time parameter.
+        */
+       __le32 max_rest_time;
+
+       /*
+        * time before sending next set of probe requests.
+        * The scanner keeps repeating probe requests transmission with
+        * period specified by repeat_probe_time.
+        * The number of probe requests specified depends on the ssid_list
+        * and bssid_list
+        */
+       __le32 repeat_probe_time;
+
+       /* time in msec between 2 consequetive probe requests with in a set. */
+       __le32 probe_spacing_time;
+
+       /*
+        * data inactivity time in msec on bss channel that will be used by
+        * scanner for measuring the inactivity.
+        */
+       __le32 idle_time;
+
+       /* maximum time in msec allowed for scan  */
+       __le32 max_scan_time;
+
+       /*
+        * delay in msec before sending first probe request after switching
+        * to a channel
+        */
+       __le32 probe_delay;
+
+       /* Scan control flags */
+       __le32 scan_ctrl_flags;
+
+       /*
+        * TLV (tag length value )  paramerters follow the scan_cmd structure.
+        * TLV can contain channel list, bssid list, ssid list and
+        * ie. the TLV tags are defined above;
+        */
+} __packed;
+
+
 struct wmi_ssid_arg {
        int len;
        const u8 *ssid;
@@ -1509,6 +2154,60 @@ struct wmi_csa_event {
 #define VDEV_DEFAULT_STATS_UPDATE_PERIOD    500
 #define PEER_DEFAULT_STATS_UPDATE_PERIOD    500
 
+struct wmi_pdev_param_map {
+       u32 tx_chain_mask;
+       u32 rx_chain_mask;
+       u32 txpower_limit2g;
+       u32 txpower_limit5g;
+       u32 txpower_scale;
+       u32 beacon_gen_mode;
+       u32 beacon_tx_mode;
+       u32 resmgr_offchan_mode;
+       u32 protection_mode;
+       u32 dynamic_bw;
+       u32 non_agg_sw_retry_th;
+       u32 agg_sw_retry_th;
+       u32 sta_kickout_th;
+       u32 ac_aggrsize_scaling;
+       u32 ltr_enable;
+       u32 ltr_ac_latency_be;
+       u32 ltr_ac_latency_bk;
+       u32 ltr_ac_latency_vi;
+       u32 ltr_ac_latency_vo;
+       u32 ltr_ac_latency_timeout;
+       u32 ltr_sleep_override;
+       u32 ltr_rx_override;
+       u32 ltr_tx_activity_timeout;
+       u32 l1ss_enable;
+       u32 dsleep_enable;
+       u32 pcielp_txbuf_flush;
+       u32 pcielp_txbuf_watermark;
+       u32 pcielp_txbuf_tmo_en;
+       u32 pcielp_txbuf_tmo_value;
+       u32 pdev_stats_update_period;
+       u32 vdev_stats_update_period;
+       u32 peer_stats_update_period;
+       u32 bcnflt_stats_update_period;
+       u32 pmf_qos;
+       u32 arp_ac_override;
+       u32 arpdhcp_ac_override;
+       u32 dcs;
+       u32 ani_enable;
+       u32 ani_poll_period;
+       u32 ani_listen_period;
+       u32 ani_ofdm_level;
+       u32 ani_cck_level;
+       u32 dyntxchain;
+       u32 proxy_sta;
+       u32 idle_ps_config;
+       u32 power_gating_sleep;
+       u32 fast_channel_reset;
+       u32 burst_dur;
+       u32 burst_enable;
+};
+
+#define WMI_PDEV_PARAM_UNSUPPORTED 0
+
 enum wmi_pdev_param {
        /* TX chian mask */
        WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
@@ -1608,6 +2307,97 @@ enum wmi_pdev_param {
        WMI_PDEV_PARAM_POWER_GATING_SLEEP,
 };
 
+enum wmi_10x_pdev_param {
+       /* TX chian mask */
+       WMI_10X_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
+       /* RX chian mask */
+       WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
+       /* TX power limit for 2G Radio */
+       WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
+       /* TX power limit for 5G Radio */
+       WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
+       /* TX power scale */
+       WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
+       /* Beacon generation mode . 0: host, 1: target   */
+       WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
+       /* Beacon generation mode . 0: staggered 1: bursted   */
+       WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
+       /*
+        * Resource manager off chan mode .
+        * 0: turn off off chan mode. 1: turn on offchan mode
+        */
+       WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       /*
+        * Protection mode:
+        * 0: no protection 1:use CTS-to-self 2: use RTS/CTS
+        */
+       WMI_10X_PDEV_PARAM_PROTECTION_MODE,
+       /* Dynamic bandwidth 0: disable 1: enable */
+       WMI_10X_PDEV_PARAM_DYNAMIC_BW,
+       /* Non aggregrate/ 11g sw retry threshold.0-disable */
+       WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       /* aggregrate sw retry threshold. 0-disable*/
+       WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
+       /* Station kickout threshold (non of consecutive failures).0-disable */
+       WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
+       /* Aggerate size scaling configuration per AC */
+       WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       /* LTR enable */
+       WMI_10X_PDEV_PARAM_LTR_ENABLE,
+       /* LTR latency for BE, in us */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       /* LTR latency for BK, in us */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       /* LTR latency for VI, in us */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       /* LTR latency for VO, in us  */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       /* LTR AC latency timeout, in ms */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       /* LTR platform latency override, in us */
+       WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       /* LTR-RX override, in us */
+       WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
+       /* Tx activity timeout for LTR, in us */
+       WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       /* L1SS state machine enable */
+       WMI_10X_PDEV_PARAM_L1SS_ENABLE,
+       /* Deep sleep state machine enable */
+       WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
+       /* pdev level stats update period in ms */
+       WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       /* vdev level stats update period in ms */
+       WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       /* peer level stats update period in ms */
+       WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       /* beacon filter status update period */
+       WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */
+       WMI_10X_PDEV_PARAM_PMF_QOS,
+       /* Access category on which ARP and DHCP frames are sent */
+       WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+       /* DCS configuration */
+       WMI_10X_PDEV_PARAM_DCS,
+       /* Enable/Disable ANI on target */
+       WMI_10X_PDEV_PARAM_ANI_ENABLE,
+       /* configure the ANI polling period */
+       WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
+       /* configure the ANI listening period */
+       WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       /* configure OFDM immunity level */
+       WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
+       /* configure CCK immunity level */
+       WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
+       /* Enable/Disable CDD for 1x1 STAs in rate control module */
+       WMI_10X_PDEV_PARAM_DYNTXCHAIN,
+       /* Enable/Disable Fast channel reset*/
+       WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
+       /* Set Bursting DUR */
+       WMI_10X_PDEV_PARAM_BURST_DUR,
+       /* Set Bursting Enable*/
+       WMI_10X_PDEV_PARAM_BURST_ENABLE,
+};
+
 struct wmi_pdev_set_param_cmd {
        __le32 param_id;
        __le32 param_value;
@@ -2132,6 +2922,61 @@ enum wmi_rate_preamble {
 /* Value to disable fixed rate setting */
 #define WMI_FIXED_RATE_NONE    (0xff)
 
+struct wmi_vdev_param_map {
+       u32 rts_threshold;
+       u32 fragmentation_threshold;
+       u32 beacon_interval;
+       u32 listen_interval;
+       u32 multicast_rate;
+       u32 mgmt_tx_rate;
+       u32 slot_time;
+       u32 preamble;
+       u32 swba_time;
+       u32 wmi_vdev_stats_update_period;
+       u32 wmi_vdev_pwrsave_ageout_time;
+       u32 wmi_vdev_host_swba_interval;
+       u32 dtim_period;
+       u32 wmi_vdev_oc_scheduler_air_time_limit;
+       u32 wds;
+       u32 atim_window;
+       u32 bmiss_count_max;
+       u32 bmiss_first_bcnt;
+       u32 bmiss_final_bcnt;
+       u32 feature_wmm;
+       u32 chwidth;
+       u32 chextoffset;
+       u32 disable_htprotection;
+       u32 sta_quickkickout;
+       u32 mgmt_rate;
+       u32 protection_mode;
+       u32 fixed_rate;
+       u32 sgi;
+       u32 ldpc;
+       u32 tx_stbc;
+       u32 rx_stbc;
+       u32 intra_bss_fwd;
+       u32 def_keyid;
+       u32 nss;
+       u32 bcast_data_rate;
+       u32 mcast_data_rate;
+       u32 mcast_indicate;
+       u32 dhcp_indicate;
+       u32 unknown_dest_indicate;
+       u32 ap_keepalive_min_idle_inactive_time_secs;
+       u32 ap_keepalive_max_idle_inactive_time_secs;
+       u32 ap_keepalive_max_unresponsive_time_secs;
+       u32 ap_enable_nawds;
+       u32 mcast2ucast_set;
+       u32 enable_rtscts;
+       u32 txbf;
+       u32 packet_powersave;
+       u32 drop_unencry;
+       u32 tx_encap_type;
+       u32 ap_detect_out_of_sync_sleeping_sta_time_secs;
+};
+
+#define WMI_VDEV_PARAM_UNSUPPORTED 0
+
 /* the definition of different VDEV parameters */
 enum wmi_vdev_param {
        /* RTS Threshold */
@@ -2263,6 +3108,121 @@ enum wmi_vdev_param {
        WMI_VDEV_PARAM_TX_ENCAP_TYPE,
 };
 
+/* the definition of different VDEV parameters */
+enum wmi_10x_vdev_param {
+       /* RTS Threshold */
+       WMI_10X_VDEV_PARAM_RTS_THRESHOLD = 0x1,
+       /* Fragmentation threshold */
+       WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       /* beacon interval in TUs */
+       WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
+       /* Listen interval in TUs */
+       WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
+       /* muticast rate in Mbps */
+       WMI_10X_VDEV_PARAM_MULTICAST_RATE,
+       /* management frame rate in Mbps */
+       WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
+       /* slot time (long vs short) */
+       WMI_10X_VDEV_PARAM_SLOT_TIME,
+       /* preamble (long vs short) */
+       WMI_10X_VDEV_PARAM_PREAMBLE,
+       /* SWBA time (time before tbtt in msec) */
+       WMI_10X_VDEV_PARAM_SWBA_TIME,
+       /* time period for updating VDEV stats */
+       WMI_10X_VDEV_STATS_UPDATE_PERIOD,
+       /* age out time in msec for frames queued for station in power save */
+       WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
+       /*
+        * Host SWBA interval (time in msec before tbtt for SWBA event
+        * generation).
+        */
+       WMI_10X_VDEV_HOST_SWBA_INTERVAL,
+       /* DTIM period (specified in units of num beacon intervals) */
+       WMI_10X_VDEV_PARAM_DTIM_PERIOD,
+       /*
+        * scheduler air time limit for this VDEV. used by off chan
+        * scheduler.
+        */
+       WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       /* enable/dsiable WDS for this VDEV  */
+       WMI_10X_VDEV_PARAM_WDS,
+       /* ATIM Window */
+       WMI_10X_VDEV_PARAM_ATIM_WINDOW,
+       /* BMISS max */
+       WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
+       /* WMM enables/disabled */
+       WMI_10X_VDEV_PARAM_FEATURE_WMM,
+       /* Channel width */
+       WMI_10X_VDEV_PARAM_CHWIDTH,
+       /* Channel Offset */
+       WMI_10X_VDEV_PARAM_CHEXTOFFSET,
+       /* Disable HT Protection */
+       WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
+       /* Quick STA Kickout */
+       WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
+       /* Rate to be used with Management frames */
+       WMI_10X_VDEV_PARAM_MGMT_RATE,
+       /* Protection Mode */
+       WMI_10X_VDEV_PARAM_PROTECTION_MODE,
+       /* Fixed rate setting */
+       WMI_10X_VDEV_PARAM_FIXED_RATE,
+       /* Short GI Enable/Disable */
+       WMI_10X_VDEV_PARAM_SGI,
+       /* Enable LDPC */
+       WMI_10X_VDEV_PARAM_LDPC,
+       /* Enable Tx STBC */
+       WMI_10X_VDEV_PARAM_TX_STBC,
+       /* Enable Rx STBC */
+       WMI_10X_VDEV_PARAM_RX_STBC,
+       /* Intra BSS forwarding  */
+       WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
+       /* Setting Default xmit key for Vdev */
+       WMI_10X_VDEV_PARAM_DEF_KEYID,
+       /* NSS width */
+       WMI_10X_VDEV_PARAM_NSS,
+       /* Set the custom rate for the broadcast data frames */
+       WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
+       /* Set the custom rate (rate-code) for multicast data frames */
+       WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
+       /* Tx multicast packet indicate Enable/Disable */
+       WMI_10X_VDEV_PARAM_MCAST_INDICATE,
+       /* Tx DHCP packet indicate Enable/Disable */
+       WMI_10X_VDEV_PARAM_DHCP_INDICATE,
+       /* Enable host inspection of Tx unicast packet to unknown destination */
+       WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+
+       /* The minimum amount of time AP begins to consider STA inactive */
+       WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered inactive when there is no recent
+        * TX/RX activity and no downlink frames are buffered for it. Once a
+        * STA exceeds the maximum idle inactive time, the AP will send an
+        * 802.11 data-null as a keep alive to verify the STA is still
+        * associated. If the STA does ACK the data-null, or if the data-null
+        * is buffered and the STA does not retrieve it, the STA will be
+        * considered unresponsive
+        * (see WMI_10X_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS).
+        */
+       WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered unresponsive if there is no recent
+        * TX/RX activity and downlink frames are buffered for it. Once a STA
+        * exceeds the maximum unresponsive time, the AP will send a
+        * WMI_10X_STA_KICKOUT event to the host so the STA can be deleted. */
+       WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+
+       /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */
+       WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
+
+       WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
+       /* Enable/Disable RTS-CTS */
+       WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
+
+       WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+};
+
 /* slot time long */
 #define WMI_VDEV_SLOT_TIME_LONG                0x1
 /* slot time short */
@@ -3064,8 +4024,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar);
 int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
 int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
                                  u16 rd5g, u16 ctl2g, u16 ctl5g);
-int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
-                             u32 value);
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value);
 int ath10k_wmi_cmd_init(struct ath10k *ar);
 int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *);
 void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
@@ -3085,7 +4044,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
                       const u8 *bssid);
 int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id);
 int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
-                             enum wmi_vdev_param param_id, u32 param_value);
+                             u32 param_id, u32 param_value);
 int ath10k_wmi_vdev_install_key(struct ath10k *ar,
                                const struct wmi_vdev_install_key_arg *arg);
 int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
@@ -3115,5 +4074,6 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
 int ath10k_wmi_force_fw_hang(struct ath10k *ar,
                             enum wmi_force_fw_hang_type type, u32 delay_ms);
+int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
 
 #endif /* _WMI_H_ */
index 48161edec8de84769fd9f3db92fa1c4aa165d70b..69f58b073e85ff1a183ec1f06e803ff9da00806c 100644 (file)
@@ -1663,15 +1663,15 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
        ah->stats.tx_bytes_count += skb->len;
        info = IEEE80211_SKB_CB(skb);
 
+       size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates));
+       memcpy(info->status.rates, bf->rates, size);
+
        tries[0] = info->status.rates[0].count;
        tries[1] = info->status.rates[1].count;
        tries[2] = info->status.rates[2].count;
 
        ieee80211_tx_info_clear_status(info);
 
-       size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates));
-       memcpy(info->status.rates, bf->rates, size);
-
        for (i = 0; i < ts->ts_final_idx; i++) {
                struct ieee80211_tx_rate *r =
                        &info->status.rates[i];
index a2c8ff8097939240f4fecc4e238203b6a5c7de73..14cab1403dd6071d48179922346b676d89e9610b 100644 (file)
@@ -60,7 +60,7 @@
 /* disable credit flow control on a specific service */
 #define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL          (1 << 3)
 #define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT    8
-#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK     0xFF00
+#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK     0xFF00U
 
 /* connect response status codes */
 #define HTC_SERVICE_SUCCESS      0
index 7944c25c9a43d54940ffc3e5ee9bc95591fe69d6..32f139e2e897e255b4c378c33c6d99df8abd51e0 100644 (file)
@@ -84,6 +84,26 @@ config ATH9K_DFS_CERTIFIED
          developed. At this point enabling this option won't do anything
          except increase code size.
 
+config ATH9K_TX99
+       bool "Atheros ath9k TX99 testing support"
+       depends on CFG80211_CERTIFICATION_ONUS
+       default n
+       ---help---
+         Say N. This should only be enabled on systems undergoing
+         certification testing and evaluation in a controlled environment.
+         Enabling this will only enable TX99 support, all other modes of
+         operation will be disabled.
+
+         TX99 support enables Specific Absorption Rate (SAR) testing.
+         SAR is the unit of measurement for the amount of radio frequency(RF)
+         absorbed by the body when using a wireless device. The RF exposure
+         limits used are expressed in the terms of SAR, which is a measure
+         of the electric and magnetic field strength and power density for
+         transmitters operating at frequencies from 300 kHz to 100 GHz.
+         Regulatory bodies around the world require that wireless device
+         be evaluated to meet the RF exposure limits set forth in the
+         governmental SAR regulations.
+
 config ATH9K_LEGACY_RATE_CONTROL
        bool "Atheros ath9k rate control"
        depends on ATH9K
index 75ee9e7704ce627eb52939c25d5ea0b6d1eb4f3d..6205ef5a9321e8072c283d24e8e7814b2a8392df 100644 (file)
@@ -14,9 +14,7 @@ ath9k-$(CONFIG_ATH9K_AHB) += ahb.o
 ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
 ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
 ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \
-               dfs.o \
-               dfs_pattern_detector.o \
-               dfs_pri_detector.o
+               dfs.o
 ath9k-$(CONFIG_PM_SLEEP) += wow.o
 
 obj-$(CONFIG_ATH9K) += ath9k.o
index be466b0ef7a7725c736114ee439596ca45a4a380..d28923b7435b257f13a91e3f8896115c30adb1c9 100644 (file)
@@ -338,10 +338,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
                    aniState->cckNoiseImmunityLevel !=
                    ATH9K_ANI_CCK_DEF_LEVEL) {
                        ath_dbg(common, ANI,
-                               "Restore defaults: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n",
+                               "Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
                                ah->opmode,
                                chan->channel,
-                               chan->channelFlags,
                                is_scanning,
                                aniState->ofdmNoiseImmunityLevel,
                                aniState->cckNoiseImmunityLevel);
@@ -354,10 +353,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
                 * restore historical levels for this channel
                 */
                ath_dbg(common, ANI,
-                       "Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n",
+                       "Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
                        ah->opmode,
                        chan->channel,
-                       chan->channelFlags,
                        is_scanning,
                        aniState->ofdmNoiseImmunityLevel,
                        aniState->cckNoiseImmunityLevel);
index cb6435e7c6f52afb44b74ebebccbcba5ddb2f673..ff415e863ee9cc5140879ca008df366b8f1ad967 100644 (file)
@@ -666,14 +666,13 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah,
        if (IS_CHAN_HT40(chan)) {
                phymode |= AR_PHY_FC_DYN2040_EN;
 
-               if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
-                   (chan->chanmode == CHANNEL_G_HT40PLUS))
+               if (IS_CHAN_HT40PLUS(chan))
                        phymode |= AR_PHY_FC_DYN2040_PRI_CH;
 
        }
        REG_WRITE(ah, AR_PHY_TURBO, phymode);
 
-       ath9k_hw_set11nmac2040(ah);
+       ath9k_hw_set11nmac2040(ah, chan);
 
        ENABLE_REGWRITE_BUFFER(ah);
 
@@ -691,31 +690,12 @@ static int ar5008_hw_process_ini(struct ath_hw *ah,
        int i, regWrites = 0;
        u32 modesIndex, freqIndex;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               modesIndex = 1;
+       if (IS_CHAN_5GHZ(chan)) {
                freqIndex = 1;
-               break;
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               modesIndex = 2;
-               freqIndex = 1;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               modesIndex = 4;
-               freqIndex = 2;
-               break;
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               modesIndex = 3;
+               modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
+       } else {
                freqIndex = 2;
-               break;
-
-       default:
-               return -EINVAL;
+               modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
        }
 
        /*
@@ -814,8 +794,10 @@ static void ar5008_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan)
        if (chan == NULL)
                return;
 
-       rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
-               ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
+       if (IS_CHAN_2GHZ(chan))
+               rfMode |= AR_PHY_MODE_DYNAMIC;
+       else
+               rfMode |= AR_PHY_MODE_OFDM;
 
        if (!AR_SREV_9280_20_OR_LATER(ah))
                rfMode |= (IS_CHAN_5GHZ(chan)) ?
@@ -1218,12 +1200,11 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
 
        iniDef = &aniState->iniDef;
 
-       ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
+       ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n",
                ah->hw_version.macVersion,
                ah->hw_version.macRev,
                ah->opmode,
-               chan->channel,
-               chan->channelFlags);
+               chan->channel);
 
        val = REG_READ(ah, AR_PHY_SFCORR);
        iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
index 32376ad74011b7c132a4cb9717acd5c79719c475..cdc74005650ce9848147ee0e051c64f9df989ffd 100644 (file)
@@ -33,15 +33,12 @@ static bool ar9002_hw_is_cal_supported(struct ath_hw *ah,
        bool supported = false;
        switch (ah->supp_cals & cal_type) {
        case IQ_MISMATCH_CAL:
-               /* Run IQ Mismatch for non-CCK only */
-               if (!IS_CHAN_B(chan))
-                       supported = true;
+               supported = true;
                break;
        case ADC_GAIN_CAL:
        case ADC_DC_CAL:
                /* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */
-               if (!IS_CHAN_B(chan) &&
-                   !((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
+               if (!((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
                      IS_CHAN_HT20(chan)))
                        supported = true;
                break;
index fb61b081d1721124518f7c4cad85792cedaefac0..5c95fd9e9c9e9c861edb283a2a8cfbf4a9b5e3b9 100644 (file)
@@ -419,28 +419,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan)
        u32 modesIndex;
        int i;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               modesIndex = 1;
-               break;
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               modesIndex = 2;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               modesIndex = 4;
-               break;
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               modesIndex = 3;
-               break;
-
-       default:
-               return;
-       }
+       if (IS_CHAN_5GHZ(chan))
+               modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
+       else
+               modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
 
        ENABLE_REGWRITE_BUFFER(ah);
 
index 17970d49d858e80b37e86097b6c97f73af61e29a..f087117b2e6b6b2592ae8eb7a2270314cffb7325 100644 (file)
@@ -680,6 +680,26 @@ static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah)
        }
 }
 
+static void ar9002_hw_tx99_start(struct ath_hw *ah, u32 qnum)
+{
+       REG_SET_BIT(ah, 0x9864, 0x7f000);
+       REG_SET_BIT(ah, 0x9924, 0x7f00fe);
+       REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+       REG_WRITE(ah, AR_CR, AR_CR_RXD);
+       REG_WRITE(ah, AR_DLCL_IFS(qnum), 0);
+       REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20);
+       REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20);
+       REG_WRITE(ah, AR_D_FPCTL, 0x10|qnum);
+       REG_WRITE(ah, AR_TIME_OUT, 0x00000400);
+       REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff);
+       REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ);
+}
+
+static void ar9002_hw_tx99_stop(struct ath_hw *ah)
+{
+       REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+}
+
 void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -701,6 +721,8 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
        ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity;
 #endif
+       ops->tx99_start = ar9002_hw_tx99_start;
+       ops->tx99_stop = ar9002_hw_tx99_stop;
 
        ar9002_hw_set_nf_limits(ah);
 }
index 0131ba2f5d51ebf572fe93a1f3aa59547036dd93..11f53589a3f34879b6ab3e8d9062e294fb6f7260 100644 (file)
@@ -551,8 +551,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
        if (IS_CHAN_HT40(chan)) {
                phymode |= AR_PHY_GC_DYN2040_EN;
                /* Configure control (primary) channel at +-10MHz */
-               if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
-                   (chan->chanmode == CHANNEL_G_HT40PLUS))
+               if (IS_CHAN_HT40PLUS(chan))
                        phymode |= AR_PHY_GC_DYN2040_PRI_CH;
 
        }
@@ -565,7 +564,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
        REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode);
 
        /* Configure MAC for 20/40 operation */
-       ath9k_hw_set11nmac2040(ah);
+       ath9k_hw_set11nmac2040(ah, chan);
 
        /* global transmit timeout (25 TUs default)*/
        REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
@@ -682,41 +681,22 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah,
 {
        int ret;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               if (chan->channel <= 5350)
-                       ret = 1;
-               else if ((chan->channel > 5350) && (chan->channel <= 5600))
-                       ret = 3;
+       if (IS_CHAN_2GHZ(chan)) {
+               if (IS_CHAN_HT40(chan))
+                       return 7;
                else
-                       ret = 5;
-               break;
-
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               if (chan->channel <= 5350)
-                       ret = 2;
-               else if ((chan->channel > 5350) && (chan->channel <= 5600))
-                       ret = 4;
-               else
-                       ret = 6;
-               break;
-
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               ret = 8;
-               break;
+                       return 8;
+       }
 
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               ret = 7;
-               break;
+       if (chan->channel <= 5350)
+               ret = 1;
+       else if ((chan->channel > 5350) && (chan->channel <= 5600))
+               ret = 3;
+       else
+               ret = 5;
 
-       default:
-               ret = -EINVAL;
-       }
+       if (IS_CHAN_HT40(chan))
+               ret++;
 
        return ret;
 }
@@ -727,28 +707,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
        unsigned int regWrites = 0, i;
        u32 modesIndex;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               modesIndex = 1;
-               break;
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               modesIndex = 2;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               modesIndex = 4;
-               break;
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               modesIndex = 3;
-               break;
-
-       default:
-               return -EINVAL;
-       }
+       if (IS_CHAN_5GHZ(chan))
+               modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
+       else
+               modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
 
        /*
         * SOC, MAC, BB, RADIO initvals.
@@ -846,8 +808,10 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah,
        if (chan == NULL)
                return;
 
-       rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
-               ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
+       if (IS_CHAN_2GHZ(chan))
+               rfMode |= AR_PHY_MODE_DYNAMIC;
+       else
+               rfMode |= AR_PHY_MODE_OFDM;
 
        if (IS_CHAN_A_FAST_CLOCK(ah, chan))
                rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
@@ -1273,12 +1237,11 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
        aniState = &ah->ani;
        iniDef = &aniState->iniDef;
 
-       ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
+       ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n",
                ah->hw_version.macVersion,
                ah->hw_version.macRev,
                ah->opmode,
-               chan->channel,
-               chan->channelFlags);
+               chan->channel);
 
        val = REG_READ(ah, AR_PHY_SFCORR);
        iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
@@ -1536,28 +1499,10 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
        unsigned int regWrites = 0;
        u32 modesIndex;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               modesIndex = 1;
-               break;
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               modesIndex = 2;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               modesIndex = 4;
-               break;
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               modesIndex = 3;
-               break;
-
-       default:
-               return -EINVAL;
-       }
+       if (IS_CHAN_5GHZ(chan))
+               modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
+       else
+               modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
 
        if (modesIndex == ah->modes_index) {
                *ini_reloaded = false;
@@ -1672,6 +1617,98 @@ static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah)
        }
 }
 
+static void ar9003_hw_tx99_start(struct ath_hw *ah, u32 qnum)
+{
+       REG_SET_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR);
+       REG_SET_BIT(ah, 0x9864, 0x7f000);
+       REG_SET_BIT(ah, 0x9924, 0x7f00fe);
+       REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+       REG_WRITE(ah, AR_CR, AR_CR_RXD);
+       REG_WRITE(ah, AR_DLCL_IFS(qnum), 0);
+       REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); /* 50 OK */
+       REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20);
+       REG_WRITE(ah, AR_TIME_OUT, 0x00000400);
+       REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff);
+       REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ);
+}
+
+static void ar9003_hw_tx99_stop(struct ath_hw *ah)
+{
+       REG_CLR_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR);
+       REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+}
+
+static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower)
+{
+       static s16 p_pwr_array[ar9300RateSize] = { 0 };
+       unsigned int i;
+
+       if (txpower <= MAX_RATE_POWER) {
+               for (i = 0; i < ar9300RateSize; i++)
+                       p_pwr_array[i] = txpower;
+       } else {
+               for (i = 0; i < ar9300RateSize; i++)
+                       p_pwr_array[i] = MAX_RATE_POWER;
+       }
+
+       REG_WRITE(ah, 0xa458, 0);
+
+       REG_WRITE(ah, 0xa3c0,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24],  8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24],  0));
+       REG_WRITE(ah, 0xa3c4,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_54],  24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_48],  16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_36],   8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0));
+       REG_WRITE(ah, 0xa3c8,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L],  0));
+       REG_WRITE(ah, 0xa3cc,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11S],   24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11L],   16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_5S],     8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L],  0));
+       REG_WRITE(ah, 0xa3d0,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_5],  24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_4],  16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_1_3_9_11_17_19], 8)|
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_0_8_16], 0));
+       REG_WRITE(ah, 0xa3d4,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_13], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_12], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_7],   8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_6],   0));
+       REG_WRITE(ah, 0xa3e4,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_21], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_20], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_15],  8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_14],  0));
+       REG_WRITE(ah, 0xa3e8,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_23], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_22], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_23],  8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_22],  0));
+       REG_WRITE(ah, 0xa3d8,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_5], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_4], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_1_3_9_11_17_19], 8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_0_8_16], 0));
+       REG_WRITE(ah, 0xa3dc,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_13], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_12], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_7],   8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_6],   0));
+       REG_WRITE(ah, 0xa3ec,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_21], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_20], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_15],  8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14],  0));
+}
+
 void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -1711,6 +1748,9 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
        ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity;
 #endif
+       ops->tx99_start = ar9003_hw_tx99_start;
+       ops->tx99_stop = ar9003_hw_tx99_stop;
+       ops->tx99_set_txpower = ar9003_hw_tx99_set_txpower;
 
        ar9003_hw_set_nf_limits(ah);
        ar9003_hw_set_radar_conf(ah);
index 8878f2dada2dd421474a9516fff41e3018bd9ceb..4c3bbe4f309533a04f4262913ac4c7c074a8d73c 100644 (file)
@@ -64,7 +64,6 @@ struct ath_node;
 
 struct ath_config {
        u16 txpowlimit;
-       u8 cabqReadytime;
 };
 
 /*************************/
@@ -207,6 +206,14 @@ struct ath_frame_info {
        u8 baw_tracked : 1;
 };
 
+struct ath_rxbuf {
+       struct list_head list;
+       struct sk_buff *bf_mpdu;
+       void *bf_desc;
+       dma_addr_t bf_daddr;
+       dma_addr_t bf_buf_addr;
+};
+
 struct ath_buf_state {
        u8 bf_type;
        u8 bfs_paprd;
@@ -307,7 +314,7 @@ struct ath_rx {
        struct ath_descdma rxdma;
        struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
 
-       struct ath_buf *buf_hold;
+       struct ath_rxbuf *buf_hold;
        struct sk_buff *frag;
 
        u32 ampdu_ref;
@@ -771,6 +778,11 @@ struct ath_softc {
        enum spectral_mode spectral_mode;
        struct ath_spec_scan spec_config;
 
+       struct ieee80211_vif *tx99_vif;
+       struct sk_buff *tx99_skb;
+       bool tx99_state;
+       s16 tx99_power;
+
 #ifdef CONFIG_PM_SLEEP
        atomic_t wow_got_bmiss_intr;
        atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */
@@ -879,6 +891,7 @@ static inline u8 spectral_bitmap_weight(u8 *bins)
  */
 enum ath_fft_sample_type {
        ATH_FFT_SAMPLE_HT20 = 1,
+       ATH_FFT_SAMPLE_HT20_40,
 };
 
 struct fft_sample_tlv {
@@ -905,6 +918,39 @@ struct fft_sample_ht20 {
        u8 data[SPECTRAL_HT20_NUM_BINS];
 } __packed;
 
+struct fft_sample_ht20_40 {
+       struct fft_sample_tlv tlv;
+
+       u8 channel_type;
+       __be16 freq;
+
+       s8 lower_rssi;
+       s8 upper_rssi;
+
+       __be64 tsf;
+
+       s8 lower_noise;
+       s8 upper_noise;
+
+       __be16 lower_max_magnitude;
+       __be16 upper_max_magnitude;
+
+       u8 lower_max_index;
+       u8 upper_max_index;
+
+       u8 lower_bitmap_weight;
+       u8 upper_bitmap_weight;
+
+       u8 max_exp;
+
+       u8 data[SPECTRAL_HT20_40_NUM_BINS];
+} __packed;
+
+int ath9k_tx99_init(struct ath_softc *sc);
+void ath9k_tx99_deinit(struct ath_softc *sc);
+int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb,
+                   struct ath_tx_control *txctl);
+
 void ath9k_tasklet(unsigned long data);
 int ath_cabq_update(struct ath_softc *);
 
@@ -926,7 +972,6 @@ void ath9k_deinit_device(struct ath_softc *sc);
 void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
 void ath9k_reload_chainmask_settings(struct ath_softc *sc);
 
-bool ath9k_uses_beacons(int type);
 void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
 int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
                               enum spectral_mode spectral_mode);
index d438a0341e685d6cffd628186d6323441dc50c96..278365b8a8955dda362292b99949e63155be354c 100644 (file)
@@ -63,13 +63,13 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
        return ath9k_hw_get_nf_limits(ah, chan)->nominal;
 }
 
-s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan,
+                          s16 nf)
 {
        s8 noise = ATH_DEFAULT_NOISE_FLOOR;
 
-       if (chan && chan->noisefloor) {
-               s8 delta = chan->noisefloor -
-                          ATH9K_NF_CAL_NOISE_THRESH -
+       if (nf) {
+               s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH -
                           ath9k_hw_get_default_nf(ah, chan);
                if (delta > 0)
                        noise += delta;
@@ -186,7 +186,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
 bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_conf *conf = &common->hw->conf;
        struct ath9k_cal_list *currCal = ah->cal_list_curr;
 
        if (!ah->caldata)
@@ -208,7 +207,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
                return true;
 
        ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n",
-               currCal->calData->calType, conf->chandef.chan->center_freq);
+               currCal->calData->calType, ah->curchan->chan->center_freq);
 
        ah->caldata->CalValid &= ~currCal->calData->calType;
        currCal->calState = CAL_WAITING;
@@ -242,7 +241,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
        int32_t val;
        u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_conf *conf = &common->hw->conf;
        s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
 
        if (ah->caldata)
@@ -252,7 +250,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
                if (chainmask & (1 << i)) {
                        s16 nfval;
 
-                       if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))
+                       if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
                                continue;
 
                        if (h)
@@ -314,7 +312,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
        ENABLE_REGWRITE_BUFFER(ah);
        for (i = 0; i < NUM_NF_READINGS; i++) {
                if (chainmask & (1 << i)) {
-                       if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))
+                       if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
                                continue;
 
                        val = REG_READ(ah, ah->nf_regs[i]);
@@ -394,7 +392,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
        clear_bit(NFCAL_PENDING, &caldata->cal_flags);
        ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray);
        chan->noisefloor = h[0].privNF;
-       ah->noise = ath9k_hw_getchan_noise(ah, chan);
+       ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor);
        return true;
 }
 EXPORT_SYMBOL(ath9k_hw_getnf);
@@ -408,7 +406,6 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
 
        ah->caldata->channel = chan->channel;
        ah->caldata->channelFlags = chan->channelFlags;
-       ah->caldata->chanmode = chan->chanmode;
        h = ah->caldata->nfCalHist;
        default_nf = ath9k_hw_get_default_nf(ah, chan);
        for (i = 0; i < NUM_NF_READINGS; i++) {
index 3d70b8c2bcdd0a3ddad872f0ceff9c241f59f0a4..b8ed95e9a335d90d86e8d0dd4031e5abc2e8dffc 100644 (file)
@@ -116,7 +116,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
 void ath9k_hw_bstuck_nfcal(struct ath_hw *ah);
 void ath9k_hw_reset_calibration(struct ath_hw *ah,
                                struct ath9k_cal_list *currCal);
-s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan,
+                          s16 nf);
 
 
 #endif /* CALIB_H */
index d3063c21e16c7efbd67d073c756de2198e5794e5..a7e5a05b2eff88e8d367cea6403058cc2813ebbc 100644 (file)
@@ -49,103 +49,64 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
 
-static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef)
-{
-       u32 chanmode = 0;
-
-       switch (chandef->chan->band) {
-       case IEEE80211_BAND_2GHZ:
-               switch (chandef->width) {
-               case NL80211_CHAN_WIDTH_20_NOHT:
-               case NL80211_CHAN_WIDTH_20:
-                       chanmode = CHANNEL_G_HT20;
-                       break;
-               case NL80211_CHAN_WIDTH_40:
-                       if (chandef->center_freq1 > chandef->chan->center_freq)
-                               chanmode = CHANNEL_G_HT40PLUS;
-                       else
-                               chanmode = CHANNEL_G_HT40MINUS;
-                       break;
-               default:
-                       break;
-               }
-               break;
-       case IEEE80211_BAND_5GHZ:
-               switch (chandef->width) {
-               case NL80211_CHAN_WIDTH_20_NOHT:
-               case NL80211_CHAN_WIDTH_20:
-                       chanmode = CHANNEL_A_HT20;
-                       break;
-               case NL80211_CHAN_WIDTH_40:
-                       if (chandef->center_freq1 > chandef->chan->center_freq)
-                               chanmode = CHANNEL_A_HT40PLUS;
-                       else
-                               chanmode = CHANNEL_A_HT40MINUS;
-                       break;
-               default:
-                       break;
-               }
-               break;
-       default:
-               break;
-       }
-
-       return chanmode;
-}
-
 /*
  * Update internal channel flags.
  */
-void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
-                              struct cfg80211_chan_def *chandef)
+static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
+                                     struct cfg80211_chan_def *chandef)
 {
-       ichan->channel = chandef->chan->center_freq;
-       ichan->chan = chandef->chan;
-
-       if (chandef->chan->band == IEEE80211_BAND_2GHZ) {
-               ichan->chanmode = CHANNEL_G;
-               ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
-       } else {
-               ichan->chanmode = CHANNEL_A;
-               ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
-       }
+       struct ieee80211_channel *chan = chandef->chan;
+       u16 flags = 0;
+
+       ichan->channel = chan->center_freq;
+       ichan->chan = chan;
+
+       if (chan->band == IEEE80211_BAND_5GHZ)
+               flags |= CHANNEL_5GHZ;
 
        switch (chandef->width) {
        case NL80211_CHAN_WIDTH_5:
-               ichan->channelFlags |= CHANNEL_QUARTER;
+               flags |= CHANNEL_QUARTER;
                break;
        case NL80211_CHAN_WIDTH_10:
-               ichan->channelFlags |= CHANNEL_HALF;
+               flags |= CHANNEL_HALF;
                break;
        case NL80211_CHAN_WIDTH_20_NOHT:
                break;
        case NL80211_CHAN_WIDTH_20:
+               flags |= CHANNEL_HT;
+               break;
        case NL80211_CHAN_WIDTH_40:
-               ichan->chanmode = ath9k_get_extchanmode(chandef);
+               if (chandef->center_freq1 > chandef->chan->center_freq)
+                       flags |= CHANNEL_HT40PLUS | CHANNEL_HT;
+               else
+                       flags |= CHANNEL_HT40MINUS | CHANNEL_HT;
                break;
        default:
                WARN_ON(1);
        }
+
+       ichan->channelFlags = flags;
 }
-EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
 
 /*
  * Get the internal channel reference.
  */
-struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
-                                              struct ath_hw *ah)
+struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
+                                           struct ath_hw *ah,
+                                           struct cfg80211_chan_def *chandef)
 {
-       struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+       struct ieee80211_channel *curchan = chandef->chan;
        struct ath9k_channel *channel;
        u8 chan_idx;
 
        chan_idx = curchan->hw_value;
        channel = &ah->channels[chan_idx];
-       ath9k_cmn_update_ichannel(channel, &hw->conf.chandef);
+       ath9k_cmn_update_ichannel(channel, chandef);
 
        return channel;
 }
-EXPORT_SYMBOL(ath9k_cmn_get_curchannel);
+EXPORT_SYMBOL(ath9k_cmn_get_channel);
 
 int ath9k_cmn_count_streams(unsigned int chainmask, int max)
 {
index e039bcbfbd7923b4f8013f1c4535915a83d4b1fc..eb85e1bdca889a4e43b46261be31e691ff403363 100644 (file)
        (((x) + ((mul)/2)) / (mul))
 
 int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
-void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
-                              struct cfg80211_chan_def *chandef);
-struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
-                                              struct ath_hw *ah);
+struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
+                                           struct ath_hw *ah,
+                                           struct cfg80211_chan_def *chandef);
 int ath9k_cmn_count_streams(unsigned int chainmask, int max);
 void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
                                  enum ath_stomp_type stomp_type);
index 1be2c787aac9197609964c4e7adaf68ac1a4d0e2..83a2c59f680b0445173d25cc5ebccc2f7eb5777e 100644 (file)
@@ -1050,6 +1050,9 @@ static ssize_t write_file_spec_scan_ctl(struct file *file,
        char buf[32];
        ssize_t len;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return -EOPNOTSUPP;
+
        len = min(count, sizeof(buf) - 1);
        if (copy_from_user(buf, user_buf, len))
                return -EFAULT;
@@ -1775,6 +1778,111 @@ void ath9k_deinit_debug(struct ath_softc *sc)
        }
 }
 
+static ssize_t read_file_tx99(struct file *file, char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[3];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->tx99_state);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_tx99(struct file *file, const char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       char buf[32];
+       bool start;
+       ssize_t len;
+       int r;
+
+       if (sc->nvifs > 1)
+               return -EOPNOTSUPP;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       if (strtobool(buf, &start))
+               return -EINVAL;
+
+       if (start == sc->tx99_state) {
+               if (!start)
+                       return count;
+               ath_dbg(common, XMIT, "Resetting TX99\n");
+               ath9k_tx99_deinit(sc);
+       }
+
+       if (!start) {
+               ath9k_tx99_deinit(sc);
+               return count;
+       }
+
+       r = ath9k_tx99_init(sc);
+       if (r)
+               return r;
+
+       return count;
+}
+
+static const struct file_operations fops_tx99 = {
+       .read = read_file_tx99,
+       .write = write_file_tx99,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_tx99_power(struct file *file,
+                                   char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d (%d dBm)\n",
+                     sc->tx99_power,
+                     sc->tx99_power / 2);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_tx99_power(struct file *file,
+                                    const char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       int r;
+       u8 tx_power;
+
+       r = kstrtou8_from_user(user_buf, count, 0, &tx_power);
+       if (r)
+               return r;
+
+       if (tx_power > MAX_RATE_POWER)
+               return -EINVAL;
+
+       sc->tx99_power = tx_power;
+
+       ath9k_ps_wakeup(sc);
+       ath9k_hw_tx99_set_txpower(sc->sc_ah, sc->tx99_power);
+       ath9k_ps_restore(sc);
+
+       return count;
+}
+
+static const struct file_operations fops_tx99_power = {
+       .read = read_file_tx99_power,
+       .write = write_file_tx99_power,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 int ath9k_init_debug(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
@@ -1866,5 +1974,15 @@ int ath9k_init_debug(struct ath_hw *ah)
        debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_btcoex);
 #endif
+       if (config_enabled(CONFIG_ATH9K_TX99) &&
+           AR_SREV_9300_20_OR_LATER(ah)) {
+               debugfs_create_file("tx99", S_IRUSR | S_IWUSR,
+                                   sc->debug.debugfs_phy, sc,
+                                   &fops_tx99);
+               debugfs_create_file("tx99_power", S_IRUSR | S_IWUSR,
+                                   sc->debug.debugfs_phy, sc,
+                                   &fops_tx99_power);
+       }
+
        return 0;
 }
index 3c839f06a06afb7903382fcf91554fa8046d252f..c6fa3d5b5d74e3137fc34f08a430ccb1c4a670e5 100644 (file)
@@ -17,7 +17,7 @@
 
 #ifndef ATH9K_DFS_H
 #define ATH9K_DFS_H
-#include "dfs_pattern_detector.h"
+#include "../dfs_pattern_detector.h"
 
 #if defined(CONFIG_ATH9K_DFS_CERTIFIED)
 /**
index 821599135d8a0f856457ca5108b5c6e162d01247..90b8342d1ed4bd2e95389541f299d49d59261e6a 100644 (file)
 
 #include "ath9k.h"
 #include "dfs_debug.h"
+#include "../dfs_pattern_detector.h"
 
-
-struct ath_dfs_pool_stats global_dfs_pool_stats = { 0 };
+static struct ath_dfs_pool_stats dfs_pool_stats = { 0 };
 
 #define ATH9K_DFS_STAT(s, p) \
        len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \
                         sc->debug.stats.dfs_stats.p);
 #define ATH9K_DFS_POOL_STAT(s, p) \
        len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \
-                        global_dfs_pool_stats.p);
+                        dfs_pool_stats.p);
 
 static ssize_t read_file_dfs(struct file *file, char __user *user_buf,
                             size_t count, loff_t *ppos)
@@ -44,6 +44,9 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf,
        if (buf == NULL)
                return -ENOMEM;
 
+       if (sc->dfs_detector)
+               dfs_pool_stats = sc->dfs_detector->get_stats(sc->dfs_detector);
+
        len += scnprintf(buf + len, size - len, "DFS support for "
                         "macVersion = 0x%x, macRev = 0x%x: %s\n",
                         hw_ver->macVersion, hw_ver->macRev,
index e36810a4b585c51515e6fc3336410e1b58e3a9b0..0a7ddf4c88c93eb55589dedff95394e2157c3510 100644 (file)
@@ -51,25 +51,11 @@ struct ath_dfs_stats {
        u32 radar_detected;
 };
 
-/**
- * struct ath_dfs_pool_stats - DFS Statistics for global pools
- */
-struct ath_dfs_pool_stats {
-       u32 pool_reference;
-       u32 pulse_allocated;
-       u32 pulse_alloc_error;
-       u32 pulse_used;
-       u32 pseq_allocated;
-       u32 pseq_alloc_error;
-       u32 pseq_used;
-};
 #if defined(CONFIG_ATH9K_DFS_DEBUGFS)
 
 #define DFS_STAT_INC(sc, c) (sc->debug.stats.dfs_stats.c++)
 void ath9k_dfs_init_debug(struct ath_softc *sc);
 
-#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
-#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
 extern struct ath_dfs_pool_stats global_dfs_pool_stats;
 
 #else
@@ -77,8 +63,6 @@ extern struct ath_dfs_pool_stats global_dfs_pool_stats;
 #define DFS_STAT_INC(sc, c) do { } while (0)
 static inline void ath9k_dfs_init_debug(struct ath_softc *sc) { }
 
-#define DFS_POOL_STAT_INC(c) do { } while (0)
-#define DFS_POOL_STAT_DEC(c) do { } while (0)
 #endif /* CONFIG_ATH9K_DFS_DEBUGFS */
 
 #endif /* ATH9K_DFS_DEBUG_H */
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c
deleted file mode 100644 (file)
index 491305c..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * 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/slab.h>
-#include <linux/export.h>
-
-#include "dfs_pattern_detector.h"
-#include "dfs_pri_detector.h"
-#include "ath9k.h"
-
-/*
- * tolerated deviation of radar time stamp in usecs on both sides
- * TODO: this might need to be HW-dependent
- */
-#define PRI_TOLERANCE  16
-
-/**
- * struct radar_types - contains array of patterns defined for one DFS domain
- * @domain: DFS regulatory domain
- * @num_radar_types: number of radar types to follow
- * @radar_types: radar types array
- */
-struct radar_types {
-       enum nl80211_dfs_regions region;
-       u32 num_radar_types;
-       const struct radar_detector_specs *radar_types;
-};
-
-/* percentage on ppb threshold to trigger detection */
-#define MIN_PPB_THRESH 50
-#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
-#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
-/* percentage of pulse width tolerance */
-#define WIDTH_TOLERANCE 5
-#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100)
-#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100)
-
-#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)     \
-{                                                              \
-       ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
-       (PRF2PRI(PMAX) - PRI_TOLERANCE),                        \
-       (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF,  \
-       PPB_THRESH(PPB), PRI_TOLERANCE,                         \
-}
-
-/* radar types as defined by ETSI EN-301-893 v1.5.1 */
-static const struct radar_detector_specs etsi_radar_ref_types_v15[] = {
-       ETSI_PATTERN(0,  0,  1,  700,  700, 1, 18),
-       ETSI_PATTERN(1,  0,  5,  200, 1000, 1, 10),
-       ETSI_PATTERN(2,  0, 15,  200, 1600, 1, 15),
-       ETSI_PATTERN(3,  0, 15, 2300, 4000, 1, 25),
-       ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20),
-       ETSI_PATTERN(5,  0,  2,  300,  400, 3, 10),
-       ETSI_PATTERN(6,  0,  2,  400, 1200, 3, 15),
-};
-
-static const struct radar_types etsi_radar_types_v15 = {
-       .region                 = NL80211_DFS_ETSI,
-       .num_radar_types        = ARRAY_SIZE(etsi_radar_ref_types_v15),
-       .radar_types            = etsi_radar_ref_types_v15,
-};
-
-/* for now, we support ETSI radar types, FCC and JP are TODO */
-static const struct radar_types *dfs_domains[] = {
-       &etsi_radar_types_v15,
-};
-
-/**
- * get_dfs_domain_radar_types() - get radar types for a given DFS domain
- * @param domain DFS domain
- * @return radar_types ptr on success, NULL if DFS domain is not supported
- */
-static const struct radar_types *
-get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
-{
-       u32 i;
-       for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
-               if (dfs_domains[i]->region == region)
-                       return dfs_domains[i];
-       }
-       return NULL;
-}
-
-/**
- * struct channel_detector - detector elements for a DFS channel
- * @head: list_head
- * @freq: frequency for this channel detector in MHz
- * @detectors: array of dynamically created detector elements for this freq
- *
- * Channel detectors are required to provide multi-channel DFS detection, e.g.
- * to support off-channel scanning. A pattern detector has a list of channels
- * radar pulses have been reported for in the past.
- */
-struct channel_detector {
-       struct list_head head;
-       u16 freq;
-       struct pri_detector **detectors;
-};
-
-/* channel_detector_reset() - reset detector lines for a given channel */
-static void channel_detector_reset(struct dfs_pattern_detector *dpd,
-                                  struct channel_detector *cd)
-{
-       u32 i;
-       if (cd == NULL)
-               return;
-       for (i = 0; i < dpd->num_radar_types; i++)
-               cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts);
-}
-
-/* channel_detector_exit() - destructor */
-static void channel_detector_exit(struct dfs_pattern_detector *dpd,
-                                 struct channel_detector *cd)
-{
-       u32 i;
-       if (cd == NULL)
-               return;
-       list_del(&cd->head);
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               struct pri_detector *de = cd->detectors[i];
-               if (de != NULL)
-                       de->exit(de);
-       }
-       kfree(cd->detectors);
-       kfree(cd);
-}
-
-static struct channel_detector *
-channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq)
-{
-       u32 sz, i;
-       struct channel_detector *cd;
-       struct ath_common *common = ath9k_hw_common(dpd->ah);
-
-       cd = kmalloc(sizeof(*cd), GFP_ATOMIC);
-       if (cd == NULL)
-               goto fail;
-
-       INIT_LIST_HEAD(&cd->head);
-       cd->freq = freq;
-       sz = sizeof(cd->detectors) * dpd->num_radar_types;
-       cd->detectors = kzalloc(sz, GFP_ATOMIC);
-       if (cd->detectors == NULL)
-               goto fail;
-
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               const struct radar_detector_specs *rs = &dpd->radar_spec[i];
-               struct pri_detector *de = pri_detector_init(rs);
-               if (de == NULL)
-                       goto fail;
-               cd->detectors[i] = de;
-       }
-       list_add(&cd->head, &dpd->channel_detectors);
-       return cd;
-
-fail:
-       ath_dbg(common, DFS,
-               "failed to allocate channel_detector for freq=%d\n", freq);
-       channel_detector_exit(dpd, cd);
-       return NULL;
-}
-
-/**
- * channel_detector_get() - get channel detector for given frequency
- * @param dpd instance pointer
- * @param freq frequency in MHz
- * @return pointer to channel detector on success, NULL otherwise
- *
- * Return existing channel detector for the given frequency or return a
- * newly create one.
- */
-static struct channel_detector *
-channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq)
-{
-       struct channel_detector *cd;
-       list_for_each_entry(cd, &dpd->channel_detectors, head) {
-               if (cd->freq == freq)
-                       return cd;
-       }
-       return channel_detector_create(dpd, freq);
-}
-
-/*
- * DFS Pattern Detector
- */
-
-/* dpd_reset(): reset all channel detectors */
-static void dpd_reset(struct dfs_pattern_detector *dpd)
-{
-       struct channel_detector *cd;
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry(cd, &dpd->channel_detectors, head)
-                       channel_detector_reset(dpd, cd);
-
-}
-static void dpd_exit(struct dfs_pattern_detector *dpd)
-{
-       struct channel_detector *cd, *cd0;
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
-                       channel_detector_exit(dpd, cd);
-       kfree(dpd);
-}
-
-static bool
-dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
-{
-       u32 i;
-       struct channel_detector *cd;
-
-       /*
-        * pulses received for a non-supported or un-initialized
-        * domain are treated as detected radars for fail-safety
-        */
-       if (dpd->region == NL80211_DFS_UNSET)
-               return true;
-
-       cd = channel_detector_get(dpd, event->freq);
-       if (cd == NULL)
-               return false;
-
-       dpd->last_pulse_ts = event->ts;
-       /* reset detector on time stamp wraparound, caused by TSF reset */
-       if (event->ts < dpd->last_pulse_ts)
-               dpd_reset(dpd);
-
-       /* do type individual pattern matching */
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               struct pri_detector *pd = cd->detectors[i];
-               struct pri_sequence *ps = pd->add_pulse(pd, event);
-               if (ps != NULL) {
-                       ath_dbg(ath9k_hw_common(dpd->ah), DFS,
-                               "DFS: radar found on freq=%d: id=%d, pri=%d, "
-                               "count=%d, count_false=%d\n",
-                               event->freq, pd->rs->type_id,
-                               ps->pri, ps->count, ps->count_falses);
-                       channel_detector_reset(dpd, cd);
-                       return true;
-               }
-       }
-       return false;
-}
-
-static bool dpd_set_domain(struct dfs_pattern_detector *dpd,
-                          enum nl80211_dfs_regions region)
-{
-       const struct radar_types *rt;
-       struct channel_detector *cd, *cd0;
-
-       if (dpd->region == region)
-               return true;
-
-       dpd->region = NL80211_DFS_UNSET;
-
-       rt = get_dfs_domain_radar_types(region);
-       if (rt == NULL)
-               return false;
-
-       /* delete all channel detectors for previous DFS domain */
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
-                       channel_detector_exit(dpd, cd);
-       dpd->radar_spec = rt->radar_types;
-       dpd->num_radar_types = rt->num_radar_types;
-
-       dpd->region = region;
-       return true;
-}
-
-static struct dfs_pattern_detector default_dpd = {
-       .exit           = dpd_exit,
-       .set_dfs_domain = dpd_set_domain,
-       .add_pulse      = dpd_add_pulse,
-       .region         = NL80211_DFS_UNSET,
-};
-
-struct dfs_pattern_detector *
-dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region)
-{
-       struct dfs_pattern_detector *dpd;
-       struct ath_common *common = ath9k_hw_common(ah);
-
-       dpd = kmalloc(sizeof(*dpd), GFP_KERNEL);
-       if (dpd == NULL)
-               return NULL;
-
-       *dpd = default_dpd;
-       INIT_LIST_HEAD(&dpd->channel_detectors);
-
-       dpd->ah = ah;
-       if (dpd->set_dfs_domain(dpd, region))
-               return dpd;
-
-       ath_dbg(common, DFS,"Could not set DFS domain to %d", region);
-       kfree(dpd);
-       return NULL;
-}
-EXPORT_SYMBOL(dfs_pattern_detector_init);
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h
deleted file mode 100644 (file)
index 90a5abc..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * 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 DFS_PATTERN_DETECTOR_H
-#define DFS_PATTERN_DETECTOR_H
-
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/nl80211.h>
-
-/**
- * struct pulse_event - describing pulses reported by PHY
- * @ts: pulse time stamp in us
- * @freq: channel frequency in MHz
- * @width: pulse duration in us
- * @rssi: rssi of radar event
- */
-struct pulse_event {
-       u64 ts;
-       u16 freq;
-       u8 width;
-       u8 rssi;
-};
-
-/**
- * struct radar_detector_specs - detector specs for a radar pattern type
- * @type_id: pattern type, as defined by regulatory
- * @width_min: minimum radar pulse width in [us]
- * @width_max: maximum radar pulse width in [us]
- * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
- * @pri_max: minimum pri in [us] (including tolerance)
- * @num_pri: maximum number of different pri for this type
- * @ppb: pulses per bursts for this type
- * @ppb_thresh: number of pulses required to trigger detection
- * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
- */
-struct radar_detector_specs {
-       u8 type_id;
-       u8 width_min;
-       u8 width_max;
-       u16 pri_min;
-       u16 pri_max;
-       u8 num_pri;
-       u8 ppb;
-       u8 ppb_thresh;
-       u8 max_pri_tolerance;
-};
-
-/**
- * struct dfs_pattern_detector - DFS pattern detector
- * @exit(): destructor
- * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes
- * @add_pulse(): add radar pulse to detector, returns true on detection
- * @region: active DFS region, NL80211_DFS_UNSET until set
- * @num_radar_types: number of different radar types
- * @last_pulse_ts: time stamp of last valid pulse in usecs
- * @radar_detector_specs: array of radar detection specs
- * @channel_detectors: list connecting channel_detector elements
- */
-struct dfs_pattern_detector {
-       void (*exit)(struct dfs_pattern_detector *dpd);
-       bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd,
-                          enum nl80211_dfs_regions region);
-       bool (*add_pulse)(struct dfs_pattern_detector *dpd,
-                         struct pulse_event *pe);
-
-       enum nl80211_dfs_regions region;
-       u8 num_radar_types;
-       u64 last_pulse_ts;
-       /* needed for ath_dbg() */
-       struct ath_hw *ah;
-
-       const struct radar_detector_specs *radar_spec;
-       struct list_head channel_detectors;
-};
-
-/**
- * dfs_pattern_detector_init() - constructor for pattern detector class
- * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation
- * @return instance pointer on success, NULL otherwise
- */
-#if defined(CONFIG_ATH9K_DFS_CERTIFIED)
-extern struct dfs_pattern_detector *
-dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region);
-#else
-static inline struct dfs_pattern_detector *
-dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region)
-{
-       return NULL;
-}
-#endif /* CONFIG_ATH9K_DFS_CERTIFIED */
-
-#endif /* DFS_PATTERN_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c
deleted file mode 100644 (file)
index c718fc3..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * 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/slab.h>
-#include <linux/spinlock.h>
-
-#include "ath9k.h"
-#include "dfs_pattern_detector.h"
-#include "dfs_pri_detector.h"
-#include "dfs_debug.h"
-
-/**
- * struct pulse_elem - elements in pulse queue
- * @ts: time stamp in usecs
- */
-struct pulse_elem {
-       struct list_head head;
-       u64 ts;
-};
-
-/**
- * pde_get_multiple() - get number of multiples considering a given tolerance
- * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
- */
-static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
-{
-       u32 remainder;
-       u32 factor;
-       u32 delta;
-
-       if (fraction == 0)
-               return 0;
-
-       delta = (val < fraction) ? (fraction - val) : (val - fraction);
-
-       if (delta <= tolerance)
-               /* val and fraction are within tolerance */
-               return 1;
-
-       factor = val / fraction;
-       remainder = val % fraction;
-       if (remainder > tolerance) {
-               /* no exact match */
-               if ((fraction - remainder) <= tolerance)
-                       /* remainder is within tolerance */
-                       factor++;
-               else
-                       factor = 0;
-       }
-       return factor;
-}
-
-/**
- * DOC: Singleton Pulse and Sequence Pools
- *
- * Instances of pri_sequence and pulse_elem are kept in singleton pools to
- * reduce the number of dynamic allocations. They are shared between all
- * instances and grow up to the peak number of simultaneously used objects.
- *
- * Memory is freed after all references to the pools are released.
- */
-static u32 singleton_pool_references;
-static LIST_HEAD(pulse_pool);
-static LIST_HEAD(pseq_pool);
-static DEFINE_SPINLOCK(pool_lock);
-
-static void pool_register_ref(void)
-{
-       spin_lock_bh(&pool_lock);
-       singleton_pool_references++;
-       DFS_POOL_STAT_INC(pool_reference);
-       spin_unlock_bh(&pool_lock);
-}
-
-static void pool_deregister_ref(void)
-{
-       spin_lock_bh(&pool_lock);
-       singleton_pool_references--;
-       DFS_POOL_STAT_DEC(pool_reference);
-       if (singleton_pool_references == 0) {
-               /* free singleton pools with no references left */
-               struct pri_sequence *ps, *ps0;
-               struct pulse_elem *p, *p0;
-
-               list_for_each_entry_safe(p, p0, &pulse_pool, head) {
-                       list_del(&p->head);
-                       DFS_POOL_STAT_DEC(pulse_allocated);
-                       kfree(p);
-               }
-               list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
-                       list_del(&ps->head);
-                       DFS_POOL_STAT_DEC(pseq_allocated);
-                       kfree(ps);
-               }
-       }
-       spin_unlock_bh(&pool_lock);
-}
-
-static void pool_put_pulse_elem(struct pulse_elem *pe)
-{
-       spin_lock_bh(&pool_lock);
-       list_add(&pe->head, &pulse_pool);
-       DFS_POOL_STAT_DEC(pulse_used);
-       spin_unlock_bh(&pool_lock);
-}
-
-static void pool_put_pseq_elem(struct pri_sequence *pse)
-{
-       spin_lock_bh(&pool_lock);
-       list_add(&pse->head, &pseq_pool);
-       DFS_POOL_STAT_DEC(pseq_used);
-       spin_unlock_bh(&pool_lock);
-}
-
-static struct pri_sequence *pool_get_pseq_elem(void)
-{
-       struct pri_sequence *pse = NULL;
-       spin_lock_bh(&pool_lock);
-       if (!list_empty(&pseq_pool)) {
-               pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
-               list_del(&pse->head);
-               DFS_POOL_STAT_INC(pseq_used);
-       }
-       spin_unlock_bh(&pool_lock);
-       return pse;
-}
-
-static struct pulse_elem *pool_get_pulse_elem(void)
-{
-       struct pulse_elem *pe = NULL;
-       spin_lock_bh(&pool_lock);
-       if (!list_empty(&pulse_pool)) {
-               pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
-               list_del(&pe->head);
-               DFS_POOL_STAT_INC(pulse_used);
-       }
-       spin_unlock_bh(&pool_lock);
-       return pe;
-}
-
-static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
-{
-       struct list_head *l = &pde->pulses;
-       if (list_empty(l))
-               return NULL;
-       return list_entry(l->prev, struct pulse_elem, head);
-}
-
-static bool pulse_queue_dequeue(struct pri_detector *pde)
-{
-       struct pulse_elem *p = pulse_queue_get_tail(pde);
-       if (p != NULL) {
-               list_del_init(&p->head);
-               pde->count--;
-               /* give it back to pool */
-               pool_put_pulse_elem(p);
-       }
-       return (pde->count > 0);
-}
-
-/* remove pulses older than window */
-static void pulse_queue_check_window(struct pri_detector *pde)
-{
-       u64 min_valid_ts;
-       struct pulse_elem *p;
-
-       /* there is no delta time with less than 2 pulses */
-       if (pde->count < 2)
-               return;
-
-       if (pde->last_ts <= pde->window_size)
-               return;
-
-       min_valid_ts = pde->last_ts - pde->window_size;
-       while ((p = pulse_queue_get_tail(pde)) != NULL) {
-               if (p->ts >= min_valid_ts)
-                       return;
-               pulse_queue_dequeue(pde);
-       }
-}
-
-static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
-{
-       struct pulse_elem *p = pool_get_pulse_elem();
-       if (p == NULL) {
-               p = kmalloc(sizeof(*p), GFP_ATOMIC);
-               if (p == NULL) {
-                       DFS_POOL_STAT_INC(pulse_alloc_error);
-                       return false;
-               }
-               DFS_POOL_STAT_INC(pulse_allocated);
-               DFS_POOL_STAT_INC(pulse_used);
-       }
-       INIT_LIST_HEAD(&p->head);
-       p->ts = ts;
-       list_add(&p->head, &pde->pulses);
-       pde->count++;
-       pde->last_ts = ts;
-       pulse_queue_check_window(pde);
-       if (pde->count >= pde->max_count)
-               pulse_queue_dequeue(pde);
-       return true;
-}
-
-static bool pseq_handler_create_sequences(struct pri_detector *pde,
-                                         u64 ts, u32 min_count)
-{
-       struct pulse_elem *p;
-       list_for_each_entry(p, &pde->pulses, head) {
-               struct pri_sequence ps, *new_ps;
-               struct pulse_elem *p2;
-               u32 tmp_false_count;
-               u64 min_valid_ts;
-               u32 delta_ts = ts - p->ts;
-
-               if (delta_ts < pde->rs->pri_min)
-                       /* ignore too small pri */
-                       continue;
-
-               if (delta_ts > pde->rs->pri_max)
-                       /* stop on too large pri (sorted list) */
-                       break;
-
-               /* build a new sequence with new potential pri */
-               ps.count = 2;
-               ps.count_falses = 0;
-               ps.first_ts = p->ts;
-               ps.last_ts = ts;
-               ps.pri = ts - p->ts;
-               ps.dur = ps.pri * (pde->rs->ppb - 1)
-                               + 2 * pde->rs->max_pri_tolerance;
-
-               p2 = p;
-               tmp_false_count = 0;
-               min_valid_ts = ts - ps.dur;
-               /* check which past pulses are candidates for new sequence */
-               list_for_each_entry_continue(p2, &pde->pulses, head) {
-                       u32 factor;
-                       if (p2->ts < min_valid_ts)
-                               /* stop on crossing window border */
-                               break;
-                       /* check if pulse match (multi)PRI */
-                       factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
-                                                 pde->rs->max_pri_tolerance);
-                       if (factor > 0) {
-                               ps.count++;
-                               ps.first_ts = p2->ts;
-                               /*
-                                * on match, add the intermediate falses
-                                * and reset counter
-                                */
-                               ps.count_falses += tmp_false_count;
-                               tmp_false_count = 0;
-                       } else {
-                               /* this is a potential false one */
-                               tmp_false_count++;
-                       }
-               }
-               if (ps.count < min_count)
-                       /* did not reach minimum count, drop sequence */
-                       continue;
-
-               /* this is a valid one, add it */
-               ps.deadline_ts = ps.first_ts + ps.dur;
-               new_ps = pool_get_pseq_elem();
-               if (new_ps == NULL) {
-                       new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
-                       if (new_ps == NULL) {
-                               DFS_POOL_STAT_INC(pseq_alloc_error);
-                               return false;
-                       }
-                       DFS_POOL_STAT_INC(pseq_allocated);
-                       DFS_POOL_STAT_INC(pseq_used);
-               }
-               memcpy(new_ps, &ps, sizeof(ps));
-               INIT_LIST_HEAD(&new_ps->head);
-               list_add(&new_ps->head, &pde->sequences);
-       }
-       return true;
-}
-
-/* check new ts and add to all matching existing sequences */
-static u32
-pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
-{
-       u32 max_count = 0;
-       struct pri_sequence *ps, *ps2;
-       list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
-               u32 delta_ts;
-               u32 factor;
-
-               /* first ensure that sequence is within window */
-               if (ts > ps->deadline_ts) {
-                       list_del_init(&ps->head);
-                       pool_put_pseq_elem(ps);
-                       continue;
-               }
-
-               delta_ts = ts - ps->last_ts;
-               factor = pde_get_multiple(delta_ts, ps->pri,
-                                         pde->rs->max_pri_tolerance);
-               if (factor > 0) {
-                       ps->last_ts = ts;
-                       ps->count++;
-
-                       if (max_count < ps->count)
-                               max_count = ps->count;
-               } else {
-                       ps->count_falses++;
-               }
-       }
-       return max_count;
-}
-
-static struct pri_sequence *
-pseq_handler_check_detection(struct pri_detector *pde)
-{
-       struct pri_sequence *ps;
-
-       if (list_empty(&pde->sequences))
-               return NULL;
-
-       list_for_each_entry(ps, &pde->sequences, head) {
-               /*
-                * we assume to have enough matching confidence if we
-                * 1) have enough pulses
-                * 2) have more matching than false pulses
-                */
-               if ((ps->count >= pde->rs->ppb_thresh) &&
-                   (ps->count * pde->rs->num_pri >= ps->count_falses))
-                       return ps;
-       }
-       return NULL;
-}
-
-
-/* free pulse queue and sequences list and give objects back to pools */
-static void pri_detector_reset(struct pri_detector *pde, u64 ts)
-{
-       struct pri_sequence *ps, *ps0;
-       struct pulse_elem *p, *p0;
-       list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
-               list_del_init(&ps->head);
-               pool_put_pseq_elem(ps);
-       }
-       list_for_each_entry_safe(p, p0, &pde->pulses, head) {
-               list_del_init(&p->head);
-               pool_put_pulse_elem(p);
-       }
-       pde->count = 0;
-       pde->last_ts = ts;
-}
-
-static void pri_detector_exit(struct pri_detector *de)
-{
-       pri_detector_reset(de, 0);
-       pool_deregister_ref();
-       kfree(de);
-}
-
-static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
-                                                  struct pulse_event *event)
-{
-       u32 max_updated_seq;
-       struct pri_sequence *ps;
-       u64 ts = event->ts;
-       const struct radar_detector_specs *rs = de->rs;
-
-       /* ignore pulses not within width range */
-       if ((rs->width_min > event->width) || (rs->width_max < event->width))
-               return NULL;
-
-       if ((ts - de->last_ts) < rs->max_pri_tolerance)
-               /* if delta to last pulse is too short, don't use this pulse */
-               return NULL;
-       de->last_ts = ts;
-
-       max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
-
-       if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
-               pri_detector_reset(de, ts);
-               return NULL;
-       }
-
-       ps = pseq_handler_check_detection(de);
-
-       if (ps == NULL)
-               pulse_queue_enqueue(de, ts);
-
-       return ps;
-}
-
-struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
-{
-       struct pri_detector *de;
-
-       de = kzalloc(sizeof(*de), GFP_ATOMIC);
-       if (de == NULL)
-               return NULL;
-       de->exit = pri_detector_exit;
-       de->add_pulse = pri_detector_add_pulse;
-       de->reset = pri_detector_reset;
-
-       INIT_LIST_HEAD(&de->sequences);
-       INIT_LIST_HEAD(&de->pulses);
-       de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
-       de->max_count = rs->ppb * 2;
-       de->rs = rs;
-
-       pool_register_ref();
-       return de;
-}
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h
deleted file mode 100644 (file)
index 723962d..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * 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 DFS_PRI_DETECTOR_H
-#define DFS_PRI_DETECTOR_H
-
-#include <linux/list.h>
-
-/**
- * struct pri_sequence - sequence of pulses matching one PRI
- * @head: list_head
- * @pri: pulse repetition interval (PRI) in usecs
- * @dur: duration of sequence in usecs
- * @count: number of pulses in this sequence
- * @count_falses: number of not matching pulses in this sequence
- * @first_ts: time stamp of first pulse in usecs
- * @last_ts: time stamp of last pulse in usecs
- * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
- */
-struct pri_sequence {
-       struct list_head head;
-       u32 pri;
-       u32 dur;
-       u32 count;
-       u32 count_falses;
-       u64 first_ts;
-       u64 last_ts;
-       u64 deadline_ts;
-};
-
-/**
- * struct pri_detector - PRI detector element for a dedicated radar type
- * @exit(): destructor
- * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected
- * @reset(): clear states and reset to given time stamp
- * @rs: detector specs for this detector element
- * @last_ts: last pulse time stamp considered for this element in usecs
- * @sequences: list_head holding potential pulse sequences
- * @pulses: list connecting pulse_elem objects
- * @count: number of pulses in queue
- * @max_count: maximum number of pulses to be queued
- * @window_size: window size back from newest pulse time stamp in usecs
- */
-struct pri_detector {
-       void (*exit)     (struct pri_detector *de);
-       struct pri_sequence *
-            (*add_pulse)(struct pri_detector *de, struct pulse_event *e);
-       void (*reset)    (struct pri_detector *de, u64 ts);
-
-/* private: internal use only */
-       const struct radar_detector_specs *rs;
-       u64 last_ts;
-       struct list_head sequences;
-       struct list_head pulses;
-       u32 count;
-       u32 max_count;
-       u32 window_size;
-};
-
-struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs);
-
-#endif /* DFS_PRI_DETECTOR_H */
index d44258172c0f640236719a7e8eb4e8480c9c682b..9a2657fdd9ccd4ec62f96f8a639182e2ded29fec 100644 (file)
 static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
                                              struct ath9k_channel *ichan)
 {
-       enum htc_phymode mode;
-
-       mode = -EINVAL;
-
-       switch (ichan->chanmode) {
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               mode = HTC_MODE_11NG;
-               break;
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               mode = HTC_MODE_11NA;
-               break;
-       default:
-               break;
-       }
+       if (IS_CHAN_5GHZ(ichan))
+               return HTC_MODE_11NA;
 
-       WARN_ON(mode < 0);
-
-       return mode;
+       return HTC_MODE_11NG;
 }
 
 bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
@@ -926,7 +906,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
        WMI_CMD(WMI_FLUSH_RECV_CMDID);
 
        /* setup initial channel */
-       init_channel = ath9k_cmn_get_curchannel(hw, ah);
+       init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
 
        ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
        if (ret) {
@@ -1208,9 +1188,7 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
                ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
                        curchan->center_freq);
 
-               ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
-                                         &hw->conf.chandef);
-
+               ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef);
                if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
                        ath_err(common, "Unable to set channel\n");
                        ret = -EINVAL;
index 83f4927aeacae1d07a2b18057ea313ad716b0cec..4f9378ddf07f21455a33a908180b9a28729bf6cd 100644 (file)
@@ -78,6 +78,22 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
 }
 
+static inline void ath9k_hw_tx99_start(struct ath_hw *ah, u32 qnum)
+{
+       ath9k_hw_ops(ah)->tx99_start(ah, qnum);
+}
+
+static inline void ath9k_hw_tx99_stop(struct ath_hw *ah)
+{
+       ath9k_hw_ops(ah)->tx99_stop(ah);
+}
+
+static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power)
+{
+       if (ath9k_hw_ops(ah)->tx99_set_txpower)
+               ath9k_hw_ops(ah)->tx99_set_txpower(ah, power);
+}
+
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
 
 static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
index f11e8389a9be0e2ae47c2086dfa32de8a5cd25fc..54b04155e43b1058575aa44df3e6ece1ab18e55e 100644 (file)
@@ -130,29 +130,29 @@ void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause)
 
 static void ath9k_hw_set_clockrate(struct ath_hw *ah)
 {
-       struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_channel *chan = ah->curchan;
        unsigned int clockrate;
 
        /* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */
        if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah))
                clockrate = 117;
-       else if (!ah->curchan) /* should really check for CCK instead */
+       else if (!chan) /* should really check for CCK instead */
                clockrate = ATH9K_CLOCK_RATE_CCK;
-       else if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ)
+       else if (IS_CHAN_2GHZ(chan))
                clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
        else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)
                clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
        else
                clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
 
-       if (conf_is_ht40(conf))
+       if (IS_CHAN_HT40(chan))
                clockrate *= 2;
 
        if (ah->curchan) {
-               if (IS_CHAN_HALF_RATE(ah->curchan))
+               if (IS_CHAN_HALF_RATE(chan))
                        clockrate /= 2;
-               if (IS_CHAN_QUARTER_RATE(ah->curchan))
+               if (IS_CHAN_QUARTER_RATE(chan))
                        clockrate /= 4;
        }
 
@@ -190,10 +190,7 @@ EXPORT_SYMBOL(ath9k_hw_wait);
 void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan,
                          int hw_delay)
 {
-       if (IS_CHAN_B(chan))
-               hw_delay = (4 * hw_delay) / 22;
-       else
-               hw_delay /= 10;
+       hw_delay /= 10;
 
        if (IS_CHAN_HALF_RATE(chan))
                hw_delay *= 2;
@@ -294,8 +291,7 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
                return;
        }
 
-       if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
-           (chan->chanmode == CHANNEL_G_HT40PLUS)) {
+       if (IS_CHAN_HT40PLUS(chan)) {
                centers->synth_center =
                        chan->channel + HT40_CHANNEL_CENTER_SHIFT;
                extoff = 1;
@@ -1042,7 +1038,6 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu)
 void ath9k_hw_init_global_settings(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_conf *conf = &common->hw->conf;
        const struct ath9k_channel *chan = ah->curchan;
        int acktimeout, ctstimeout, ack_offset = 0;
        int slottime;
@@ -1117,8 +1112,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
         * BA frames in some implementations, but it has been found to fix ACK
         * timeout issues in other cases as well.
         */
-       if (conf->chandef.chan &&
-           conf->chandef.chan->band == IEEE80211_BAND_2GHZ &&
+       if (IS_CHAN_2GHZ(chan) &&
            !IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) {
                acktimeout += 64 - sifstime - ah->slottime;
                ctstimeout += 48 - sifstime - ah->slottime;
@@ -1160,9 +1154,7 @@ u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan)
 {
        u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band);
 
-       if (IS_CHAN_B(chan))
-               ctl |= CTL_11B;
-       else if (IS_CHAN_G(chan))
+       if (IS_CHAN_2GHZ(chan))
                ctl |= CTL_11G;
        else
                ctl |= CTL_11A;
@@ -1510,10 +1502,8 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
        int r;
 
        if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) {
-               u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
-               u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
-               band_switch = (cur != new);
-               mode_diff = (chan->chanmode != ah->curchan->chanmode);
+               band_switch = IS_CHAN_5GHZ(ah->curchan) != IS_CHAN_5GHZ(chan);
+               mode_diff = (chan->channelFlags != ah->curchan->channelFlags);
        }
 
        for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
@@ -1552,9 +1542,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
        ath9k_hw_set_clockrate(ah);
        ath9k_hw_apply_txpower(ah, chan, false);
 
-       if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
-               ath9k_hw_set_delta_slope(ah, chan);
-
+       ath9k_hw_set_delta_slope(ah, chan);
        ath9k_hw_spur_mitigate_freq(ah, chan);
 
        if (band_switch || ini_reloaded)
@@ -1824,20 +1812,11 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
                goto fail;
 
        /*
-        * If cross-band fcc is not supoprted, bail out if
-        * either channelFlags or chanmode differ.
-        *
-        * chanmode will be different if the HT operating mode
-        * changes because of CSA.
+        * If cross-band fcc is not supoprted, bail out if channelFlags differ.
         */
-       if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) {
-               if ((chan->channelFlags & CHANNEL_ALL) !=
-                   (ah->curchan->channelFlags & CHANNEL_ALL))
-                       goto fail;
-
-               if (chan->chanmode != ah->curchan->chanmode)
-                       goto fail;
-       }
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) &&
+           chan->channelFlags != ah->curchan->channelFlags)
+               goto fail;
 
        if (!ath9k_hw_check_alive(ah))
                goto fail;
@@ -1899,15 +1878,14 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ah->caldata = caldata;
        if (caldata && (chan->channel != caldata->channel ||
-                       chan->channelFlags != caldata->channelFlags ||
-                       chan->chanmode != caldata->chanmode)) {
+                       chan->channelFlags != caldata->channelFlags)) {
                /* Operating channel changed, reset channel calibration data */
                memset(caldata, 0, sizeof(*caldata));
                ath9k_init_nfcal_hist_buffer(ah, chan);
        } else if (caldata) {
                clear_bit(PAPRD_PACKET_SENT, &caldata->cal_flags);
        }
-       ah->noise = ath9k_hw_getchan_noise(ah, chan);
+       ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor);
 
        if (fastcc) {
                r = ath9k_hw_do_fastcc(ah, chan);
@@ -1989,9 +1967,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ath9k_hw_init_mfp(ah);
 
-       if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
-               ath9k_hw_set_delta_slope(ah, chan);
-
+       ath9k_hw_set_delta_slope(ah, chan);
        ath9k_hw_spur_mitigate_freq(ah, chan);
        ah->eep_ops->set_board_values(ah, chan);
 
@@ -2968,12 +2944,11 @@ void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set)
 }
 EXPORT_SYMBOL(ath9k_hw_set_tsfadjust);
 
-void ath9k_hw_set11nmac2040(struct ath_hw *ah)
+void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan)
 {
-       struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
        u32 macmode;
 
-       if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca)
+       if (IS_CHAN_HT40(chan) && !ah->config.cwm_ignore_extcca)
                macmode = AR_2040_JOINED_RX_CLEAR;
        else
                macmode = 0;
index 2babf931b4598b3c0cf2a2e77a2d0976c8eebf09..9ea24f1cba73f812de0b3352806e89434c0d59f9 100644 (file)
@@ -369,36 +369,6 @@ enum ath9k_int {
        ATH9K_INT_NOCARD = 0xffffffff
 };
 
-#define CHANNEL_CCK       0x00020
-#define CHANNEL_OFDM      0x00040
-#define CHANNEL_2GHZ      0x00080
-#define CHANNEL_5GHZ      0x00100
-#define CHANNEL_PASSIVE   0x00200
-#define CHANNEL_DYN       0x00400
-#define CHANNEL_HALF      0x04000
-#define CHANNEL_QUARTER   0x08000
-#define CHANNEL_HT20      0x10000
-#define CHANNEL_HT40PLUS  0x20000
-#define CHANNEL_HT40MINUS 0x40000
-
-#define CHANNEL_A           (CHANNEL_5GHZ|CHANNEL_OFDM)
-#define CHANNEL_B           (CHANNEL_2GHZ|CHANNEL_CCK)
-#define CHANNEL_G           (CHANNEL_2GHZ|CHANNEL_OFDM)
-#define CHANNEL_G_HT20      (CHANNEL_2GHZ|CHANNEL_HT20)
-#define CHANNEL_A_HT20      (CHANNEL_5GHZ|CHANNEL_HT20)
-#define CHANNEL_G_HT40PLUS  (CHANNEL_2GHZ|CHANNEL_HT40PLUS)
-#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS)
-#define CHANNEL_A_HT40PLUS  (CHANNEL_5GHZ|CHANNEL_HT40PLUS)
-#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS)
-#define CHANNEL_ALL                            \
-       (CHANNEL_OFDM|                          \
-        CHANNEL_CCK|                           \
-        CHANNEL_2GHZ |                         \
-        CHANNEL_5GHZ |                         \
-        CHANNEL_HT20 |                         \
-        CHANNEL_HT40PLUS |                     \
-        CHANNEL_HT40MINUS)
-
 #define MAX_RTT_TABLE_ENTRY     6
 #define MAX_IQCAL_MEASUREMENT  8
 #define MAX_CL_TAB_ENTRY       16
@@ -417,8 +387,7 @@ enum ath9k_cal_flags {
 
 struct ath9k_hw_cal_data {
        u16 channel;
-       u32 channelFlags;
-       u32 chanmode;
+       u16 channelFlags;
        unsigned long cal_flags;
        int32_t CalValid;
        int8_t iCoff;
@@ -436,33 +405,34 @@ struct ath9k_hw_cal_data {
 struct ath9k_channel {
        struct ieee80211_channel *chan;
        u16 channel;
-       u32 channelFlags;
-       u32 chanmode;
+       u16 channelFlags;
        s16 noisefloor;
 };
 
-#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \
-       (((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \
-       (((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \
-       (((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS))
-#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0)
-#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0)
-#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0)
-#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0)
-#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0)
+#define CHANNEL_5GHZ           BIT(0)
+#define CHANNEL_HALF           BIT(1)
+#define CHANNEL_QUARTER                BIT(2)
+#define CHANNEL_HT             BIT(3)
+#define CHANNEL_HT40PLUS       BIT(4)
+#define CHANNEL_HT40MINUS      BIT(5)
+
+#define IS_CHAN_5GHZ(_c) (!!((_c)->channelFlags & CHANNEL_5GHZ))
+#define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c))
+
+#define IS_CHAN_HALF_RATE(_c) (!!((_c)->channelFlags & CHANNEL_HALF))
+#define IS_CHAN_QUARTER_RATE(_c) (!!((_c)->channelFlags & CHANNEL_QUARTER))
 #define IS_CHAN_A_FAST_CLOCK(_ah, _c)                  \
-       ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) &&  \
-        ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
-
-/* These macros check chanmode and not channelFlags */
-#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B)
-#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) ||        \
-                         ((_c)->chanmode == CHANNEL_G_HT20))
-#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) ||    \
-                         ((_c)->chanmode == CHANNEL_A_HT40MINUS) ||    \
-                         ((_c)->chanmode == CHANNEL_G_HT40PLUS) ||     \
-                         ((_c)->chanmode == CHANNEL_G_HT40MINUS))
-#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c)))
+       (IS_CHAN_5GHZ(_c) && ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
+
+#define IS_CHAN_HT(_c) ((_c)->channelFlags & CHANNEL_HT)
+
+#define IS_CHAN_HT20(_c) (IS_CHAN_HT(_c) && !IS_CHAN_HT40(_c))
+
+#define IS_CHAN_HT40(_c) \
+       (!!((_c)->channelFlags & (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS)))
+
+#define IS_CHAN_HT40PLUS(_c) ((_c)->channelFlags & CHANNEL_HT40PLUS)
+#define IS_CHAN_HT40MINUS(_c) ((_c)->channelFlags & CHANNEL_HT40MINUS)
 
 enum ath9k_power_mode {
        ATH9K_PM_AWAKE = 0,
@@ -733,6 +703,10 @@ struct ath_hw_ops {
        void (*spectral_scan_trigger)(struct ath_hw *ah);
        void (*spectral_scan_wait)(struct ath_hw *ah);
 
+       void (*tx99_start)(struct ath_hw *ah, u32 qnum);
+       void (*tx99_stop)(struct ath_hw *ah);
+       void (*tx99_set_txpower)(struct ath_hw *ah, u8 power);
+
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
        void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable);
 #endif
@@ -1033,7 +1007,7 @@ void ath9k_hw_reset_tsf(struct ath_hw *ah);
 void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
 void ath9k_hw_init_global_settings(struct ath_hw *ah);
 u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);
-void ath9k_hw_set11nmac2040(struct ath_hw *ah);
+void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan);
 void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period);
 void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
                                    const struct ath9k_beacon_state *bs);
index e3d11c41a14541188119c169392fc8d61f562549..e89db64532f567442ae13106a77a09bb282f2445 100644 (file)
@@ -347,7 +347,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        u8 *ds;
-       struct ath_buf *bf;
        int i, bsize, desc_len;
 
        ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
@@ -399,33 +398,68 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
                ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
 
        /* allocate buffers */
-       bsize = sizeof(struct ath_buf) * nbuf;
-       bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
-       if (!bf)
-               return -ENOMEM;
+       if (is_tx) {
+               struct ath_buf *bf;
+
+               bsize = sizeof(struct ath_buf) * nbuf;
+               bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+               if (!bf)
+                       return -ENOMEM;
 
-       for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
-               bf->bf_desc = ds;
-               bf->bf_daddr = DS2PHYS(dd, ds);
-
-               if (!(sc->sc_ah->caps.hw_caps &
-                     ATH9K_HW_CAP_4KB_SPLITTRANS)) {
-                       /*
-                        * Skip descriptor addresses which can cause 4KB
-                        * boundary crossing (addr + length) with a 32 dword
-                        * descriptor fetch.
-                        */
-                       while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
-                               BUG_ON((caddr_t) bf->bf_desc >=
-                                      ((caddr_t) dd->dd_desc +
-                                       dd->dd_desc_len));
-
-                               ds += (desc_len * ndesc);
-                               bf->bf_desc = ds;
-                               bf->bf_daddr = DS2PHYS(dd, ds);
+               for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
+                       bf->bf_desc = ds;
+                       bf->bf_daddr = DS2PHYS(dd, ds);
+
+                       if (!(sc->sc_ah->caps.hw_caps &
+                                 ATH9K_HW_CAP_4KB_SPLITTRANS)) {
+                               /*
+                                * Skip descriptor addresses which can cause 4KB
+                                * boundary crossing (addr + length) with a 32 dword
+                                * descriptor fetch.
+                                */
+                               while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
+                                       BUG_ON((caddr_t) bf->bf_desc >=
+                                                  ((caddr_t) dd->dd_desc +
+                                               dd->dd_desc_len));
+
+                                       ds += (desc_len * ndesc);
+                                       bf->bf_desc = ds;
+                                       bf->bf_daddr = DS2PHYS(dd, ds);
+                               }
                        }
+                       list_add_tail(&bf->list, head);
+               }
+       } else {
+               struct ath_rxbuf *bf;
+
+               bsize = sizeof(struct ath_rxbuf) * nbuf;
+               bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+               if (!bf)
+                       return -ENOMEM;
+
+               for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
+                       bf->bf_desc = ds;
+                       bf->bf_daddr = DS2PHYS(dd, ds);
+
+                       if (!(sc->sc_ah->caps.hw_caps &
+                                 ATH9K_HW_CAP_4KB_SPLITTRANS)) {
+                               /*
+                                * Skip descriptor addresses which can cause 4KB
+                                * boundary crossing (addr + length) with a 32 dword
+                                * descriptor fetch.
+                                */
+                               while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
+                                       BUG_ON((caddr_t) bf->bf_desc >=
+                                                  ((caddr_t) dd->dd_desc +
+                                               dd->dd_desc_len));
+
+                                       ds += (desc_len * ndesc);
+                                       bf->bf_desc = ds;
+                                       bf->bf_daddr = DS2PHYS(dd, ds);
+                               }
+                       }
+                       list_add_tail(&bf->list, head);
                }
-               list_add_tail(&bf->list, head);
        }
        return 0;
 }
@@ -437,7 +471,6 @@ static int ath9k_init_queues(struct ath_softc *sc)
        sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah);
        sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
 
-       sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
        ath_cabq_update(sc);
 
        sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
@@ -647,7 +680,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        sc->sc_ah = ah;
        pCap = &ah->caps;
 
-       sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET);
+       common = ath9k_hw_common(ah);
+       sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
+       sc->tx99_power = MAX_RATE_POWER + 1;
 
        if (!pdata) {
                ah->ah_flags |= AH_USE_EEPROM;
@@ -661,7 +696,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
                ah->external_reset = pdata->external_reset;
        }
 
-       common = ath9k_hw_common(ah);
        common->ops = &ah->reg_ops;
        common->bus_ops = bus_ops;
        common->ah = ah;
@@ -752,6 +786,7 @@ err_queues:
        ath9k_hw_deinit(ah);
 err_hw:
        ath9k_eeprom_release(sc);
+       dev_kfree_skb_any(sc->tx99_skb);
        return ret;
 }
 
@@ -768,7 +803,7 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
                chan = &sband->channels[i];
                ah->curchan = &ah->channels[chan->hw_value];
                cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
-               ath9k_cmn_update_ichannel(ah->curchan, &chandef);
+               ath9k_cmn_get_channel(sc->hw, ah, &chandef);
                ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
        }
 }
@@ -809,7 +844,6 @@ static const struct ieee80211_iface_limit if_limits[] = {
                                 BIT(NL80211_IFTYPE_P2P_GO) },
 };
 
-
 static const struct ieee80211_iface_limit if_dfs_limits[] = {
        { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) },
 };
@@ -870,17 +904,18 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 
        hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
 
-       hw->wiphy->interface_modes =
-               BIT(NL80211_IFTYPE_P2P_GO) |
-               BIT(NL80211_IFTYPE_P2P_CLIENT) |
-               BIT(NL80211_IFTYPE_AP) |
-               BIT(NL80211_IFTYPE_WDS) |
-               BIT(NL80211_IFTYPE_STATION) |
-               BIT(NL80211_IFTYPE_ADHOC) |
-               BIT(NL80211_IFTYPE_MESH_POINT);
-
-       hw->wiphy->iface_combinations = if_comb;
-       hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+       if (!config_enabled(CONFIG_ATH9K_TX99)) {
+               hw->wiphy->interface_modes =
+                       BIT(NL80211_IFTYPE_P2P_GO) |
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                       BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_WDS) |
+                       BIT(NL80211_IFTYPE_STATION) |
+                       BIT(NL80211_IFTYPE_ADHOC) |
+                       BIT(NL80211_IFTYPE_MESH_POINT);
+               hw->wiphy->iface_combinations = if_comb;
+               hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+       }
 
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
index 84a60644f93acd05ab8ff5fe08bdc01b48fd814e..aed7e29dc50f152b954278020b8b2999f3e795e7 100644 (file)
@@ -28,6 +28,13 @@ void ath_tx_complete_poll_work(struct work_struct *work)
        int i;
        bool needreset = false;
 
+
+       if (sc->tx99_state) {
+               ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
+                       "skip tx hung detection on tx99\n");
+               return;
+       }
+
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                txq = sc->tx.txq_map[i];
 
@@ -70,7 +77,7 @@ void ath_hw_check(struct work_struct *work)
        ath9k_ps_wakeup(sc);
        is_alive = ath9k_hw_check_alive(sc->sc_ah);
 
-       if (is_alive && !AR_SREV_9300(sc->sc_ah))
+       if ((is_alive && !AR_SREV_9300(sc->sc_ah)) || sc->tx99_state)
                goto out;
        else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
                ath_dbg(common, RESET,
@@ -141,6 +148,9 @@ void ath_hw_pll_work(struct work_struct *work)
        if (!test_bit(SC_OP_BEACONS, &sc->sc_flags))
                return;
 
+       if (sc->tx99_state)
+               return;
+
        ath9k_ps_wakeup(sc);
        pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
        ath9k_ps_restore(sc);
@@ -518,7 +528,8 @@ void ath_update_survey_nf(struct ath_softc *sc, int channel)
 
        if (chan->noisefloor) {
                survey->filled |= SURVEY_INFO_NOISE_DBM;
-               survey->noise = ath9k_hw_getchan_noise(ah, chan);
+               survey->noise = ath9k_hw_getchan_noise(ah, chan,
+                                                      chan->noisefloor);
        }
 }
 
index a3eff0986a3f95c1ea6b523fc71b02a1ac2b4eba..6a18f9d3e9cc952ef116f9fda1bf56f37526d8bc 100644 (file)
@@ -374,7 +374,6 @@ EXPORT_SYMBOL(ath9k_hw_releasetxqueue);
 bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ath9k_channel *chan = ah->curchan;
        struct ath9k_tx_queue_info *qi;
        u32 cwMin, chanCwMin, value;
 
@@ -387,10 +386,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
        ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q);
 
        if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) {
-               if (chan && IS_CHAN_B(chan))
-                       chanCwMin = INIT_CWMIN_11B;
-               else
-                       chanCwMin = INIT_CWMIN;
+               chanCwMin = INIT_CWMIN;
 
                for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1);
        } else
index bfccaceed44ef82f34c7e9df6562975f4322bf5e..e3eed81f24391c61b1389a749d471cf4ad662056 100644 (file)
@@ -603,8 +603,6 @@ enum ath9k_tx_queue_flags {
 #define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001
 
 #define ATH9K_DECOMP_MASK_SIZE     128
-#define ATH9K_READY_TIME_LO_BOUND  50
-#define ATH9K_READY_TIME_HI_BOUND  96
 
 enum ath9k_pkt_type {
        ATH9K_PKT_TYPE_NORMAL = 0,
index cdb3b1e10b952421d44ee9a3489e46a1431e951f..74f452c7b1667c47a65506a077042f2b0668c3a8 100644 (file)
@@ -208,6 +208,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        unsigned long flags;
+       int i;
 
        if (ath_startrecv(sc) != 0) {
                ath_err(common, "Unable to restart recv logic\n");
@@ -235,6 +236,15 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
                }
        work:
                ath_restart_work(sc);
+
+               for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
+                       if (!ATH_TXQ_SETUP(sc, i))
+                               continue;
+
+                       spin_lock_bh(&sc->tx.txq[i].axq_lock);
+                       ath_txq_schedule(sc, &sc->tx.txq[i]);
+                       spin_unlock_bh(&sc->tx.txq[i].axq_lock);
+               }
        }
 
        ieee80211_wake_queues(sc->hw);
@@ -302,17 +312,91 @@ out:
  * by reseting the chip.  To accomplish this we must first cleanup any pending
  * DMA, then restart stuff.
 */
-static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
-                   struct ath9k_channel *hchan)
+static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef)
 {
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ieee80211_hw *hw = sc->hw;
+       struct ath9k_channel *hchan;
+       struct ieee80211_channel *chan = chandef->chan;
+       unsigned long flags;
+       bool offchannel;
+       int pos = chan->hw_value;
+       int old_pos = -1;
        int r;
 
        if (test_bit(SC_OP_INVALID, &sc->sc_flags))
                return -EIO;
 
+       offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
+
+       if (ah->curchan)
+               old_pos = ah->curchan - &ah->channels[0];
+
+       ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+               chan->center_freq, chandef->width);
+
+       /* update survey stats for the old channel before switching */
+       spin_lock_irqsave(&common->cc_lock, flags);
+       ath_update_survey_stats(sc);
+       spin_unlock_irqrestore(&common->cc_lock, flags);
+
+       ath9k_cmn_get_channel(hw, ah, chandef);
+
+       /*
+        * If the operating channel changes, change the survey in-use flags
+        * along with it.
+        * Reset the survey data for the new channel, unless we're switching
+        * back to the operating channel from an off-channel operation.
+        */
+       if (!offchannel && sc->cur_survey != &sc->survey[pos]) {
+               if (sc->cur_survey)
+                       sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
+
+               sc->cur_survey = &sc->survey[pos];
+
+               memset(sc->cur_survey, 0, sizeof(struct survey_info));
+               sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
+       } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
+               memset(&sc->survey[pos], 0, sizeof(struct survey_info));
+       }
+
+       hchan = &sc->sc_ah->channels[pos];
        r = ath_reset_internal(sc, hchan);
+       if (r)
+               return r;
 
-       return r;
+       /*
+        * The most recent snapshot of channel->noisefloor for the old
+        * channel is only available after the hardware reset. Copy it to
+        * the survey stats now.
+        */
+       if (old_pos >= 0)
+               ath_update_survey_nf(sc, old_pos);
+
+       /*
+        * Enable radar pulse detection if on a DFS channel. Spectral
+        * scanning and radar detection can not be used concurrently.
+        */
+       if (hw->conf.radar_enabled) {
+               u32 rxfilter;
+
+               /* set HW specific DFS configuration */
+               ath9k_hw_set_radar_params(ah);
+               rxfilter = ath9k_hw_getrxfilter(ah);
+               rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
+                               ATH9K_RX_FILTER_PHYERR;
+               ath9k_hw_setrxfilter(ah, rxfilter);
+               ath_dbg(common, DFS, "DFS enabled at freq %d\n",
+                       chan->center_freq);
+       } else {
+               /* perform spectral scan if requested. */
+               if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
+                       sc->spectral_mode == SPECTRAL_CHANSCAN)
+                       ath9k_spectral_scan_trigger(hw);
+       }
+
+       return 0;
 }
 
 static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
@@ -545,21 +629,10 @@ chip_reset:
 
 static int ath_reset(struct ath_softc *sc)
 {
-       int i, r;
+       int r;
 
        ath9k_ps_wakeup(sc);
-
        r = ath_reset_internal(sc, NULL);
-
-       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
-               if (!ATH_TXQ_SETUP(sc, i))
-                       continue;
-
-               spin_lock_bh(&sc->tx.txq[i].axq_lock);
-               ath_txq_schedule(sc, &sc->tx.txq[i]);
-               spin_unlock_bh(&sc->tx.txq[i].axq_lock);
-       }
-
        ath9k_ps_restore(sc);
 
        return r;
@@ -601,7 +674,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
        ath9k_ps_wakeup(sc);
        mutex_lock(&sc->mutex);
 
-       init_channel = ath9k_cmn_get_curchannel(hw, ah);
+       init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
 
        /* Reset SERDES registers */
        ath9k_hw_configpcipowersave(ah, false);
@@ -804,7 +877,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        }
 
        if (!ah->curchan)
-               ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
+               ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
 
        ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        ath9k_hw_phy_disable(ah);
@@ -823,7 +896,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        ath_dbg(common, CONFIG, "Driver halt\n");
 }
 
-bool ath9k_uses_beacons(int type)
+static bool ath9k_uses_beacons(int type)
 {
        switch (type) {
        case NL80211_IFTYPE_AP:
@@ -973,6 +1046,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&sc->mutex);
 
+       if (config_enabled(CONFIG_ATH9K_TX99)) {
+               if (sc->nvifs >= 1) {
+                       mutex_unlock(&sc->mutex);
+                       return -EOPNOTSUPP;
+               }
+               sc->tx99_vif = vif;
+       }
+
        ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
        sc->nvifs++;
 
@@ -1001,9 +1082,15 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 
-       ath_dbg(common, CONFIG, "Change Interface\n");
        mutex_lock(&sc->mutex);
 
+       if (config_enabled(CONFIG_ATH9K_TX99)) {
+               mutex_unlock(&sc->mutex);
+               return -EOPNOTSUPP;
+       }
+
+       ath_dbg(common, CONFIG, "Change Interface\n");
+
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
 
@@ -1033,6 +1120,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
        mutex_lock(&sc->mutex);
 
        sc->nvifs--;
+       sc->tx99_vif = NULL;
 
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
@@ -1054,6 +1142,9 @@ static void ath9k_enable_ps(struct ath_softc *sc)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return;
+
        sc->ps_enabled = true;
        if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
                if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) {
@@ -1070,6 +1161,9 @@ static void ath9k_disable_ps(struct ath_softc *sc)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return;
+
        sc->ps_enabled = false;
        ath9k_hw_setpower(ah, ATH9K_PM_AWAKE);
        if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
@@ -1093,6 +1187,9 @@ void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw)
        struct ath_common *common = ath9k_hw_common(ah);
        u32 rxfilter;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return;
+
        if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
                ath_err(common, "spectrum analyzer not implemented on this hardware\n");
                return;
@@ -1208,81 +1305,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
        }
 
        if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
-               struct ieee80211_channel *curchan = hw->conf.chandef.chan;
-               int pos = curchan->hw_value;
-               int old_pos = -1;
-               unsigned long flags;
-
-               if (ah->curchan)
-                       old_pos = ah->curchan - &ah->channels[0];
-
-               ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
-                       curchan->center_freq, hw->conf.chandef.width);
-
-               /* update survey stats for the old channel before switching */
-               spin_lock_irqsave(&common->cc_lock, flags);
-               ath_update_survey_stats(sc);
-               spin_unlock_irqrestore(&common->cc_lock, flags);
-
-               ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
-                                         &conf->chandef);
-
-               /*
-                * If the operating channel changes, change the survey in-use flags
-                * along with it.
-                * Reset the survey data for the new channel, unless we're switching
-                * back to the operating channel from an off-channel operation.
-                */
-               if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) &&
-                   sc->cur_survey != &sc->survey[pos]) {
-
-                       if (sc->cur_survey)
-                               sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
-
-                       sc->cur_survey = &sc->survey[pos];
-
-                       memset(sc->cur_survey, 0, sizeof(struct survey_info));
-                       sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
-               } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
-                       memset(&sc->survey[pos], 0, sizeof(struct survey_info));
-               }
-
-               if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
+               if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
                        ath_err(common, "Unable to set channel\n");
                        mutex_unlock(&sc->mutex);
                        ath9k_ps_restore(sc);
                        return -EINVAL;
                }
-
-               /*
-                * The most recent snapshot of channel->noisefloor for the old
-                * channel is only available after the hardware reset. Copy it to
-                * the survey stats now.
-                */
-               if (old_pos >= 0)
-                       ath_update_survey_nf(sc, old_pos);
-
-               /*
-                * Enable radar pulse detection if on a DFS channel. Spectral
-                * scanning and radar detection can not be used concurrently.
-                */
-               if (hw->conf.radar_enabled) {
-                       u32 rxfilter;
-
-                       /* set HW specific DFS configuration */
-                       ath9k_hw_set_radar_params(ah);
-                       rxfilter = ath9k_hw_getrxfilter(ah);
-                       rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
-                                   ATH9K_RX_FILTER_PHYERR;
-                       ath9k_hw_setrxfilter(ah, rxfilter);
-                       ath_dbg(common, DFS, "DFS enabled at freq %d\n",
-                               curchan->center_freq);
-               } else {
-                       /* perform spectral scan if requested. */
-                       if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
-                           sc->spectral_mode == SPECTRAL_CHANSCAN)
-                               ath9k_spectral_scan_trigger(hw);
-               }
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
@@ -1741,6 +1769,9 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
        unsigned long flags;
        int pos;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return -EOPNOTSUPP;
+
        spin_lock_irqsave(&common->cc_lock, flags);
        if (idx == 0)
                ath_update_survey_stats(sc);
@@ -1773,6 +1804,9 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return;
+
        mutex_lock(&sc->mutex);
        ah->coverage_class = coverage_class;
 
@@ -2339,6 +2373,134 @@ static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
        sc->csa_vif = vif;
 }
 
+static void ath9k_tx99_stop(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       ath_drain_all_txq(sc);
+       ath_startrecv(sc);
+
+       ath9k_hw_set_interrupts(ah);
+       ath9k_hw_enable_interrupts(ah);
+
+       ieee80211_wake_queues(sc->hw);
+
+       kfree_skb(sc->tx99_skb);
+       sc->tx99_skb = NULL;
+       sc->tx99_state = false;
+
+       ath9k_hw_tx99_stop(sc->sc_ah);
+       ath_dbg(common, XMIT, "TX99 stopped\n");
+}
+
+static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
+{
+       static u8 PN9Data[] = {0xff, 0x87, 0xb8, 0x59, 0xb7, 0xa1, 0xcc, 0x24,
+                              0x57, 0x5e, 0x4b, 0x9c, 0x0e, 0xe9, 0xea, 0x50,
+                              0x2a, 0xbe, 0xb4, 0x1b, 0xb6, 0xb0, 0x5d, 0xf1,
+                              0xe6, 0x9a, 0xe3, 0x45, 0xfd, 0x2c, 0x53, 0x18,
+                              0x0c, 0xca, 0xc9, 0xfb, 0x49, 0x37, 0xe5, 0xa8,
+                              0x51, 0x3b, 0x2f, 0x61, 0xaa, 0x72, 0x18, 0x84,
+                              0x02, 0x23, 0x23, 0xab, 0x63, 0x89, 0x51, 0xb3,
+                              0xe7, 0x8b, 0x72, 0x90, 0x4c, 0xe8, 0xfb, 0xc0};
+       u32 len = 1200;
+       struct ieee80211_hw *hw = sc->hw;
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_tx_info *tx_info;
+       struct sk_buff *skb;
+
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       skb_put(skb, len);
+
+       memset(skb->data, 0, len);
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
+       hdr->duration_id = 0;
+
+       memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
+       memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
+       memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
+
+       hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
+
+       tx_info = IEEE80211_SKB_CB(skb);
+       memset(tx_info, 0, sizeof(*tx_info));
+       tx_info->band = hw->conf.chandef.chan->band;
+       tx_info->flags = IEEE80211_TX_CTL_NO_ACK;
+       tx_info->control.vif = sc->tx99_vif;
+
+       memcpy(skb->data + sizeof(*hdr), PN9Data, sizeof(PN9Data));
+
+       return skb;
+}
+
+void ath9k_tx99_deinit(struct ath_softc *sc)
+{
+       ath_reset(sc);
+
+       ath9k_ps_wakeup(sc);
+       ath9k_tx99_stop(sc);
+       ath9k_ps_restore(sc);
+}
+
+int ath9k_tx99_init(struct ath_softc *sc)
+{
+       struct ieee80211_hw *hw = sc->hw;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_tx_control txctl;
+       int r;
+
+       if (sc->sc_flags & SC_OP_INVALID) {
+               ath_err(common,
+                       "driver is in invalid state unable to use TX99");
+               return -EINVAL;
+       }
+
+       sc->tx99_skb = ath9k_build_tx99_skb(sc);
+       if (!sc->tx99_skb)
+               return -ENOMEM;
+
+       memset(&txctl, 0, sizeof(txctl));
+       txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
+
+       ath_reset(sc);
+
+       ath9k_ps_wakeup(sc);
+
+       ath9k_hw_disable_interrupts(ah);
+       atomic_set(&ah->intr_ref_cnt, -1);
+       ath_drain_all_txq(sc);
+       ath_stoprecv(sc);
+
+       sc->tx99_state = true;
+
+       ieee80211_stop_queues(hw);
+
+       if (sc->tx99_power == MAX_RATE_POWER + 1)
+               sc->tx99_power = MAX_RATE_POWER;
+
+       ath9k_hw_tx99_set_txpower(ah, sc->tx99_power);
+       r = ath9k_tx99_send(sc, sc->tx99_skb, &txctl);
+       if (r) {
+               ath_dbg(common, XMIT, "Failed to xmit TX99 skb\n");
+               return r;
+       }
+
+       ath_dbg(common, XMIT, "TX99 xmit started using %d ( %ddBm)\n",
+               sc->tx99_power,
+               sc->tx99_power / 2);
+
+       /* We leave the harware awake as it will be chugging on */
+
+       return 0;
+}
+
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
index 815bee21c19a0fe1aeb2194fbdba2372be2b279b..0ac1b5f04256517050be277696e9a9eae4f2391c 100644 (file)
@@ -661,9 +661,9 @@ void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all)
        chan_start = wlan_chan - 10;
        chan_end = wlan_chan + 10;
 
-       if (chan->chanmode == CHANNEL_G_HT40PLUS)
+       if (IS_CHAN_HT40PLUS(chan))
                chan_end += 20;
-       else if (chan->chanmode == CHANNEL_G_HT40MINUS)
+       else if (IS_CHAN_HT40MINUS(chan))
                chan_start -= 20;
 
        /* adjust side band */
@@ -707,11 +707,11 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
 
        if (setchannel) {
                struct ath9k_hw_cal_data *caldata = &sc->caldata;
-               if ((caldata->chanmode == CHANNEL_G_HT40PLUS) &&
+               if (IS_CHAN_HT40PLUS(ah->curchan) &&
                    (ah->curchan->channel > caldata->channel) &&
                    (ah->curchan->channel <= caldata->channel + 20))
                        return;
-               if ((caldata->chanmode == CHANNEL_G_HT40MINUS) &&
+               if (IS_CHAN_HT40MINUS(ah->curchan) &&
                    (ah->curchan->channel < caldata->channel) &&
                    (ah->curchan->channel >= caldata->channel - 20))
                        return;
index 4ee472a5a4e4ee6e81b1c0ffc820b6f685fb7ad1..95ddca5495d492cb5a1bf4bb99d8ee3283a9e1ec 100644 (file)
@@ -19,7 +19,7 @@
 #include "ath9k.h"
 #include "ar9003_mac.h"
 
-#define SKB_CB_ATHBUF(__skb)   (*((struct ath_buf **)__skb->cb))
+#define SKB_CB_ATHBUF(__skb)   (*((struct ath_rxbuf **)__skb->cb))
 
 static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
 {
@@ -35,7 +35,7 @@ static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
  * buffer (or rx fifo). This can incorrectly acknowledge packets
  * to a sender if last desc is self-linked.
  */
-static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
+static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
@@ -68,7 +68,7 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
        sc->rx.rxlink = &ds->ds_link;
 }
 
-static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
+static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf)
 {
        if (sc->rx.buf_hold)
                ath_rx_buf_link(sc, sc->rx.buf_hold);
@@ -112,13 +112,13 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc,
        struct ath_hw *ah = sc->sc_ah;
        struct ath_rx_edma *rx_edma;
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
 
        rx_edma = &sc->rx.rx_edma[qtype];
        if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize)
                return false;
 
-       bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+       bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
        list_del_init(&bf->list);
 
        skb = bf->bf_mpdu;
@@ -138,7 +138,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
                                  enum ath9k_rx_qtype qtype)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_buf *bf, *tbf;
+       struct ath_rxbuf *bf, *tbf;
 
        if (list_empty(&sc->rx.rxbuf)) {
                ath_dbg(common, QUEUE, "No free rx buf available\n");
@@ -154,7 +154,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
 static void ath_rx_remove_buffer(struct ath_softc *sc,
                                 enum ath9k_rx_qtype qtype)
 {
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        struct ath_rx_edma *rx_edma;
        struct sk_buff *skb;
 
@@ -171,7 +171,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
 
        ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP);
        ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP);
@@ -199,7 +199,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_hw *ah = sc->sc_ah;
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        int error = 0, i;
        u32 size;
 
@@ -211,7 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
        ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
                               ah->caps.rx_hp_qdepth);
 
-       size = sizeof(struct ath_buf) * nbufs;
+       size = sizeof(struct ath_rxbuf) * nbufs;
        bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
        if (!bf)
                return -ENOMEM;
@@ -271,7 +271,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        int error = 0;
 
        spin_lock_init(&sc->sc_pcu_lock);
@@ -332,7 +332,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
                ath_rx_edma_cleanup(sc);
@@ -375,6 +375,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
 {
        u32 rfilt;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return 0;
+
        rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
                | ATH9K_RX_FILTER_MCAST;
 
@@ -427,7 +430,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
 int ath_startrecv(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
-       struct ath_buf *bf, *tbf;
+       struct ath_rxbuf *bf, *tbf;
 
        if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
                ath_edma_start_recv(sc);
@@ -447,7 +450,7 @@ int ath_startrecv(struct ath_softc *sc)
        if (list_empty(&sc->rx.rxbuf))
                goto start_recv;
 
-       bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+       bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
        ath9k_hw_putrxbuf(ah, bf->bf_daddr);
        ath9k_hw_rxena(ah);
 
@@ -603,13 +606,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon)
 static bool ath_edma_get_buffers(struct ath_softc *sc,
                                 enum ath9k_rx_qtype qtype,
                                 struct ath_rx_status *rs,
-                                struct ath_buf **dest)
+                                struct ath_rxbuf **dest)
 {
        struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype];
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        int ret;
 
        skb = skb_peek(&rx_edma->rx_fifo);
@@ -653,11 +656,11 @@ static bool ath_edma_get_buffers(struct ath_softc *sc,
        return true;
 }
 
-static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
+static struct ath_rxbuf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
                                                struct ath_rx_status *rs,
                                                enum ath9k_rx_qtype qtype)
 {
-       struct ath_buf *bf = NULL;
+       struct ath_rxbuf *bf = NULL;
 
        while (ath_edma_get_buffers(sc, qtype, rs, &bf)) {
                if (!bf)
@@ -668,13 +671,13 @@ static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
        return NULL;
 }
 
-static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
+static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc,
                                           struct ath_rx_status *rs)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath_desc *ds;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        int ret;
 
        if (list_empty(&sc->rx.rxbuf)) {
@@ -682,7 +685,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
                return NULL;
        }
 
-       bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+       bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
        if (bf == sc->rx.buf_hold)
                return NULL;
 
@@ -702,7 +705,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
        ret = ath9k_hw_rxprocdesc(ah, ds, rs);
        if (ret == -EINPROGRESS) {
                struct ath_rx_status trs;
-               struct ath_buf *tbf;
+               struct ath_rxbuf *tbf;
                struct ath_desc *tds;
 
                memset(&trs, 0, sizeof(trs));
@@ -711,7 +714,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
                        return NULL;
                }
 
-               tbf = list_entry(bf->list.next, struct ath_buf, list);
+               tbf = list_entry(bf->list.next, struct ath_rxbuf, list);
 
                /*
                 * On some hardware the descriptor status words could
@@ -972,14 +975,15 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
 {
 #ifdef CONFIG_ATH9K_DEBUGFS
        struct ath_hw *ah = sc->sc_ah;
-       u8 bins[SPECTRAL_HT20_NUM_BINS];
-       u8 *vdata = (u8 *)hdr;
-       struct fft_sample_ht20 fft_sample;
+       u8 num_bins, *bins, *vdata = (u8 *)hdr;
+       struct fft_sample_ht20 fft_sample_20;
+       struct fft_sample_ht20_40 fft_sample_40;
+       struct fft_sample_tlv *tlv;
        struct ath_radar_info *radar_info;
-       struct ath_ht20_mag_info *mag_info;
        int len = rs->rs_datalen;
        int dc_pos;
-       u16 length, max_magnitude;
+       u16 fft_len, length, freq = ah->curchan->chan->center_freq;
+       enum nl80211_channel_type chan_type;
 
        /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
         * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
@@ -997,45 +1001,44 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
        if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
                return 0;
 
-       /* Variation in the data length is possible and will be fixed later.
-        * Note that we only support HT20 for now.
-        *
-        * TODO: add HT20_40 support as well.
-        */
-       if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) ||
-           (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1))
-               return 1;
-
-       fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20;
-       length = sizeof(fft_sample) - sizeof(fft_sample.tlv);
-       fft_sample.tlv.length = __cpu_to_be16(length);
+       chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
+       if ((chan_type == NL80211_CHAN_HT40MINUS) ||
+           (chan_type == NL80211_CHAN_HT40PLUS)) {
+               fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
+               num_bins = SPECTRAL_HT20_40_NUM_BINS;
+               bins = (u8 *)fft_sample_40.data;
+       } else {
+               fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
+               num_bins = SPECTRAL_HT20_NUM_BINS;
+               bins = (u8 *)fft_sample_20.data;
+       }
 
-       fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq);
-       fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
-       fft_sample.noise = ah->noise;
+       /* Variation in the data length is possible and will be fixed later */
+       if ((len > fft_len + 2) || (len < fft_len - 1))
+               return 1;
 
-       switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) {
+       switch (len - fft_len) {
        case 0:
                /* length correct, nothing to do. */
-               memcpy(bins, vdata, SPECTRAL_HT20_NUM_BINS);
+               memcpy(bins, vdata, num_bins);
                break;
        case -1:
                /* first byte missing, duplicate it. */
-               memcpy(&bins[1], vdata, SPECTRAL_HT20_NUM_BINS - 1);
+               memcpy(&bins[1], vdata, num_bins - 1);
                bins[0] = vdata[0];
                break;
        case 2:
                /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
                memcpy(bins, vdata, 30);
                bins[30] = vdata[31];
-               memcpy(&bins[31], &vdata[33], SPECTRAL_HT20_NUM_BINS - 31);
+               memcpy(&bins[31], &vdata[33], num_bins - 31);
                break;
        case 1:
                /* MAC added 2 extra bytes AND first byte is missing. */
                bins[0] = vdata[0];
-               memcpy(&bins[0], vdata, 30);
+               memcpy(&bins[1], vdata, 30);
                bins[31] = vdata[31];
-               memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32);
+               memcpy(&bins[32], &vdata[33], num_bins - 32);
                break;
        default:
                return 1;
@@ -1044,23 +1047,93 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
        /* DC value (value in the middle) is the blind spot of the spectral
         * sample and invalid, interpolate it.
         */
-       dc_pos = SPECTRAL_HT20_NUM_BINS / 2;
+       dc_pos = num_bins / 2;
        bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;
 
-       /* mag data is at the end of the frame, in front of radar_info */
-       mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
+       if ((chan_type == NL80211_CHAN_HT40MINUS) ||
+           (chan_type == NL80211_CHAN_HT40PLUS)) {
+               s8 lower_rssi, upper_rssi;
+               s16 ext_nf;
+               u8 lower_max_index, upper_max_index;
+               u8 lower_bitmap_w, upper_bitmap_w;
+               u16 lower_mag, upper_mag;
+               struct ath9k_hw_cal_data *caldata = ah->caldata;
+               struct ath_ht20_40_mag_info *mag_info;
+
+               if (caldata)
+                       ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
+                                       caldata->nfCalHist[3].privNF);
+               else
+                       ext_nf = ATH_DEFAULT_NOISE_FLOOR;
+
+               length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
+               fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
+               fft_sample_40.tlv.length = __cpu_to_be16(length);
+               fft_sample_40.freq = __cpu_to_be16(freq);
+               fft_sample_40.channel_type = chan_type;
+
+               if (chan_type == NL80211_CHAN_HT40PLUS) {
+                       lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
+                       upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0);
 
-       /* copy raw bins without scaling them */
-       memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS);
-       fft_sample.max_exp = mag_info->max_exp & 0xf;
+                       fft_sample_40.lower_noise = ah->noise;
+                       fft_sample_40.upper_noise = ext_nf;
+               } else {
+                       lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0);
+                       upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
 
-       max_magnitude = spectral_max_magnitude(mag_info->all_bins);
-       fft_sample.max_magnitude = __cpu_to_be16(max_magnitude);
-       fft_sample.max_index = spectral_max_index(mag_info->all_bins);
-       fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins);
-       fft_sample.tsf = __cpu_to_be64(tsf);
+                       fft_sample_40.lower_noise = ext_nf;
+                       fft_sample_40.upper_noise = ah->noise;
+               }
+               fft_sample_40.lower_rssi = lower_rssi;
+               fft_sample_40.upper_rssi = upper_rssi;
+
+               mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1;
+               lower_mag = spectral_max_magnitude(mag_info->lower_bins);
+               upper_mag = spectral_max_magnitude(mag_info->upper_bins);
+               fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
+               fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
+               lower_max_index = spectral_max_index(mag_info->lower_bins);
+               upper_max_index = spectral_max_index(mag_info->upper_bins);
+               fft_sample_40.lower_max_index = lower_max_index;
+               fft_sample_40.upper_max_index = upper_max_index;
+               lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
+               upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
+               fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
+               fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
+               fft_sample_40.max_exp = mag_info->max_exp & 0xf;
+
+               fft_sample_40.tsf = __cpu_to_be64(tsf);
+
+               tlv = (struct fft_sample_tlv *)&fft_sample_40;
+       } else {
+               u8 max_index, bitmap_w;
+               u16 magnitude;
+               struct ath_ht20_mag_info *mag_info;
+
+               length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
+               fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
+               fft_sample_20.tlv.length = __cpu_to_be16(length);
+               fft_sample_20.freq = __cpu_to_be16(freq);
+
+               fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
+               fft_sample_20.noise = ah->noise;
+
+               mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
+               magnitude = spectral_max_magnitude(mag_info->all_bins);
+               fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
+               max_index = spectral_max_index(mag_info->all_bins);
+               fft_sample_20.max_index = max_index;
+               bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
+               fft_sample_20.bitmap_weight = bitmap_w;
+               fft_sample_20.max_exp = mag_info->max_exp & 0xf;
+
+               fft_sample_20.tsf = __cpu_to_be64(tsf);
+
+               tlv = (struct fft_sample_tlv *)&fft_sample_20;
+       }
 
-       ath_debug_send_fft_sample(sc, &fft_sample.tlv);
+       ath_debug_send_fft_sample(sc, tlv);
        return 1;
 #else
        return 0;
@@ -1269,13 +1342,6 @@ static void ath9k_antenna_check(struct ath_softc *sc,
        if (!(ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB))
                return;
 
-       /*
-        * All MPDUs in an aggregate will use the same LNA
-        * as the first MPDU.
-        */
-       if (rs->rs_isaggr && !rs->rs_firstaggr)
-               return;
-
        /*
         * Change the default rx antenna if rx diversity
         * chooses the other antenna 3 times in a row.
@@ -1315,7 +1381,7 @@ static void ath9k_apply_ampdu_details(struct ath_softc *sc,
 
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 {
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb;
        struct ieee80211_rx_status *rxs;
        struct ath_hw *ah = sc->sc_ah;
index 62c93a655df9b9e73b95455f33c58dc3161bc3c8..09cdbcd097394a3a2c324230c2743f5d181b0900 100644 (file)
@@ -399,6 +399,7 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf)
        tbf->bf_buf_addr = bf->bf_buf_addr;
        memcpy(tbf->bf_desc, bf->bf_desc, sc->sc_ah->caps.tx_desc_len);
        tbf->bf_state = bf->bf_state;
+       tbf->bf_state.stale = false;
 
        return tbf;
 }
@@ -1240,12 +1241,13 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                if (bf->bf_next)
                        info.link = bf->bf_next->bf_daddr;
                else
-                       info.link = 0;
+                       info.link = (sc->tx99_state) ? bf->bf_daddr : 0;
 
                if (!bf_first) {
                        bf_first = bf;
 
-                       info.flags = ATH9K_TXDESC_INTREQ;
+                       if (!sc->tx99_state)
+                               info.flags = ATH9K_TXDESC_INTREQ;
                        if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) ||
                            txq == sc->tx.uapsdq)
                                info.flags |= ATH9K_TXDESC_CLRDMASK;
@@ -1389,11 +1391,15 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
                      u16 tid, u16 *ssn)
 {
        struct ath_atx_tid *txtid;
+       struct ath_txq *txq;
        struct ath_node *an;
        u8 density;
 
        an = (struct ath_node *)sta->drv_priv;
        txtid = ATH_AN_2_TID(an, tid);
+       txq = txtid->ac->txq;
+
+       ath_txq_lock(sc, txq);
 
        /* update ampdu factor/density, they may have changed. This may happen
         * in HT IBSS when a beacon with HT-info is received after the station
@@ -1417,6 +1423,8 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
        memset(txtid->tx_buf, 0, sizeof(txtid->tx_buf));
        txtid->baw_head = txtid->baw_tail = 0;
 
+       ath_txq_unlock_complete(sc, txq);
+
        return 0;
 }
 
@@ -1555,8 +1563,10 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
                        __skb_unlink(bf->bf_mpdu, tid_q);
                        list_add_tail(&bf->list, &bf_q);
                        ath_set_rates(tid->an->vif, tid->an->sta, bf);
-                       ath_tx_addto_baw(sc, tid, bf);
-                       bf->bf_state.bf_type &= ~BUF_AGGR;
+                       if (bf_isampdu(bf)) {
+                               ath_tx_addto_baw(sc, tid, bf);
+                               bf->bf_state.bf_type &= ~BUF_AGGR;
+                       }
                        if (bf_tail)
                                bf_tail->bf_next = bf;
 
@@ -1695,16 +1705,9 @@ int ath_cabq_update(struct ath_softc *sc)
        int qnum = sc->beacon.cabq->axq_qnum;
 
        ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
-       /*
-        * Ensure the readytime % is within the bounds.
-        */
-       if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND)
-               sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND;
-       else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND)
-               sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND;
 
        qi.tqi_readyTime = (cur_conf->beacon_interval *
-                           sc->config.cabqReadytime) / 100;
+                           ATH_CABQ_READY_TIME) / 100;
        ath_txq_update(sc, qnum, &qi);
 
        return 0;
@@ -1939,7 +1942,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
                        txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
        }
 
-       if (!edma) {
+       if (!edma || sc->tx99_state) {
                TX_STAT_INC(txq->axq_qnum, txstart);
                ath9k_hw_txstart(ah, txq->axq_qnum);
        }
@@ -1950,7 +1953,9 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
                        if (bf_is_ampdu_not_probing(bf))
                                txq->axq_ampdu_depth++;
 
-                       bf = bf->bf_lastbf->bf_next;
+                       bf_last = bf->bf_lastbf;
+                       bf = bf_last->bf_next;
+                       bf_last->bf_next = NULL;
                }
        }
 }
@@ -1958,15 +1963,18 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
                               struct ath_atx_tid *tid, struct sk_buff *skb)
 {
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ath_frame_info *fi = get_frame_info(skb);
        struct list_head bf_head;
-       struct ath_buf *bf;
-
-       bf = fi->bf;
+       struct ath_buf *bf = fi->bf;
 
        INIT_LIST_HEAD(&bf_head);
        list_add_tail(&bf->list, &bf_head);
        bf->bf_state.bf_type = 0;
+       if (tid && (tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
+               bf->bf_state.bf_type = BUF_AMPDU;
+               ath_tx_addto_baw(sc, tid, bf);
+       }
 
        bf->bf_next = NULL;
        bf->bf_lastbf = bf;
@@ -2013,6 +2021,9 @@ static void setup_frame_info(struct ieee80211_hw *hw,
                fi->keyix = ATH9K_TXKEYIX_INVALID;
        fi->keytype = keytype;
        fi->framelen = framelen;
+
+       if (!rate)
+               return;
        fi->rtscts_rate = rate->hw_value;
        if (short_preamble)
                fi->rtscts_rate |= rate->hw_value_short;
@@ -2023,8 +2034,7 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate)
        struct ath_hw *ah = sc->sc_ah;
        struct ath9k_channel *curchan = ah->curchan;
 
-       if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) &&
-           (curchan->channelFlags & CHANNEL_5GHZ) &&
+       if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && IS_CHAN_5GHZ(curchan) &&
            (chainmask == 0x7) && (rate < 0x90))
                return 0x3;
        else if (AR_SREV_9462(ah) && ath9k_hw_btcoex_is_enabled(ah) &&
@@ -2365,6 +2375,8 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
 
        dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE);
        bf->bf_buf_addr = 0;
+       if (sc->tx99_state)
+               goto skip_tx_complete;
 
        if (bf->bf_state.bfs_paprd) {
                if (time_after(jiffies,
@@ -2377,6 +2389,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
                ath_debug_stat_tx(sc, bf, ts, txq, tx_flags);
                ath_tx_complete(sc, skb, tx_flags, txq);
        }
+skip_tx_complete:
        /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't
         * accidentally reference it later.
         */
@@ -2735,3 +2748,46 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
                ath_txq_unlock(sc, txq);
        }
 }
+
+int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb,
+                   struct ath_tx_control *txctl)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ath_frame_info *fi = get_frame_info(skb);
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_buf *bf;
+       int padpos, padsize;
+
+       padpos = ieee80211_hdrlen(hdr->frame_control);
+       padsize = padpos & 3;
+
+       if (padsize && skb->len > padpos) {
+               if (skb_headroom(skb) < padsize) {
+                       ath_dbg(common, XMIT,
+                               "tx99 padding failed\n");
+               return -EINVAL;
+               }
+
+               skb_push(skb, padsize);
+               memmove(skb->data, skb->data + padsize, padpos);
+       }
+
+       fi->keyix = ATH9K_TXKEYIX_INVALID;
+       fi->framelen = skb->len + FCS_LEN;
+       fi->keytype = ATH9K_KEY_TYPE_CLEAR;
+
+       bf = ath_tx_setup_buffer(sc, txctl->txq, NULL, skb);
+       if (!bf) {
+               ath_dbg(common, XMIT, "tx99 buffer setup failed\n");
+               return -EINVAL;
+       }
+
+       ath_set_rates(sc->tx99_vif, NULL, bf);
+
+       ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr);
+       ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum);
+
+       ath_tx_send_normal(sc, txctl->txq, NULL, skb);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c
new file mode 100644 (file)
index 0000000..a1a69c5
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * 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/slab.h>
+#include <linux/export.h>
+
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+#include "ath.h"
+
+/*
+ * tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE  16
+
+/**
+ * struct radar_types - contains array of patterns defined for one DFS domain
+ * @domain: DFS regulatory domain
+ * @num_radar_types: number of radar types to follow
+ * @radar_types: radar types array
+ */
+struct radar_types {
+       enum nl80211_dfs_regions region;
+       u32 num_radar_types;
+       const struct radar_detector_specs *radar_types;
+};
+
+/* percentage on ppb threshold to trigger detection */
+#define MIN_PPB_THRESH 50
+#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
+#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
+/* percentage of pulse width tolerance */
+#define WIDTH_TOLERANCE 5
+#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100)
+#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100)
+
+#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)     \
+{                                                              \
+       ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
+       (PRF2PRI(PMAX) - PRI_TOLERANCE),                        \
+       (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF,  \
+       PPB_THRESH(PPB), PRI_TOLERANCE,                         \
+}
+
+/* radar types as defined by ETSI EN-301-893 v1.5.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v15[] = {
+       ETSI_PATTERN(0,  0,  1,  700,  700, 1, 18),
+       ETSI_PATTERN(1,  0,  5,  200, 1000, 1, 10),
+       ETSI_PATTERN(2,  0, 15,  200, 1600, 1, 15),
+       ETSI_PATTERN(3,  0, 15, 2300, 4000, 1, 25),
+       ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20),
+       ETSI_PATTERN(5,  0,  2,  300,  400, 3, 10),
+       ETSI_PATTERN(6,  0,  2,  400, 1200, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v15 = {
+       .region                 = NL80211_DFS_ETSI,
+       .num_radar_types        = ARRAY_SIZE(etsi_radar_ref_types_v15),
+       .radar_types            = etsi_radar_ref_types_v15,
+};
+
+/* for now, we support ETSI radar types, FCC and JP are TODO */
+static const struct radar_types *dfs_domains[] = {
+       &etsi_radar_types_v15,
+};
+
+/**
+ * get_dfs_domain_radar_types() - get radar types for a given DFS domain
+ * @param domain DFS domain
+ * @return radar_types ptr on success, NULL if DFS domain is not supported
+ */
+static const struct radar_types *
+get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
+{
+       u32 i;
+       for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+               if (dfs_domains[i]->region == region)
+                       return dfs_domains[i];
+       }
+       return NULL;
+}
+
+/**
+ * struct channel_detector - detector elements for a DFS channel
+ * @head: list_head
+ * @freq: frequency for this channel detector in MHz
+ * @detectors: array of dynamically created detector elements for this freq
+ *
+ * Channel detectors are required to provide multi-channel DFS detection, e.g.
+ * to support off-channel scanning. A pattern detector has a list of channels
+ * radar pulses have been reported for in the past.
+ */
+struct channel_detector {
+       struct list_head head;
+       u16 freq;
+       struct pri_detector **detectors;
+};
+
+/* channel_detector_reset() - reset detector lines for a given channel */
+static void channel_detector_reset(struct dfs_pattern_detector *dpd,
+                                  struct channel_detector *cd)
+{
+       u32 i;
+       if (cd == NULL)
+               return;
+       for (i = 0; i < dpd->num_radar_types; i++)
+               cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts);
+}
+
+/* channel_detector_exit() - destructor */
+static void channel_detector_exit(struct dfs_pattern_detector *dpd,
+                                 struct channel_detector *cd)
+{
+       u32 i;
+       if (cd == NULL)
+               return;
+       list_del(&cd->head);
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               struct pri_detector *de = cd->detectors[i];
+               if (de != NULL)
+                       de->exit(de);
+       }
+       kfree(cd->detectors);
+       kfree(cd);
+}
+
+static struct channel_detector *
+channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq)
+{
+       u32 sz, i;
+       struct channel_detector *cd;
+
+       cd = kmalloc(sizeof(*cd), GFP_ATOMIC);
+       if (cd == NULL)
+               goto fail;
+
+       INIT_LIST_HEAD(&cd->head);
+       cd->freq = freq;
+       sz = sizeof(cd->detectors) * dpd->num_radar_types;
+       cd->detectors = kzalloc(sz, GFP_ATOMIC);
+       if (cd->detectors == NULL)
+               goto fail;
+
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+               struct pri_detector *de = pri_detector_init(rs);
+               if (de == NULL)
+                       goto fail;
+               cd->detectors[i] = de;
+       }
+       list_add(&cd->head, &dpd->channel_detectors);
+       return cd;
+
+fail:
+       ath_dbg(dpd->common, DFS,
+               "failed to allocate channel_detector for freq=%d\n", freq);
+       channel_detector_exit(dpd, cd);
+       return NULL;
+}
+
+/**
+ * channel_detector_get() - get channel detector for given frequency
+ * @param dpd instance pointer
+ * @param freq frequency in MHz
+ * @return pointer to channel detector on success, NULL otherwise
+ *
+ * Return existing channel detector for the given frequency or return a
+ * newly create one.
+ */
+static struct channel_detector *
+channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq)
+{
+       struct channel_detector *cd;
+       list_for_each_entry(cd, &dpd->channel_detectors, head) {
+               if (cd->freq == freq)
+                       return cd;
+       }
+       return channel_detector_create(dpd, freq);
+}
+
+/*
+ * DFS Pattern Detector
+ */
+
+/* dpd_reset(): reset all channel detectors */
+static void dpd_reset(struct dfs_pattern_detector *dpd)
+{
+       struct channel_detector *cd;
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry(cd, &dpd->channel_detectors, head)
+                       channel_detector_reset(dpd, cd);
+
+}
+static void dpd_exit(struct dfs_pattern_detector *dpd)
+{
+       struct channel_detector *cd, *cd0;
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+                       channel_detector_exit(dpd, cd);
+       kfree(dpd);
+}
+
+static bool
+dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
+{
+       u32 i;
+       struct channel_detector *cd;
+
+       /*
+        * pulses received for a non-supported or un-initialized
+        * domain are treated as detected radars for fail-safety
+        */
+       if (dpd->region == NL80211_DFS_UNSET)
+               return true;
+
+       cd = channel_detector_get(dpd, event->freq);
+       if (cd == NULL)
+               return false;
+
+       dpd->last_pulse_ts = event->ts;
+       /* reset detector on time stamp wraparound, caused by TSF reset */
+       if (event->ts < dpd->last_pulse_ts)
+               dpd_reset(dpd);
+
+       /* do type individual pattern matching */
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               struct pri_detector *pd = cd->detectors[i];
+               struct pri_sequence *ps = pd->add_pulse(pd, event);
+               if (ps != NULL) {
+                       ath_dbg(dpd->common, DFS,
+                               "DFS: radar found on freq=%d: id=%d, pri=%d, "
+                               "count=%d, count_false=%d\n",
+                               event->freq, pd->rs->type_id,
+                               ps->pri, ps->count, ps->count_falses);
+                       channel_detector_reset(dpd, cd);
+                       return true;
+               }
+       }
+       return false;
+}
+
+static struct ath_dfs_pool_stats
+dpd_get_stats(struct dfs_pattern_detector *dpd)
+{
+       return global_dfs_pool_stats;
+}
+
+static bool dpd_set_domain(struct dfs_pattern_detector *dpd,
+                          enum nl80211_dfs_regions region)
+{
+       const struct radar_types *rt;
+       struct channel_detector *cd, *cd0;
+
+       if (dpd->region == region)
+               return true;
+
+       dpd->region = NL80211_DFS_UNSET;
+
+       rt = get_dfs_domain_radar_types(region);
+       if (rt == NULL)
+               return false;
+
+       /* delete all channel detectors for previous DFS domain */
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+                       channel_detector_exit(dpd, cd);
+       dpd->radar_spec = rt->radar_types;
+       dpd->num_radar_types = rt->num_radar_types;
+
+       dpd->region = region;
+       return true;
+}
+
+static struct dfs_pattern_detector default_dpd = {
+       .exit           = dpd_exit,
+       .set_dfs_domain = dpd_set_domain,
+       .add_pulse      = dpd_add_pulse,
+       .get_stats      = dpd_get_stats,
+       .region         = NL80211_DFS_UNSET,
+};
+
+struct dfs_pattern_detector *
+dfs_pattern_detector_init(struct ath_common *common,
+                         enum nl80211_dfs_regions region)
+{
+       struct dfs_pattern_detector *dpd;
+
+       if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS))
+               return NULL;
+
+       dpd = kmalloc(sizeof(*dpd), GFP_KERNEL);
+       if (dpd == NULL)
+               return NULL;
+
+       *dpd = default_dpd;
+       INIT_LIST_HEAD(&dpd->channel_detectors);
+
+       dpd->common = common;
+       if (dpd->set_dfs_domain(dpd, region))
+               return dpd;
+
+       ath_dbg(common, DFS,"Could not set DFS domain to %d", region);
+       kfree(dpd);
+       return NULL;
+}
+EXPORT_SYMBOL(dfs_pattern_detector_init);
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h
new file mode 100644 (file)
index 0000000..dde2652
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * 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 DFS_PATTERN_DETECTOR_H
+#define DFS_PATTERN_DETECTOR_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/nl80211.h>
+
+/**
+ * struct ath_dfs_pool_stats - DFS Statistics for global pools
+ */
+struct ath_dfs_pool_stats {
+       u32 pool_reference;
+       u32 pulse_allocated;
+       u32 pulse_alloc_error;
+       u32 pulse_used;
+       u32 pseq_allocated;
+       u32 pseq_alloc_error;
+       u32 pseq_used;
+};
+
+/**
+ * struct pulse_event - describing pulses reported by PHY
+ * @ts: pulse time stamp in us
+ * @freq: channel frequency in MHz
+ * @width: pulse duration in us
+ * @rssi: rssi of radar event
+ */
+struct pulse_event {
+       u64 ts;
+       u16 freq;
+       u8 width;
+       u8 rssi;
+};
+
+/**
+ * struct radar_detector_specs - detector specs for a radar pattern type
+ * @type_id: pattern type, as defined by regulatory
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
+ */
+struct radar_detector_specs {
+       u8 type_id;
+       u8 width_min;
+       u8 width_max;
+       u16 pri_min;
+       u16 pri_max;
+       u8 num_pri;
+       u8 ppb;
+       u8 ppb_thresh;
+       u8 max_pri_tolerance;
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @exit(): destructor
+ * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes
+ * @add_pulse(): add radar pulse to detector, returns true on detection
+ * @region: active DFS region, NL80211_DFS_UNSET until set
+ * @num_radar_types: number of different radar types
+ * @last_pulse_ts: time stamp of last valid pulse in usecs
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+       void (*exit)(struct dfs_pattern_detector *dpd);
+       bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd,
+                          enum nl80211_dfs_regions region);
+       bool (*add_pulse)(struct dfs_pattern_detector *dpd,
+                         struct pulse_event *pe);
+
+       struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd);
+       enum nl80211_dfs_regions region;
+       u8 num_radar_types;
+       u64 last_pulse_ts;
+       /* needed for ath_dbg() */
+       struct ath_common *common;
+
+       const struct radar_detector_specs *radar_spec;
+       struct list_head channel_detectors;
+};
+
+/**
+ * dfs_pattern_detector_init() - constructor for pattern detector class
+ * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation
+ * @return instance pointer on success, NULL otherwise
+ */
+extern struct dfs_pattern_detector *
+dfs_pattern_detector_init(struct ath_common *common,
+                         enum nl80211_dfs_regions region);
+#endif /* DFS_PATTERN_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c
new file mode 100644 (file)
index 0000000..43b6081
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * 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/slab.h>
+#include <linux/spinlock.h>
+
+#include "ath.h"
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+
+struct ath_dfs_pool_stats global_dfs_pool_stats = {};
+
+#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
+#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+       struct list_head head;
+       u64 ts;
+};
+
+/**
+ * pde_get_multiple() - get number of multiples considering a given tolerance
+ * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
+ */
+static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
+{
+       u32 remainder;
+       u32 factor;
+       u32 delta;
+
+       if (fraction == 0)
+               return 0;
+
+       delta = (val < fraction) ? (fraction - val) : (val - fraction);
+
+       if (delta <= tolerance)
+               /* val and fraction are within tolerance */
+               return 1;
+
+       factor = val / fraction;
+       remainder = val % fraction;
+       if (remainder > tolerance) {
+               /* no exact match */
+               if ((fraction - remainder) <= tolerance)
+                       /* remainder is within tolerance */
+                       factor++;
+               else
+                       factor = 0;
+       }
+       return factor;
+}
+
+/**
+ * DOC: Singleton Pulse and Sequence Pools
+ *
+ * Instances of pri_sequence and pulse_elem are kept in singleton pools to
+ * reduce the number of dynamic allocations. They are shared between all
+ * instances and grow up to the peak number of simultaneously used objects.
+ *
+ * Memory is freed after all references to the pools are released.
+ */
+static u32 singleton_pool_references;
+static LIST_HEAD(pulse_pool);
+static LIST_HEAD(pseq_pool);
+static DEFINE_SPINLOCK(pool_lock);
+
+static void pool_register_ref(void)
+{
+       spin_lock_bh(&pool_lock);
+       singleton_pool_references++;
+       DFS_POOL_STAT_INC(pool_reference);
+       spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+       spin_lock_bh(&pool_lock);
+       singleton_pool_references--;
+       DFS_POOL_STAT_DEC(pool_reference);
+       if (singleton_pool_references == 0) {
+               /* free singleton pools with no references left */
+               struct pri_sequence *ps, *ps0;
+               struct pulse_elem *p, *p0;
+
+               list_for_each_entry_safe(p, p0, &pulse_pool, head) {
+                       list_del(&p->head);
+                       DFS_POOL_STAT_DEC(pulse_allocated);
+                       kfree(p);
+               }
+               list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+                       list_del(&ps->head);
+                       DFS_POOL_STAT_DEC(pseq_allocated);
+                       kfree(ps);
+               }
+       }
+       spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pulse_elem(struct pulse_elem *pe)
+{
+       spin_lock_bh(&pool_lock);
+       list_add(&pe->head, &pulse_pool);
+       DFS_POOL_STAT_DEC(pulse_used);
+       spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pseq_elem(struct pri_sequence *pse)
+{
+       spin_lock_bh(&pool_lock);
+       list_add(&pse->head, &pseq_pool);
+       DFS_POOL_STAT_DEC(pseq_used);
+       spin_unlock_bh(&pool_lock);
+}
+
+static struct pri_sequence *pool_get_pseq_elem(void)
+{
+       struct pri_sequence *pse = NULL;
+       spin_lock_bh(&pool_lock);
+       if (!list_empty(&pseq_pool)) {
+               pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
+               list_del(&pse->head);
+               DFS_POOL_STAT_INC(pseq_used);
+       }
+       spin_unlock_bh(&pool_lock);
+       return pse;
+}
+
+static struct pulse_elem *pool_get_pulse_elem(void)
+{
+       struct pulse_elem *pe = NULL;
+       spin_lock_bh(&pool_lock);
+       if (!list_empty(&pulse_pool)) {
+               pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
+               list_del(&pe->head);
+               DFS_POOL_STAT_INC(pulse_used);
+       }
+       spin_unlock_bh(&pool_lock);
+       return pe;
+}
+
+static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
+{
+       struct list_head *l = &pde->pulses;
+       if (list_empty(l))
+               return NULL;
+       return list_entry(l->prev, struct pulse_elem, head);
+}
+
+static bool pulse_queue_dequeue(struct pri_detector *pde)
+{
+       struct pulse_elem *p = pulse_queue_get_tail(pde);
+       if (p != NULL) {
+               list_del_init(&p->head);
+               pde->count--;
+               /* give it back to pool */
+               pool_put_pulse_elem(p);
+       }
+       return (pde->count > 0);
+}
+
+/* remove pulses older than window */
+static void pulse_queue_check_window(struct pri_detector *pde)
+{
+       u64 min_valid_ts;
+       struct pulse_elem *p;
+
+       /* there is no delta time with less than 2 pulses */
+       if (pde->count < 2)
+               return;
+
+       if (pde->last_ts <= pde->window_size)
+               return;
+
+       min_valid_ts = pde->last_ts - pde->window_size;
+       while ((p = pulse_queue_get_tail(pde)) != NULL) {
+               if (p->ts >= min_valid_ts)
+                       return;
+               pulse_queue_dequeue(pde);
+       }
+}
+
+static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
+{
+       struct pulse_elem *p = pool_get_pulse_elem();
+       if (p == NULL) {
+               p = kmalloc(sizeof(*p), GFP_ATOMIC);
+               if (p == NULL) {
+                       DFS_POOL_STAT_INC(pulse_alloc_error);
+                       return false;
+               }
+               DFS_POOL_STAT_INC(pulse_allocated);
+               DFS_POOL_STAT_INC(pulse_used);
+       }
+       INIT_LIST_HEAD(&p->head);
+       p->ts = ts;
+       list_add(&p->head, &pde->pulses);
+       pde->count++;
+       pde->last_ts = ts;
+       pulse_queue_check_window(pde);
+       if (pde->count >= pde->max_count)
+               pulse_queue_dequeue(pde);
+       return true;
+}
+
+static bool pseq_handler_create_sequences(struct pri_detector *pde,
+                                         u64 ts, u32 min_count)
+{
+       struct pulse_elem *p;
+       list_for_each_entry(p, &pde->pulses, head) {
+               struct pri_sequence ps, *new_ps;
+               struct pulse_elem *p2;
+               u32 tmp_false_count;
+               u64 min_valid_ts;
+               u32 delta_ts = ts - p->ts;
+
+               if (delta_ts < pde->rs->pri_min)
+                       /* ignore too small pri */
+                       continue;
+
+               if (delta_ts > pde->rs->pri_max)
+                       /* stop on too large pri (sorted list) */
+                       break;
+
+               /* build a new sequence with new potential pri */
+               ps.count = 2;
+               ps.count_falses = 0;
+               ps.first_ts = p->ts;
+               ps.last_ts = ts;
+               ps.pri = ts - p->ts;
+               ps.dur = ps.pri * (pde->rs->ppb - 1)
+                               + 2 * pde->rs->max_pri_tolerance;
+
+               p2 = p;
+               tmp_false_count = 0;
+               min_valid_ts = ts - ps.dur;
+               /* check which past pulses are candidates for new sequence */
+               list_for_each_entry_continue(p2, &pde->pulses, head) {
+                       u32 factor;
+                       if (p2->ts < min_valid_ts)
+                               /* stop on crossing window border */
+                               break;
+                       /* check if pulse match (multi)PRI */
+                       factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
+                                                 pde->rs->max_pri_tolerance);
+                       if (factor > 0) {
+                               ps.count++;
+                               ps.first_ts = p2->ts;
+                               /*
+                                * on match, add the intermediate falses
+                                * and reset counter
+                                */
+                               ps.count_falses += tmp_false_count;
+                               tmp_false_count = 0;
+                       } else {
+                               /* this is a potential false one */
+                               tmp_false_count++;
+                       }
+               }
+               if (ps.count < min_count)
+                       /* did not reach minimum count, drop sequence */
+                       continue;
+
+               /* this is a valid one, add it */
+               ps.deadline_ts = ps.first_ts + ps.dur;
+               new_ps = pool_get_pseq_elem();
+               if (new_ps == NULL) {
+                       new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
+                       if (new_ps == NULL) {
+                               DFS_POOL_STAT_INC(pseq_alloc_error);
+                               return false;
+                       }
+                       DFS_POOL_STAT_INC(pseq_allocated);
+                       DFS_POOL_STAT_INC(pseq_used);
+               }
+               memcpy(new_ps, &ps, sizeof(ps));
+               INIT_LIST_HEAD(&new_ps->head);
+               list_add(&new_ps->head, &pde->sequences);
+       }
+       return true;
+}
+
+/* check new ts and add to all matching existing sequences */
+static u32
+pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
+{
+       u32 max_count = 0;
+       struct pri_sequence *ps, *ps2;
+       list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
+               u32 delta_ts;
+               u32 factor;
+
+               /* first ensure that sequence is within window */
+               if (ts > ps->deadline_ts) {
+                       list_del_init(&ps->head);
+                       pool_put_pseq_elem(ps);
+                       continue;
+               }
+
+               delta_ts = ts - ps->last_ts;
+               factor = pde_get_multiple(delta_ts, ps->pri,
+                                         pde->rs->max_pri_tolerance);
+               if (factor > 0) {
+                       ps->last_ts = ts;
+                       ps->count++;
+
+                       if (max_count < ps->count)
+                               max_count = ps->count;
+               } else {
+                       ps->count_falses++;
+               }
+       }
+       return max_count;
+}
+
+static struct pri_sequence *
+pseq_handler_check_detection(struct pri_detector *pde)
+{
+       struct pri_sequence *ps;
+
+       if (list_empty(&pde->sequences))
+               return NULL;
+
+       list_for_each_entry(ps, &pde->sequences, head) {
+               /*
+                * we assume to have enough matching confidence if we
+                * 1) have enough pulses
+                * 2) have more matching than false pulses
+                */
+               if ((ps->count >= pde->rs->ppb_thresh) &&
+                   (ps->count * pde->rs->num_pri >= ps->count_falses))
+                       return ps;
+       }
+       return NULL;
+}
+
+
+/* free pulse queue and sequences list and give objects back to pools */
+static void pri_detector_reset(struct pri_detector *pde, u64 ts)
+{
+       struct pri_sequence *ps, *ps0;
+       struct pulse_elem *p, *p0;
+       list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
+               list_del_init(&ps->head);
+               pool_put_pseq_elem(ps);
+       }
+       list_for_each_entry_safe(p, p0, &pde->pulses, head) {
+               list_del_init(&p->head);
+               pool_put_pulse_elem(p);
+       }
+       pde->count = 0;
+       pde->last_ts = ts;
+}
+
+static void pri_detector_exit(struct pri_detector *de)
+{
+       pri_detector_reset(de, 0);
+       pool_deregister_ref();
+       kfree(de);
+}
+
+static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
+                                                  struct pulse_event *event)
+{
+       u32 max_updated_seq;
+       struct pri_sequence *ps;
+       u64 ts = event->ts;
+       const struct radar_detector_specs *rs = de->rs;
+
+       /* ignore pulses not within width range */
+       if ((rs->width_min > event->width) || (rs->width_max < event->width))
+               return NULL;
+
+       if ((ts - de->last_ts) < rs->max_pri_tolerance)
+               /* if delta to last pulse is too short, don't use this pulse */
+               return NULL;
+       de->last_ts = ts;
+
+       max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
+
+       if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
+               pri_detector_reset(de, ts);
+               return NULL;
+       }
+
+       ps = pseq_handler_check_detection(de);
+
+       if (ps == NULL)
+               pulse_queue_enqueue(de, ts);
+
+       return ps;
+}
+
+struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
+{
+       struct pri_detector *de;
+
+       de = kzalloc(sizeof(*de), GFP_ATOMIC);
+       if (de == NULL)
+               return NULL;
+       de->exit = pri_detector_exit;
+       de->add_pulse = pri_detector_add_pulse;
+       de->reset = pri_detector_reset;
+
+       INIT_LIST_HEAD(&de->sequences);
+       INIT_LIST_HEAD(&de->pulses);
+       de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
+       de->max_count = rs->ppb * 2;
+       de->rs = rs;
+
+       pool_register_ref();
+       return de;
+}
diff --git a/drivers/net/wireless/ath/dfs_pri_detector.h b/drivers/net/wireless/ath/dfs_pri_detector.h
new file mode 100644 (file)
index 0000000..79f0fff
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * 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 DFS_PRI_DETECTOR_H
+#define DFS_PRI_DETECTOR_H
+
+#include <linux/list.h>
+
+extern struct ath_dfs_pool_stats global_dfs_pool_stats;
+
+/**
+ * struct pri_sequence - sequence of pulses matching one PRI
+ * @head: list_head
+ * @pri: pulse repetition interval (PRI) in usecs
+ * @dur: duration of sequence in usecs
+ * @count: number of pulses in this sequence
+ * @count_falses: number of not matching pulses in this sequence
+ * @first_ts: time stamp of first pulse in usecs
+ * @last_ts: time stamp of last pulse in usecs
+ * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
+ */
+struct pri_sequence {
+       struct list_head head;
+       u32 pri;
+       u32 dur;
+       u32 count;
+       u32 count_falses;
+       u64 first_ts;
+       u64 last_ts;
+       u64 deadline_ts;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @exit(): destructor
+ * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected
+ * @reset(): clear states and reset to given time stamp
+ * @rs: detector specs for this detector element
+ * @last_ts: last pulse time stamp considered for this element in usecs
+ * @sequences: list_head holding potential pulse sequences
+ * @pulses: list connecting pulse_elem objects
+ * @count: number of pulses in queue
+ * @max_count: maximum number of pulses to be queued
+ * @window_size: window size back from newest pulse time stamp in usecs
+ */
+struct pri_detector {
+       void (*exit)     (struct pri_detector *de);
+       struct pri_sequence *
+            (*add_pulse)(struct pri_detector *de, struct pulse_event *e);
+       void (*reset)    (struct pri_detector *de, u64 ts);
+
+/* private: internal use only */
+       const struct radar_detector_specs *rs;
+       u64 last_ts;
+       struct list_head sequences;
+       struct list_head pulses;
+       u32 count;
+       u32 max_count;
+       u32 window_size;
+};
+
+struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs);
+
+#endif /* DFS_PRI_DETECTOR_H */
index 7d077c752dd56ca9e3c3d9cb0aae0c951795cadb..c00687e05688e6498b70131ba11c1105d0b933b1 100644 (file)
@@ -356,14 +356,131 @@ static u16 ath_regd_find_country_by_name(char *alpha2)
        return -1;
 }
 
+static int __ath_reg_dyn_country(struct wiphy *wiphy,
+                                struct ath_regulatory *reg,
+                                struct regulatory_request *request)
+{
+       u16 country_code;
+
+       if (!ath_is_world_regd(reg))
+               return -EINVAL;
+
+       country_code = ath_regd_find_country_by_name(request->alpha2);
+       if (country_code == (u16) -1)
+               return -EINVAL;
+
+       reg->current_rd = COUNTRY_ERD_FLAG;
+       reg->current_rd |= country_code;
+
+       __ath_regd_init(reg);
+
+       ath_reg_apply_world_flags(wiphy, request->initiator, reg);
+
+       return 0;
+}
+
+static void ath_reg_dyn_country(struct wiphy *wiphy,
+                               struct ath_regulatory *reg,
+                               struct regulatory_request *request)
+{
+       if (__ath_reg_dyn_country(wiphy, reg, request))
+               return;
+
+       printk(KERN_DEBUG "ath: regdomain 0x%0x "
+                         "dynamically updated by %s\n",
+              reg->current_rd,
+              reg_initiator_name(request->initiator));
+}
+
+static bool dynamic_country_user_possible(struct ath_regulatory *reg)
+{
+       if (config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
+               return true;
+
+       switch (reg->country_code) {
+       case CTRY_UNITED_STATES:
+       case CTRY_JAPAN1:
+       case CTRY_JAPAN2:
+       case CTRY_JAPAN3:
+       case CTRY_JAPAN4:
+       case CTRY_JAPAN5:
+       case CTRY_JAPAN6:
+       case CTRY_JAPAN7:
+       case CTRY_JAPAN8:
+       case CTRY_JAPAN9:
+       case CTRY_JAPAN10:
+       case CTRY_JAPAN11:
+       case CTRY_JAPAN12:
+       case CTRY_JAPAN13:
+       case CTRY_JAPAN14:
+       case CTRY_JAPAN15:
+       case CTRY_JAPAN16:
+       case CTRY_JAPAN17:
+       case CTRY_JAPAN18:
+       case CTRY_JAPAN19:
+       case CTRY_JAPAN20:
+       case CTRY_JAPAN21:
+       case CTRY_JAPAN22:
+       case CTRY_JAPAN23:
+       case CTRY_JAPAN24:
+       case CTRY_JAPAN25:
+       case CTRY_JAPAN26:
+       case CTRY_JAPAN27:
+       case CTRY_JAPAN28:
+       case CTRY_JAPAN29:
+       case CTRY_JAPAN30:
+       case CTRY_JAPAN31:
+       case CTRY_JAPAN32:
+       case CTRY_JAPAN33:
+       case CTRY_JAPAN34:
+       case CTRY_JAPAN35:
+       case CTRY_JAPAN36:
+       case CTRY_JAPAN37:
+       case CTRY_JAPAN38:
+       case CTRY_JAPAN39:
+       case CTRY_JAPAN40:
+       case CTRY_JAPAN41:
+       case CTRY_JAPAN42:
+       case CTRY_JAPAN43:
+       case CTRY_JAPAN44:
+       case CTRY_JAPAN45:
+       case CTRY_JAPAN46:
+       case CTRY_JAPAN47:
+       case CTRY_JAPAN48:
+       case CTRY_JAPAN49:
+       case CTRY_JAPAN50:
+       case CTRY_JAPAN51:
+       case CTRY_JAPAN52:
+       case CTRY_JAPAN53:
+       case CTRY_JAPAN54:
+       case CTRY_JAPAN55:
+       case CTRY_JAPAN56:
+       case CTRY_JAPAN57:
+       case CTRY_JAPAN58:
+       case CTRY_JAPAN59:
+               return false;
+       }
+
+       return true;
+}
+
+static void ath_reg_dyn_country_user(struct wiphy *wiphy,
+                                    struct ath_regulatory *reg,
+                                    struct regulatory_request *request)
+{
+       if (!config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS))
+               return;
+       if (!dynamic_country_user_possible(reg))
+               return;
+       ath_reg_dyn_country(wiphy, reg, request);
+}
+
 void ath_reg_notifier_apply(struct wiphy *wiphy,
                            struct regulatory_request *request,
                            struct ath_regulatory *reg)
 {
        struct ath_common *common = container_of(reg, struct ath_common,
                                                 regulatory);
-       u16 country_code;
-
        /* We always apply this */
        ath_reg_apply_radar_flags(wiphy);
 
@@ -388,25 +505,12 @@ void ath_reg_notifier_apply(struct wiphy *wiphy,
                       sizeof(struct ath_regulatory));
                break;
        case NL80211_REGDOM_SET_BY_DRIVER:
+               break;
        case NL80211_REGDOM_SET_BY_USER:
+               ath_reg_dyn_country_user(wiphy, reg, request);
                break;
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-               if (!ath_is_world_regd(reg))
-                       break;
-
-               country_code = ath_regd_find_country_by_name(request->alpha2);
-               if (country_code == (u16) -1)
-                       break;
-
-               reg->current_rd = COUNTRY_ERD_FLAG;
-               reg->current_rd |= country_code;
-
-               printk(KERN_DEBUG "ath: regdomain 0x%0x updated by CountryIE\n",
-                       reg->current_rd);
-               __ath_regd_init(reg);
-
-               ath_reg_apply_world_flags(wiphy, request->initiator, reg);
-
+               ath_reg_dyn_country(wiphy, reg, request);
                break;
        }
 }
index 682bcd650f709faf915d2e79950c675c9749e756..5b84f7ae0b1e3820cdee9737f9e4bacd8ed7b8d3 100644 (file)
 
 #ifdef CONFIG_WCN36XX_DEBUGFS
 
-static int wcn36xx_debugfs_open(struct inode *inode, struct file *file)
-{
-       file->private_data = inode->i_private;
-
-       return 0;
-}
-
 static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf,
                                   size_t count, loff_t *ppos)
 {
@@ -103,7 +96,7 @@ static ssize_t write_file_bool_bmps(struct file *file,
 }
 
 static const struct file_operations fops_wcn36xx_bmps = {
-       .open  =       wcn36xx_debugfs_open,
+       .open = simple_open,
        .read  =       read_file_bool_bmps,
        .write =       write_file_bool_bmps,
 };
@@ -145,7 +138,7 @@ static ssize_t write_file_dump(struct file *file,
 }
 
 static const struct file_operations fops_wcn36xx_dump = {
-       .open  =       wcn36xx_debugfs_open,
+       .open = simple_open,
        .write =       write_file_dump,
 };
 
index b827d51c30a37b93f1747d7df9b2049aeb97e25f..9c35479790b69f2621ec18e7863565818b505ef7 100644 (file)
@@ -3212,7 +3212,7 @@ static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype)
        if (subtype == IEEE80211_STYPE_REASSOC_RESP &&
            status != WLAN_STATUS_ASSOC_DENIED_RATES &&
            status != WLAN_STATUS_CAPS_UNSUPPORTED &&
-           priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) {
+           priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) {
                mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
                priv->ReAssociationRequestRetryCnt++;
                send_association_request(priv, 1);
index 7c970d3ae358834a4230f35826d6c598661ee323..05ee7f10cc8f577532e9180ac9874adcf9343d0f 100644 (file)
@@ -164,7 +164,8 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field,
                }
                en_addr = en_addrs[override][i];
 
-               val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1;
+               if (e)
+                       val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1;
 
                if (off) {
                        b43_phy_mask(dev, en_addr, ~en_mask);
index e13b1a65c65fe7469882b4eee83e6a08f567bce4..3e10b801eee84bc420867815ced96a3fca3428b4 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/card.h>
-#include <linux/mmc/host.h>
 #include <linux/platform_data/brcmfmac-sdio.h>
 
 #include <defs.h>
@@ -239,7 +238,9 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
                func_num = SDIO_FUNC_1;
                reg_size = 4;
 
-               brcmf_sdio_addrprep(sdiodev, reg_size, &addr);
+               ret = brcmf_sdio_addrprep(sdiodev, reg_size, &addr);
+               if (ret)
+                       goto done;
        }
 
        do {
@@ -255,6 +256,7 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
                                                       func_num, addr, data, 4);
        } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
 
+done:
        if (ret != 0)
                brcmf_err("failed with %d\n", ret);
 
@@ -315,8 +317,36 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
                *ret = retval;
 }
 
+static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
+                            bool write, u32 addr, struct sk_buff *pkt)
+{
+       unsigned int req_sz;
+
+       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
+       if (brcmf_pm_resume_error(sdiodev))
+               return -EIO;
+
+       /* Single skb use the standard mmc interface */
+       req_sz = pkt->len + 3;
+       req_sz &= (uint)~3;
+
+       if (write)
+               return sdio_memcpy_toio(sdiodev->func[fn], addr,
+                                       ((u8 *)(pkt->data)),
+                                       req_sz);
+       else if (fn == 1)
+               return sdio_memcpy_fromio(sdiodev->func[fn],
+                                         ((u8 *)(pkt->data)),
+                                         addr, req_sz);
+       else
+               /* function 2 read is FIFO operation */
+               return sdio_readsb(sdiodev->func[fn],
+                                  ((u8 *)(pkt->data)), addr,
+                                  req_sz);
+}
+
 /**
- * brcmf_sdio_buffrw - SDIO interface function for block data access
+ * brcmf_sdio_sglist_rw - SDIO interface function for block data access
  * @sdiodev: brcmfmac sdio device
  * @fn: SDIO function number
  * @write: direction flag
@@ -327,12 +357,13 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
  * stack for block data access. It assumes that the skb passed down by the
  * caller has already been padded and aligned.
  */
-static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
-                            bool write, u32 addr, struct sk_buff_head *pktlist)
+static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
+                               bool write, u32 addr,
+                               struct sk_buff_head *pktlist)
 {
        unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
-       unsigned int max_blks, max_req_sz, orig_offset, dst_offset;
-       unsigned short max_seg_sz, seg_sz;
+       unsigned int max_req_sz, orig_offset, dst_offset;
+       unsigned short max_seg_cnt, seg_sz;
        unsigned char *pkt_data, *orig_data, *dst_data;
        struct sk_buff *pkt_next = NULL, *local_pkt_next;
        struct sk_buff_head local_list, *target_list;
@@ -341,7 +372,6 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
        struct mmc_data mmc_dat;
        struct sg_table st;
        struct scatterlist *sgl;
-       struct mmc_host *host;
        int ret = 0;
 
        if (!pktlist->qlen)
@@ -351,27 +381,6 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
        if (brcmf_pm_resume_error(sdiodev))
                return -EIO;
 
-       /* Single skb use the standard mmc interface */
-       if (pktlist->qlen == 1) {
-               pkt_next = pktlist->next;
-               req_sz = pkt_next->len + 3;
-               req_sz &= (uint)~3;
-
-               if (write)
-                       return sdio_memcpy_toio(sdiodev->func[fn], addr,
-                                               ((u8 *)(pkt_next->data)),
-                                               req_sz);
-               else if (fn == 1)
-                       return sdio_memcpy_fromio(sdiodev->func[fn],
-                                                 ((u8 *)(pkt_next->data)),
-                                                 addr, req_sz);
-               else
-                       /* function 2 read is FIFO operation */
-                       return sdio_readsb(sdiodev->func[fn],
-                                          ((u8 *)(pkt_next->data)), addr,
-                                          req_sz);
-       }
-
        target_list = pktlist;
        /* for host with broken sg support, prepare a page aligned list */
        __skb_queue_head_init(&local_list);
@@ -398,38 +407,46 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
                target_list = &local_list;
        }
 
-       host = sdiodev->func[fn]->card->host;
        func_blk_sz = sdiodev->func[fn]->cur_blksize;
-       /* Blocks per command is limited by host count, host transfer
-        * size and the maximum for IO_RW_EXTENDED of 511 blocks.
-        */
-       max_blks = min_t(unsigned int, host->max_blk_count, 511u);
-       max_req_sz = min_t(unsigned int, host->max_req_size,
-                          max_blks * func_blk_sz);
-       max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC);
-       max_seg_sz = min_t(unsigned short, max_seg_sz, target_list->qlen);
+       max_req_sz = sdiodev->max_request_size;
+       max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count,
+                           target_list->qlen);
        seg_sz = target_list->qlen;
        pkt_offset = 0;
        pkt_next = target_list->next;
 
-       if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) {
+       if (sg_alloc_table(&st, max_seg_cnt, GFP_KERNEL)) {
                ret = -ENOMEM;
                goto exit;
        }
 
+       memset(&mmc_req, 0, sizeof(struct mmc_request));
+       memset(&mmc_cmd, 0, sizeof(struct mmc_command));
+       memset(&mmc_dat, 0, sizeof(struct mmc_data));
+
+       mmc_dat.sg = st.sgl;
+       mmc_dat.blksz = func_blk_sz;
+       mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+       mmc_cmd.opcode = SD_IO_RW_EXTENDED;
+       mmc_cmd.arg = write ? 1<<31 : 0;        /* write flag  */
+       mmc_cmd.arg |= (fn & 0x7) << 28;        /* SDIO func num */
+       mmc_cmd.arg |= 1<<27;                   /* block mode */
+       /* for function 1 the addr will be incremented */
+       mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
+       mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+       mmc_req.cmd = &mmc_cmd;
+       mmc_req.data = &mmc_dat;
+
        while (seg_sz) {
                req_sz = 0;
                sg_cnt = 0;
-               memset(&mmc_req, 0, sizeof(struct mmc_request));
-               memset(&mmc_cmd, 0, sizeof(struct mmc_command));
-               memset(&mmc_dat, 0, sizeof(struct mmc_data));
                sgl = st.sgl;
                /* prep sg table */
                while (pkt_next != (struct sk_buff *)target_list) {
                        pkt_data = pkt_next->data + pkt_offset;
                        sg_data_sz = pkt_next->len - pkt_offset;
-                       if (sg_data_sz > host->max_seg_size)
-                               sg_data_sz = host->max_seg_size;
+                       if (sg_data_sz > sdiodev->max_segment_size)
+                               sg_data_sz = sdiodev->max_segment_size;
                        if (sg_data_sz > max_req_sz - req_sz)
                                sg_data_sz = max_req_sz - req_sz;
 
@@ -444,7 +461,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
                                pkt_next = pkt_next->next;
                        }
 
-                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz)
+                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt)
                                break;
                }
                seg_sz -= sg_cnt;
@@ -455,27 +472,17 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
                        ret = -ENOTBLK;
                        goto exit;
                }
-               mmc_dat.sg = st.sgl;
+
                mmc_dat.sg_len = sg_cnt;
-               mmc_dat.blksz = func_blk_sz;
                mmc_dat.blocks = req_sz / func_blk_sz;
-               mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
-               mmc_cmd.opcode = SD_IO_RW_EXTENDED;
-               mmc_cmd.arg = write ? 1<<31 : 0;        /* write flag  */
-               mmc_cmd.arg |= (fn & 0x7) << 28;        /* SDIO func num */
-               mmc_cmd.arg |= 1<<27;                   /* block mode */
-               /* incrementing addr for function 1 */
-               mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
                mmc_cmd.arg |= (addr & 0x1FFFF) << 9;   /* address */
                mmc_cmd.arg |= mmc_dat.blocks & 0x1FF;  /* block count */
-               mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
-               mmc_req.cmd = &mmc_cmd;
-               mmc_req.data = &mmc_dat;
+               /* incrementing addr for function 1 */
                if (fn == 1)
                        addr += req_sz;
 
                mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card);
-               mmc_wait_for_req(host, &mmc_req);
+               mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req);
 
                ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
                if (ret != 0) {
@@ -546,7 +553,6 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
 {
        uint width;
        int err = 0;
-       struct sk_buff_head pkt_list;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
@@ -556,19 +562,17 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        if (err)
                goto done;
 
-       skb_queue_head_init(&pkt_list);
-       skb_queue_tail(&pkt_list, pkt);
-       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list);
-       skb_dequeue_tail(&pkt_list);
+       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pkt);
 
 done:
        return err;
 }
 
 int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                           uint flags, struct sk_buff_head *pktq)
+                           uint flags, struct sk_buff_head *pktq, uint totlen)
 {
-       uint incr_fix;
+       struct sk_buff *glom_skb;
+       struct sk_buff *skb;
        uint width;
        int err = 0;
 
@@ -580,8 +584,22 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        if (err)
                goto done;
 
-       incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
-       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq);
+       if (pktq->qlen == 1)
+               err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq->next);
+       else if (!sdiodev->sg_support) {
+               glom_skb = brcmu_pkt_buf_get_skb(totlen);
+               if (!glom_skb)
+                       return -ENOMEM;
+               err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, glom_skb);
+               if (err)
+                       goto done;
+
+               skb_queue_walk(pktq, skb) {
+                       memcpy(skb->data, glom_skb->data, skb->len);
+                       skb_pull(glom_skb, skb->len);
+               }
+       } else
+               err = brcmf_sdio_sglist_rw(sdiodev, fn, false, addr, pktq);
 
 done:
        return err;
@@ -592,7 +610,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, u8 *buf, uint nbytes)
 {
        struct sk_buff *mypkt;
-       struct sk_buff_head pktq;
+       uint width;
        int err;
 
        mypkt = brcmu_pkt_buf_get_skb(nbytes);
@@ -603,10 +621,12 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        }
 
        memcpy(mypkt->data, buf, nbytes);
-       __skb_queue_head_init(&pktq);
-       __skb_queue_tail(&pktq, mypkt);
-       err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq);
-       __skb_dequeue_tail(&pktq);
+
+       width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
+
+       if (!err)
+               err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, mypkt);
 
        brcmu_pkt_buf_free_skb(mypkt);
        return err;
@@ -617,16 +637,26 @@ int
 brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, struct sk_buff_head *pktq)
 {
+       struct sk_buff *skb;
        uint width;
-       int err = 0;
+       int err;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pktq->qlen);
 
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       brcmf_sdio_addrprep(sdiodev, width, &addr);
+       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
+       if (err)
+               return err;
 
-       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq);
+       if (pktq->qlen == 1 || !sdiodev->sg_support)
+               skb_queue_walk(pktq, skb) {
+                       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, skb);
+                       if (err)
+                               break;
+               }
+       else
+               err = brcmf_sdio_sglist_rw(sdiodev, fn, true, addr, pktq);
 
        return err;
 }
@@ -639,7 +669,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
        struct sk_buff *pkt;
        u32 sdaddr;
        uint dsize;
-       struct sk_buff_head pkt_list;
 
        dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
        pkt = dev_alloc_skb(dsize);
@@ -648,7 +677,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                return -EIO;
        }
        pkt->priority = 0;
-       skb_queue_head_init(&pkt_list);
 
        /* Determine initial transfer parameters */
        sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
@@ -676,10 +704,8 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                skb_put(pkt, dsize);
                if (write)
                        memcpy(pkt->data, data, dsize);
-               skb_queue_tail(&pkt_list, pkt);
                bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write,
-                                            sdaddr, &pkt_list);
-               skb_dequeue_tail(&pkt_list);
+                                            sdaddr, pkt);
                if (bcmerror) {
                        brcmf_err("membytes transfer failed\n");
                        break;
index 091c905871ccd100baad84d3c35ab9172709d901..905704e335d7164b90a4ecb6fd51c2324911c154 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
 #include <linux/suspend.h>
 #include <linux/errno.h>
 #include <linux/sched.h>       /* request_irq() */
@@ -315,6 +316,8 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
        int err;
        struct brcmf_sdio_dev *sdiodev;
        struct brcmf_bus *bus_if;
+       struct mmc_host *host;
+       uint max_blocks;
 
        brcmf_dbg(SDIO, "Enter\n");
        brcmf_dbg(SDIO, "Class=%x\n", func->class);
@@ -361,6 +364,20 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
                brcmf_err("F2 error, probe failed %d...\n", err);
                goto fail;
        }
+
+       /*
+        * determine host related variables after brcmf_sdio_probe()
+        * as func->cur_blksize is properly set and F2 init has been
+        * completed successfully.
+        */
+       host = func->card->host;
+       sdiodev->sg_support = host->max_segs > 1;
+       max_blocks = min_t(uint, host->max_blk_count, 511u);
+       sdiodev->max_request_size = min_t(uint, host->max_req_size,
+                                         max_blocks * func->cur_blksize);
+       sdiodev->max_segment_count = min_t(uint, host->max_segs,
+                                          SG_MAX_SINGLE_ALLOC);
+       sdiodev->max_segment_size = host->max_seg_size;
        brcmf_dbg(SDIO, "F2 init completed...\n");
        return 0;
 
@@ -459,8 +476,6 @@ static struct sdio_driver brcmf_sdmmc_driver = {
 
 static int brcmf_sdio_pd_probe(struct platform_device *pdev)
 {
-       int ret;
-
        brcmf_dbg(SDIO, "Enter\n");
 
        brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev);
@@ -468,11 +483,7 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev)
        if (brcmfmac_sdio_pdata->power_on)
                brcmfmac_sdio_pdata->power_on();
 
-       ret = sdio_register_driver(&brcmf_sdmmc_driver);
-       if (ret)
-               brcmf_err("sdio_register_driver failed: %d\n", ret);
-
-       return ret;
+       return 0;
 }
 
 static int brcmf_sdio_pd_remove(struct platform_device *pdev)
@@ -495,6 +506,15 @@ static struct platform_driver brcmf_sdio_pd = {
        }
 };
 
+void brcmf_sdio_register(void)
+{
+       int ret;
+
+       ret = sdio_register_driver(&brcmf_sdmmc_driver);
+       if (ret)
+               brcmf_err("sdio_register_driver failed: %d\n", ret);
+}
+
 void brcmf_sdio_exit(void)
 {
        brcmf_dbg(SDIO, "Enter\n");
@@ -505,18 +525,13 @@ void brcmf_sdio_exit(void)
                sdio_unregister_driver(&brcmf_sdmmc_driver);
 }
 
-void brcmf_sdio_init(void)
+void __init brcmf_sdio_init(void)
 {
        int ret;
 
        brcmf_dbg(SDIO, "Enter\n");
 
        ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
-       if (ret == -ENODEV) {
-               brcmf_dbg(SDIO, "No platform data available, registering without.\n");
-               ret = sdio_register_driver(&brcmf_sdmmc_driver);
-       }
-
-       if (ret)
-               brcmf_err("driver registration failed: %d\n", ret);
+       if (ret == -ENODEV)
+               brcmf_dbg(SDIO, "No platform data available.\n");
 }
index 200ee9b485bf0e0f31ba2e6b7cee81f095cb97bc..7640d8a99e61813d8ab191f5688dfdf29b051fff 100644 (file)
@@ -156,10 +156,11 @@ extern int brcmf_bus_start(struct device *dev);
 #ifdef CONFIG_BRCMFMAC_SDIO
 extern void brcmf_sdio_exit(void);
 extern void brcmf_sdio_init(void);
+extern void brcmf_sdio_register(void);
 #endif
 #ifdef CONFIG_BRCMFMAC_USB
 extern void brcmf_usb_exit(void);
-extern void brcmf_usb_init(void);
+extern void brcmf_usb_register(void);
 #endif
 
 #endif                         /* _BRCMF_BUS_H_ */
index 42bf19a2eeee3beec051d6184066c34eef2a2dec..64e9cff241b9156745bab110010520bfba730394 100644 (file)
@@ -1225,21 +1225,23 @@ u32 brcmf_get_chip_info(struct brcmf_if *ifp)
        return bus->chip << 4 | bus->chiprev;
 }
 
-static void brcmf_driver_init(struct work_struct *work)
+static void brcmf_driver_register(struct work_struct *work)
 {
-       brcmf_debugfs_init();
-
 #ifdef CONFIG_BRCMFMAC_SDIO
-       brcmf_sdio_init();
+       brcmf_sdio_register();
 #endif
 #ifdef CONFIG_BRCMFMAC_USB
-       brcmf_usb_init();
+       brcmf_usb_register();
 #endif
 }
-static DECLARE_WORK(brcmf_driver_work, brcmf_driver_init);
+static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
 
 static int __init brcmfmac_module_init(void)
 {
+       brcmf_debugfs_init();
+#ifdef CONFIG_BRCMFMAC_SDIO
+       brcmf_sdio_init();
+#endif
        if (!schedule_work(&brcmf_driver_work))
                return -EBUSY;
 
index 67f05db4b9b8e11918771b087925c12d184821fd..b02953c4ade721235fbe0e4ed662aaa422ab21f7 100644 (file)
@@ -1147,6 +1147,8 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
        u8 rx_seq, fc, tx_seq_max;
        u32 swheader;
 
+       trace_brcmf_sdpcm_hdr(false, header);
+
        /* hw header */
        len = get_unaligned_le16(header);
        checksum = get_unaligned_le16(header + sizeof(u16));
@@ -1269,6 +1271,7 @@ static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
                     SDPCM_DOFFSET_MASK;
        *(((__le32 *)header) + 1) = cpu_to_le32(sw_header);
        *(((__le32 *)header) + 2) = 0;
+       trace_brcmf_sdpcm_hdr(true, header);
 }
 
 static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
@@ -1389,7 +1392,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                sdio_claim_host(bus->sdiodev->func[1]);
                errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
                                bus->sdiodev->sbwad,
-                               SDIO_FUNC_2, F2SYNC, &bus->glom);
+                               SDIO_FUNC_2, F2SYNC, &bus->glom, dlen);
                sdio_release_host(bus->sdiodev->func[1]);
                bus->sdcnt.f2rxdata++;
 
@@ -1877,6 +1880,56 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus)
 /* bit mask of data length chopped from the previous packet */
 #define ALIGN_SKB_CHOP_LEN_MASK        0x7fff
 
+static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio_dev *sdiodev,
+                                   struct sk_buff_head *pktq,
+                                   struct sk_buff *pkt, uint chan)
+{
+       struct sk_buff *pkt_pad;
+       u16 tail_pad, tail_chop, sg_align;
+       unsigned int blksize;
+       u8 *dat_buf;
+       int ntail;
+
+       blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+       sg_align = 4;
+       if (sdiodev->pdata && sdiodev->pdata->sd_sgentry_align > 4)
+               sg_align = sdiodev->pdata->sd_sgentry_align;
+       /* sg entry alignment should be a divisor of block size */
+       WARN_ON(blksize % sg_align);
+
+       /* Check tail padding */
+       pkt_pad = NULL;
+       tail_chop = pkt->len % sg_align;
+       tail_pad = sg_align - tail_chop;
+       tail_pad += blksize - (pkt->len + tail_pad) % blksize;
+       if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) {
+               pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop);
+               if (pkt_pad == NULL)
+                       return -ENOMEM;
+               memcpy(pkt_pad->data,
+                      pkt->data + pkt->len - tail_chop,
+                      tail_chop);
+               *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
+               skb_trim(pkt, pkt->len - tail_chop);
+               __skb_queue_after(pktq, pkt, pkt_pad);
+       } else {
+               ntail = pkt->data_len + tail_pad -
+                       (pkt->end - pkt->tail);
+               if (skb_cloned(pkt) || ntail > 0)
+                       if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC))
+                               return -ENOMEM;
+               if (skb_linearize(pkt))
+                       return -ENOMEM;
+               dat_buf = (u8 *)(pkt->data);
+               __skb_put(pkt, tail_pad);
+       }
+
+       if (pkt_pad)
+               return pkt->len + tail_chop;
+       else
+               return pkt->len - tail_pad;
+}
+
 /**
  * brcmf_sdio_txpkt_prep - packet preparation for transmit
  * @bus: brcmf_sdio structure pointer
@@ -1893,24 +1946,16 @@ static int
 brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
                      uint chan)
 {
-       u16 head_pad, tail_pad, tail_chop, head_align, sg_align;
-       int ntail;
-       struct sk_buff *pkt_next, *pkt_new;
+       u16 head_pad, head_align;
+       struct sk_buff *pkt_next;
        u8 *dat_buf;
-       unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+       int err;
        struct brcmf_sdio_hdrinfo hd_info = {0};
 
        /* SDIO ADMA requires at least 32 bit alignment */
        head_align = 4;
-       sg_align = 4;
-       if (bus->sdiodev->pdata) {
-               head_align = bus->sdiodev->pdata->sd_head_align > 4 ?
-                            bus->sdiodev->pdata->sd_head_align : 4;
-               sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ?
-                          bus->sdiodev->pdata->sd_sgentry_align : 4;
-       }
-       /* sg entry alignment should be a divisor of block size */
-       WARN_ON(blksize % sg_align);
+       if (bus->sdiodev->pdata && bus->sdiodev->pdata->sd_head_align > 4)
+               head_align = bus->sdiodev->pdata->sd_head_align;
 
        pkt_next = pktq->next;
        dat_buf = (u8 *)(pkt_next->data);
@@ -1929,40 +1974,20 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
                memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
        }
 
-       /* Check tail padding */
-       pkt_new = NULL;
-       tail_chop = pkt_next->len % sg_align;
-       tail_pad = sg_align - tail_chop;
-       tail_pad += blksize - (pkt_next->len + tail_pad) % blksize;
-       if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) {
-               pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop);
-               if (pkt_new == NULL)
-                       return -ENOMEM;
-               memcpy(pkt_new->data,
-                      pkt_next->data + pkt_next->len - tail_chop,
-                      tail_chop);
-               *(u32 *)(pkt_new->cb) = ALIGN_SKB_FLAG + tail_chop;
-               skb_trim(pkt_next, pkt_next->len - tail_chop);
-               __skb_queue_after(pktq, pkt_next, pkt_new);
+       if (bus->sdiodev->sg_support && pktq->qlen > 1) {
+               err = brcmf_sdio_txpkt_prep_sg(bus->sdiodev, pktq,
+                                              pkt_next, chan);
+               if (err < 0)
+                       return err;
+               hd_info.len = (u16)err;
        } else {
-               ntail = pkt_next->data_len + tail_pad -
-                       (pkt_next->end - pkt_next->tail);
-               if (skb_cloned(pkt_next) || ntail > 0)
-                       if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC))
-                               return -ENOMEM;
-               if (skb_linearize(pkt_next))
-                       return -ENOMEM;
-               dat_buf = (u8 *)(pkt_next->data);
-               __skb_put(pkt_next, tail_pad);
+               hd_info.len = pkt_next->len;
        }
 
-       /* Now prep the header */
-       if (pkt_new)
-               hd_info.len = pkt_next->len + tail_chop;
-       else
-               hd_info.len = pkt_next->len - tail_pad;
        hd_info.channel = chan;
        hd_info.dat_offset = head_pad + bus->tx_hdrlen;
+
+       /* Now fill the header */
        brcmf_sdio_hdpack(bus, dat_buf, &hd_info);
 
        if (BRCMF_BYTES_ON() &&
index 2b5407f002e53bf90b8e320d0b54384b2a200f9b..1b034ea46f932ff4c8aea837b88603861b2e44c0 100644 (file)
@@ -178,6 +178,10 @@ struct brcmf_sdio_dev {
        bool irq_en;                    /* irq enable flags */
        spinlock_t irq_en_lock;
        bool irq_wake;                  /* irq wake enable flags */
+       bool sg_support;
+       uint max_request_size;
+       ushort max_segment_count;
+       uint max_segment_size;
 };
 
 /* Register/deregister interrupt handler. */
@@ -221,7 +225,7 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, u8 *buf, uint nbytes);
 extern int
 brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                       uint flags, struct sk_buff_head *pktq);
+                       uint flags, struct sk_buff_head *pktq, uint totlen);
 
 /* Flags bits */
 
index bc29171128991a9836d8237c556be00482f66d00..3c67529b90743123691d08aa7ed4d3fdb4a45c6d 100644 (file)
@@ -78,13 +78,15 @@ TRACE_EVENT(brcmf_hexdump,
        TP_ARGS(data, len),
        TP_STRUCT__entry(
                __field(unsigned long, len)
+               __field(unsigned long, addr)
                __dynamic_array(u8, hdata, len)
        ),
        TP_fast_assign(
                __entry->len = len;
+               __entry->addr = (unsigned long)data;
                memcpy(__get_dynamic_array(hdata), data, len);
        ),
-       TP_printk("hexdump [length=%lu]", __entry->len)
+       TP_printk("hexdump [addr=%lx, length=%lu]", __entry->addr, __entry->len)
 );
 
 TRACE_EVENT(brcmf_bdchdr,
@@ -108,6 +110,23 @@ TRACE_EVENT(brcmf_bdchdr,
        TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen)
 );
 
+TRACE_EVENT(brcmf_sdpcm_hdr,
+       TP_PROTO(bool tx, void *data),
+       TP_ARGS(tx, data),
+       TP_STRUCT__entry(
+               __field(u8, tx)
+               __field(u16, len)
+               __array(u8, hdr, 12)
+       ),
+       TP_fast_assign(
+               memcpy(__entry->hdr, data, 12);
+               __entry->len = __entry->hdr[0] | (__entry->hdr[1] << 8);
+               __entry->tx = tx ? 1 : 0;
+       ),
+       TP_printk("sdpcm: %s len %u, seq %d", __entry->tx ? "TX" : "RX",
+                 __entry->len, __entry->hdr[4])
+);
+
 #ifdef CONFIG_BRCM_TRACING
 
 #undef TRACE_INCLUDE_PATH
index bf6758d95600bba97bbb43f273d4b2c571fa590f..422f44c631756b2332dc10d4c1fb24c8172343ff 100644 (file)
@@ -1536,7 +1536,7 @@ void brcmf_usb_exit(void)
        brcmf_release_fw(&fw_image_list);
 }
 
-void brcmf_usb_init(void)
+void brcmf_usb_register(void)
 {
        brcmf_dbg(USB, "Enter\n");
        INIT_LIST_HEAD(&fw_image_list);
index 3a6544710c8ab222cc126ef53d5f19143498cc9e..edc5d105ff980e40e1ad2221a4c7686f87b0cea1 100644 (file)
@@ -457,6 +457,8 @@ static int brcms_ops_start(struct ieee80211_hw *hw)
        if (err != 0)
                brcms_err(wl->wlc->hw->d11core, "%s: brcms_up() returned %d\n",
                          __func__, err);
+
+       bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, true);
        return err;
 }
 
@@ -479,6 +481,8 @@ static void brcms_ops_stop(struct ieee80211_hw *hw)
                return;
        }
 
+       bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, false);
+
        /* put driver in down state */
        spin_lock_bh(&wl->lock);
        brcms_down(wl);
index e310752f0e33014ade59028b754b2fdd38076cb6..40078f5f932ec6b1ea3a26af5453f2bc5d2206ec 100644 (file)
@@ -42,7 +42,6 @@ struct hwbus_priv {
        spinlock_t              lock; /* Serialize all bus operations */
        wait_queue_head_t       wq;
        int claimed;
-       int irq_disabled;
 };
 
 #define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
@@ -238,9 +237,9 @@ static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
        struct hwbus_priv *self = dev_id;
 
        if (self->core) {
-               disable_irq_nosync(self->func->irq);
-               self->irq_disabled = 1;
+               cw1200_spi_lock(self);
                cw1200_irq_handler(self->core);
+               cw1200_spi_unlock(self);
                return IRQ_HANDLED;
        } else {
                return IRQ_NONE;
@@ -253,9 +252,10 @@ static int cw1200_spi_irq_subscribe(struct hwbus_priv *self)
 
        pr_debug("SW IRQ subscribe\n");
 
-       ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler,
-                                     IRQF_TRIGGER_HIGH,
-                                     "cw1200_wlan_irq", self);
+       ret = request_threaded_irq(self->func->irq, NULL,
+                                  cw1200_spi_irq_handler,
+                                  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                  "cw1200_wlan_irq", self);
        if (WARN_ON(ret < 0))
                goto exit;
 
@@ -273,22 +273,13 @@ exit:
 
 static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
 {
+       int ret = 0;
+
        pr_debug("SW IRQ unsubscribe\n");
        disable_irq_wake(self->func->irq);
        free_irq(self->func->irq, self);
 
-       return 0;
-}
-
-static int cw1200_spi_irq_enable(struct hwbus_priv *self, int enable)
-{
-       /* Disables are handled by the interrupt handler */
-       if (enable && self->irq_disabled) {
-               enable_irq(self->func->irq);
-               self->irq_disabled = 0;
-       }
-
-       return 0;
+       return ret;
 }
 
 static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
@@ -368,7 +359,6 @@ static struct hwbus_ops cw1200_spi_hwbus_ops = {
        .unlock                 = cw1200_spi_unlock,
        .align_size             = cw1200_spi_align_size,
        .power_mgmt             = cw1200_spi_pm,
-       .irq_enable             = cw1200_spi_irq_enable,
 };
 
 /* Probe Function to be called by SPI stack when device is discovered */
index 0b2061bbc68bfa99ba9bce91dcdba18b99984879..acdff0f7f952e03fa25b51b47a9e83eda37d85c6 100644 (file)
@@ -485,7 +485,7 @@ int cw1200_load_firmware(struct cw1200_common *priv)
 
        /* Enable interrupt signalling */
        priv->hwbus_ops->lock(priv->hwbus_priv);
-       ret = __cw1200_irq_enable(priv, 2);
+       ret = __cw1200_irq_enable(priv, 1);
        priv->hwbus_ops->unlock(priv->hwbus_priv);
        if (ret < 0)
                goto unsubscribe;
index 51dfb3a90735521f18a1c572440e3ec8e486f5c8..8b2fc831c3de75f6f33799cc12c6174bad6d837b 100644 (file)
@@ -28,7 +28,6 @@ struct hwbus_ops {
        void (*unlock)(struct hwbus_priv *self);
        size_t (*align_size)(struct hwbus_priv *self, size_t size);
        int (*power_mgmt)(struct hwbus_priv *self, bool suspend);
-       int (*irq_enable)(struct hwbus_priv *self, int enable);
 };
 
 #endif /* CW1200_HWBUS_H */
index 41bd7615ccaa31f2ba8bf8c183a88db0a73bca60..ff230b7aeedd646e1591d01bdd1514afa86d7f0b 100644 (file)
@@ -273,21 +273,6 @@ int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
        u16 val16;
        int ret;
 
-       /* We need to do this hack because the SPI layer can sleep on I/O
-          and the general path involves I/O to the device in interrupt
-          context.
-
-          However, the initial enable call needs to go to the hardware.
-
-          We don't worry about shutdown because we do a full reset which
-          clears the interrupt enabled bits.
-       */
-       if (priv->hwbus_ops->irq_enable) {
-               ret = priv->hwbus_ops->irq_enable(priv->hwbus_priv, enable);
-               if (ret || enable < 2)
-                       return ret;
-       }
-
        if (HIF_8601_SILICON == priv->hw_type) {
                ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
                if (ret < 0) {
index da442b81370a769054b4b77a946daa4bbd856ed8..1fef5240e6adc07317b7128bf7d7617385fa9aa8 100644 (file)
@@ -433,27 +433,19 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
        /* Copy MAC header from skb into command buffer */
        memcpy(tx_cmd->hdr, hdr, hdr_len);
 
+       txq_id = info->hw_queue;
+
        if (is_agg)
                txq_id = priv->tid_data[sta_id][tid].agg.txq_id;
        else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
-               /*
-                * Send this frame after DTIM -- there's a special queue
-                * reserved for this for contexts that support AP mode.
-                */
-               txq_id = ctx->mcast_queue;
-
                /*
                 * The microcode will clear the more data
                 * bit in the last frame it transmits.
                 */
                hdr->frame_control |=
                        cpu_to_le16(IEEE80211_FCTL_MOREDATA);
-       } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
-               txq_id = IWL_AUX_QUEUE;
-       else
-               txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)];
+       }
 
-       WARN_ON_ONCE(!is_agg && txq_id != info->hw_queue);
        WARN_ON_ONCE(is_agg &&
                     priv->queue_to_mac80211[txq_id] != info->hw_queue);
 
index 30d45e2fc193a5bc04987acbdef9b0252a2b5c01..8ac305be68f489be533606cb8260dbfd4dc5cfda 100644 (file)
@@ -240,6 +240,12 @@ const struct iwl_cfg iwl6035_2agn_cfg = {
        .ht_params = &iwl6000_ht_params,
 };
 
+const struct iwl_cfg iwl6035_2agn_sff_cfg = {
+       .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN",
+       IWL_DEVICE_6035,
+       .ht_params = &iwl6000_ht_params,
+};
+
 const struct iwl_cfg iwl1030_bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN",
        IWL_DEVICE_6030,
index 261e4a12fd8e1a99dba8f29fd0982b15f07e380c..18f232e8e81253b31d730755f242b7a51552157c 100644 (file)
@@ -280,6 +280,7 @@ extern const struct iwl_cfg iwl2000_2bgn_cfg;
 extern const struct iwl_cfg iwl2000_2bgn_d_cfg;
 extern const struct iwl_cfg iwl2030_2bgn_cfg;
 extern const struct iwl_cfg iwl6035_2agn_cfg;
+extern const struct iwl_cfg iwl6035_2agn_sff_cfg;
 extern const struct iwl_cfg iwl105_bgn_cfg;
 extern const struct iwl_cfg iwl105_bgn_d_cfg;
 extern const struct iwl_cfg iwl135_bgn_cfg;
index 2dc64b5c94c1dcd0f9d7f6fea3431435966b7b95..143292b4dbbffeac763f86d5c9d9b1badae1aecc 100644 (file)
@@ -601,8 +601,10 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
 {
        int ret;
 
-       WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
-                 "%s bad state = %d", __func__, trans->state);
+       if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
+               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               return -EIO;
+       }
 
        if (!(cmd->flags & CMD_ASYNC))
                lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
@@ -638,8 +640,8 @@ static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
 static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
                               struct iwl_device_cmd *dev_cmd, int queue)
 {
-       WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
-                 "%s bad state = %d", __func__, trans->state);
+       if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
 
        return trans->ops->tx(trans, skb, dev_cmd, queue);
 }
@@ -647,16 +649,16 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
 static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue,
                                     int ssn, struct sk_buff_head *skbs)
 {
-       WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
-                 "%s bad state = %d", __func__, trans->state);
+       if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
 
        trans->ops->reclaim(trans, queue, ssn, skbs);
 }
 
 static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue)
 {
-       WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
-                 "%s bad state = %d", __func__, trans->state);
+       if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
 
        trans->ops->txq_disable(trans, queue);
 }
@@ -667,8 +669,8 @@ static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue,
 {
        might_sleep();
 
-       WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
-                 "%s bad state = %d", __func__, trans->state);
+       if (unlikely((trans->state != IWL_TRANS_FW_ALIVE)))
+               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
 
        trans->ops->txq_enable(trans, queue, fifo, sta_id, tid,
                                 frame_limit, ssn);
@@ -683,8 +685,8 @@ static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue,
 
 static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans)
 {
-       WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
-                 "%s bad state = %d", __func__, trans->state);
+       if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
 
        return trans->ops->wait_tx_queue_empty(trans);
 }
index 80d5f88a9d321b080c8cc63a4f0b58b0a35f0d0f..550824aa84ea4bde8794b267bda27401c6b6f058 100644 (file)
@@ -273,7 +273,10 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                if (!mvmvif->queue_params[ac].uapsd)
                        continue;
 
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+               if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+
                cmd->uapsd_ac_flags |= BIT(ac);
 
                /* QNDP TID - the highest TID with no admission control */
index 778dcd9320fe32de4a3f6ecd1e78c23b841f7bd8..dff7592e1ff84e5c3f6a05d673e2a39514cbb870 100644 (file)
@@ -97,10 +97,10 @@ static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif)
 
 static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif)
 {
-       if (vif->bss_conf.assoc)
-               return cpu_to_le32(vif->bss_conf.beacon_int);
-       else
+       if (!vif->bss_conf.assoc)
                return 0;
+
+       return cpu_to_le32(ieee80211_tu_to_usec(vif->bss_conf.beacon_int));
 }
 
 static inline __le32
@@ -423,6 +423,11 @@ static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
                        return false;
                }
 
+               /*
+                * If scan cannot be aborted, it means that we had a
+                * SCAN_COMPLETE_NOTIFICATION in the pipe and it called
+                * ieee80211_scan_completed already.
+                */
                IWL_DEBUG_SCAN(mvm, "Scan cannot be aborted, exit now: %d\n",
                               *resp);
                return true;
@@ -446,14 +451,19 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
                                               SCAN_COMPLETE_NOTIFICATION };
        int ret;
 
+       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+               return;
+
        iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
                                   scan_abort_notif,
                                   ARRAY_SIZE(scan_abort_notif),
                                   iwl_mvm_scan_abort_notif, NULL);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD,
+                                  CMD_SYNC | CMD_SEND_IN_RFKILL, 0, NULL);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
+               /* mac80211's state will be cleaned in the fw_restart flow */
                goto out_remove_notif;
        }
 
index ddf15e1cffa21ad4129c3ce6a1a9441a20f1189c..941c0c88f982639b28436a60e52a0fdc6fba8c4b 100644 (file)
@@ -139,13 +139,16 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 
 /* 6x00 Series */
        {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
+       {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)},
        {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
+       {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
        {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
        {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
+       {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)},
        {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
        {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
 
@@ -153,12 +156,16 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)},
+       {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)},
+       {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)},
+       {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)},
+       {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */
        {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */
@@ -240,8 +247,11 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 
 /* 6x35 Series */
        {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)},
+       {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)},
+       {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)},
+       {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)},
        {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)},
        {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)},
 
@@ -260,54 +270,86 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 #if IS_ENABLED(CONFIG_IWLMVM)
 /* 7260 Series */
        {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
        {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
        {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)},
 
 /* 3160 Series */
        {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)},
        {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
        {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)},
 
 /* 7265 Series */
        {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
index cc184402b88a658f27ab54899ae7fb35e1122ee2..059c5acad3a0d2e7b9eb73bf6466ef088e54e5c2 100644 (file)
@@ -1102,6 +1102,8 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
                 * non-AGG queue.
                 */
                iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+
+               ssn = trans_pcie->txq[txq_id].q.read_ptr;
        }
 
        /* Place first TFD at index corresponding to start sequence number.
@@ -1463,7 +1465,8 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
        spin_unlock_bh(&txq->lock);
 }
 
-#define HOST_COMPLETE_TIMEOUT (2 * HZ)
+#define HOST_COMPLETE_TIMEOUT  (2 * HZ)
+#define COMMAND_POKE_TIMEOUT   (HZ / 10)
 
 static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans,
                                    struct iwl_host_cmd *cmd)
@@ -1491,6 +1494,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int cmd_idx;
        int ret;
+       int timeout = HOST_COMPLETE_TIMEOUT;
 
        IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
                       get_cmd_string(trans_pcie, cmd->id));
@@ -1514,10 +1518,29 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
                return ret;
        }
 
-       ret = wait_event_timeout(trans_pcie->wait_command_queue,
-                                !test_bit(STATUS_HCMD_ACTIVE,
-                                          &trans_pcie->status),
-                                HOST_COMPLETE_TIMEOUT);
+       while (timeout > 0) {
+               unsigned long flags;
+
+               timeout -= COMMAND_POKE_TIMEOUT;
+               ret = wait_event_timeout(trans_pcie->wait_command_queue,
+                                        !test_bit(STATUS_HCMD_ACTIVE,
+                                                  &trans_pcie->status),
+                                        COMMAND_POKE_TIMEOUT);
+               if (ret)
+                       break;
+               /* poke the device - it may have lost the command */
+               if (iwl_trans_grab_nic_access(trans, true, &flags)) {
+                       iwl_trans_release_nic_access(trans, &flags);
+                       IWL_DEBUG_INFO(trans,
+                                      "Tried to wake NIC for command %s\n",
+                                      get_cmd_string(trans_pcie, cmd->id));
+               } else {
+                       IWL_ERR(trans, "Failed to poke NIC for command %s\n",
+                               get_cmd_string(trans_pcie, cmd->id));
+                       break;
+               }
+       }
+
        if (!ret) {
                if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) {
                        struct iwl_txq *txq =
index c0f9e7e862f66e8bfcb549ae4a7f3ba7425306ae..51b92b5df11956adfedadb5137f8f6b61342b0c4 100644 (file)
@@ -53,6 +53,11 @@ static void main_firmware_cb(const struct firmware *firmware, void *context)
 
        /* Firmware found! */
        lbs_fw_loaded(priv, 0, priv->helper_fw, firmware);
+       if (priv->helper_fw) {
+               release_firmware (priv->helper_fw);
+               priv->helper_fw = NULL;
+       }
+       release_firmware (firmware);
 }
 
 static void helper_firmware_cb(const struct firmware *firmware, void *context)
index c94dd6802672a053d795b052710d5ada4ff3e2bb..ef8c98e21098479d231df5844fb2abab8ddab04f 100644 (file)
@@ -754,14 +754,14 @@ static void if_cs_prog_firmware(struct lbs_private *priv, int ret,
        if (ret == 0 && (card->model != MODEL_8305))
                ret = if_cs_prog_real(card, mainfw);
        if (ret)
-               goto out;
+               return;
 
        /* Now actually get the IRQ */
        ret = request_irq(card->p_dev->irq, if_cs_interrupt,
                IRQF_SHARED, DRV_NAME, card);
        if (ret) {
                pr_err("error in request_irq\n");
-               goto out;
+               return;
        }
 
        /*
@@ -777,10 +777,6 @@ static void if_cs_prog_firmware(struct lbs_private *priv, int ret,
                pr_err("could not activate card\n");
                free_irq(card->p_dev->irq, card);
        }
-
-out:
-       release_firmware(helper);
-       release_firmware(mainfw);
 }
 
 
index 45578335e4200f2f47a3fc8284f9bd09127209ee..991238afd1b60c04ae437fafeb7a108713ea3134 100644 (file)
@@ -708,20 +708,16 @@ static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret,
 
        ret = if_sdio_prog_helper(card, helper);
        if (ret)
-               goto out;
+               return;
 
        lbs_deb_sdio("Helper firmware loaded\n");
 
        ret = if_sdio_prog_real(card, mainfw);
        if (ret)
-               goto out;
+               return;
 
        lbs_deb_sdio("Firmware loaded\n");
        if_sdio_finish_power_on(card);
-
-out:
-       release_firmware(helper);
-       release_firmware(mainfw);
 }
 
 static int if_sdio_prog_firmware(struct if_sdio_card *card)
index 5d39ec880d84350bd270b0115b87574582fa0fed..83669151bb8242e31931735700eb27fe4cd01821 100644 (file)
@@ -1094,11 +1094,7 @@ static int if_spi_init_card(struct if_spi_card *card)
                goto out;
 
 out:
-       release_firmware(helper);
-       release_firmware(mainfw);
-
        lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
-
        return err;
 }
 
index 27980778d992db783e58f777446a1cc03d40c28c..dff08a2896a38f644894749c006787e26b1f3ea6 100644 (file)
@@ -844,7 +844,7 @@ static void if_usb_prog_firmware(struct lbs_private *priv, int ret,
        cardp->fw = fw;
        if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) {
                ret = -EINVAL;
-               goto release_fw;
+               goto done;
        }
 
        /* Cancel any pending usb business */
@@ -861,7 +861,7 @@ restart:
        if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
                lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
                ret = -EIO;
-               goto release_fw;
+               goto done;
        }
 
        cardp->bootcmdresp = 0;
@@ -883,14 +883,14 @@ restart:
                usb_kill_urb(cardp->tx_urb);
                if (if_usb_submit_rx_urb(cardp) < 0)
                        ret = -EIO;
-               goto release_fw;
+               goto done;
        } else if (cardp->bootcmdresp <= 0) {
                if (--reset_count >= 0) {
                        if_usb_reset_device(cardp);
                        goto restart;
                }
                ret = -EIO;
-               goto release_fw;
+               goto done;
        }
 
        i = 0;
@@ -921,14 +921,14 @@ restart:
 
                pr_info("FW download failure, time = %d ms\n", i * 100);
                ret = -EIO;
-               goto release_fw;
+               goto done;
        }
 
        cardp->priv->fw_ready = 1;
        if_usb_submit_rx_urb(cardp);
 
        if (lbs_start_card(priv))
-               goto release_fw;
+               goto done;
 
        if_usb_setup_firmware(priv);
 
@@ -939,11 +939,8 @@ restart:
        if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL))
                priv->ehs_remove_supported = false;
 
- release_fw:
-       release_firmware(cardp->fw);
-       cardp->fw = NULL;
-
  done:
+       cardp->fw = NULL;
        lbs_deb_leave(LBS_DEB_USB);
 }
 
index 2cd3f54e1efa14652e83691e3353290d17e76939..de0df86704e714778891b7fb1af6b2cfc1340bef 100644 (file)
@@ -167,6 +167,7 @@ struct hwsim_vif_priv {
        u32 magic;
        u8 bssid[ETH_ALEN];
        bool assoc;
+       bool bcn_en;
        u16 aid;
 };
 
@@ -1170,6 +1171,16 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw,
        *total_flags = data->rx_filter;
 }
 
+static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       unsigned int *count = data;
+       struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+
+       if (vp->bcn_en)
+               (*count)++;
+}
+
 static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
                                            struct ieee80211_vif *vif,
                                            struct ieee80211_bss_conf *info,
@@ -1180,7 +1191,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 
        hwsim_check_magic(vif);
 
-       wiphy_debug(hw->wiphy, "%s(changed=0x%x)\n", __func__, changed);
+       wiphy_debug(hw->wiphy, "%s(changed=0x%x vif->addr=%pM)\n",
+                   __func__, changed, vif->addr);
 
        if (changed & BSS_CHANGED_BSSID) {
                wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n",
@@ -1202,6 +1214,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BEACON_ENABLED) {
                wiphy_debug(hw->wiphy, "  BCN EN: %d\n", info->enable_beacon);
+               vp->bcn_en = info->enable_beacon;
                if (data->started &&
                    !hrtimer_is_queued(&data->beacon_timer.timer) &&
                    info->enable_beacon) {
@@ -1215,8 +1228,16 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
                        tasklet_hrtimer_start(&data->beacon_timer,
                                              ns_to_ktime(until_tbtt * 1000),
                                              HRTIMER_MODE_REL);
-               } else if (!info->enable_beacon)
-                       tasklet_hrtimer_cancel(&data->beacon_timer);
+               } else if (!info->enable_beacon) {
+                       unsigned int count = 0;
+                       ieee80211_iterate_active_interfaces(
+                               data->hw, IEEE80211_IFACE_ITER_NORMAL,
+                               mac80211_hwsim_bcn_en_iter, &count);
+                       wiphy_debug(hw->wiphy, "  beaconing vifs remaining: %u",
+                                   count);
+                       if (count == 0)
+                               tasklet_hrtimer_cancel(&data->beacon_timer);
+               }
        }
 
        if (changed & BSS_CHANGED_ERP_CTS_PROT) {
index 21c688264708316b34c51d196aa36fff5f5aaf01..1214c587fd08587f263b2c979eab9bc902b53ae1 100644 (file)
@@ -150,7 +150,7 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
  */
 int
 mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
-                         struct mwifiex_ra_list_tbl *pra_list, int headroom,
+                         struct mwifiex_ra_list_tbl *pra_list,
                          int ptrindex, unsigned long ra_list_flags)
                          __releases(&priv->wmm.ra_list_spinlock)
 {
@@ -160,6 +160,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
        int pad = 0, ret;
        struct mwifiex_tx_param tx_param;
        struct txpd *ptx_pd = NULL;
+       int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN;
 
        skb_src = skb_peek(&pra_list->skb_head);
        if (!skb_src) {
index 900e1c62a0cceb4499457be3b76be7e9589be415..892098d6a69687dd2d8c1fc61612a6fb9999d754 100644 (file)
@@ -26,7 +26,7 @@
 int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv,
                                struct sk_buff *skb);
 int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
-                             struct mwifiex_ra_list_tbl *ptr, int headroom,
+                             struct mwifiex_ra_list_tbl *ptr,
                              int ptr_index, unsigned long flags)
                              __releases(&priv->wmm.ra_list_spinlock);
 
index fb3fa18390b8fb4bb31fdcdb45e7a54511f3433f..e47f4e3012b85344c7d400d78799634cb5bf0234 100644 (file)
@@ -1155,7 +1155,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
        uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions);
 
        if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) &&
-           adapter->iface_type == MWIFIEX_SDIO) {
+           adapter->iface_type != MWIFIEX_USB) {
                mwifiex_hs_activated_event(priv, true);
                return 0;
        } else {
@@ -1167,8 +1167,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
        }
        if (conditions != HS_CFG_CANCEL) {
                adapter->is_hs_configured = true;
-               if (adapter->iface_type == MWIFIEX_USB ||
-                   adapter->iface_type == MWIFIEX_PCIE)
+               if (adapter->iface_type == MWIFIEX_USB)
                        mwifiex_hs_activated_event(priv, true);
        } else {
                adapter->is_hs_configured = false;
index 717fbe2e0e5aea2b5d86482da86cce7b3ccc393d..4e4686e6ac092b3ce23282c575eadc2b40071eff 100644 (file)
@@ -1422,13 +1422,19 @@ static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac)
  */
 int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
 {
+       int ret = 0;
+
        if (!priv->media_connected)
                return 0;
 
        switch (priv->bss_mode) {
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
-               return mwifiex_deauthenticate_infra(priv, mac);
+               ret = mwifiex_deauthenticate_infra(priv, mac);
+               if (ret)
+                       cfg80211_disconnected(priv->netdev, 0, NULL, 0,
+                                             GFP_KERNEL);
+               break;
        case NL80211_IFTYPE_ADHOC:
                return mwifiex_send_cmd_sync(priv,
                                             HostCmd_CMD_802_11_AD_HOC_STOP,
@@ -1440,7 +1446,7 @@ int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
                break;
        }
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(mwifiex_deauthenticate);
 
index fd778337deeec4e58b1bddb4d08668aaf51890d9..9d7c9d354d34aeb9e0b3e4bf854fc6a999671958 100644 (file)
@@ -358,10 +358,12 @@ process_start:
                }
        } while (true);
 
-       if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter))
+       spin_lock_irqsave(&adapter->main_proc_lock, flags);
+       if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter)) {
+               spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
                goto process_start;
+       }
 
-       spin_lock_irqsave(&adapter->main_proc_lock, flags);
        adapter->mwifiex_processing = false;
        spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
 
@@ -880,7 +882,9 @@ mwifiex_add_card(void *card, struct semaphore *sem,
        adapter->cmd_wait_q.status = 0;
        adapter->scan_wait_q_woken = false;
 
-       adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE");
+       adapter->workqueue =
+               alloc_workqueue("MWIFIEX_WORK_QUEUE",
+                               WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
        if (!adapter->workqueue)
                goto err_kmalloc;
 
index 8b057524b252e535307644ddaa98e12df75a35cd..8c351f71f72f9f6f6a17650198ba805bf7c1f5cd 100644 (file)
@@ -118,7 +118,8 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
        dev_dbg(adapter->dev,
                "info: successfully disconnected from %pM: reason code %d\n",
                priv->cfg_bssid, reason_code);
-       if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+       if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+           priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
                cfg80211_disconnected(priv->netdev, reason_code, NULL, 0,
                                      GFP_KERNEL);
        }
index 2472d4b7f00e997bc9e6b21586edcf42fe692498..1c70b8d092270ba3a456664aaf6ec3e9da4b3b59 100644 (file)
@@ -447,9 +447,6 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message)
         */
        adapter->is_suspended = true;
 
-       for (i = 0; i < adapter->priv_num; i++)
-               netif_carrier_off(adapter->priv[i]->netdev);
-
        if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb)
                usb_kill_urb(card->rx_cmd.urb);
 
@@ -509,10 +506,6 @@ static int mwifiex_usb_resume(struct usb_interface *intf)
                                                  MWIFIEX_RX_CMD_BUF_SIZE);
        }
 
-       for (i = 0; i < adapter->priv_num; i++)
-               if (adapter->priv[i]->media_connected)
-                       netif_carrier_on(adapter->priv[i]->netdev);
-
        /* Disable Host Sleep */
        if (adapter->hs_activated)
                mwifiex_cancel_hs(mwifiex_get_priv(adapter,
index 8f8fea015cb44f4bf926d94f5f78286dd31f7ecc..5dd0ccc70b863ea15fad25d739adebd39ce09dbd 100644 (file)
@@ -1239,8 +1239,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
                if (enable_tx_amsdu && mwifiex_is_amsdu_allowed(priv, tid) &&
                    mwifiex_is_11n_aggragation_possible(priv, ptr,
                                                        adapter->tx_buf_size))
-                       mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN,
-                                                 ptr_index, flags);
+                       mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags);
                        /* ra_list_spinlock has been freed in
                           mwifiex_11n_aggregate_pkt() */
                else
index b9deef66cf4b8179893b918fbd0fb3365480a686..e328d3058c419a0c7c4c367fd7e62ada31ddded4 100644 (file)
@@ -83,6 +83,7 @@ static struct usb_device_id p54u_table[] = {
        {USB_DEVICE(0x06a9, 0x000e)},   /* Westell 802.11g USB (A90-211WG-01) */
        {USB_DEVICE(0x06b9, 0x0121)},   /* Thomson SpeedTouch 121g */
        {USB_DEVICE(0x0707, 0xee13)},   /* SMC 2862W-G version 2 */
+       {USB_DEVICE(0x07aa, 0x0020)},   /* Corega WLUSB2GTST USB */
        {USB_DEVICE(0x0803, 0x4310)},   /* Zoom 4410a */
        {USB_DEVICE(0x083a, 0x4521)},   /* Siemens Gigaset USB Adapter 54 version 2 */
        {USB_DEVICE(0x083a, 0x4531)},   /* T-Com Sinus 154 data II */
@@ -979,6 +980,7 @@ static int p54u_load_firmware(struct ieee80211_hw *dev,
        if (err) {
                dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
                                          "(%d)!\n", p54u_fwlist[i].fw, err);
+               usb_put_dev(udev);
        }
 
        return err;
index a18b0051a74551d1d62205ba8452292a3cce45a6..006b8bcb2e31dfc5c21d7ed602546003e4a9e54c 100644 (file)
@@ -58,11 +58,11 @@ config RT61PCI
 
 config RT2800PCI
        tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support"
-       depends on PCI || SOC_RT288X || SOC_RT305X
+       depends on PCI
        select RT2800_LIB
+       select RT2800_LIB_MMIO
        select RT2X00_LIB_MMIO
-       select RT2X00_LIB_PCI if PCI
-       select RT2X00_LIB_SOC if SOC_RT288X || SOC_RT305X
+       select RT2X00_LIB_PCI
        select RT2X00_LIB_FIRMWARE
        select RT2X00_LIB_CRYPTO
        select CRC_CCITT
@@ -199,9 +199,30 @@ config RT2800USB_UNKNOWN
 
 endif
 
+config RT2800SOC
+       tristate "Ralink WiSoC support"
+       depends on SOC_RT288X || SOC_RT305X
+       select RT2X00_LIB_SOC
+       select RT2X00_LIB_MMIO
+       select RT2X00_LIB_CRYPTO
+       select RT2X00_LIB_FIRMWARE
+       select RT2800_LIB
+       select RT2800_LIB_MMIO
+       ---help---
+         This adds support for Ralink WiSoC devices.
+         Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352.
+
+         When compiled as a module, this driver will be called rt2800soc.
+
+
 config RT2800_LIB
        tristate
 
+config RT2800_LIB_MMIO
+       tristate
+       select RT2X00_LIB_MMIO
+       select RT2800_LIB
+
 config RT2X00_LIB_MMIO
        tristate
 
index f069d8bc5b678332151d7a6fe337f44e29205d82..24a66015a4959f6fd6d44a18e6bd8d0aed7d1c1c 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_RT2X00_LIB_PCI)          += rt2x00pci.o
 obj-$(CONFIG_RT2X00_LIB_SOC)           += rt2x00soc.o
 obj-$(CONFIG_RT2X00_LIB_USB)           += rt2x00usb.o
 obj-$(CONFIG_RT2800_LIB)               += rt2800lib.o
+obj-$(CONFIG_RT2800_LIB_MMIO)          += rt2800mmio.o
 obj-$(CONFIG_RT2400PCI)                        += rt2400pci.o
 obj-$(CONFIG_RT2500PCI)                        += rt2500pci.o
 obj-$(CONFIG_RT61PCI)                  += rt61pci.o
@@ -21,3 +22,4 @@ obj-$(CONFIG_RT2800PCI)                       += rt2800pci.o
 obj-$(CONFIG_RT2500USB)                        += rt2500usb.o
 obj-$(CONFIG_RT73USB)                  += rt73usb.o
 obj-$(CONFIG_RT2800USB)                        += rt2800usb.o
+obj-$(CONFIG_RT2800SOC)                        += rt2800soc.o
index 3d53a09da5a12da22f2697c9a22b7dfb49daaff0..38ed9a3e44c8c00c06ea8f1cdd9ae1a68bbe2d60 100644 (file)
@@ -1261,7 +1261,7 @@ static void rt2400pci_fill_rxdone(struct queue_entry *entry,
         */
        rxdesc->timestamp = ((u64)rx_high << 32) | rx_low;
        rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL) & ~0x08;
-       rxdesc->rssi = rt2x00_get_field32(word2, RXD_W3_RSSI) -
+       rxdesc->rssi = rt2x00_get_field32(word3, RXD_W3_RSSI) -
            entry->queue->rt2x00dev->rssi_offset;
        rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT);
 
index e3eb95292a7f42c01042c7ab7352d5d3784baba0..aab6b5e4f5ddd0fb0a52b7385388f3a07c738add 100644 (file)
 /*
  * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number.
  */
-#define MAC_CSR0_3290                          0x0000
+#define MAC_CSR0_3290                  0x0000
 
 /*
  * E2PROM_CSR: PCI EEPROM control register.
 /*
  * COEX_CFG_0
  */
-#define COEX_CFG0                      0x0040
+#define COEX_CFG0              0x0040
 #define COEX_CFG_ANT           FIELD32(0xff000000)
 /*
  * COEX_CFG_1
  */
-#define COEX_CFG1                      0x0044
+#define COEX_CFG1              0x0044
 
 /*
  * COEX_CFG_2
  */
-#define COEX_CFG2                      0x0048
+#define COEX_CFG2              0x0048
 #define BT_COEX_CFG1           FIELD32(0xff000000)
 #define BT_COEX_CFG0           FIELD32(0x00ff0000)
 #define WL_COEX_CFG1           FIELD32(0x0000ff00)
 #define PLL_RESERVED_INPUT2    FIELD32(0x0000ff00)
 #define PLL_CONTROL            FIELD32(0x00070000)
 #define PLL_LPF_R1             FIELD32(0x00080000)
-#define PLL_LPF_C1_CTRL        FIELD32(0x00300000)
-#define PLL_LPF_C2_CTRL        FIELD32(0x00c00000)
+#define PLL_LPF_C1_CTRL                FIELD32(0x00300000)
+#define PLL_LPF_C2_CTRL                FIELD32(0x00c00000)
 #define PLL_CP_CURRENT_CTRL    FIELD32(0x03000000)
 #define PLL_PFD_DELAY_CTRL     FIELD32(0x0c000000)
 #define PLL_LOCK_CTRL          FIELD32(0x70000000)
@@ -2166,7 +2166,7 @@ struct mac_iveiv_entry {
  */
 #define RFCSR6_R1                      FIELD8(0x03)
 #define RFCSR6_R2                      FIELD8(0x40)
-#define RFCSR6_TXDIV           FIELD8(0x0c)
+#define RFCSR6_TXDIV                   FIELD8(0x0c)
 /* bits for RF3053 */
 #define RFCSR6_VCO_IC                  FIELD8(0xc0)
 
@@ -2204,13 +2204,13 @@ struct mac_iveiv_entry {
  * RFCSR 12:
  */
 #define RFCSR12_TX_POWER               FIELD8(0x1f)
-#define RFCSR12_DR0                            FIELD8(0xe0)
+#define RFCSR12_DR0                    FIELD8(0xe0)
 
 /*
  * RFCSR 13:
  */
 #define RFCSR13_TX_POWER               FIELD8(0x1f)
-#define RFCSR13_DR0                            FIELD8(0xe0)
+#define RFCSR13_DR0                    FIELD8(0xe0)
 
 /*
  * RFCSR 15:
@@ -2228,7 +2228,7 @@ struct mac_iveiv_entry {
 #define RFCSR17_TXMIXER_GAIN           FIELD8(0x07)
 #define RFCSR17_TX_LO1_EN              FIELD8(0x08)
 #define RFCSR17_R                      FIELD8(0x20)
-#define RFCSR17_CODE                   FIELD8(0x7f)
+#define RFCSR17_CODE                   FIELD8(0x7f)
 
 /* RFCSR 18 */
 #define RFCSR18_XO_TUNE_BYPASS         FIELD8(0x40)
@@ -2451,7 +2451,7 @@ enum rt2800_eeprom_word {
  */
 #define EEPROM_NIC_CONF0_RXPATH                FIELD16(0x000f)
 #define EEPROM_NIC_CONF0_TXPATH                FIELD16(0x00f0)
-#define EEPROM_NIC_CONF0_RF_TYPE               FIELD16(0x0f00)
+#define EEPROM_NIC_CONF0_RF_TYPE       FIELD16(0x0f00)
 
 /*
  * EEPROM NIC Configuration 1
@@ -2473,18 +2473,18 @@ enum rt2800_eeprom_word {
  * DAC_TEST: 0: disable, 1: enable
  */
 #define EEPROM_NIC_CONF1_HW_RADIO              FIELD16(0x0001)
-#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC               FIELD16(0x0002)
-#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G               FIELD16(0x0004)
-#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G               FIELD16(0x0008)
+#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC       FIELD16(0x0002)
+#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G       FIELD16(0x0004)
+#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G       FIELD16(0x0008)
 #define EEPROM_NIC_CONF1_CARDBUS_ACCEL         FIELD16(0x0010)
 #define EEPROM_NIC_CONF1_BW40M_SB_2G           FIELD16(0x0020)
 #define EEPROM_NIC_CONF1_BW40M_SB_5G           FIELD16(0x0040)
 #define EEPROM_NIC_CONF1_WPS_PBC               FIELD16(0x0080)
 #define EEPROM_NIC_CONF1_BW40M_2G              FIELD16(0x0100)
 #define EEPROM_NIC_CONF1_BW40M_5G              FIELD16(0x0200)
-#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA             FIELD16(0x400)
+#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA     FIELD16(0x400)
 #define EEPROM_NIC_CONF1_ANT_DIVERSITY         FIELD16(0x1800)
-#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC               FIELD16(0x2000)
+#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC       FIELD16(0x2000)
 #define EEPROM_NIC_CONF1_BT_COEXIST            FIELD16(0x4000)
 #define EEPROM_NIC_CONF1_DAC_TEST              FIELD16(0x8000)
 
@@ -2523,9 +2523,9 @@ enum rt2800_eeprom_word {
  * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream
  * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved
  */
-#define EEPROM_NIC_CONF2_RX_STREAM             FIELD16(0x000f)
-#define EEPROM_NIC_CONF2_TX_STREAM             FIELD16(0x00f0)
-#define EEPROM_NIC_CONF2_CRYSTAL               FIELD16(0x0600)
+#define EEPROM_NIC_CONF2_RX_STREAM     FIELD16(0x000f)
+#define EEPROM_NIC_CONF2_TX_STREAM     FIELD16(0x00f0)
+#define EEPROM_NIC_CONF2_CRYSTAL       FIELD16(0x0600)
 
 /*
  * EEPROM LNA
@@ -2792,7 +2792,7 @@ enum rt2800_eeprom_word {
 #define MCU_CURRENT                    0x36
 #define MCU_LED                                0x50
 #define MCU_LED_STRENGTH               0x51
-#define MCU_LED_AG_CONF                0x52
+#define MCU_LED_AG_CONF                        0x52
 #define MCU_LED_ACT_CONF               0x53
 #define MCU_LED_LED_POLARITY           0x54
 #define MCU_RADAR                      0x60
@@ -2801,7 +2801,7 @@ enum rt2800_eeprom_word {
 #define MCU_FREQ_OFFSET                        0x74
 #define MCU_BBP_SIGNAL                 0x80
 #define MCU_POWER_SAVE                 0x83
-#define MCU_BAND_SELECT                0x91
+#define MCU_BAND_SELECT                        0x91
 
 /*
  * MCU mailbox tokens
index a114cab413e9d7b4ceb8aea33b02258bc095ac71..c5738f14c4ba21b7a60453ab282309089134bdc2 100644 (file)
@@ -1780,7 +1780,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
        rt2800_bbp_read(rt2x00dev, 3, &r3);
 
        if (rt2x00_rt(rt2x00dev, RT3572) &&
-           test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+           rt2x00_has_cap_bt_coexist(rt2x00dev))
                rt2800_config_3572bt_ant(rt2x00dev);
 
        /*
@@ -1792,7 +1792,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
                break;
        case 2:
                if (rt2x00_rt(rt2x00dev, RT3572) &&
-                   test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+                   rt2x00_has_cap_bt_coexist(rt2x00dev))
                        rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1);
                else
                        rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
@@ -1822,7 +1822,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
                break;
        case 2:
                if (rt2x00_rt(rt2x00dev, RT3572) &&
-                   test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+                   rt2x00_has_cap_bt_coexist(rt2x00dev)) {
                        rt2x00_set_field8(&r3, BBP3_RX_ADC, 1);
                        rt2x00_set_field8(&r3, BBP3_RX_ANTENNA,
                                rt2x00dev->curr_band == IEEE80211_BAND_5GHZ);
@@ -2131,7 +2131,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
        rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
        rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
-       if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+       if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
                if (rf->channel <= 14) {
                        rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
                        rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
@@ -2664,7 +2664,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
        if (rf->channel <= 14) {
                int idx = rf->channel-1;
 
-               if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
                        if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) {
                                /* r55/r59 value array of channel 1~14 */
                                static const char r55_bt_rev[] = {0x83, 0x83,
@@ -3210,8 +3210,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        if (rf->channel <= 14) {
                if (!rt2x00_rt(rt2x00dev, RT5390) &&
                    !rt2x00_rt(rt2x00dev, RT5392)) {
-                       if (test_bit(CAPABILITY_EXTERNAL_LNA_BG,
-                                    &rt2x00dev->cap_flags)) {
+                       if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
                                rt2800_bbp_write(rt2x00dev, 82, 0x62);
                                rt2800_bbp_write(rt2x00dev, 75, 0x46);
                        } else {
@@ -3236,7 +3235,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                if (rt2x00_rt(rt2x00dev, RT3593))
                        rt2800_bbp_write(rt2x00dev, 83, 0x9a);
 
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev))
                        rt2800_bbp_write(rt2x00dev, 75, 0x46);
                else
                        rt2800_bbp_write(rt2x00dev, 75, 0x50);
@@ -3272,7 +3271,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                /* Turn on primary PAs */
                rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN,
                                   rf->channel > 14);
-               if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_bt_coexist(rt2x00dev))
                        rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1);
                else
                        rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN,
@@ -3574,7 +3573,7 @@ static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev,
 {
        int delta;
 
-       if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_power_limit(rt2x00dev))
                return 0;
 
        /*
@@ -3603,7 +3602,7 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
        if (rt2x00_rt(rt2x00dev, RT3593))
                return min_t(u8, txpower, 0xc);
 
-       if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) {
+       if (rt2x00_has_cap_power_limit(rt2x00dev)) {
                /*
                 * Check if eirp txpower exceed txpower_limit.
                 * We use OFDM 6M as criterion and its eirp txpower
@@ -5524,7 +5523,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
        ant = (div_mode == 3) ? 1 : 0;
 
        /* check if this is a Bluetooth combo card */
-       if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+       if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
                u32 reg;
 
                rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
@@ -5833,7 +5832,7 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev)
            rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
            rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) ||
            rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) {
-               if (!test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
+               if (!rt2x00_has_cap_external_lna_bg(rt2x00dev))
                        rt2x00_set_field8(&rfcsr, RFCSR17_R, 1);
        }
 
@@ -6476,7 +6475,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
        rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
        rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
 
-       rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
        rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
        rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
        rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
@@ -6514,7 +6513,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
        rt2800_rfcsr_write(rt2x00dev, 56, 0x22);
        rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
        rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
-       rt2800_rfcsr_write(rt2x00dev, 59, 0x63);
+       rt2800_rfcsr_write(rt2x00dev, 59, 0x8f);
 
        rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
        if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
@@ -6534,7 +6533,6 @@ static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev)
        rt2800_rf_init_calibration(rt2x00dev, 2);
 
        rt2800_rfcsr_write(rt2x00dev, 1, 0x17);
-       rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
        rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
        rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
        rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
@@ -7248,7 +7246,7 @@ static const struct rf_channel rf_vals[] = {
 
 /*
  * RF value list for rt3xxx
- * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052)
+ * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053)
  */
 static const struct rf_channel rf_vals_3x[] = {
        {1,  241, 2, 2 },
@@ -7444,72 +7442,6 @@ static const struct rf_channel rf_vals_5592_xtal40[] = {
        {196, 83, 0, 12, 1},
 };
 
-static const struct rf_channel rf_vals_3053[] = {
-       /* Channel, N, R, K */
-       {1, 241, 2, 2},
-       {2, 241, 2, 7},
-       {3, 242, 2, 2},
-       {4, 242, 2, 7},
-       {5, 243, 2, 2},
-       {6, 243, 2, 7},
-       {7, 244, 2, 2},
-       {8, 244, 2, 7},
-       {9, 245, 2, 2},
-       {10, 245, 2, 7},
-       {11, 246, 2, 2},
-       {12, 246, 2, 7},
-       {13, 247, 2, 2},
-       {14, 248, 2, 4},
-
-       {36, 0x56, 0, 4},
-       {38, 0x56, 0, 6},
-       {40, 0x56, 0, 8},
-       {44, 0x57, 0, 0},
-       {46, 0x57, 0, 2},
-       {48, 0x57, 0, 4},
-       {52, 0x57, 0, 8},
-       {54, 0x57, 0, 10},
-       {56, 0x58, 0, 0},
-       {60, 0x58, 0, 4},
-       {62, 0x58, 0, 6},
-       {64, 0x58, 0, 8},
-
-       {100, 0x5B, 0, 8},
-       {102, 0x5B, 0, 10},
-       {104, 0x5C, 0, 0},
-       {108, 0x5C, 0, 4},
-       {110, 0x5C, 0, 6},
-       {112, 0x5C, 0, 8},
-
-       /* NOTE: Channel 114 has been removed intentionally.
-        * The EEPROM contains no TX power values for that,
-        * and it is disabled in the vendor driver as well.
-        */
-
-       {116, 0x5D, 0, 0},
-       {118, 0x5D, 0, 2},
-       {120, 0x5D, 0, 4},
-       {124, 0x5D, 0, 8},
-       {126, 0x5D, 0, 10},
-       {128, 0x5E, 0, 0},
-       {132, 0x5E, 0, 4},
-       {134, 0x5E, 0, 6},
-       {136, 0x5E, 0, 8},
-       {140, 0x5F, 0, 0},
-
-       {149, 0x5F, 0, 9},
-       {151, 0x5F, 0, 11},
-       {153, 0x60, 0, 1},
-       {157, 0x60, 0, 5},
-       {159, 0x60, 0, 7},
-       {161, 0x60, 0, 9},
-       {165, 0x61, 0, 1},
-       {167, 0x61, 0, 3},
-       {169, 0x61, 0, 5},
-       {171, 0x61, 0, 7},
-       {173, 0x61, 0, 9},
-};
-
 static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 {
        struct hw_mode_spec *spec = &rt2x00dev->spec;
@@ -7518,7 +7450,6 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        char *default_power2;
        char *default_power3;
        unsigned int i;
-       u16 eeprom;
        u32 reg;
 
        /*
@@ -7567,49 +7498,48 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->max_report_rates = 7;
        rt2x00dev->hw->max_rate_tries = 1;
 
-       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
-
        /*
         * Initialize hw_mode information.
         */
-       spec->supported_bands = SUPPORT_BAND_2GHZ;
        spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
-       if (rt2x00_rf(rt2x00dev, RF2820) ||
-           rt2x00_rf(rt2x00dev, RF2720)) {
+       switch (rt2x00dev->chip.rf) {
+       case RF2720:
+       case RF2820:
                spec->num_channels = 14;
                spec->channels = rf_vals;
-       } else if (rt2x00_rf(rt2x00dev, RF2850) ||
-                  rt2x00_rf(rt2x00dev, RF2750)) {
-               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+               break;
+
+       case RF2750:
+       case RF2850:
                spec->num_channels = ARRAY_SIZE(rf_vals);
                spec->channels = rf_vals;
-       } else if (rt2x00_rf(rt2x00dev, RF3020) ||
-                  rt2x00_rf(rt2x00dev, RF2020) ||
-                  rt2x00_rf(rt2x00dev, RF3021) ||
-                  rt2x00_rf(rt2x00dev, RF3022) ||
-                  rt2x00_rf(rt2x00dev, RF3070) ||
-                  rt2x00_rf(rt2x00dev, RF3290) ||
-                  rt2x00_rf(rt2x00dev, RF3320) ||
-                  rt2x00_rf(rt2x00dev, RF3322) ||
-                  rt2x00_rf(rt2x00dev, RF5360) ||
-                  rt2x00_rf(rt2x00dev, RF5370) ||
-                  rt2x00_rf(rt2x00dev, RF5372) ||
-                  rt2x00_rf(rt2x00dev, RF5390) ||
-                  rt2x00_rf(rt2x00dev, RF5392)) {
+               break;
+
+       case RF2020:
+       case RF3020:
+       case RF3021:
+       case RF3022:
+       case RF3070:
+       case RF3290:
+       case RF3320:
+       case RF3322:
+       case RF5360:
+       case RF5370:
+       case RF5372:
+       case RF5390:
+       case RF5392:
                spec->num_channels = 14;
                spec->channels = rf_vals_3x;
-       } else if (rt2x00_rf(rt2x00dev, RF3052)) {
-               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+               break;
+
+       case RF3052:
+       case RF3053:
                spec->num_channels = ARRAY_SIZE(rf_vals_3x);
                spec->channels = rf_vals_3x;
-       } else if (rt2x00_rf(rt2x00dev, RF3053)) {
-               spec->supported_bands |= SUPPORT_BAND_5GHZ;
-               spec->num_channels = ARRAY_SIZE(rf_vals_3053);
-               spec->channels = rf_vals_3053;
-       } else if (rt2x00_rf(rt2x00dev, RF5592)) {
-               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+               break;
 
+       case RF5592:
                rt2800_register_read(rt2x00dev, MAC_DEBUG_INDEX, &reg);
                if (rt2x00_get_field32(reg, MAC_DEBUG_INDEX_XTAL)) {
                        spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal40);
@@ -7618,11 +7548,16 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                        spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal20);
                        spec->channels = rf_vals_5592_xtal20;
                }
+               break;
        }
 
        if (WARN_ON_ONCE(!spec->channels))
                return -ENODEV;
 
+       spec->supported_bands = SUPPORT_BAND_2GHZ;
+       if (spec->num_channels > 14)
+               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+
        /*
         * Initialize HT information.
         */
@@ -7637,22 +7572,21 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
            IEEE80211_HT_CAP_SGI_20 |
            IEEE80211_HT_CAP_SGI_40;
 
-       if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) >= 2)
+       if (rt2x00dev->default_ant.tx_chain_num >= 2)
                spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC;
 
-       spec->ht.cap |=
-           rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) <<
-               IEEE80211_HT_CAP_RX_STBC_SHIFT;
+       spec->ht.cap |= rt2x00dev->default_ant.rx_chain_num <<
+                       IEEE80211_HT_CAP_RX_STBC_SHIFT;
 
        spec->ht.ampdu_factor = 3;
        spec->ht.ampdu_density = 4;
        spec->ht.mcs.tx_params =
            IEEE80211_HT_MCS_TX_DEFINED |
            IEEE80211_HT_MCS_TX_RX_DIFF |
-           ((rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) - 1) <<
-               IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
+           ((rt2x00dev->default_ant.tx_chain_num - 1) <<
+            IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
 
-       switch (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH)) {
+       switch (rt2x00dev->default_ant.rx_chain_num) {
        case 3:
                spec->ht.mcs.rx_mask[2] = 0xff;
        case 2:
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/rt2x00/rt2800mmio.c
new file mode 100644 (file)
index 0000000..ae15228
--- /dev/null
@@ -0,0 +1,873 @@
+/*     Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
+ *     Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ *     Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ *     Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ *     Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ *     Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ *     Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ *     Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ *     <http://rt2x00.serialmonkey.com>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the
+ *     Free Software Foundation, Inc.,
+ *     59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*     Module: rt2800mmio
+ *     Abstract: rt2800 MMIO device routines.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/export.h>
+
+#include "rt2x00.h"
+#include "rt2x00mmio.h"
+#include "rt2800.h"
+#include "rt2800lib.h"
+#include "rt2800mmio.h"
+
+/*
+ * TX descriptor initialization
+ */
+__le32 *rt2800mmio_get_txwi(struct queue_entry *entry)
+{
+       return (__le32 *) entry->skb->data;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_get_txwi);
+
+void rt2800mmio_write_tx_desc(struct queue_entry *entry,
+                             struct txentry_desc *txdesc)
+{
+       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
+       __le32 *txd = entry_priv->desc;
+       u32 word;
+       const unsigned int txwi_size = entry->queue->winfo_size;
+
+       /*
+        * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1
+        * must contains a TXWI structure + 802.11 header + padding + 802.11
+        * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and
+        * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11
+        * data. It means that LAST_SEC0 is always 0.
+        */
+
+       /*
+        * Initialize TX descriptor
+        */
+       word = 0;
+       rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma);
+       rt2x00_desc_write(txd, 0, word);
+
+       word = 0;
+       rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len);
+       rt2x00_set_field32(&word, TXD_W1_LAST_SEC1,
+                          !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
+       rt2x00_set_field32(&word, TXD_W1_BURST,
+                          test_bit(ENTRY_TXD_BURST, &txdesc->flags));
+       rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size);
+       rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0);
+       rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0);
+       rt2x00_desc_write(txd, 1, word);
+
+       word = 0;
+       rt2x00_set_field32(&word, TXD_W2_SD_PTR1,
+                          skbdesc->skb_dma + txwi_size);
+       rt2x00_desc_write(txd, 2, word);
+
+       word = 0;
+       rt2x00_set_field32(&word, TXD_W3_WIV,
+                          !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags));
+       rt2x00_set_field32(&word, TXD_W3_QSEL, 2);
+       rt2x00_desc_write(txd, 3, word);
+
+       /*
+        * Register descriptor details in skb frame descriptor.
+        */
+       skbdesc->desc = txd;
+       skbdesc->desc_len = TXD_DESC_SIZE;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc);
+
+/*
+ * RX control handlers
+ */
+void rt2800mmio_fill_rxdone(struct queue_entry *entry,
+                           struct rxdone_entry_desc *rxdesc)
+{
+       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
+       __le32 *rxd = entry_priv->desc;
+       u32 word;
+
+       rt2x00_desc_read(rxd, 3, &word);
+
+       if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR))
+               rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
+
+       /*
+        * Unfortunately we don't know the cipher type used during
+        * decryption. This prevents us from correct providing
+        * correct statistics through debugfs.
+        */
+       rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR);
+
+       if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) {
+               /*
+                * Hardware has stripped IV/EIV data from 802.11 frame during
+                * decryption. Unfortunately the descriptor doesn't contain
+                * any fields with the EIV/IV data either, so they can't
+                * be restored by rt2x00lib.
+                */
+               rxdesc->flags |= RX_FLAG_IV_STRIPPED;
+
+               /*
+                * The hardware has already checked the Michael Mic and has
+                * stripped it from the frame. Signal this to mac80211.
+                */
+               rxdesc->flags |= RX_FLAG_MMIC_STRIPPED;
+
+               if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
+                       rxdesc->flags |= RX_FLAG_DECRYPTED;
+               else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
+                       rxdesc->flags |= RX_FLAG_MMIC_ERROR;
+       }
+
+       if (rt2x00_get_field32(word, RXD_W3_MY_BSS))
+               rxdesc->dev_flags |= RXDONE_MY_BSS;
+
+       if (rt2x00_get_field32(word, RXD_W3_L2PAD))
+               rxdesc->dev_flags |= RXDONE_L2PAD;
+
+       /*
+        * Process the RXWI structure that is at the start of the buffer.
+        */
+       rt2800_process_rxwi(entry, rxdesc);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone);
+
+/*
+ * Interrupt functions.
+ */
+static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev)
+{
+       struct ieee80211_conf conf = { .flags = 0 };
+       struct rt2x00lib_conf libconf = { .conf = &conf };
+
+       rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
+}
+
+static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status)
+{
+       __le32 *txwi;
+       u32 word;
+       int wcid, tx_wcid;
+
+       wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID);
+
+       txwi = rt2800_drv_get_txwi(entry);
+       rt2x00_desc_read(txwi, 1, &word);
+       tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
+
+       return (tx_wcid == wcid);
+}
+
+static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data)
+{
+       u32 status = *(u32 *)data;
+
+       /*
+        * rt2800pci hardware might reorder frames when exchanging traffic
+        * with multiple BA enabled STAs.
+        *
+        * For example, a tx queue
+        *    [ STA1 | STA2 | STA1 | STA2 ]
+        * can result in tx status reports
+        *    [ STA1 | STA1 | STA2 | STA2 ]
+        * when the hw decides to aggregate the frames for STA1 into one AMPDU.
+        *
+        * To mitigate this effect, associate the tx status to the first frame
+        * in the tx queue with a matching wcid.
+        */
+       if (rt2800mmio_txdone_entry_check(entry, status) &&
+           !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               /*
+                * Got a matching frame, associate the tx status with
+                * the frame
+                */
+               entry->status = status;
+               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
+               return true;
+       }
+
+       /* Check the next frame */
+       return false;
+}
+
+static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data)
+{
+       u32 status = *(u32 *)data;
+
+       /*
+        * Find the first frame without tx status and assign this status to it
+        * regardless if it matches or not.
+        */
+       if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               /*
+                * Got a matching frame, associate the tx status with
+                * the frame
+                */
+               entry->status = status;
+               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
+               return true;
+       }
+
+       /* Check the next frame */
+       return false;
+}
+static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry,
+                                             void *data)
+{
+       if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               rt2800_txdone_entry(entry, entry->status,
+                                   rt2800mmio_get_txwi(entry));
+               return false;
+       }
+
+       /* No more frames to release */
+       return true;
+}
+
+static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev)
+{
+       struct data_queue *queue;
+       u32 status;
+       u8 qid;
+       int max_tx_done = 16;
+
+       while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) {
+               qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE);
+               if (unlikely(qid >= QID_RX)) {
+                       /*
+                        * Unknown queue, this shouldn't happen. Just drop
+                        * this tx status.
+                        */
+                       rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n",
+                                   qid);
+                       break;
+               }
+
+               queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
+               if (unlikely(queue == NULL)) {
+                       /*
+                        * The queue is NULL, this shouldn't happen. Stop
+                        * processing here and drop the tx status
+                        */
+                       rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n",
+                                   qid);
+                       break;
+               }
+
+               if (unlikely(rt2x00queue_empty(queue))) {
+                       /*
+                        * The queue is empty. Stop processing here
+                        * and drop the tx status.
+                        */
+                       rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
+                                   qid);
+                       break;
+               }
+
+               /*
+                * Let's associate this tx status with the first
+                * matching frame.
+                */
+               if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                               Q_INDEX, &status,
+                                               rt2800mmio_txdone_find_entry)) {
+                       /*
+                        * We cannot match the tx status to any frame, so just
+                        * use the first one.
+                        */
+                       if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                                       Q_INDEX, &status,
+                                                       rt2800mmio_txdone_match_first)) {
+                               rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n",
+                                           qid);
+                               break;
+                       }
+               }
+
+               /*
+                * Release all frames with a valid tx status.
+                */
+               rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                          Q_INDEX, NULL,
+                                          rt2800mmio_txdone_release_entries);
+
+               if (--max_tx_done == 0)
+                       break;
+       }
+
+       return !max_tx_done;
+}
+
+static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev,
+                                              struct rt2x00_field32 irq_field)
+{
+       u32 reg;
+
+       /*
+        * Enable a single interrupt. The interrupt mask register
+        * access needs locking.
+        */
+       spin_lock_irq(&rt2x00dev->irqmask_lock);
+       rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, &reg);
+       rt2x00_set_field32(&reg, irq_field, 1);
+       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       spin_unlock_irq(&rt2x00dev->irqmask_lock);
+}
+
+void rt2800mmio_txstatus_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       if (rt2800mmio_txdone(rt2x00dev))
+               tasklet_schedule(&rt2x00dev->txstatus_tasklet);
+
+       /*
+        * No need to enable the tx status interrupt here as we always
+        * leave it enabled to minimize the possibility of a tx status
+        * register overflow. See comment in interrupt handler.
+        */
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet);
+
+void rt2800mmio_pretbtt_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       rt2x00lib_pretbtt(rt2x00dev);
+       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet);
+
+void rt2800mmio_tbtt_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       u32 reg;
+
+       rt2x00lib_beacondone(rt2x00dev);
+
+       if (rt2x00dev->intf_ap_count) {
+               /*
+                * The rt2800pci hardware tbtt timer is off by 1us per tbtt
+                * causing beacon skew and as a result causing problems with
+                * some powersaving clients over time. Shorten the beacon
+                * interval every 64 beacons by 64us to mitigate this effect.
+                */
+               if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) {
+                       rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+                       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+                                          (rt2x00dev->beacon_int * 16) - 1);
+                       rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+               } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) {
+                       rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+                       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+                                          (rt2x00dev->beacon_int * 16));
+                       rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+               }
+               drv_data->tbtt_tick++;
+               drv_data->tbtt_tick %= BCN_TBTT_OFFSET;
+       }
+
+       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet);
+
+void rt2800mmio_rxdone_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       if (rt2x00mmio_rxdone(rt2x00dev))
+               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
+       else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet);
+
+void rt2800mmio_autowake_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       rt2800mmio_wakeup(rt2x00dev);
+       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               rt2800mmio_enable_interrupt(rt2x00dev,
+                                           INT_MASK_CSR_AUTO_WAKEUP);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet);
+
+static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
+{
+       u32 status;
+       int i;
+
+       /*
+        * The TX_FIFO_STATUS interrupt needs special care. We should
+        * read TX_STA_FIFO but we should do it immediately as otherwise
+        * the register can overflow and we would lose status reports.
+        *
+        * Hence, read the TX_STA_FIFO register and copy all tx status
+        * reports into a kernel FIFO which is handled in the txstatus
+        * tasklet. We use a tasklet to process the tx status reports
+        * because we can schedule the tasklet multiple times (when the
+        * interrupt fires again during tx status processing).
+        *
+        * Furthermore we don't disable the TX_FIFO_STATUS
+        * interrupt here but leave it enabled so that the TX_STA_FIFO
+        * can also be read while the tx status tasklet gets executed.
+        *
+        * Since we have only one producer and one consumer we don't
+        * need to lock the kfifo.
+        */
+       for (i = 0; i < rt2x00dev->tx->limit; i++) {
+               rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
+
+               if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
+                       break;
+
+               if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) {
+                       rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n");
+                       break;
+               }
+       }
+
+       /* Schedule the tasklet for processing the tx status. */
+       tasklet_schedule(&rt2x00dev->txstatus_tasklet);
+}
+
+irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance)
+{
+       struct rt2x00_dev *rt2x00dev = dev_instance;
+       u32 reg, mask;
+
+       /* Read status and ACK all interrupts */
+       rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
+       rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
+
+       if (!reg)
+               return IRQ_NONE;
+
+       if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               return IRQ_HANDLED;
+
+       /*
+        * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits
+        * for interrupts and interrupt masks we can just use the value of
+        * INT_SOURCE_CSR to create the interrupt mask.
+        */
+       mask = ~reg;
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
+               rt2800mmio_txstatus_interrupt(rt2x00dev);
+               /*
+                * Never disable the TX_FIFO_STATUS interrupt.
+                */
+               rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1);
+       }
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
+               tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet);
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT))
+               tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet);
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
+               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP))
+               tasklet_schedule(&rt2x00dev->autowake_tasklet);
+
+       /*
+        * Disable all interrupts for which a tasklet was scheduled right now,
+        * the tasklet will reenable the appropriate interrupts.
+        */
+       spin_lock(&rt2x00dev->irqmask_lock);
+       rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, &reg);
+       reg &= mask;
+       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       spin_unlock(&rt2x00dev->irqmask_lock);
+
+       return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_interrupt);
+
+void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev,
+                          enum dev_state state)
+{
+       u32 reg;
+       unsigned long flags;
+
+       /*
+        * When interrupts are being enabled, the interrupt registers
+        * should clear the register to assure a clean state.
+        */
+       if (state == STATE_RADIO_IRQ_ON) {
+               rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
+               rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
+       }
+
+       spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
+       reg = 0;
+       if (state == STATE_RADIO_IRQ_ON) {
+               rt2x00_set_field32(&reg, INT_MASK_CSR_RX_DONE, 1);
+               rt2x00_set_field32(&reg, INT_MASK_CSR_TBTT, 1);
+               rt2x00_set_field32(&reg, INT_MASK_CSR_PRE_TBTT, 1);
+               rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, 1);
+               rt2x00_set_field32(&reg, INT_MASK_CSR_AUTO_WAKEUP, 1);
+       }
+       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
+
+       if (state == STATE_RADIO_IRQ_OFF) {
+               /*
+                * Wait for possibly running tasklets to finish.
+                */
+               tasklet_kill(&rt2x00dev->txstatus_tasklet);
+               tasklet_kill(&rt2x00dev->rxdone_tasklet);
+               tasklet_kill(&rt2x00dev->autowake_tasklet);
+               tasklet_kill(&rt2x00dev->tbtt_tasklet);
+               tasklet_kill(&rt2x00dev->pretbtt_tasklet);
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq);
+
+/*
+ * Queue handlers.
+ */
+void rt2800mmio_start_queue(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       u32 reg;
+
+       switch (queue->qid) {
+       case QID_RX:
+               rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+               rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
+               rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+               break;
+       case QID_BEACON:
+               rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
+               rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+               rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, &reg);
+               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 1);
+               rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg);
+               break;
+       default:
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_start_queue);
+
+void rt2800mmio_kick_queue(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       struct queue_entry *entry;
+
+       switch (queue->qid) {
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               entry = rt2x00queue_get_entry(queue, Q_INDEX);
+               rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid),
+                                         entry->entry_idx);
+               break;
+       case QID_MGMT:
+               entry = rt2x00queue_get_entry(queue, Q_INDEX);
+               rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5),
+                                         entry->entry_idx);
+               break;
+       default:
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue);
+
+void rt2800mmio_stop_queue(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       u32 reg;
+
+       switch (queue->qid) {
+       case QID_RX:
+               rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+               rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
+               rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+               break;
+       case QID_BEACON:
+               rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 0);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
+               rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+               rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, &reg);
+               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 0);
+               rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg);
+
+               /*
+                * Wait for current invocation to finish. The tasklet
+                * won't be scheduled anymore afterwards since we disabled
+                * the TBTT and PRE TBTT timer.
+                */
+               tasklet_kill(&rt2x00dev->tbtt_tasklet);
+               tasklet_kill(&rt2x00dev->pretbtt_tasklet);
+
+               break;
+       default:
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_stop_queue);
+
+void rt2800mmio_queue_init(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       unsigned short txwi_size, rxwi_size;
+
+       rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size);
+
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 128;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->winfo_size = rxwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 64;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_BEACON:
+               queue->limit = 8;
+               queue->data_size = 0; /* No DMA required for beacons */
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_queue_init);
+
+/*
+ * Initialization functions.
+ */
+bool rt2800mmio_get_entry_state(struct queue_entry *entry)
+{
+       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
+       u32 word;
+
+       if (entry->queue->qid == QID_RX) {
+               rt2x00_desc_read(entry_priv->desc, 1, &word);
+
+               return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE));
+       } else {
+               rt2x00_desc_read(entry_priv->desc, 1, &word);
+
+               return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE));
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state);
+
+void rt2800mmio_clear_entry(struct queue_entry *entry)
+{
+       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
+       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+       u32 word;
+
+       if (entry->queue->qid == QID_RX) {
+               rt2x00_desc_read(entry_priv->desc, 0, &word);
+               rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma);
+               rt2x00_desc_write(entry_priv->desc, 0, word);
+
+               rt2x00_desc_read(entry_priv->desc, 1, &word);
+               rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0);
+               rt2x00_desc_write(entry_priv->desc, 1, word);
+
+               /*
+                * Set RX IDX in register to inform hardware that we have
+                * handled this entry and it is available for reuse again.
+                */
+               rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX,
+                                         entry->entry_idx);
+       } else {
+               rt2x00_desc_read(entry_priv->desc, 1, &word);
+               rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1);
+               rt2x00_desc_write(entry_priv->desc, 1, word);
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_clear_entry);
+
+int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev)
+{
+       struct queue_entry_priv_mmio *entry_priv;
+
+       /*
+        * Initialize registers.
+        */
+       entry_priv = rt2x00dev->tx[0].entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0,
+                                 rt2x00dev->tx[0].limit);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0);
+
+       entry_priv = rt2x00dev->tx[1].entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1,
+                                 rt2x00dev->tx[1].limit);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0);
+
+       entry_priv = rt2x00dev->tx[2].entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2,
+                                 rt2x00dev->tx[2].limit);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0);
+
+       entry_priv = rt2x00dev->tx[3].entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3,
+                                 rt2x00dev->tx[3].limit);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0);
+
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0);
+
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0);
+
+       entry_priv = rt2x00dev->rx->entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT,
+                                 rt2x00dev->rx[0].limit);
+       rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX,
+                                 rt2x00dev->rx[0].limit - 1);
+       rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0);
+
+       rt2800_disable_wpdma(rt2x00dev);
+
+       rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_init_queues);
+
+int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev)
+{
+       u32 reg;
+
+       /*
+        * Reset DMA indexes
+        */
+       rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX4, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX5, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DRX_IDX0, 1);
+       rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
+
+       rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
+       rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
+
+       if (rt2x00_is_pcie(rt2x00dev) &&
+           (rt2x00_rt(rt2x00dev, RT3090) ||
+            rt2x00_rt(rt2x00dev, RT3390) ||
+            rt2x00_rt(rt2x00dev, RT3572) ||
+            rt2x00_rt(rt2x00dev, RT3593) ||
+            rt2x00_rt(rt2x00dev, RT5390) ||
+            rt2x00_rt(rt2x00dev, RT5392) ||
+            rt2x00_rt(rt2x00dev, RT5592))) {
+               rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, &reg);
+               rt2x00_set_field32(&reg, AUX_CTRL_FORCE_PCIE_CLK, 1);
+               rt2x00_set_field32(&reg, AUX_CTRL_WAKE_PCIE_EN, 1);
+               rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg);
+       }
+
+       rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
+
+       reg = 0;
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_BBP, 1);
+       rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+       rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_init_registers);
+
+/*
+ * Device state switch handlers.
+ */
+int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev)
+{
+       /* Wait for DMA, ignore error until we initialize queues. */
+       rt2800_wait_wpdma_ready(rt2x00dev);
+
+       if (unlikely(rt2800mmio_init_queues(rt2x00dev)))
+               return -EIO;
+
+       return rt2800_enable_radio(rt2x00dev);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_enable_radio);
+
+MODULE_AUTHOR(DRV_PROJECT);
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("rt2800 MMIO library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/rt2x00/rt2800mmio.h
new file mode 100644 (file)
index 0000000..6a10de3
--- /dev/null
@@ -0,0 +1,165 @@
+/*     Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
+ *     Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ *     Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ *     Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ *     Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ *     Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ *     Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ *     Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ *     <http://rt2x00.serialmonkey.com>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the
+ *     Free Software Foundation, Inc.,
+ *     59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*     Module: rt2800mmio
+ *     Abstract: forward declarations for the rt2800mmio module.
+ */
+
+#ifndef RT2800MMIO_H
+#define RT2800MMIO_H
+
+/*
+ * Queue register offset macros
+ */
+#define TX_QUEUE_REG_OFFSET    0x10
+#define TX_BASE_PTR(__x)       (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET))
+#define TX_MAX_CNT(__x)                (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET))
+#define TX_CTX_IDX(__x)                (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET))
+#define TX_DTX_IDX(__x)                (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET))
+
+/*
+ * DMA descriptor defines.
+ */
+#define TXD_DESC_SIZE                  (4 * sizeof(__le32))
+#define RXD_DESC_SIZE                  (4 * sizeof(__le32))
+
+/*
+ * TX descriptor format for TX, PRIO and Beacon Ring.
+ */
+
+/*
+ * Word0
+ */
+#define TXD_W0_SD_PTR0                 FIELD32(0xffffffff)
+
+/*
+ * Word1
+ */
+#define TXD_W1_SD_LEN1                 FIELD32(0x00003fff)
+#define TXD_W1_LAST_SEC1               FIELD32(0x00004000)
+#define TXD_W1_BURST                   FIELD32(0x00008000)
+#define TXD_W1_SD_LEN0                 FIELD32(0x3fff0000)
+#define TXD_W1_LAST_SEC0               FIELD32(0x40000000)
+#define TXD_W1_DMA_DONE                        FIELD32(0x80000000)
+
+/*
+ * Word2
+ */
+#define TXD_W2_SD_PTR1                 FIELD32(0xffffffff)
+
+/*
+ * Word3
+ * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI
+ * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler.
+ *       0:MGMT, 1:HCCA 2:EDCA
+ */
+#define TXD_W3_WIV                     FIELD32(0x01000000)
+#define TXD_W3_QSEL                    FIELD32(0x06000000)
+#define TXD_W3_TCO                     FIELD32(0x20000000)
+#define TXD_W3_UCO                     FIELD32(0x40000000)
+#define TXD_W3_ICO                     FIELD32(0x80000000)
+
+/*
+ * RX descriptor format for RX Ring.
+ */
+
+/*
+ * Word0
+ */
+#define RXD_W0_SDP0                    FIELD32(0xffffffff)
+
+/*
+ * Word1
+ */
+#define RXD_W1_SDL1                    FIELD32(0x00003fff)
+#define RXD_W1_SDL0                    FIELD32(0x3fff0000)
+#define RXD_W1_LS0                     FIELD32(0x40000000)
+#define RXD_W1_DMA_DONE                        FIELD32(0x80000000)
+
+/*
+ * Word2
+ */
+#define RXD_W2_SDP1                    FIELD32(0xffffffff)
+
+/*
+ * Word3
+ * AMSDU: RX with 802.3 header, not 802.11 header.
+ * DECRYPTED: This frame is being decrypted.
+ */
+#define RXD_W3_BA                      FIELD32(0x00000001)
+#define RXD_W3_DATA                    FIELD32(0x00000002)
+#define RXD_W3_NULLDATA                        FIELD32(0x00000004)
+#define RXD_W3_FRAG                    FIELD32(0x00000008)
+#define RXD_W3_UNICAST_TO_ME           FIELD32(0x00000010)
+#define RXD_W3_MULTICAST               FIELD32(0x00000020)
+#define RXD_W3_BROADCAST               FIELD32(0x00000040)
+#define RXD_W3_MY_BSS                  FIELD32(0x00000080)
+#define RXD_W3_CRC_ERROR               FIELD32(0x00000100)
+#define RXD_W3_CIPHER_ERROR            FIELD32(0x00000600)
+#define RXD_W3_AMSDU                   FIELD32(0x00000800)
+#define RXD_W3_HTC                     FIELD32(0x00001000)
+#define RXD_W3_RSSI                    FIELD32(0x00002000)
+#define RXD_W3_L2PAD                   FIELD32(0x00004000)
+#define RXD_W3_AMPDU                   FIELD32(0x00008000)
+#define RXD_W3_DECRYPTED               FIELD32(0x00010000)
+#define RXD_W3_PLCP_SIGNAL             FIELD32(0x00020000)
+#define RXD_W3_PLCP_RSSI               FIELD32(0x00040000)
+
+/* TX descriptor initialization */
+__le32 *rt2800mmio_get_txwi(struct queue_entry *entry);
+void rt2800mmio_write_tx_desc(struct queue_entry *entry,
+                             struct txentry_desc *txdesc);
+
+/* RX control handlers */
+void rt2800mmio_fill_rxdone(struct queue_entry *entry,
+                           struct rxdone_entry_desc *rxdesc);
+
+/* Interrupt functions */
+void rt2800mmio_txstatus_tasklet(unsigned long data);
+void rt2800mmio_pretbtt_tasklet(unsigned long data);
+void rt2800mmio_tbtt_tasklet(unsigned long data);
+void rt2800mmio_rxdone_tasklet(unsigned long data);
+void rt2800mmio_autowake_tasklet(unsigned long data);
+irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance);
+void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev,
+                          enum dev_state state);
+
+/* Queue handlers */
+void rt2800mmio_start_queue(struct data_queue *queue);
+void rt2800mmio_kick_queue(struct data_queue *queue);
+void rt2800mmio_stop_queue(struct data_queue *queue);
+void rt2800mmio_queue_init(struct data_queue *queue);
+
+/* Initialization functions */
+bool rt2800mmio_get_entry_state(struct queue_entry *entry);
+void rt2800mmio_clear_entry(struct queue_entry *entry);
+int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev);
+int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev);
+
+/* Device state switch handlers. */
+int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev);
+
+#endif /* RT2800MMIO_H */
index f8f2abbfbb6554f1f432c3e7a8b51facb5d9adec..b504455b4fec10c69c4cc09c1f44be4b92ac3be4 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pci.h>
-#include <linux/platform_device.h>
 #include <linux/eeprom_93cx6.h>
 
 #include "rt2x00.h"
 #include "rt2x00mmio.h"
 #include "rt2x00pci.h"
-#include "rt2x00soc.h"
 #include "rt2800lib.h"
+#include "rt2800mmio.h"
 #include "rt2800.h"
 #include "rt2800pci.h"
 
@@ -90,27 +89,6 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token)
        rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0);
 }
 
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-static int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
-{
-       void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
-
-       if (!base_addr)
-               return -ENOMEM;
-
-       memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
-
-       iounmap(base_addr);
-       return 0;
-}
-#else
-static inline int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
-{
-       return -ENOMEM;
-}
-#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */
-
-#ifdef CONFIG_PCI
 static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom)
 {
        struct rt2x00_dev *rt2x00dev = eeprom->data;
@@ -183,112 +161,6 @@ static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
 {
        return rt2800_read_eeprom_efuse(rt2x00dev);
 }
-#else
-static inline int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev)
-{
-       return 0;
-}
-
-static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
-{
-       return -EOPNOTSUPP;
-}
-#endif /* CONFIG_PCI */
-
-/*
- * Queue handlers.
- */
-static void rt2800pci_start_queue(struct data_queue *queue)
-{
-       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-       u32 reg;
-
-       switch (queue->qid) {
-       case QID_RX:
-               rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
-               rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
-               rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-               break;
-       case QID_BEACON:
-               rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
-               rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-
-               rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, &reg);
-               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 1);
-               rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg);
-               break;
-       default:
-               break;
-       }
-}
-
-static void rt2800pci_kick_queue(struct data_queue *queue)
-{
-       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-       struct queue_entry *entry;
-
-       switch (queue->qid) {
-       case QID_AC_VO:
-       case QID_AC_VI:
-       case QID_AC_BE:
-       case QID_AC_BK:
-               entry = rt2x00queue_get_entry(queue, Q_INDEX);
-               rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid),
-                                         entry->entry_idx);
-               break;
-       case QID_MGMT:
-               entry = rt2x00queue_get_entry(queue, Q_INDEX);
-               rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5),
-                                         entry->entry_idx);
-               break;
-       default:
-               break;
-       }
-}
-
-static void rt2800pci_stop_queue(struct data_queue *queue)
-{
-       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-       u32 reg;
-
-       switch (queue->qid) {
-       case QID_RX:
-               rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
-               rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
-               rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-               break;
-       case QID_BEACON:
-               rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 0);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
-               rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-
-               rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, &reg);
-               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 0);
-               rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg);
-
-               /*
-                * Wait for current invocation to finish. The tasklet
-                * won't be scheduled anymore afterwards since we disabled
-                * the TBTT and PRE TBTT timer.
-                */
-               tasklet_kill(&rt2x00dev->tbtt_tasklet);
-               tasklet_kill(&rt2x00dev->pretbtt_tasklet);
-
-               break;
-       default:
-               break;
-       }
-}
 
 /*
  * Firmware functions
@@ -331,218 +203,14 @@ static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev,
        return 0;
 }
 
-/*
- * Initialization functions.
- */
-static bool rt2800pci_get_entry_state(struct queue_entry *entry)
-{
-       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
-       u32 word;
-
-       if (entry->queue->qid == QID_RX) {
-               rt2x00_desc_read(entry_priv->desc, 1, &word);
-
-               return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE));
-       } else {
-               rt2x00_desc_read(entry_priv->desc, 1, &word);
-
-               return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE));
-       }
-}
-
-static void rt2800pci_clear_entry(struct queue_entry *entry)
-{
-       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
-       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
-       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       u32 word;
-
-       if (entry->queue->qid == QID_RX) {
-               rt2x00_desc_read(entry_priv->desc, 0, &word);
-               rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma);
-               rt2x00_desc_write(entry_priv->desc, 0, word);
-
-               rt2x00_desc_read(entry_priv->desc, 1, &word);
-               rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0);
-               rt2x00_desc_write(entry_priv->desc, 1, word);
-
-               /*
-                * Set RX IDX in register to inform hardware that we have
-                * handled this entry and it is available for reuse again.
-                */
-               rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX,
-                                         entry->entry_idx);
-       } else {
-               rt2x00_desc_read(entry_priv->desc, 1, &word);
-               rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1);
-               rt2x00_desc_write(entry_priv->desc, 1, word);
-       }
-}
-
-static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev)
-{
-       struct queue_entry_priv_mmio *entry_priv;
-
-       /*
-        * Initialize registers.
-        */
-       entry_priv = rt2x00dev->tx[0].entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0,
-                                 rt2x00dev->tx[0].limit);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0);
-
-       entry_priv = rt2x00dev->tx[1].entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1,
-                                 rt2x00dev->tx[1].limit);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0);
-
-       entry_priv = rt2x00dev->tx[2].entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2,
-                                 rt2x00dev->tx[2].limit);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0);
-
-       entry_priv = rt2x00dev->tx[3].entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3,
-                                 rt2x00dev->tx[3].limit);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0);
-
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0);
-
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0);
-
-       entry_priv = rt2x00dev->rx->entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT,
-                                 rt2x00dev->rx[0].limit);
-       rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX,
-                                 rt2x00dev->rx[0].limit - 1);
-       rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0);
-
-       rt2800_disable_wpdma(rt2x00dev);
-
-       rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0);
-
-       return 0;
-}
-
 /*
  * Device state switch handlers.
  */
-static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
-                                enum dev_state state)
-{
-       u32 reg;
-       unsigned long flags;
-
-       /*
-        * When interrupts are being enabled, the interrupt registers
-        * should clear the register to assure a clean state.
-        */
-       if (state == STATE_RADIO_IRQ_ON) {
-               rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
-               rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
-       }
-
-       spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
-       reg = 0;
-       if (state == STATE_RADIO_IRQ_ON) {
-               rt2x00_set_field32(&reg, INT_MASK_CSR_RX_DONE, 1);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_TBTT, 1);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_PRE_TBTT, 1);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, 1);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_AUTO_WAKEUP, 1);
-       }
-       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
-       spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
-
-       if (state == STATE_RADIO_IRQ_OFF) {
-               /*
-                * Wait for possibly running tasklets to finish.
-                */
-               tasklet_kill(&rt2x00dev->txstatus_tasklet);
-               tasklet_kill(&rt2x00dev->rxdone_tasklet);
-               tasklet_kill(&rt2x00dev->autowake_tasklet);
-               tasklet_kill(&rt2x00dev->tbtt_tasklet);
-               tasklet_kill(&rt2x00dev->pretbtt_tasklet);
-       }
-}
-
-static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev)
-{
-       u32 reg;
-
-       /*
-        * Reset DMA indexes
-        */
-       rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX4, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX5, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DRX_IDX0, 1);
-       rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
-
-       rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
-       rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
-
-       if (rt2x00_is_pcie(rt2x00dev) &&
-           (rt2x00_rt(rt2x00dev, RT3090) ||
-            rt2x00_rt(rt2x00dev, RT3390) ||
-            rt2x00_rt(rt2x00dev, RT3572) ||
-            rt2x00_rt(rt2x00dev, RT3593) ||
-            rt2x00_rt(rt2x00dev, RT5390) ||
-            rt2x00_rt(rt2x00dev, RT5392) ||
-            rt2x00_rt(rt2x00dev, RT5592))) {
-               rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, &reg);
-               rt2x00_set_field32(&reg, AUX_CTRL_FORCE_PCIE_CLK, 1);
-               rt2x00_set_field32(&reg, AUX_CTRL_WAKE_PCIE_EN, 1);
-               rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg);
-       }
-
-       rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
-
-       reg = 0;
-       rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
-       rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_BBP, 1);
-       rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-
-       rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
-
-       return 0;
-}
-
 static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
 
-       /* Wait for DMA, ignore error until we initialize queues. */
-       rt2800_wait_wpdma_ready(rt2x00dev);
-
-       if (unlikely(rt2800pci_init_queues(rt2x00dev)))
-               return -EIO;
-
-       retval = rt2800_enable_radio(rt2x00dev);
+       retval = rt2800mmio_enable_radio(rt2x00dev);
        if (retval)
                return retval;
 
@@ -559,15 +227,6 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev)
        return retval;
 }
 
-static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev)
-{
-       if (rt2x00_is_soc(rt2x00dev)) {
-               rt2800_disable_radio(rt2x00dev);
-               rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0);
-               rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0);
-       }
-}
-
 static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev,
                               enum dev_state state)
 {
@@ -601,12 +260,11 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
                 * After the radio has been disabled, the device should
                 * be put to sleep for powersaving.
                 */
-               rt2800pci_disable_radio(rt2x00dev);
                rt2800pci_set_state(rt2x00dev, STATE_SLEEP);
                break;
        case STATE_RADIO_IRQ_ON:
        case STATE_RADIO_IRQ_OFF:
-               rt2800pci_toggle_irq(rt2x00dev, state);
+               rt2800mmio_toggle_irq(rt2x00dev, state);
                break;
        case STATE_DEEP_SLEEP:
        case STATE_SLEEP:
@@ -626,470 +284,6 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
        return retval;
 }
 
-/*
- * TX descriptor initialization
- */
-static __le32 *rt2800pci_get_txwi(struct queue_entry *entry)
-{
-       return (__le32 *) entry->skb->data;
-}
-
-static void rt2800pci_write_tx_desc(struct queue_entry *entry,
-                                   struct txentry_desc *txdesc)
-{
-       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
-       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
-       __le32 *txd = entry_priv->desc;
-       u32 word;
-       const unsigned int txwi_size = entry->queue->winfo_size;
-
-       /*
-        * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1
-        * must contains a TXWI structure + 802.11 header + padding + 802.11
-        * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and
-        * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11
-        * data. It means that LAST_SEC0 is always 0.
-        */
-
-       /*
-        * Initialize TX descriptor
-        */
-       word = 0;
-       rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma);
-       rt2x00_desc_write(txd, 0, word);
-
-       word = 0;
-       rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len);
-       rt2x00_set_field32(&word, TXD_W1_LAST_SEC1,
-                          !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
-       rt2x00_set_field32(&word, TXD_W1_BURST,
-                          test_bit(ENTRY_TXD_BURST, &txdesc->flags));
-       rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size);
-       rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0);
-       rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0);
-       rt2x00_desc_write(txd, 1, word);
-
-       word = 0;
-       rt2x00_set_field32(&word, TXD_W2_SD_PTR1,
-                          skbdesc->skb_dma + txwi_size);
-       rt2x00_desc_write(txd, 2, word);
-
-       word = 0;
-       rt2x00_set_field32(&word, TXD_W3_WIV,
-                          !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags));
-       rt2x00_set_field32(&word, TXD_W3_QSEL, 2);
-       rt2x00_desc_write(txd, 3, word);
-
-       /*
-        * Register descriptor details in skb frame descriptor.
-        */
-       skbdesc->desc = txd;
-       skbdesc->desc_len = TXD_DESC_SIZE;
-}
-
-/*
- * RX control handlers
- */
-static void rt2800pci_fill_rxdone(struct queue_entry *entry,
-                                 struct rxdone_entry_desc *rxdesc)
-{
-       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
-       __le32 *rxd = entry_priv->desc;
-       u32 word;
-
-       rt2x00_desc_read(rxd, 3, &word);
-
-       if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR))
-               rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
-
-       /*
-        * Unfortunately we don't know the cipher type used during
-        * decryption. This prevents us from correct providing
-        * correct statistics through debugfs.
-        */
-       rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR);
-
-       if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) {
-               /*
-                * Hardware has stripped IV/EIV data from 802.11 frame during
-                * decryption. Unfortunately the descriptor doesn't contain
-                * any fields with the EIV/IV data either, so they can't
-                * be restored by rt2x00lib.
-                */
-               rxdesc->flags |= RX_FLAG_IV_STRIPPED;
-
-               /*
-                * The hardware has already checked the Michael Mic and has
-                * stripped it from the frame. Signal this to mac80211.
-                */
-               rxdesc->flags |= RX_FLAG_MMIC_STRIPPED;
-
-               if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
-                       rxdesc->flags |= RX_FLAG_DECRYPTED;
-               else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
-                       rxdesc->flags |= RX_FLAG_MMIC_ERROR;
-       }
-
-       if (rt2x00_get_field32(word, RXD_W3_MY_BSS))
-               rxdesc->dev_flags |= RXDONE_MY_BSS;
-
-       if (rt2x00_get_field32(word, RXD_W3_L2PAD))
-               rxdesc->dev_flags |= RXDONE_L2PAD;
-
-       /*
-        * Process the RXWI structure that is at the start of the buffer.
-        */
-       rt2800_process_rxwi(entry, rxdesc);
-}
-
-/*
- * Interrupt functions.
- */
-static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
-{
-       struct ieee80211_conf conf = { .flags = 0 };
-       struct rt2x00lib_conf libconf = { .conf = &conf };
-
-       rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
-}
-
-static bool rt2800pci_txdone_entry_check(struct queue_entry *entry, u32 status)
-{
-       __le32 *txwi;
-       u32 word;
-       int wcid, tx_wcid;
-
-       wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID);
-
-       txwi = rt2800_drv_get_txwi(entry);
-       rt2x00_desc_read(txwi, 1, &word);
-       tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
-
-       return (tx_wcid == wcid);
-}
-
-static bool rt2800pci_txdone_find_entry(struct queue_entry *entry, void *data)
-{
-       u32 status = *(u32 *)data;
-
-       /*
-        * rt2800pci hardware might reorder frames when exchanging traffic
-        * with multiple BA enabled STAs.
-        *
-        * For example, a tx queue
-        *    [ STA1 | STA2 | STA1 | STA2 ]
-        * can result in tx status reports
-        *    [ STA1 | STA1 | STA2 | STA2 ]
-        * when the hw decides to aggregate the frames for STA1 into one AMPDU.
-        *
-        * To mitigate this effect, associate the tx status to the first frame
-        * in the tx queue with a matching wcid.
-        */
-       if (rt2800pci_txdone_entry_check(entry, status) &&
-           !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
-               /*
-                * Got a matching frame, associate the tx status with
-                * the frame
-                */
-               entry->status = status;
-               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
-               return true;
-       }
-
-       /* Check the next frame */
-       return false;
-}
-
-static bool rt2800pci_txdone_match_first(struct queue_entry *entry, void *data)
-{
-       u32 status = *(u32 *)data;
-
-       /*
-        * Find the first frame without tx status and assign this status to it
-        * regardless if it matches or not.
-        */
-       if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
-               /*
-                * Got a matching frame, associate the tx status with
-                * the frame
-                */
-               entry->status = status;
-               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
-               return true;
-       }
-
-       /* Check the next frame */
-       return false;
-}
-static bool rt2800pci_txdone_release_entries(struct queue_entry *entry,
-                                            void *data)
-{
-       if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
-               rt2800_txdone_entry(entry, entry->status,
-                                   rt2800pci_get_txwi(entry));
-               return false;
-       }
-
-       /* No more frames to release */
-       return true;
-}
-
-static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
-{
-       struct data_queue *queue;
-       u32 status;
-       u8 qid;
-       int max_tx_done = 16;
-
-       while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) {
-               qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE);
-               if (unlikely(qid >= QID_RX)) {
-                       /*
-                        * Unknown queue, this shouldn't happen. Just drop
-                        * this tx status.
-                        */
-                       rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n",
-                                   qid);
-                       break;
-               }
-
-               queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
-               if (unlikely(queue == NULL)) {
-                       /*
-                        * The queue is NULL, this shouldn't happen. Stop
-                        * processing here and drop the tx status
-                        */
-                       rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n",
-                                   qid);
-                       break;
-               }
-
-               if (unlikely(rt2x00queue_empty(queue))) {
-                       /*
-                        * The queue is empty. Stop processing here
-                        * and drop the tx status.
-                        */
-                       rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
-                                   qid);
-                       break;
-               }
-
-               /*
-                * Let's associate this tx status with the first
-                * matching frame.
-                */
-               if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
-                                               Q_INDEX, &status,
-                                               rt2800pci_txdone_find_entry)) {
-                       /*
-                        * We cannot match the tx status to any frame, so just
-                        * use the first one.
-                        */
-                       if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
-                                                       Q_INDEX, &status,
-                                                       rt2800pci_txdone_match_first)) {
-                               rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n",
-                                           qid);
-                               break;
-                       }
-               }
-
-               /*
-                * Release all frames with a valid tx status.
-                */
-               rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
-                                          Q_INDEX, NULL,
-                                          rt2800pci_txdone_release_entries);
-
-               if (--max_tx_done == 0)
-                       break;
-       }
-
-       return !max_tx_done;
-}
-
-static inline void rt2800pci_enable_interrupt(struct rt2x00_dev *rt2x00dev,
-                                             struct rt2x00_field32 irq_field)
-{
-       u32 reg;
-
-       /*
-        * Enable a single interrupt. The interrupt mask register
-        * access needs locking.
-        */
-       spin_lock_irq(&rt2x00dev->irqmask_lock);
-       rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, &reg);
-       rt2x00_set_field32(&reg, irq_field, 1);
-       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
-       spin_unlock_irq(&rt2x00dev->irqmask_lock);
-}
-
-static void rt2800pci_txstatus_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       if (rt2800pci_txdone(rt2x00dev))
-               tasklet_schedule(&rt2x00dev->txstatus_tasklet);
-
-       /*
-        * No need to enable the tx status interrupt here as we always
-        * leave it enabled to minimize the possibility of a tx status
-        * register overflow. See comment in interrupt handler.
-        */
-}
-
-static void rt2800pci_pretbtt_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       rt2x00lib_pretbtt(rt2x00dev);
-       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT);
-}
-
-static void rt2800pci_tbtt_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
-       u32 reg;
-
-       rt2x00lib_beacondone(rt2x00dev);
-
-       if (rt2x00dev->intf_ap_count) {
-               /*
-                * The rt2800pci hardware tbtt timer is off by 1us per tbtt
-                * causing beacon skew and as a result causing problems with
-                * some powersaving clients over time. Shorten the beacon
-                * interval every 64 beacons by 64us to mitigate this effect.
-                */
-               if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) {
-                       rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-                       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
-                                          (rt2x00dev->beacon_int * 16) - 1);
-                       rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-               } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) {
-                       rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-                       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
-                                          (rt2x00dev->beacon_int * 16));
-                       rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-               }
-               drv_data->tbtt_tick++;
-               drv_data->tbtt_tick %= BCN_TBTT_OFFSET;
-       }
-
-       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT);
-}
-
-static void rt2800pci_rxdone_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       if (rt2x00mmio_rxdone(rt2x00dev))
-               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
-       else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE);
-}
-
-static void rt2800pci_autowake_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       rt2800pci_wakeup(rt2x00dev);
-       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP);
-}
-
-static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
-{
-       u32 status;
-       int i;
-
-       /*
-        * The TX_FIFO_STATUS interrupt needs special care. We should
-        * read TX_STA_FIFO but we should do it immediately as otherwise
-        * the register can overflow and we would lose status reports.
-        *
-        * Hence, read the TX_STA_FIFO register and copy all tx status
-        * reports into a kernel FIFO which is handled in the txstatus
-        * tasklet. We use a tasklet to process the tx status reports
-        * because we can schedule the tasklet multiple times (when the
-        * interrupt fires again during tx status processing).
-        *
-        * Furthermore we don't disable the TX_FIFO_STATUS
-        * interrupt here but leave it enabled so that the TX_STA_FIFO
-        * can also be read while the tx status tasklet gets executed.
-        *
-        * Since we have only one producer and one consumer we don't
-        * need to lock the kfifo.
-        */
-       for (i = 0; i < rt2x00dev->tx->limit; i++) {
-               rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
-
-               if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
-                       break;
-
-               if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) {
-                       rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n");
-                       break;
-               }
-       }
-
-       /* Schedule the tasklet for processing the tx status. */
-       tasklet_schedule(&rt2x00dev->txstatus_tasklet);
-}
-
-static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
-{
-       struct rt2x00_dev *rt2x00dev = dev_instance;
-       u32 reg, mask;
-
-       /* Read status and ACK all interrupts */
-       rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
-       rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
-
-       if (!reg)
-               return IRQ_NONE;
-
-       if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               return IRQ_HANDLED;
-
-       /*
-        * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits
-        * for interrupts and interrupt masks we can just use the value of
-        * INT_SOURCE_CSR to create the interrupt mask.
-        */
-       mask = ~reg;
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
-               rt2800pci_txstatus_interrupt(rt2x00dev);
-               /*
-                * Never disable the TX_FIFO_STATUS interrupt.
-                */
-               rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1);
-       }
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
-               tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet);
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT))
-               tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet);
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
-               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP))
-               tasklet_schedule(&rt2x00dev->autowake_tasklet);
-
-       /*
-        * Disable all interrupts for which a tasklet was scheduled right now,
-        * the tasklet will reenable the appropriate interrupts.
-        */
-       spin_lock(&rt2x00dev->irqmask_lock);
-       rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, &reg);
-       reg &= mask;
-       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
-       spin_unlock(&rt2x00dev->irqmask_lock);
-
-       return IRQ_HANDLED;
-}
-
 /*
  * Device probe functions.
  */
@@ -1097,9 +291,7 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
 
-       if (rt2x00_is_soc(rt2x00dev))
-               retval = rt2800pci_read_eeprom_soc(rt2x00dev);
-       else if (rt2800pci_efuse_detect(rt2x00dev))
+       if (rt2800pci_efuse_detect(rt2x00dev))
                retval = rt2800pci_read_eeprom_efuse(rt2x00dev);
        else
                retval = rt2800pci_read_eeprom_pci(rt2x00dev);
@@ -1145,25 +337,25 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
        .read_eeprom            = rt2800pci_read_eeprom,
        .hwcrypt_disabled       = rt2800pci_hwcrypt_disabled,
        .drv_write_firmware     = rt2800pci_write_firmware,
-       .drv_init_registers     = rt2800pci_init_registers,
-       .drv_get_txwi           = rt2800pci_get_txwi,
+       .drv_init_registers     = rt2800mmio_init_registers,
+       .drv_get_txwi           = rt2800mmio_get_txwi,
 };
 
 static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
-       .irq_handler            = rt2800pci_interrupt,
-       .txstatus_tasklet       = rt2800pci_txstatus_tasklet,
-       .pretbtt_tasklet        = rt2800pci_pretbtt_tasklet,
-       .tbtt_tasklet           = rt2800pci_tbtt_tasklet,
-       .rxdone_tasklet         = rt2800pci_rxdone_tasklet,
-       .autowake_tasklet       = rt2800pci_autowake_tasklet,
+       .irq_handler            = rt2800mmio_interrupt,
+       .txstatus_tasklet       = rt2800mmio_txstatus_tasklet,
+       .pretbtt_tasklet        = rt2800mmio_pretbtt_tasklet,
+       .tbtt_tasklet           = rt2800mmio_tbtt_tasklet,
+       .rxdone_tasklet         = rt2800mmio_rxdone_tasklet,
+       .autowake_tasklet       = rt2800mmio_autowake_tasklet,
        .probe_hw               = rt2800_probe_hw,
        .get_firmware_name      = rt2800pci_get_firmware_name,
        .check_firmware         = rt2800_check_firmware,
        .load_firmware          = rt2800_load_firmware,
        .initialize             = rt2x00mmio_initialize,
        .uninitialize           = rt2x00mmio_uninitialize,
-       .get_entry_state        = rt2800pci_get_entry_state,
-       .clear_entry            = rt2800pci_clear_entry,
+       .get_entry_state        = rt2800mmio_get_entry_state,
+       .clear_entry            = rt2800mmio_clear_entry,
        .set_device_state       = rt2800pci_set_device_state,
        .rfkill_poll            = rt2800_rfkill_poll,
        .link_stats             = rt2800_link_stats,
@@ -1171,15 +363,15 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .link_tuner             = rt2800_link_tuner,
        .gain_calibration       = rt2800_gain_calibration,
        .vco_calibration        = rt2800_vco_calibration,
-       .start_queue            = rt2800pci_start_queue,
-       .kick_queue             = rt2800pci_kick_queue,
-       .stop_queue             = rt2800pci_stop_queue,
+       .start_queue            = rt2800mmio_start_queue,
+       .kick_queue             = rt2800mmio_kick_queue,
+       .stop_queue             = rt2800mmio_stop_queue,
        .flush_queue            = rt2x00mmio_flush_queue,
-       .write_tx_desc          = rt2800pci_write_tx_desc,
+       .write_tx_desc          = rt2800mmio_write_tx_desc,
        .write_tx_data          = rt2800_write_tx_data,
        .write_beacon           = rt2800_write_beacon,
        .clear_beacon           = rt2800_clear_beacon,
-       .fill_rxdone            = rt2800pci_fill_rxdone,
+       .fill_rxdone            = rt2800mmio_fill_rxdone,
        .config_shared_key      = rt2800_config_shared_key,
        .config_pairwise_key    = rt2800_config_pairwise_key,
        .config_filter          = rt2800_config_filter,
@@ -1191,49 +383,6 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .sta_remove             = rt2800_sta_remove,
 };
 
-static void rt2800pci_queue_init(struct data_queue *queue)
-{
-       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-       unsigned short txwi_size, rxwi_size;
-
-       rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size);
-
-       switch (queue->qid) {
-       case QID_RX:
-               queue->limit = 128;
-               queue->data_size = AGGREGATION_SIZE;
-               queue->desc_size = RXD_DESC_SIZE;
-               queue->winfo_size = rxwi_size;
-               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
-               break;
-
-       case QID_AC_VO:
-       case QID_AC_VI:
-       case QID_AC_BE:
-       case QID_AC_BK:
-               queue->limit = 64;
-               queue->data_size = AGGREGATION_SIZE;
-               queue->desc_size = TXD_DESC_SIZE;
-               queue->winfo_size = txwi_size;
-               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
-               break;
-
-       case QID_BEACON:
-               queue->limit = 8;
-               queue->data_size = 0; /* No DMA required for beacons */
-               queue->desc_size = TXD_DESC_SIZE;
-               queue->winfo_size = txwi_size;
-               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
-               break;
-
-       case QID_ATIM:
-               /* fallthrough */
-       default:
-               BUG();
-               break;
-       }
-}
-
 static const struct rt2x00_ops rt2800pci_ops = {
        .name                   = KBUILD_MODNAME,
        .drv_data_size          = sizeof(struct rt2800_drv_data),
@@ -1241,7 +390,7 @@ static const struct rt2x00_ops rt2800pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .queue_init             = rt2800pci_queue_init,
+       .queue_init             = rt2800mmio_queue_init,
        .lib                    = &rt2800pci_rt2x00_ops,
        .drv                    = &rt2800pci_rt2800_ops,
        .hw                     = &rt2800pci_mac80211_ops,
@@ -1253,7 +402,6 @@ static const struct rt2x00_ops rt2800pci_ops = {
 /*
  * RT2800pci module information.
  */
-#ifdef CONFIG_PCI
 static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
        { PCI_DEVICE(0x1814, 0x0601) },
        { PCI_DEVICE(0x1814, 0x0681) },
@@ -1298,38 +446,15 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
 #endif
        { 0, }
 };
-#endif /* CONFIG_PCI */
 
 MODULE_AUTHOR(DRV_PROJECT);
 MODULE_VERSION(DRV_VERSION);
 MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver.");
 MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards");
-#ifdef CONFIG_PCI
 MODULE_FIRMWARE(FIRMWARE_RT2860);
 MODULE_DEVICE_TABLE(pci, rt2800pci_device_table);
-#endif /* CONFIG_PCI */
 MODULE_LICENSE("GPL");
 
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-static int rt2800soc_probe(struct platform_device *pdev)
-{
-       return rt2x00soc_probe(pdev, &rt2800pci_ops);
-}
-
-static struct platform_driver rt2800soc_driver = {
-       .driver         = {
-               .name           = "rt2800_wmac",
-               .owner          = THIS_MODULE,
-               .mod_name       = KBUILD_MODNAME,
-       },
-       .probe          = rt2800soc_probe,
-       .remove         = rt2x00soc_remove,
-       .suspend        = rt2x00soc_suspend,
-       .resume         = rt2x00soc_resume,
-};
-#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */
-
-#ifdef CONFIG_PCI
 static int rt2800pci_probe(struct pci_dev *pci_dev,
                           const struct pci_device_id *id)
 {
@@ -1344,39 +469,5 @@ static struct pci_driver rt2800pci_driver = {
        .suspend        = rt2x00pci_suspend,
        .resume         = rt2x00pci_resume,
 };
-#endif /* CONFIG_PCI */
-
-static int __init rt2800pci_init(void)
-{
-       int ret = 0;
-
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-       ret = platform_driver_register(&rt2800soc_driver);
-       if (ret)
-               return ret;
-#endif
-#ifdef CONFIG_PCI
-       ret = pci_register_driver(&rt2800pci_driver);
-       if (ret) {
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-               platform_driver_unregister(&rt2800soc_driver);
-#endif
-               return ret;
-       }
-#endif
-
-       return ret;
-}
-
-static void __exit rt2800pci_exit(void)
-{
-#ifdef CONFIG_PCI
-       pci_unregister_driver(&rt2800pci_driver);
-#endif
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-       platform_driver_unregister(&rt2800soc_driver);
-#endif
-}
 
-module_init(rt2800pci_init);
-module_exit(rt2800pci_exit);
+module_pci_driver(rt2800pci_driver);
index ab22a087c50dba15cf67b3ff237f8915bc8e2996..a81c9ee281c075dbf267e6b4b9b9c78de912c315 100644 (file)
 #ifndef RT2800PCI_H
 #define RT2800PCI_H
 
-/*
- * Queue register offset macros
- */
-#define TX_QUEUE_REG_OFFSET            0x10
-#define TX_BASE_PTR(__x)               (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET))
-#define TX_MAX_CNT(__x)                        (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET))
-#define TX_CTX_IDX(__x)                        (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET))
-#define TX_DTX_IDX(__x)                        (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET))
-
 /*
  * 8051 firmware image.
  */
 #define FIRMWARE_RT3290                        "rt3290.bin"
 #define FIRMWARE_IMAGE_BASE            0x2000
 
-/*
- * DMA descriptor defines.
- */
-#define TXD_DESC_SIZE                  (4 * sizeof(__le32))
-#define RXD_DESC_SIZE                  (4 * sizeof(__le32))
-
-/*
- * TX descriptor format for TX, PRIO and Beacon Ring.
- */
-
-/*
- * Word0
- */
-#define TXD_W0_SD_PTR0                 FIELD32(0xffffffff)
-
-/*
- * Word1
- */
-#define TXD_W1_SD_LEN1                 FIELD32(0x00003fff)
-#define TXD_W1_LAST_SEC1               FIELD32(0x00004000)
-#define TXD_W1_BURST                   FIELD32(0x00008000)
-#define TXD_W1_SD_LEN0                 FIELD32(0x3fff0000)
-#define TXD_W1_LAST_SEC0               FIELD32(0x40000000)
-#define TXD_W1_DMA_DONE                        FIELD32(0x80000000)
-
-/*
- * Word2
- */
-#define TXD_W2_SD_PTR1                 FIELD32(0xffffffff)
-
-/*
- * Word3
- * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI
- * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler.
- *       0:MGMT, 1:HCCA 2:EDCA
- */
-#define TXD_W3_WIV                     FIELD32(0x01000000)
-#define TXD_W3_QSEL                    FIELD32(0x06000000)
-#define TXD_W3_TCO                     FIELD32(0x20000000)
-#define TXD_W3_UCO                     FIELD32(0x40000000)
-#define TXD_W3_ICO                     FIELD32(0x80000000)
-
-/*
- * RX descriptor format for RX Ring.
- */
-
-/*
- * Word0
- */
-#define RXD_W0_SDP0                    FIELD32(0xffffffff)
-
-/*
- * Word1
- */
-#define RXD_W1_SDL1                    FIELD32(0x00003fff)
-#define RXD_W1_SDL0                    FIELD32(0x3fff0000)
-#define RXD_W1_LS0                     FIELD32(0x40000000)
-#define RXD_W1_DMA_DONE                        FIELD32(0x80000000)
-
-/*
- * Word2
- */
-#define RXD_W2_SDP1                    FIELD32(0xffffffff)
-
-/*
- * Word3
- * AMSDU: RX with 802.3 header, not 802.11 header.
- * DECRYPTED: This frame is being decrypted.
- */
-#define RXD_W3_BA                      FIELD32(0x00000001)
-#define RXD_W3_DATA                    FIELD32(0x00000002)
-#define RXD_W3_NULLDATA                        FIELD32(0x00000004)
-#define RXD_W3_FRAG                    FIELD32(0x00000008)
-#define RXD_W3_UNICAST_TO_ME           FIELD32(0x00000010)
-#define RXD_W3_MULTICAST               FIELD32(0x00000020)
-#define RXD_W3_BROADCAST               FIELD32(0x00000040)
-#define RXD_W3_MY_BSS                  FIELD32(0x00000080)
-#define RXD_W3_CRC_ERROR               FIELD32(0x00000100)
-#define RXD_W3_CIPHER_ERROR            FIELD32(0x00000600)
-#define RXD_W3_AMSDU                   FIELD32(0x00000800)
-#define RXD_W3_HTC                     FIELD32(0x00001000)
-#define RXD_W3_RSSI                    FIELD32(0x00002000)
-#define RXD_W3_L2PAD                   FIELD32(0x00004000)
-#define RXD_W3_AMPDU                   FIELD32(0x00008000)
-#define RXD_W3_DECRYPTED               FIELD32(0x00010000)
-#define RXD_W3_PLCP_SIGNAL             FIELD32(0x00020000)
-#define RXD_W3_PLCP_RSSI               FIELD32(0x00040000)
-
 #endif /* RT2800PCI_H */
diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/rt2x00/rt2800soc.c
new file mode 100644 (file)
index 0000000..1359227
--- /dev/null
@@ -0,0 +1,263 @@
+/*     Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
+ *     Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ *     Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ *     Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ *     Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ *     Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ *     Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ *     Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ *     <http://rt2x00.serialmonkey.com>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the
+ *     Free Software Foundation, Inc.,
+ *     59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*     Module: rt2800soc
+ *     Abstract: rt2800 WiSoC specific routines.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "rt2x00.h"
+#include "rt2x00mmio.h"
+#include "rt2x00soc.h"
+#include "rt2800.h"
+#include "rt2800lib.h"
+#include "rt2800mmio.h"
+
+/* Allow hardware encryption to be disabled. */
+static bool modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+static bool rt2800soc_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev)
+{
+       return modparam_nohwcrypt;
+}
+
+static void rt2800soc_disable_radio(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_disable_radio(rt2x00dev);
+       rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0);
+}
+
+static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev,
+                                     enum dev_state state)
+{
+       int retval = 0;
+
+       switch (state) {
+       case STATE_RADIO_ON:
+               retval = rt2800mmio_enable_radio(rt2x00dev);
+               break;
+
+       case STATE_RADIO_OFF:
+               rt2800soc_disable_radio(rt2x00dev);
+               break;
+
+       case STATE_RADIO_IRQ_ON:
+       case STATE_RADIO_IRQ_OFF:
+               rt2800mmio_toggle_irq(rt2x00dev, state);
+               break;
+
+       case STATE_DEEP_SLEEP:
+       case STATE_SLEEP:
+       case STATE_STANDBY:
+       case STATE_AWAKE:
+               /* These states are not supported, but don't report an error */
+               retval = 0;
+               break;
+
+       default:
+               retval = -ENOTSUPP;
+               break;
+       }
+
+       if (unlikely(retval))
+               rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n",
+                          state, retval);
+
+       return retval;
+}
+
+static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev)
+{
+       void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
+
+       if (!base_addr)
+               return -ENOMEM;
+
+       memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
+
+       iounmap(base_addr);
+       return 0;
+}
+
+/* Firmware functions */
+static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev)
+{
+       WARN_ON_ONCE(1);
+       return NULL;
+}
+
+static int rt2800soc_load_firmware(struct rt2x00_dev *rt2x00dev,
+                                  const u8 *data, const size_t len)
+{
+       WARN_ON_ONCE(1);
+       return 0;
+}
+
+static int rt2800soc_check_firmware(struct rt2x00_dev *rt2x00dev,
+                                   const u8 *data, const size_t len)
+{
+       WARN_ON_ONCE(1);
+       return 0;
+}
+
+static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev,
+                                   const u8 *data, const size_t len)
+{
+       WARN_ON_ONCE(1);
+       return 0;
+}
+
+static const struct ieee80211_ops rt2800soc_mac80211_ops = {
+       .tx                     = rt2x00mac_tx,
+       .start                  = rt2x00mac_start,
+       .stop                   = rt2x00mac_stop,
+       .add_interface          = rt2x00mac_add_interface,
+       .remove_interface       = rt2x00mac_remove_interface,
+       .config                 = rt2x00mac_config,
+       .configure_filter       = rt2x00mac_configure_filter,
+       .set_key                = rt2x00mac_set_key,
+       .sw_scan_start          = rt2x00mac_sw_scan_start,
+       .sw_scan_complete       = rt2x00mac_sw_scan_complete,
+       .get_stats              = rt2x00mac_get_stats,
+       .get_tkip_seq           = rt2800_get_tkip_seq,
+       .set_rts_threshold      = rt2800_set_rts_threshold,
+       .sta_add                = rt2x00mac_sta_add,
+       .sta_remove             = rt2x00mac_sta_remove,
+       .bss_info_changed       = rt2x00mac_bss_info_changed,
+       .conf_tx                = rt2800_conf_tx,
+       .get_tsf                = rt2800_get_tsf,
+       .rfkill_poll            = rt2x00mac_rfkill_poll,
+       .ampdu_action           = rt2800_ampdu_action,
+       .flush                  = rt2x00mac_flush,
+       .get_survey             = rt2800_get_survey,
+       .get_ringparam          = rt2x00mac_get_ringparam,
+       .tx_frames_pending      = rt2x00mac_tx_frames_pending,
+};
+
+static const struct rt2800_ops rt2800soc_rt2800_ops = {
+       .register_read          = rt2x00mmio_register_read,
+       .register_read_lock     = rt2x00mmio_register_read, /* same for SoCs */
+       .register_write         = rt2x00mmio_register_write,
+       .register_write_lock    = rt2x00mmio_register_write, /* same for SoCs */
+       .register_multiread     = rt2x00mmio_register_multiread,
+       .register_multiwrite    = rt2x00mmio_register_multiwrite,
+       .regbusy_read           = rt2x00mmio_regbusy_read,
+       .read_eeprom            = rt2800soc_read_eeprom,
+       .hwcrypt_disabled       = rt2800soc_hwcrypt_disabled,
+       .drv_write_firmware     = rt2800soc_write_firmware,
+       .drv_init_registers     = rt2800mmio_init_registers,
+       .drv_get_txwi           = rt2800mmio_get_txwi,
+};
+
+static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = {
+       .irq_handler            = rt2800mmio_interrupt,
+       .txstatus_tasklet       = rt2800mmio_txstatus_tasklet,
+       .pretbtt_tasklet        = rt2800mmio_pretbtt_tasklet,
+       .tbtt_tasklet           = rt2800mmio_tbtt_tasklet,
+       .rxdone_tasklet         = rt2800mmio_rxdone_tasklet,
+       .autowake_tasklet       = rt2800mmio_autowake_tasklet,
+       .probe_hw               = rt2800_probe_hw,
+       .get_firmware_name      = rt2800soc_get_firmware_name,
+       .check_firmware         = rt2800soc_check_firmware,
+       .load_firmware          = rt2800soc_load_firmware,
+       .initialize             = rt2x00mmio_initialize,
+       .uninitialize           = rt2x00mmio_uninitialize,
+       .get_entry_state        = rt2800mmio_get_entry_state,
+       .clear_entry            = rt2800mmio_clear_entry,
+       .set_device_state       = rt2800soc_set_device_state,
+       .rfkill_poll            = rt2800_rfkill_poll,
+       .link_stats             = rt2800_link_stats,
+       .reset_tuner            = rt2800_reset_tuner,
+       .link_tuner             = rt2800_link_tuner,
+       .gain_calibration       = rt2800_gain_calibration,
+       .vco_calibration        = rt2800_vco_calibration,
+       .start_queue            = rt2800mmio_start_queue,
+       .kick_queue             = rt2800mmio_kick_queue,
+       .stop_queue             = rt2800mmio_stop_queue,
+       .flush_queue            = rt2x00mmio_flush_queue,
+       .write_tx_desc          = rt2800mmio_write_tx_desc,
+       .write_tx_data          = rt2800_write_tx_data,
+       .write_beacon           = rt2800_write_beacon,
+       .clear_beacon           = rt2800_clear_beacon,
+       .fill_rxdone            = rt2800mmio_fill_rxdone,
+       .config_shared_key      = rt2800_config_shared_key,
+       .config_pairwise_key    = rt2800_config_pairwise_key,
+       .config_filter          = rt2800_config_filter,
+       .config_intf            = rt2800_config_intf,
+       .config_erp             = rt2800_config_erp,
+       .config_ant             = rt2800_config_ant,
+       .config                 = rt2800_config,
+       .sta_add                = rt2800_sta_add,
+       .sta_remove             = rt2800_sta_remove,
+};
+
+static const struct rt2x00_ops rt2800soc_ops = {
+       .name                   = KBUILD_MODNAME,
+       .drv_data_size          = sizeof(struct rt2800_drv_data),
+       .max_ap_intf            = 8,
+       .eeprom_size            = EEPROM_SIZE,
+       .rf_size                = RF_SIZE,
+       .tx_queues              = NUM_TX_QUEUES,
+       .queue_init             = rt2800mmio_queue_init,
+       .lib                    = &rt2800soc_rt2x00_ops,
+       .drv                    = &rt2800soc_rt2800_ops,
+       .hw                     = &rt2800soc_mac80211_ops,
+#ifdef CONFIG_RT2X00_LIB_DEBUGFS
+       .debugfs                = &rt2800_rt2x00debug,
+#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
+};
+
+static int rt2800soc_probe(struct platform_device *pdev)
+{
+       return rt2x00soc_probe(pdev, &rt2800soc_ops);
+}
+
+static struct platform_driver rt2800soc_driver = {
+       .driver         = {
+               .name           = "rt2800_wmac",
+               .owner          = THIS_MODULE,
+               .mod_name       = KBUILD_MODNAME,
+       },
+       .probe          = rt2800soc_probe,
+       .remove         = rt2x00soc_remove,
+       .suspend        = rt2x00soc_suspend,
+       .resume         = rt2x00soc_resume,
+};
+
+module_platform_driver(rt2800soc_driver);
+
+MODULE_AUTHOR(DRV_PROJECT);
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("Ralink WiSoC Wireless LAN driver.");
+MODULE_LICENSE("GPL");
index 96677ce55da46378136a2555062664133c7af931..997df03a0c2e22abd46bd274bf13f4b1e4f81f01 100644 (file)
@@ -148,6 +148,8 @@ static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev)
        return false;
 }
 
+#define TXSTATUS_READ_INTERVAL 1000000
+
 static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
                                                 int urb_status, u32 tx_status)
 {
@@ -176,8 +178,9 @@ static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
                queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
 
        if (rt2800usb_txstatus_pending(rt2x00dev)) {
-               /* Read register after 250 us */
-               hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 250000),
+               /* Read register after 1 ms */
+               hrtimer_start(&rt2x00dev->txstatus_timer,
+                             ktime_set(0, TXSTATUS_READ_INTERVAL),
                              HRTIMER_MODE_REL);
                return false;
        }
@@ -202,8 +205,9 @@ static void rt2800usb_async_read_tx_status(struct rt2x00_dev *rt2x00dev)
        if (test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags))
                return;
 
-       /* Read TX_STA_FIFO register after 500 us */
-       hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 500000),
+       /* Read TX_STA_FIFO register after 2 ms */
+       hrtimer_start(&rt2x00dev->txstatus_timer,
+                     ktime_set(0, 2*TXSTATUS_READ_INTERVAL),
                      HRTIMER_MODE_REL);
 }
 
index 30ed92a6121eeb790e48c628677eba907014e19b..e4ba2ce0f212b955f15db89df45d3b32af9942de 100644 (file)
@@ -1169,6 +1169,93 @@ static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev)
        return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC);
 }
 
+/* Helpers for capability flags */
+
+static inline bool
+rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev,
+                   enum rt2x00_capability_flags cap_flag)
+{
+       return test_bit(cap_flag, &rt2x00dev->cap_flags);
+}
+
+static inline bool
+rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO);
+}
+
+static inline bool
+rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT);
+}
+
+static inline bool
+rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS);
+}
+
+static inline bool
+rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL);
+}
+
+static inline bool
+rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT);
+}
+
+static inline bool
+rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING);
+}
+
+static inline bool
+rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE);
+}
+
+static inline bool
+rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE);
+}
+
+static inline bool
+rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A);
+}
+
+static inline bool
+rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG);
+}
+
+static inline bool
+rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA);
+}
+
+static inline bool
+rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST);
+}
+
+static inline bool
+rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION);
+}
+
 /**
  * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes.
  * @entry: Pointer to &struct queue_entry
index 1ca4c7ffc1898c9ce97d87506b0b8e78464e78ad..3db0d99d9da7a9980f63939186f7c2b7971cd533 100644 (file)
@@ -52,7 +52,7 @@ void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
 
-       if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !hw_key)
+       if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key)
                return;
 
        __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags);
@@ -80,7 +80,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev,
        struct ieee80211_key_conf *key = tx_info->control.hw_key;
        unsigned int overhead = 0;
 
-       if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !key)
+       if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key)
                return overhead;
 
        /*
index fe7a7f63a9edc2cf75c0a2caa2f6b8ffe02234aa..7f7baae5ae029e6c252676841682cf8fabf270f4 100644 (file)
@@ -750,7 +750,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
                                intf, &rt2x00debug_fop_queue_stats);
 
 #ifdef CONFIG_RT2X00_LIB_CRYPTO
-       if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_hw_crypto(rt2x00dev))
                intf->crypto_stats_entry =
                    debugfs_create_file("crypto", S_IRUGO, intf->queue_folder,
                                        intf, &rt2x00debug_fop_crypto_stats);
index 712eea9d398ffa747a1c76ebfd0da0ad84ed8854..080b1fcae5fa8f3f2b376d5b77bffab2519ddf57 100644 (file)
@@ -88,7 +88,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
        rt2x00queue_start_queues(rt2x00dev);
        rt2x00link_start_tuner(rt2x00dev);
        rt2x00link_start_agc(rt2x00dev);
-       if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
                rt2x00link_start_vcocal(rt2x00dev);
 
        /*
@@ -113,7 +113,7 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
         * Stop all queues
         */
        rt2x00link_stop_agc(rt2x00dev);
-       if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
                rt2x00link_stop_vcocal(rt2x00dev);
        rt2x00link_stop_tuner(rt2x00dev);
        rt2x00queue_stop_queues(rt2x00dev);
@@ -234,7 +234,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
         * here as they will fetch the next beacon directly prior to
         * transmission.
         */
-       if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev))
                return;
 
        /* fetch next beacon */
@@ -358,7 +358,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
         * mac80211 will expect the same data to be present it the
         * frame as it was passed to us.
         */
-       if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_hw_crypto(rt2x00dev))
                rt2x00crypto_tx_insert_iv(entry->skb, header_length);
 
        /*
index a0e3c021c12821ebce5483462f185947e9948c9a..c2b3b66291884e6171b2cd92b78024406ceb7252 100644 (file)
@@ -353,7 +353,7 @@ static void rt2x00link_tuner(struct work_struct *work)
         * do not support link tuning at all, while other devices can disable
         * the feature from the EEPROM.
         */
-       if (test_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_link_tuning(rt2x00dev))
                rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);
 
        /*
@@ -493,7 +493,7 @@ static void rt2x00link_vcocal(struct work_struct *work)
 void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
 {
        INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc);
-       if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
                INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal);
        INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
        INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
index f883802f350585322338ed7683e065bb85349ff3..7c157857f5cee925e796a49396a5843a758a01cd 100644 (file)
@@ -382,11 +382,11 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw,
         * of different types, but has no a separate filter for PS Poll frames,
         * FIF_CONTROL flag implies FIF_PSPOLL.
         */
-       if (!test_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags)) {
+       if (!rt2x00_has_cap_control_filters(rt2x00dev)) {
                if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL)
                        *total_flags |= FIF_CONTROL | FIF_PSPOLL;
        }
-       if (!test_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags)) {
+       if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) {
                if (*total_flags & FIF_CONTROL)
                        *total_flags |= FIF_PSPOLL;
        }
@@ -469,7 +469,7 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
                return 0;
 
-       if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+       if (!rt2x00_has_cap_hw_crypto(rt2x00dev))
                return -EOPNOTSUPP;
 
        /*
@@ -754,6 +754,9 @@ void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
        struct rt2x00_dev *rt2x00dev = hw->priv;
        struct data_queue *queue;
 
+       if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
+               return;
+
        tx_queue_for_each(rt2x00dev, queue)
                rt2x00queue_flush_queue(queue, drop);
 }
index 6c5d667103c4966606940af0976312165a28bc96..25da20e7e1f34ab2c0b1fd3e38a82d1faece0f9e 100644 (file)
@@ -105,13 +105,11 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
                goto exit_release_regions;
        }
 
-       pci_enable_msi(pci_dev);
-
        hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
        if (!hw) {
                rt2x00_probe_err("Failed to allocate hardware\n");
                retval = -ENOMEM;
-               goto exit_disable_msi;
+               goto exit_release_regions;
        }
 
        pci_set_drvdata(pci_dev, hw);
@@ -152,9 +150,6 @@ exit_free_reg:
 exit_free_device:
        ieee80211_free_hw(hw);
 
-exit_disable_msi:
-       pci_disable_msi(pci_dev);
-
 exit_release_regions:
        pci_release_regions(pci_dev);
 
@@ -179,8 +174,6 @@ void rt2x00pci_remove(struct pci_dev *pci_dev)
        rt2x00pci_free_reg(rt2x00dev);
        ieee80211_free_hw(hw);
 
-       pci_disable_msi(pci_dev);
-
        /*
         * Free the PCI device data.
         */
index 218e3206ce1b46733b6016139ab1d423cf5749e0..50590b1420a516863845249c96f689b28e766964 100644 (file)
@@ -61,7 +61,7 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp)
         * at least 8 bytes bytes available in headroom for IV/EIV
         * and 8 bytes for ICV data as tailroon.
         */
-       if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) {
+       if (rt2x00_has_cap_hw_crypto(rt2x00dev)) {
                head_size += 8;
                tail_size += 8;
        }
index 54d3ddfc988845cfa844b5c1d76c6a78ec62e4de..a5b69cb49012162580ef78291b696ce15abbeb94 100644 (file)
@@ -685,7 +685,7 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev,
 
        rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529));
        rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END,
-                         !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags));
+                         !rt2x00_has_cap_frame_type(rt2x00dev));
 
        /*
         * Configure the RX antenna.
@@ -813,10 +813,10 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
 
        if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
                sel = antenna_sel_a;
-               lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags);
+               lna = rt2x00_has_cap_external_lna_a(rt2x00dev);
        } else {
                sel = antenna_sel_bg;
-               lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags);
+               lna = rt2x00_has_cap_external_lna_bg(rt2x00dev);
        }
 
        for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++)
@@ -836,7 +836,7 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
        else if (rt2x00_rf(rt2x00dev, RF2527))
                rt61pci_config_antenna_2x(rt2x00dev, ant);
        else if (rt2x00_rf(rt2x00dev, RF2529)) {
-               if (test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_double_antenna(rt2x00dev))
                        rt61pci_config_antenna_2x(rt2x00dev, ant);
                else
                        rt61pci_config_antenna_2529(rt2x00dev, ant);
@@ -850,13 +850,13 @@ static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev,
        short lna_gain = 0;
 
        if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) {
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
                        lna_gain += 14;
 
                rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
                lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1);
        } else {
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev))
                        lna_gain += 14;
 
                rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom);
@@ -1054,14 +1054,14 @@ static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev,
        if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
                low_bound = 0x28;
                up_bound = 0x48;
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
                        low_bound += 0x10;
                        up_bound += 0x10;
                }
        } else {
                low_bound = 0x20;
                up_bound = 0x40;
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
                        low_bound += 0x10;
                        up_bound += 0x10;
                }
@@ -2578,7 +2578,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
         * eeprom word.
         */
        if (rt2x00_rf(rt2x00dev, RF2529) &&
-           !test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) {
+           !rt2x00_has_cap_double_antenna(rt2x00dev)) {
                rt2x00dev->default_ant.rx =
                    ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED);
                rt2x00dev->default_ant.tx =
@@ -2793,7 +2793,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        spec->supported_bands = SUPPORT_BAND_2GHZ;
        spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
-       if (!test_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags)) {
+       if (!rt2x00_has_cap_rf_sequence(rt2x00dev)) {
                spec->num_channels = 14;
                spec->channels = rf_vals_noseq;
        } else {
index 1d3880e09a13eabb73e93c77b7af948ca12421f8..1baf9c896dcd6477efba878a1c0e407411635206 100644 (file)
@@ -595,8 +595,8 @@ static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
        switch (ant->rx) {
        case ANTENNA_HW_DIVERSITY:
                rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2);
-               temp = !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)
-                      && (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ);
+               temp = !rt2x00_has_cap_frame_type(rt2x00dev) &&
+                      (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ);
                rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp);
                break;
        case ANTENNA_A:
@@ -636,7 +636,7 @@ static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev,
 
        rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0);
        rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END,
-                         !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags));
+                         !rt2x00_has_cap_frame_type(rt2x00dev));
 
        /*
         * Configure the RX antenna.
@@ -709,10 +709,10 @@ static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev,
 
        if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
                sel = antenna_sel_a;
-               lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags);
+               lna = rt2x00_has_cap_external_lna_a(rt2x00dev);
        } else {
                sel = antenna_sel_bg;
-               lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags);
+               lna = rt2x00_has_cap_external_lna_bg(rt2x00dev);
        }
 
        for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++)
@@ -740,7 +740,7 @@ static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev,
        short lna_gain = 0;
 
        if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) {
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
                        lna_gain += 14;
 
                rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
@@ -930,7 +930,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev,
                low_bound = 0x28;
                up_bound = 0x48;
 
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
                        low_bound += 0x10;
                        up_bound += 0x10;
                }
@@ -946,7 +946,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev,
                        up_bound = 0x1c;
                }
 
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
                        low_bound += 0x14;
                        up_bound += 0x10;
                }
@@ -1661,7 +1661,7 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1)
        }
 
        if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
                        if (lna == 3 || lna == 2)
                                offset += 10;
                } else {
index 04c7e57dbce2f5165626c15344c23b348f58abbf..25e50ffc44ec8b42fd35b4ab0b60a08ee09edd7d 100644 (file)
@@ -343,7 +343,8 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw,
                                        (bool)GET_RX_DESC_PAGGR(pdesc));
        rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
        if (phystatus) {
-               p_drvinfo = (struct rx_fwinfo_92c *)(pdesc + RTL_RX_DESC_SIZE);
+               p_drvinfo = (struct rx_fwinfo_92c *)(skb->data +
+                                                    stats->rx_bufshift);
                rtl92c_translate_rx_signal_stuff(hw, skb, stats, pdesc,
                                                 p_drvinfo);
        }
index 96763dcff5ae19283b5962f17efeffdac705e8cf..d224dc3bb092b0ef04545cc57891a7bdb4de4b2f 100644 (file)
@@ -2055,7 +2055,7 @@ struct rtl_priv {
           that it points to the data allocated
           beyond  this structure like:
           rtl_pci_priv or rtl_usb_priv */
-       u8 priv[0];
+       u8 priv[0] __aligned(sizeof(void *));
 };
 
 #define rtl_priv(hw)           (((struct rtl_priv *)(hw)->priv))
index 591526b991547281e4e04f341e071d57c84d58e2..be7129ba16ad651524910c1897a33b7d48570007 100644 (file)
@@ -333,11 +333,11 @@ static struct wlcore_conf wl12xx_conf = {
                .always                        = 0,
        },
        .fwlog = {
-               .mode                         = WL12XX_FWLOG_ON_DEMAND,
+               .mode                         = WL12XX_FWLOG_CONTINUOUS,
                .mem_blocks                   = 2,
                .severity                     = 0,
                .timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
-               .output                       = WL12XX_FWLOG_OUTPUT_HOST,
+               .output                       = WL12XX_FWLOG_OUTPUT_DBG_PINS,
                .threshold                    = 0,
        },
        .rate = {
@@ -717,6 +717,9 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->fw_mem_block_size = 256;
+       wl->fwlog_end = 0x2000000;
+
        /* common settings */
        wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
        wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
@@ -1262,9 +1265,10 @@ static int wl12xx_boot(struct wl1271 *wl)
                BA_SESSION_RX_CONSTRAINT_EVENT_ID |
                REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                INACTIVE_STA_EVENT_ID |
-               MAX_TX_RETRY_EVENT_ID |
                CHANNEL_SWITCH_COMPLETE_EVENT_ID;
 
+       wl->ap_event_mask = MAX_TX_RETRY_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1648,6 +1652,11 @@ static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        return true;
 }
 
+static u32 wl12xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       return hwaddr << 5;
+}
+
 static int wl12xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl12xx_ops = {
@@ -1684,6 +1693,7 @@ static struct wlcore_ops wl12xx_ops = {
        .channel_switch         = wl12xx_cmd_channel_switch,
        .pre_pkt_send           = NULL,
        .set_peer_cap           = wl12xx_set_peer_cap,
+       .convert_hwaddr         = wl12xx_convert_hwaddr,
        .lnk_high_prio          = wl12xx_lnk_high_prio,
        .lnk_low_prio           = wl12xx_lnk_low_prio,
 };
index d0daca1d23bc55154432d2a031feff4ee51b15e4..ec37b16585df939938fb1a75ef060ef0ea3d73fd 100644 (file)
@@ -456,11 +456,11 @@ static struct wlcore_conf wl18xx_conf = {
                .always                        = 0,
        },
        .fwlog = {
-               .mode                         = WL12XX_FWLOG_ON_DEMAND,
+               .mode                         = WL12XX_FWLOG_CONTINUOUS,
                .mem_blocks                   = 2,
                .severity                     = 0,
                .timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
-               .output                       = WL12XX_FWLOG_OUTPUT_HOST,
+               .output                       = WL12XX_FWLOG_OUTPUT_DBG_PINS,
                .threshold                    = 0,
        },
        .rate = {
@@ -505,7 +505,7 @@ static struct wlcore_conf wl18xx_conf = {
 
 static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
        .ht = {
-               .mode                           = HT_MODE_DEFAULT,
+               .mode                           = HT_MODE_WIDE,
        },
        .phy = {
                .phy_standalone                 = 0x00,
@@ -516,7 +516,7 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .auto_detect                    = 0x00,
                .dedicated_fem                  = FEM_NONE,
                .low_band_component             = COMPONENT_3_WAY_SWITCH,
-               .low_band_component_type        = 0x04,
+               .low_band_component_type        = 0x05,
                .high_band_component            = COMPONENT_2_WAY_SWITCH,
                .high_band_component_type       = 0x09,
                .tcxo_ldo_voltage               = 0x00,
@@ -556,15 +556,15 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .per_chan_pwr_limit_arr_11p     = { 0xff, 0xff, 0xff, 0xff,
                                                    0xff, 0xff, 0xff },
                .psat                           = 0,
-               .low_power_val                  = 0x08,
-               .med_power_val                  = 0x12,
-               .high_power_val                 = 0x18,
-               .low_power_val_2nd              = 0x05,
-               .med_power_val_2nd              = 0x0a,
-               .high_power_val_2nd             = 0x14,
                .external_pa_dc2dc              = 0,
                .number_of_assembled_ant2_4     = 2,
                .number_of_assembled_ant5       = 1,
+               .low_power_val                  = 0xff,
+               .med_power_val                  = 0xff,
+               .high_power_val                 = 0xff,
+               .low_power_val_2nd              = 0xff,
+               .med_power_val_2nd              = 0xff,
+               .high_power_val_2nd             = 0xff,
                .tx_rf_margin                   = 1,
        },
 };
@@ -686,6 +686,9 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->fw_mem_block_size = 272;
+       wl->fwlog_end = 0x40000000;
+
        wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
        wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
        wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
@@ -988,10 +991,11 @@ static int wl18xx_boot(struct wl1271 *wl)
                BA_SESSION_RX_CONSTRAINT_EVENT_ID |
                REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                INACTIVE_STA_EVENT_ID |
-               MAX_TX_FAILURE_EVENT_ID |
                CHANNEL_SWITCH_COMPLETE_EVENT_ID |
                DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
 
+       wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1604,6 +1608,11 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        return lnk->allocated_pkts < thold;
 }
 
+static u32 wl18xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       return hwaddr & ~0x80000000;
+}
+
 static int wl18xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl18xx_ops = {
@@ -1641,6 +1650,7 @@ static struct wlcore_ops wl18xx_ops = {
        .pre_pkt_send   = wl18xx_pre_pkt_send,
        .sta_rc_update  = wl18xx_sta_rc_update,
        .set_peer_cap   = wl18xx_set_peer_cap,
+       .convert_hwaddr = wl18xx_convert_hwaddr,
        .lnk_high_prio  = wl18xx_lnk_high_prio,
        .lnk_low_prio   = wl18xx_lnk_low_prio,
 };
index 7a970cd9c5551cc538ec0177758f023589c9e281..ec83675a244697537afd791613cfec9e87d13caa 100644 (file)
@@ -162,7 +162,8 @@ int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map,
 
        wl1271_debug(DEBUG_ACX, "acx mem map");
 
-       ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len);
+       ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map,
+                                    sizeof(struct acx_header), len);
        if (ret < 0)
                return ret;
 
@@ -722,6 +723,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, void *stats)
        wl1271_debug(DEBUG_ACX, "acx statistics");
 
        ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats,
+                                    sizeof(struct acx_header),
                                     wl->stats.fw_stats_len);
        if (ret < 0) {
                wl1271_warning("acx statistics failed: %d", ret);
@@ -1470,8 +1472,8 @@ int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        tsf_info->role_id = wlvif->role_id;
 
-       ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO,
-                                    tsf_info, sizeof(*tsf_info));
+       ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info,
+                               sizeof(struct acx_header), sizeof(*tsf_info));
        if (ret < 0) {
                wl1271_warning("acx tsf info interrogate failed");
                goto out;
@@ -1752,7 +1754,7 @@ int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        acx->role_id = wlvif->role_id;
        ret = wl1271_cmd_interrogate(wl, ACX_ROAMING_STATISTICS_TBL,
-                                    acx, sizeof(*acx));
+                                    acx, sizeof(*acx), sizeof(*acx));
        if (ret < 0) {
                wl1271_warning("acx roaming statistics failed: %d", ret);
                ret = -ENOMEM;
index 9e5416f8764d13899cc7de963a71a58bf7b1b0e4..34d9dfff2ad39ead03d3da275cd3e4443385a220 100644 (file)
@@ -60,7 +60,8 @@ static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
        u16 status;
        u16 poll_count = 0;
 
-       if (WARN_ON(unlikely(wl->state == WLCORE_STATE_RESTARTING)))
+       if (WARN_ON(wl->state == WLCORE_STATE_RESTARTING &&
+                   id != CMD_STOP_FWLOGGER))
                return -EIO;
 
        cmd = buf;
@@ -845,7 +846,8 @@ EXPORT_SYMBOL_GPL(wl1271_cmd_test);
  * @buf: buffer for the response, including all headers, must work with dma
  * @len: length of buf
  */
-int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
+int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf,
+                          size_t cmd_len, size_t res_len)
 {
        struct acx_header *acx = buf;
        int ret;
@@ -854,10 +856,10 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
 
        acx->id = cpu_to_le16(id);
 
-       /* payload length, does not include any headers */
-       acx->len = cpu_to_le16(len - sizeof(*acx));
+       /* response payload length, does not include any headers */
+       acx->len = cpu_to_le16(res_len - sizeof(*acx));
 
-       ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len);
+       ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, cmd_len, res_len);
        if (ret < 0)
                wl1271_error("INTERROGATE command failed");
 
index fd34123047cdd0255f5a48b7826ddd117b14814d..323d4a856e4ba80d37f52fadf0832ad09066996e 100644 (file)
@@ -45,7 +45,8 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                     enum ieee80211_band band, int channel);
 int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
-int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
+int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf,
+                          size_t cmd_len, size_t res_len);
 int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
 int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
                                  size_t len, unsigned long valid_rets);
index 2b96ff821341103ac935ade4765409b21e142409..40995c42bef8882458c331c9c6cc863210958d63 100644 (file)
@@ -1274,6 +1274,9 @@ struct conf_rx_streaming_settings {
        u8 always;
 } __packed;
 
+#define CONF_FWLOG_MIN_MEM_BLOCKS      2
+#define CONF_FWLOG_MAX_MEM_BLOCKS      16
+
 struct conf_fwlog {
        /* Continuous or on-demand */
        u8 mode;
@@ -1281,7 +1284,7 @@ struct conf_fwlog {
        /*
         * Number of memory blocks dedicated for the FW logger
         *
-        * Range: 1-3, or 0 to disable the FW logger
+        * Range: 2-16, or 0 to disable the FW logger
         */
        u8 mem_blocks;
 
index e17630c2a84948d40bb5d0f2c05f4ce1e8ccea2c..89893c7170253c8cc5400bf7941cc9ce4a3d3051 100644 (file)
@@ -437,6 +437,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        int res = 0;
        ssize_t ret;
        char *buf;
+       struct wl12xx_vif *wlvif;
 
 #define DRIVER_STATE_BUF_LEN 1024
 
@@ -450,12 +451,28 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
                          #x " = " fmt "\n", wl->x))
 
+#define DRIVER_STATE_PRINT_GENERIC(x, fmt, args...)   \
+       (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
+                         #x " = " fmt "\n", args))
+
 #define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld")
 #define DRIVER_STATE_PRINT_INT(x)  DRIVER_STATE_PRINT(x, "%d")
 #define DRIVER_STATE_PRINT_STR(x)  DRIVER_STATE_PRINT(x, "%s")
 #define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx")
 #define DRIVER_STATE_PRINT_HEX(x)  DRIVER_STATE_PRINT(x, "0x%x")
 
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       continue;
+
+               DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
+                                          wlvif->p2p ? "P2P-CL" : "STA");
+       }
+
+       wl12xx_for_each_wlvif_ap(wl, wlvif)
+               DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
+                                          wlvif->p2p ? "P2P-GO" : "AP");
+
        DRIVER_STATE_PRINT_INT(tx_blocks_available);
        DRIVER_STATE_PRINT_INT(tx_allocated_blocks);
        DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]);
@@ -474,7 +491,6 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_INT(tx_blocks_freed);
        DRIVER_STATE_PRINT_INT(rx_counter);
        DRIVER_STATE_PRINT_INT(state);
-       DRIVER_STATE_PRINT_INT(channel);
        DRIVER_STATE_PRINT_INT(band);
        DRIVER_STATE_PRINT_INT(power_level);
        DRIVER_STATE_PRINT_INT(sg_enabled);
index 67f61689b49edcba1e54198db538eb277aa4bda0..8d3b34965db3475f64178eeb6463ec2589434c3f 100644 (file)
@@ -266,6 +266,7 @@ int wl1271_event_unmask(struct wl1271 *wl)
 {
        int ret;
 
+       wl1271_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask);
        ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask));
        if (ret < 0)
                return ret;
index 7fd260c02a0a15aa2a0b8c92867a09fc9af96739..51f8d634d32f43274d2eaccb679a6dff732468f3 100644 (file)
@@ -222,6 +222,15 @@ wlcore_hw_set_peer_cap(struct wl1271 *wl,
        return 0;
 }
 
+static inline u32
+wlcore_hw_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       if (!wl->ops->convert_hwaddr)
+               BUG_ON(1);
+
+       return wl->ops->convert_hwaddr(wl, hwaddr);
+}
+
 static inline bool
 wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid,
                        struct wl1271_link *lnk)
index 5c6f11e157d9b0016633dfe932f34984fedeb65c..7699f9d07e2636e3528fa7e3c28f260bfaa63e3f 100644 (file)
@@ -571,6 +571,12 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
                ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                if (ret < 0)
                        return ret;
+
+               /* unmask ap events */
+               wl->event_mask |= wl->ap_event_mask;
+               ret = wl1271_event_unmask(wl);
+               if (ret < 0)
+                       return ret;
        /* first STA, no APs */
        } else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) {
                u8 sta_auth = wl->conf.conn.sta_sleep_auth;
index af7d9f9b3b4db2140006fb0639af3ff028af79e5..07e3d6a049adf33d40dc586c27ef66c4428b83d1 100644 (file)
@@ -165,8 +165,8 @@ static inline int __must_check wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr,
        int physical;
        int addr;
 
-       /* Addresses are stored internally as addresses to 32 bytes blocks */
-       addr = hwaddr << 5;
+       /* Convert from FW internal address which is chip arch dependent */
+       addr = wl->ops->convert_hwaddr(wl, hwaddr);
 
        physical = wlcore_translate_addr(wl, addr);
 
index bbdd10632373d0481ae7dd21b331ce566903c77c..0368b9cbfb896d6460da3635b199ef6266805644 100644 (file)
@@ -44,6 +44,7 @@
 #define WL1271_BOOT_RETRIES 3
 
 static char *fwlog_param;
+static int fwlog_mem_blocks = -1;
 static int bug_on_recovery = -1;
 static int no_recovery     = -1;
 
@@ -291,6 +292,18 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
 {
        /* Adjust settings according to optional module parameters */
 
+       /* Firmware Logger params */
+       if (fwlog_mem_blocks != -1) {
+               if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS &&
+                   fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) {
+                       wl->conf.fwlog.mem_blocks = fwlog_mem_blocks;
+               } else {
+                       wl1271_error(
+                               "Illegal fwlog_mem_blocks=%d using default %d",
+                               fwlog_mem_blocks, wl->conf.fwlog.mem_blocks);
+               }
+       }
+
        if (fwlog_param) {
                if (!strcmp(fwlog_param, "continuous")) {
                        wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
@@ -780,6 +793,7 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
        if (wl->state == WLCORE_STATE_ON) {
                wl->state = WLCORE_STATE_RESTARTING;
                set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+               wl1271_ps_elp_wakeup(wl);
                wlcore_disable_interrupts_nosync(wl);
                ieee80211_queue_work(wl->hw, &wl->recovery_work);
        }
@@ -787,19 +801,10 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
 
 size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
 {
-       size_t len = 0;
-
-       /* The FW log is a length-value list, find where the log end */
-       while (len < maxlen) {
-               if (memblock[len] == 0)
-                       break;
-               if (len + memblock[len] + 1 > maxlen)
-                       break;
-               len += memblock[len] + 1;
-       }
+       size_t len;
 
        /* Make sure we have enough room */
-       len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
+       len = min(maxlen, (size_t)(PAGE_SIZE - wl->fwlog_size));
 
        /* Fill the FW log file, consumed by the sysfs fwlog entry */
        memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
@@ -808,10 +813,9 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
        return len;
 }
 
-#define WLCORE_FW_LOG_END 0x2000000
-
 static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 {
+       struct wlcore_partition_set part, old_part;
        u32 addr;
        u32 offset;
        u32 end_of_log;
@@ -824,7 +828,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        wl1271_info("Reading FW panic log");
 
-       block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
+       block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL);
        if (!block)
                return;
 
@@ -850,17 +854,31 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
                offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
-               end_of_log = WLCORE_FW_LOG_END;
+               end_of_log = wl->fwlog_end;
        } else {
                offset = sizeof(addr);
                end_of_log = addr;
        }
 
+       old_part = wl->curr_part;
+       memset(&part, 0, sizeof(part));
+
        /* Traverse the memory blocks linked list */
        do {
-               memset(block, 0, WL12XX_HW_BLOCK_SIZE);
-               ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
-                                        false);
+               part.mem.start = wlcore_hw_convert_hwaddr(wl, addr);
+               part.mem.size  = PAGE_SIZE;
+
+               ret = wlcore_set_partition(wl, &part);
+               if (ret < 0) {
+                       wl1271_error("%s: set_partition start=0x%X size=%d",
+                               __func__, part.mem.start, part.mem.size);
+                       goto out;
+               }
+
+               memset(block, 0, wl->fw_mem_block_size);
+               ret = wlcore_read_hwaddr(wl, addr, block,
+                                       wl->fw_mem_block_size, false);
+
                if (ret < 0)
                        goto out;
 
@@ -871,8 +889,9 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                 * on demand mode and is equal to 0x2000000 in continuous mode.
                 */
                addr = le32_to_cpup((__le32 *)block);
+
                if (!wl12xx_copy_fwlog(wl, block + offset,
-                                      WL12XX_HW_BLOCK_SIZE - offset))
+                                       wl->fw_mem_block_size - offset))
                        break;
        } while (addr && (addr != end_of_log));
 
@@ -880,6 +899,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
 out:
        kfree(block);
+       wlcore_set_partition(wl, &old_part);
 }
 
 static void wlcore_print_recovery(struct wl1271 *wl)
@@ -924,7 +944,8 @@ static void wl1271_recovery_work(struct work_struct *work)
                goto out_unlock;
 
        if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) {
-               wl12xx_read_fwlog_panic(wl);
+               if (wl->conf.fwlog.output == WL12XX_FWLOG_OUTPUT_HOST)
+                       wl12xx_read_fwlog_panic(wl);
                wlcore_print_recovery(wl);
        }
 
@@ -1928,8 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
 
        /*
         * FW channels must be re-calibrated after recovery,
-        * clear the last Reg-Domain channel configuration.
+        * save current Reg-Domain channel configuration and clear it.
         */
+       memcpy(wl->reg_ch_conf_pending, wl->reg_ch_conf_last,
+              sizeof(wl->reg_ch_conf_pending));
        memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
 }
 
@@ -2623,6 +2646,12 @@ deinit:
            !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags))
                goto unlock;
 
+       if (wl->ap_count == 0 && is_ap) {
+               /* mask ap events */
+               wl->event_mask &= ~wl->ap_event_mask;
+               wl1271_event_unmask(wl);
+       }
+
        if (wl->ap_count == 0 && is_ap && wl->sta_count) {
                u8 sta_auth = wl->conf.conn.sta_sleep_auth;
                /* Configure for power according to debugfs */
@@ -6152,6 +6181,9 @@ module_param_named(fwlog, fwlog_param, charp, 0);
 MODULE_PARM_DESC(fwlog,
                 "FW logger options: continuous, ondemand, dbgpins or disable");
 
+module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
+
 module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
 
index 13e743df2e31d45815f8d8fd916e9f5370977841..7ed86203304b700c87e4b1ca48de127f7466420f 100644 (file)
@@ -92,9 +92,31 @@ out:
 static void wlcore_started_vifs_iter(void *data, u8 *mac,
                                     struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       bool active = false;
        int *count = (int *)data;
 
-       if (!vif->bss_conf.idle)
+       /*
+        * count active interfaces according to interface type.
+        * checking only bss_conf.idle is bad for some cases, e.g.
+        * we don't want to count sta in p2p_find as active interface.
+        */
+       switch (wlvif->bss_type) {
+       case BSS_TYPE_STA_BSS:
+               if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       active = true;
+               break;
+
+       case BSS_TYPE_AP_BSS:
+               if (wlvif->wl->active_sta_count > 0)
+                       active = true;
+               break;
+
+       default:
+               break;
+       }
+
+       if (active)
                (*count)++;
 }
 
index a3b7d950d8e9b0f2a989b6624e130ed7cc081d91..ddad58f614da4fa862a0e3f2b776fe0e994a742d 100644 (file)
@@ -179,7 +179,8 @@ static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
                goto out_sleep;
        }
 
-       ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd));
+       ret = wl1271_cmd_interrogate(wl, ie_id, cmd,
+                                    sizeof(struct acx_header), sizeof(*cmd));
        if (ret < 0) {
                wl1271_warning("testmode cmd interrogate failed: %d", ret);
                goto out_free;
index 54ce5d5e84db0be55da713ddcdbcd19818d507f3..06efc12a39e5175dfde449843ffbb5a7c50b157d 100644 (file)
@@ -110,6 +110,7 @@ struct wlcore_ops {
                            struct ieee80211_sta_ht_cap *ht_cap,
                            bool allow_ht_operation,
                            u32 rate_set, u8 hlid);
+       u32 (*convert_hwaddr)(struct wl1271 *wl, u32 hwaddr);
        bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
                              struct wl1271_link *lnk);
        bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
@@ -290,6 +291,12 @@ struct wl1271 {
        /* Number of valid bytes in the FW log buffer */
        ssize_t fwlog_size;
 
+       /* FW log end marker */
+       u32 fwlog_end;
+
+       /* FW memory block size */
+       u32 fw_mem_block_size;
+
        /* Sysfs FW log entry readers wait queue */
        wait_queue_head_t fwlog_waitq;
 
@@ -307,6 +314,8 @@ struct wl1271 {
 
        /* The mbox event mask */
        u32 event_mask;
+       /* events to unmask only when ap interface is up */
+       u32 ap_event_mask;
 
        /* Mailbox pointers */
        u32 mbox_size;
index 2a50e089b0e755eb9fafa79f1cff00c6e9d2d4a7..ce7261ce8b59a244837618a07ecf174762f04946 100644 (file)
@@ -550,6 +550,4 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
 #define HW_HT_RATES_OFFSET     16
 #define HW_MIMO_RATES_OFFSET   24
 
-#define WL12XX_HW_BLOCK_SIZE   256
-
 #endif /* __WLCORE_I_H__ */
index b0b64ccb7d7d5d2581047bbd8ff151dabbf3492c..c1fb20603338299eb2eecfd085653480dd726428 100644 (file)
@@ -46,6 +46,16 @@ config NFC_SIM
 
          If unsure, say N.
 
+config NFC_PORT100
+       tristate "Sony NFC Port-100 Series USB device support"
+       depends on USB
+       depends on NFC_DIGITAL
+       help
+         This adds support for Sony Port-100 chip based USB devices such as the
+         RC-S380 dongle.
+
+         If unsure, say N.
+
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 
index be7636abcb3fa2c79076f533b54ab4769251fbb3..c715fe8582a8075503f7db5de758fa250d2905bb 100644 (file)
@@ -8,5 +8,6 @@ obj-$(CONFIG_NFC_PN533)         += pn533.o
 obj-$(CONFIG_NFC_WILINK)       += nfcwilink.o
 obj-$(CONFIG_NFC_MEI_PHY)      += mei_phy.o
 obj-$(CONFIG_NFC_SIM)          += nfcsim.o
+obj-$(CONFIG_NFC_PORT100)      += port100.o
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index 606bf55e76ec5941bf69443f8113a38266b4c000..85f90090cc1d1e167de6391e3481b597451555bd 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/nfc.h>
@@ -60,13 +62,13 @@ int nfc_mei_phy_enable(void *phy_id)
 
        r = mei_cl_enable_device(phy->device);
        if (r < 0) {
-               pr_err("MEI_PHY: Could not enable device\n");
+               pr_err("Could not enable device\n");
                return r;
        }
 
        r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy);
        if (r) {
-               pr_err("MEY_PHY: Event cb registration failed\n");
+               pr_err("Event cb registration failed\n");
                mei_cl_disable_device(phy->device);
                phy->powered = 0;
 
index 101089495bf81f6b5ee115f219f8b13f3f5cb667..696e3467eccc33ab1ee97c6da2a4940c489ee086 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
@@ -95,12 +97,8 @@ static int check_crc(struct sk_buff *skb)
                crc = crc ^ skb->data[i];
 
        if (crc != skb->data[skb->len-1]) {
-               pr_err(MICROREAD_I2C_DRIVER_NAME
-                      ": CRC error 0x%x != 0x%x\n",
-                      crc, skb->data[skb->len-1]);
-
-               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
-
+               pr_err("CRC error 0x%x != 0x%x\n", crc, skb->data[skb->len-1]);
+               pr_info("%s: BAD CRC\n", __func__);
                return -EPERM;
        }
 
@@ -160,18 +158,15 @@ static int microread_i2c_read(struct microread_i2c_phy *phy,
        u8 tmp[MICROREAD_I2C_LLC_MAX_SIZE - 1];
        struct i2c_client *client = phy->i2c_dev;
 
-       pr_debug("%s\n", __func__);
-
        r = i2c_master_recv(client, &len, 1);
        if (r != 1) {
-               dev_err(&client->dev, "cannot read len byte\n");
+               nfc_err(&client->dev, "cannot read len byte\n");
                return -EREMOTEIO;
        }
 
        if ((len < MICROREAD_I2C_LLC_MIN_SIZE) ||
            (len > MICROREAD_I2C_LLC_MAX_SIZE)) {
-               dev_err(&client->dev, "invalid len byte\n");
-               pr_err("invalid len byte\n");
+               nfc_err(&client->dev, "invalid len byte\n");
                r = -EBADMSG;
                goto flush;
        }
@@ -228,7 +223,6 @@ static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id)
        }
 
        client = phy->i2c_dev;
-       dev_dbg(&client->dev, "IRQ\n");
 
        if (phy->hard_fault != 0)
                return IRQ_HANDLED;
@@ -263,20 +257,18 @@ static int microread_i2c_probe(struct i2c_client *client,
                dev_get_platdata(&client->dev);
        int r;
 
-       dev_dbg(&client->dev, "client %p", client);
+       dev_dbg(&client->dev, "client %p\n", client);
 
        if (!pdata) {
-               dev_err(&client->dev, "client %p: missing platform data",
+               nfc_err(&client->dev, "client %p: missing platform data\n",
                        client);
                return -EINVAL;
        }
 
        phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy),
                           GFP_KERNEL);
-       if (!phy) {
-               dev_err(&client->dev, "Can't allocate microread phy");
+       if (!phy)
                return -ENOMEM;
-       }
 
        i2c_set_clientdata(client, phy);
        phy->i2c_dev = client;
@@ -285,7 +277,7 @@ static int microread_i2c_probe(struct i2c_client *client,
                                 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
                                 MICROREAD_I2C_DRIVER_NAME, phy);
        if (r) {
-               dev_err(&client->dev, "Unable to register IRQ handler");
+               nfc_err(&client->dev, "Unable to register IRQ handler\n");
                return r;
        }
 
@@ -296,7 +288,7 @@ static int microread_i2c_probe(struct i2c_client *client,
        if (r < 0)
                goto err_irq;
 
-       dev_info(&client->dev, "Probed");
+       nfc_info(&client->dev, "Probed");
 
        return 0;
 
@@ -310,8 +302,6 @@ static int microread_i2c_remove(struct i2c_client *client)
 {
        struct microread_i2c_phy *phy = i2c_get_clientdata(client);
 
-       dev_dbg(&client->dev, "%s\n", __func__);
-
        microread_remove(phy->hdev);
 
        free_irq(client->irq, phy);
index cdf1bc53b257f6ae34e3f2148de97b47f53521b7..72fafec3d46058908ea841f2c89273c179b6d30d 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/nfc.h>
@@ -59,8 +61,6 @@ static int microread_mei_remove(struct mei_cl_device *device)
 {
        struct nfc_mei_phy *phy = mei_cl_get_drvdata(device);
 
-       pr_info("Removing microread\n");
-
        microread_remove(phy->hdev);
 
        nfc_mei_phy_free(phy);
index cdb9f6de132a5c16b564e2a1228bbc3ae594ec2f..970ded6bfcf562b278e43b03e5112042c798fd0c 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -546,7 +548,7 @@ exit:
        kfree_skb(skb);
 
        if (r)
-               pr_err("Failed to handle discovered target err=%d", r);
+               pr_err("Failed to handle discovered target err=%d\n", r);
 }
 
 static int microread_event_received(struct nfc_hci_dev *hdev, u8 gate,
@@ -656,7 +658,6 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
 
        info = kzalloc(sizeof(struct microread_info), GFP_KERNEL);
        if (!info) {
-               pr_err("Cannot allocate memory for microread_info.\n");
                r = -ENOMEM;
                goto err_info_alloc;
        }
@@ -686,7 +687,7 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                                             MICROREAD_CMD_TAILROOM,
                                             phy_payload);
        if (!info->hdev) {
-               pr_err("Cannot allocate nfc hdev.\n");
+               pr_err("Cannot allocate nfc hdev\n");
                r = -ENOMEM;
                goto err_alloc_hdev;
        }
index 9a53f13c88df50a705c84b89e4fe7c081dbe3c88..93111fa8d2829735ec1ddea6c09c6da86bbb9459 100644 (file)
 #include <linux/nfc.h>
 #include <net/nfc/nfc.h>
 
-#define DEV_ERR(_dev, fmt, args...) nfc_dev_err(&_dev->nfc_dev->dev, \
+#define DEV_ERR(_dev, fmt, args...) nfc_err(&_dev->nfc_dev->dev, \
                                                "%s: " fmt, __func__, ## args)
 
-#define DEV_DBG(_dev, fmt, args...) nfc_dev_dbg(&_dev->nfc_dev->dev, \
+#define DEV_DBG(_dev, fmt, args...) dev_dbg(&_dev->nfc_dev->dev, \
                                                "%s: " fmt, __func__, ## args)
 
 #define NFCSIM_VERSION "0.1"
@@ -64,7 +64,7 @@ static struct workqueue_struct *wq;
 
 static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
 {
-       DEV_DBG(dev, "shutdown=%d", shutdown);
+       DEV_DBG(dev, "shutdown=%d\n", shutdown);
 
        mutex_lock(&dev->lock);
 
@@ -84,7 +84,7 @@ static int nfcsim_target_found(struct nfcsim *dev)
 {
        struct nfc_target nfc_tgt;
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        memset(&nfc_tgt, 0, sizeof(struct nfc_target));
 
@@ -98,7 +98,7 @@ static int nfcsim_dev_up(struct nfc_dev *nfc_dev)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        mutex_lock(&dev->lock);
 
@@ -113,7 +113,7 @@ static int nfcsim_dev_down(struct nfc_dev *nfc_dev)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        mutex_lock(&dev->lock);
 
@@ -143,7 +143,7 @@ static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
 
        remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len);
        if (!remote_gb) {
-               DEV_ERR(peer, "Can't get remote general bytes");
+               DEV_ERR(peer, "Can't get remote general bytes\n");
 
                mutex_unlock(&peer->lock);
                return -EINVAL;
@@ -155,7 +155,7 @@ static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
 
        rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len);
        if (rc) {
-               DEV_ERR(dev, "Can't set remote general bytes");
+               DEV_ERR(dev, "Can't set remote general bytes\n");
                mutex_unlock(&dev->lock);
                return rc;
        }
@@ -172,7 +172,7 @@ static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        nfcsim_cleanup_dev(dev, 0);
 
@@ -188,7 +188,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
        mutex_lock(&dev->lock);
 
        if (dev->polling_mode != NFCSIM_POLL_NONE) {
-               DEV_ERR(dev, "Already in polling mode");
+               DEV_ERR(dev, "Already in polling mode\n");
                rc = -EBUSY;
                goto exit;
        }
@@ -200,7 +200,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
                dev->polling_mode |= NFCSIM_POLL_TARGET;
 
        if (dev->polling_mode == NFCSIM_POLL_NONE) {
-               DEV_ERR(dev, "Unsupported polling mode");
+               DEV_ERR(dev, "Unsupported polling mode\n");
                rc = -EINVAL;
                goto exit;
        }
@@ -210,7 +210,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
 
        queue_delayed_work(wq, &dev->poll_work, 0);
 
-       DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X", im_protocols,
+       DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X\n", im_protocols,
                tm_protocols);
 
        rc = 0;
@@ -224,7 +224,7 @@ static void nfcsim_stop_poll(struct nfc_dev *nfc_dev)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "Stop poll");
+       DEV_DBG(dev, "Stop poll\n");
 
        mutex_lock(&dev->lock);
 
@@ -240,7 +240,7 @@ static int nfcsim_activate_target(struct nfc_dev *nfc_dev,
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        return -ENOTSUPP;
 }
@@ -250,7 +250,7 @@ static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev,
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 }
 
 static void nfcsim_wq_recv(struct work_struct *work)
@@ -267,7 +267,7 @@ static void nfcsim_wq_recv(struct work_struct *work)
 
        if (dev->initiator) {
                if (!dev->cb) {
-                       DEV_ERR(dev, "Null recv callback");
+                       DEV_ERR(dev, "Null recv callback\n");
                        dev_kfree_skb(dev->clone_skb);
                        goto exit;
                }
@@ -310,7 +310,7 @@ static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
        peer->clone_skb = skb_clone(skb, GFP_KERNEL);
 
        if (!peer->clone_skb) {
-               DEV_ERR(dev, "skb_clone failed");
+               DEV_ERR(dev, "skb_clone failed\n");
                mutex_unlock(&peer->lock);
                err = -ENOMEM;
                goto exit;
@@ -397,13 +397,13 @@ static void nfcsim_wq_poll(struct work_struct *work)
        nfcsim_set_polling_mode(dev);
 
        if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
-               DEV_DBG(dev, "Not polling");
+               DEV_DBG(dev, "Not polling\n");
                goto unlock;
        }
 
        DEV_DBG(dev, "Polling as %s",
                dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ?
-               "initiator" : "target");
+               "initiator\n" : "target\n");
 
        if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
                goto sched_work;
index 59f95d8fc98c887228d15e0421c69906bdd85154..71308645593f638a2879df4348f1349f0e031ce4 100644 (file)
@@ -146,13 +146,11 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
        unsigned long comp_ret;
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "get_bts_file_name entry");
-
        skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd),
                                        GFP_KERNEL);
        if (!skb) {
-               nfc_dev_err(&drv->pdev->dev,
-                               "no memory for nci_vs_nfcc_info_cmd");
+               nfc_err(&drv->pdev->dev,
+                       "no memory for nci_vs_nfcc_info_cmd\n");
                return -ENOMEM;
        }
 
@@ -170,21 +168,19 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
 
        comp_ret = wait_for_completion_timeout(&drv->completed,
                                msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
-       nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
-                       comp_ret);
+       dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
+               comp_ret);
        if (comp_ret == 0) {
-               nfc_dev_err(&drv->pdev->dev,
-                               "timeout on wait_for_completion_timeout");
+               nfc_err(&drv->pdev->dev,
+                       "timeout on wait_for_completion_timeout\n");
                return -ETIMEDOUT;
        }
 
-       nfc_dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d",
-                       drv->nfcc_info.plen,
-                       drv->nfcc_info.status);
+       dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n",
+               drv->nfcc_info.plen, drv->nfcc_info.status);
 
        if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) {
-               nfc_dev_err(&drv->pdev->dev,
-                               "invalid nci_vs_nfcc_info_rsp");
+               nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n");
                return -EINVAL;
        }
 
@@ -195,7 +191,7 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
                        drv->nfcc_info.sw_ver_z,
                        drv->nfcc_info.patch_id);
 
-       nfc_dev_info(&drv->pdev->dev, "nfcwilink FW file name: %s", file_name);
+       nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name);
 
        return 0;
 }
@@ -207,15 +203,13 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
        unsigned long comp_ret;
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "send_bts_cmd entry");
-
        /* verify valid cmd for the NFC channel */
        if ((len <= sizeof(struct nfcwilink_hdr)) ||
                (len > BTS_FILE_CMD_MAX_LEN) ||
                (hdr->chnl != NFCWILINK_CHNL) ||
                (hdr->opcode != NFCWILINK_OPCODE)) {
-               nfc_dev_err(&drv->pdev->dev,
-                       "ignoring invalid bts cmd, len %d, chnl %d, opcode %d",
+               nfc_err(&drv->pdev->dev,
+                       "ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n",
                        len, hdr->chnl, hdr->opcode);
                return 0;
        }
@@ -226,7 +220,7 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
 
        skb = nfcwilink_skb_alloc(len, GFP_KERNEL);
        if (!skb) {
-               nfc_dev_err(&drv->pdev->dev, "no memory for bts cmd");
+               nfc_err(&drv->pdev->dev, "no memory for bts cmd\n");
                return -ENOMEM;
        }
 
@@ -238,11 +232,11 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
 
        comp_ret = wait_for_completion_timeout(&drv->completed,
                                msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
-       nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
-                       comp_ret);
+       dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
+               comp_ret);
        if (comp_ret == 0) {
-               nfc_dev_err(&drv->pdev->dev,
-                               "timeout on wait_for_completion_timeout");
+               nfc_err(&drv->pdev->dev,
+                       "timeout on wait_for_completion_timeout\n");
                return -ETIMEDOUT;
        }
 
@@ -257,8 +251,6 @@ static int nfcwilink_download_fw(struct nfcwilink *drv)
        __u8 *ptr;
        int len, rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "download_fw entry");
-
        set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
 
        rc = nfcwilink_get_bts_file_name(drv, file_name);
@@ -267,7 +259,7 @@ static int nfcwilink_download_fw(struct nfcwilink *drv)
 
        rc = request_firmware(&fw, file_name, &drv->pdev->dev);
        if (rc) {
-               nfc_dev_err(&drv->pdev->dev, "request_firmware failed %d", rc);
+               nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc);
 
                /* if the file is not found, don't exit with failure */
                if (rc == -ENOENT)
@@ -280,14 +272,14 @@ static int nfcwilink_download_fw(struct nfcwilink *drv)
        ptr = (__u8 *)fw->data;
 
        if ((len == 0) || (ptr == NULL)) {
-               nfc_dev_dbg(&drv->pdev->dev,
-                               "request_firmware returned size %d", len);
+               dev_dbg(&drv->pdev->dev,
+                       "request_firmware returned size %d\n", len);
                goto release_fw;
        }
 
        if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) !=
                        BTS_FILE_HDR_MAGIC) {
-               nfc_dev_err(&drv->pdev->dev, "wrong bts magic number");
+               nfc_err(&drv->pdev->dev, "wrong bts magic number\n");
                rc = -EINVAL;
                goto release_fw;
        }
@@ -302,8 +294,8 @@ static int nfcwilink_download_fw(struct nfcwilink *drv)
                action_len =
                        __le16_to_cpu(((struct bts_file_action *)ptr)->len);
 
-               nfc_dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d",
-                               action_type, action_len);
+               dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n",
+                       action_type, action_len);
 
                switch (action_type) {
                case BTS_FILE_ACTION_TYPE_SEND_CMD:
@@ -333,8 +325,6 @@ static void nfcwilink_register_complete(void *priv_data, char data)
 {
        struct nfcwilink *drv = priv_data;
 
-       nfc_dev_dbg(&drv->pdev->dev, "register_complete entry");
-
        /* store ST registration status */
        drv->st_register_cb_status = data;
 
@@ -356,7 +346,7 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
                return -EFAULT;
        }
 
-       nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
+       dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len);
 
        /* strip the ST header
        (apart for the chnl byte, which is not received in the hdr) */
@@ -370,7 +360,7 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
        /* Forward skb to NCI core layer */
        rc = nci_recv_frame(drv->ndev, skb);
        if (rc < 0) {
-               nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc);
+               nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc);
                return rc;
        }
 
@@ -396,8 +386,6 @@ static int nfcwilink_open(struct nci_dev *ndev)
        unsigned long comp_ret;
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "open entry");
-
        if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) {
                rc = -EBUSY;
                goto exit;
@@ -415,9 +403,9 @@ static int nfcwilink_open(struct nci_dev *ndev)
                        &drv->completed,
                        msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT));
 
-                       nfc_dev_dbg(&drv->pdev->dev,
-                       "wait_for_completion_timeout returned %ld",
-                       comp_ret);
+                       dev_dbg(&drv->pdev->dev,
+                               "wait_for_completion_timeout returned %ld\n",
+                               comp_ret);
 
                        if (comp_ret == 0) {
                                /* timeout */
@@ -425,13 +413,12 @@ static int nfcwilink_open(struct nci_dev *ndev)
                                goto clear_exit;
                        } else if (drv->st_register_cb_status != 0) {
                                rc = drv->st_register_cb_status;
-                               nfc_dev_err(&drv->pdev->dev,
-                               "st_register_cb failed %d", rc);
+                               nfc_err(&drv->pdev->dev,
+                                       "st_register_cb failed %d\n", rc);
                                goto clear_exit;
                        }
                } else {
-                       nfc_dev_err(&drv->pdev->dev,
-                               "st_register failed %d", rc);
+                       nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc);
                        goto clear_exit;
                }
        }
@@ -441,8 +428,8 @@ static int nfcwilink_open(struct nci_dev *ndev)
        drv->st_write = nfcwilink_proto.write;
 
        if (nfcwilink_download_fw(drv)) {
-               nfc_dev_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d",
-                               rc);
+               nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n",
+                       rc);
                /* open should succeed, even if the FW download failed */
        }
 
@@ -460,14 +447,12 @@ static int nfcwilink_close(struct nci_dev *ndev)
        struct nfcwilink *drv = nci_get_drvdata(ndev);
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "close entry");
-
        if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags))
                return 0;
 
        rc = st_unregister(&nfcwilink_proto);
        if (rc)
-               nfc_dev_err(&drv->pdev->dev, "st_unregister failed %d", rc);
+               nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc);
 
        drv->st_write = NULL;
 
@@ -480,7 +465,7 @@ static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
        struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
        long len;
 
-       nfc_dev_dbg(&drv->pdev->dev, "send entry, len %d", skb->len);
+       dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len);
 
        if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) {
                kfree_skb(skb);
@@ -498,7 +483,7 @@ static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
        len = drv->st_write(skb);
        if (len < 0) {
                kfree_skb(skb);
-               nfc_dev_err(&drv->pdev->dev, "st_write failed %ld", len);
+               nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len);
                return -EFAULT;
        }
 
@@ -517,8 +502,6 @@ static int nfcwilink_probe(struct platform_device *pdev)
        int rc;
        __u32 protocols;
 
-       nfc_dev_dbg(&pdev->dev, "probe entry");
-
        drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL);
        if (!drv) {
                rc = -ENOMEM;
@@ -538,7 +521,7 @@ static int nfcwilink_probe(struct platform_device *pdev)
                                        NFCWILINK_HDR_LEN,
                                        0);
        if (!drv->ndev) {
-               nfc_dev_err(&pdev->dev, "nci_allocate_device failed");
+               nfc_err(&pdev->dev, "nci_allocate_device failed\n");
                rc = -ENOMEM;
                goto exit;
        }
@@ -548,7 +531,7 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        rc = nci_register_device(drv->ndev);
        if (rc < 0) {
-               nfc_dev_err(&pdev->dev, "nci_register_device failed %d", rc);
+               nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc);
                goto free_dev_exit;
        }
 
@@ -568,8 +551,6 @@ static int nfcwilink_remove(struct platform_device *pdev)
        struct nfcwilink *drv = dev_get_drvdata(&pdev->dev);
        struct nci_dev *ndev;
 
-       nfc_dev_dbg(&pdev->dev, "remove entry");
-
        if (!drv)
                return -EFAULT;
 
@@ -578,8 +559,6 @@ static int nfcwilink_remove(struct platform_device *pdev)
        nci_unregister_device(ndev);
        nci_free_device(ndev);
 
-       dev_set_drvdata(&pdev->dev, NULL);
-
        return 0;
 }
 
index 5df730be88a388ba28f705fde82fdce04b1ff88e..2daf04c073383c53ae4374d8a9613546fe13d09b 100644 (file)
@@ -150,6 +150,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_TG_INIT_AS_TARGET 0x8c
 #define PN533_CMD_TG_GET_DATA 0x86
 #define PN533_CMD_TG_SET_DATA 0x8e
+#define PN533_CMD_TG_SET_META_DATA 0x94
 #define PN533_CMD_UNDEF 0xff
 
 #define PN533_CMD_RESPONSE(cmd) (cmd + 1)
@@ -373,6 +374,8 @@ struct pn533 {
        struct delayed_work poll_work;
        struct work_struct mi_rx_work;
        struct work_struct mi_tx_work;
+       struct work_struct mi_tm_rx_work;
+       struct work_struct mi_tm_tx_work;
        struct work_struct tg_work;
        struct work_struct rf_work;
 
@@ -387,6 +390,7 @@ struct pn533 {
        struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
        u8 poll_mod_count;
        u8 poll_mod_curr;
+       u8 poll_dep;
        u32 poll_protocols;
        u32 listen_protocols;
        struct timer_list listen_timer;
@@ -722,32 +726,32 @@ static void pn533_recv_response(struct urb *urb)
                break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-               nfc_dev_dbg(&dev->interface->dev,
-                           "The urb has been canceled (status %d)",
-                           urb->status);
+               dev_dbg(&dev->interface->dev,
+                       "The urb has been canceled (status %d)\n",
+                       urb->status);
                goto sched_wq;
        case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev,
-                           "Urb failure (status %d)", urb->status);
+               nfc_err(&dev->interface->dev,
+                       "Urb failure (status %d)\n", urb->status);
                goto sched_wq;
        }
 
        in_frame = dev->in_urb->transfer_buffer;
 
-       nfc_dev_dbg(&dev->interface->dev, "Received a frame.");
+       dev_dbg(&dev->interface->dev, "Received a frame\n");
        print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
                             dev->ops->rx_frame_size(in_frame), false);
 
        if (!dev->ops->rx_is_frame_valid(in_frame, dev)) {
-               nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
+               nfc_err(&dev->interface->dev, "Received an invalid frame\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
 
        if (!pn533_rx_frame_is_cmd_response(dev, in_frame)) {
-               nfc_dev_err(&dev->interface->dev,
-                           "It it not the response to the last command");
+               nfc_err(&dev->interface->dev,
+                       "It it not the response to the last command\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
@@ -777,29 +781,29 @@ static void pn533_recv_ack(struct urb *urb)
                break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-               nfc_dev_dbg(&dev->interface->dev,
-                           "The urb has been stopped (status %d)",
-                           urb->status);
+               dev_dbg(&dev->interface->dev,
+                       "The urb has been stopped (status %d)\n",
+                       urb->status);
                goto sched_wq;
        case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev,
-                           "Urb failure (status %d)", urb->status);
+               nfc_err(&dev->interface->dev,
+                       "Urb failure (status %d)\n", urb->status);
                goto sched_wq;
        }
 
        in_frame = dev->in_urb->transfer_buffer;
 
        if (!pn533_std_rx_frame_is_ack(in_frame)) {
-               nfc_dev_err(&dev->interface->dev, "Received an invalid ack");
+               nfc_err(&dev->interface->dev, "Received an invalid ack\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
 
        rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "usb_submit_urb failed with result %d", rc);
+               nfc_err(&dev->interface->dev,
+                       "usb_submit_urb failed with result %d\n", rc);
                cmd->status = rc;
                goto sched_wq;
        }
@@ -823,8 +827,6 @@ static int pn533_send_ack(struct pn533 *dev, gfp_t flags)
        /* spec 7.1.1.3:  Preamble, SoPC (2), ACK Code (2), Postamble */
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
-
        dev->out_urb->transfer_buffer = ack;
        dev->out_urb->transfer_buffer_length = sizeof(ack);
        rc = usb_submit_urb(dev->out_urb, flags);
@@ -927,7 +929,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
        struct pn533_cmd *cmd;
        int rc = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", cmd_code);
+       dev_dbg(&dev->interface->dev, "Sending command 0x%x\n", cmd_code);
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (!cmd)
@@ -954,8 +956,8 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
                goto unlock;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x", __func__,
-                   cmd_code);
+       dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x\n",
+               __func__, cmd_code);
 
        INIT_LIST_HEAD(&cmd->queue);
        list_add_tail(&cmd->queue, &dev->cmd_queue);
@@ -1168,14 +1170,14 @@ static void pn533_send_complete(struct urb *urb)
                break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-               nfc_dev_dbg(&dev->interface->dev,
-                           "The urb has been stopped (status %d)",
-                           urb->status);
+               dev_dbg(&dev->interface->dev,
+                       "The urb has been stopped (status %d)\n",
+                       urb->status);
                break;
        case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev,
-                           "Urb failure (status %d)", urb->status);
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
+                       urb->status);
        }
 }
 
@@ -1452,8 +1454,8 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
        struct nfc_target nfc_tgt;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s - modulation=%d", __func__,
-                   dev->poll_mod_curr);
+       dev_dbg(&dev->interface->dev, "%s: modulation=%d\n",
+               __func__, dev->poll_mod_curr);
 
        if (tg != 1)
                return -EPROTO;
@@ -1475,8 +1477,8 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
                rc = pn533_target_found_type_b(&nfc_tgt, tgdata, tgdata_len);
                break;
        default:
-               nfc_dev_err(&dev->interface->dev,
-                           "Unknown current poll modulation");
+               nfc_err(&dev->interface->dev,
+                       "Unknown current poll modulation\n");
                return -EPROTO;
        }
 
@@ -1484,14 +1486,14 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
                return rc;
 
        if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) {
-               nfc_dev_dbg(&dev->interface->dev,
-                           "The Tg found doesn't have the desired protocol");
+               dev_dbg(&dev->interface->dev,
+                       "The Tg found doesn't have the desired protocol\n");
                return -EAGAIN;
        }
 
-       nfc_dev_dbg(&dev->interface->dev,
-                   "Target found - supported protocols: 0x%x",
-                   nfc_tgt.supported_protocols);
+       dev_dbg(&dev->interface->dev,
+               "Target found - supported protocols: 0x%x\n",
+               nfc_tgt.supported_protocols);
 
        dev->tgt_available_prots = nfc_tgt.supported_protocols;
 
@@ -1548,7 +1550,8 @@ static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp)
        u8 nbtg, tg, *tgdata;
        int rc, tgdata_len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       /* Toggle the DEP polling */
+       dev->poll_dep = 1;
 
        nbtg = resp->data[0];
        tg = resp->data[1];
@@ -1624,37 +1627,130 @@ static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev)
 
 #define PN533_CMD_DATAEXCH_HEAD_LEN 1
 #define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+static void pn533_wq_tm_mi_recv(struct work_struct *work);
+static struct sk_buff *pn533_build_response(struct pn533 *dev);
+
 static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg,
                                      struct sk_buff *resp)
 {
-       u8 status;
+       struct sk_buff *skb;
+       u8 status, ret, mi;
+       int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
-       if (IS_ERR(resp))
+       if (IS_ERR(resp)) {
+               skb_queue_purge(&dev->resp_q);
                return PTR_ERR(resp);
+       }
 
        status = resp->data[0];
+
+       ret = status & PN533_CMD_RET_MASK;
+       mi = status & PN533_CMD_MI_MASK;
+
        skb_pull(resp, sizeof(status));
 
-       if (status != 0) {
-               nfc_tm_deactivated(dev->nfc_dev);
-               dev->tgt_mode = 0;
-               dev_kfree_skb(resp);
-               return 0;
+       if (ret != PN533_CMD_RET_SUCCESS) {
+               rc = -EIO;
+               goto error;
        }
 
-       return nfc_tm_data_received(dev->nfc_dev, resp);
+       skb_queue_tail(&dev->resp_q, resp);
+
+       if (mi) {
+               queue_work(dev->wq, &dev->mi_tm_rx_work);
+               return -EINPROGRESS;
+       }
+
+       skb = pn533_build_response(dev);
+       if (!skb) {
+               rc = -EIO;
+               goto error;
+       }
+
+       return nfc_tm_data_received(dev->nfc_dev, skb);
+
+error:
+       nfc_tm_deactivated(dev->nfc_dev);
+       dev->tgt_mode = 0;
+       skb_queue_purge(&dev->resp_q);
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static void pn533_wq_tm_mi_recv(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, mi_tm_rx_work);
+       struct sk_buff *skb;
+       int rc;
+
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
+
+       skb = pn533_alloc_skb(dev, 0);
+       if (!skb)
+               return;
+
+       rc = pn533_send_cmd_direct_async(dev,
+                                       PN533_CMD_TG_GET_DATA,
+                                       skb,
+                                       pn533_tm_get_data_complete,
+                                       NULL);
+
+       if (rc < 0)
+               dev_kfree_skb(skb);
+
+       return;
+}
+
+static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
+                                 struct sk_buff *resp);
+static void pn533_wq_tm_mi_send(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, mi_tm_tx_work);
+       struct sk_buff *skb;
+       int rc;
+
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
+
+       /* Grab the first skb in the queue */
+       skb = skb_dequeue(&dev->fragment_skb);
+       if (skb == NULL) {      /* No more data */
+               /* Reset the queue for future use */
+               skb_queue_head_init(&dev->fragment_skb);
+               goto error;
+       }
+
+       /* last entry - remove MI bit */
+       if (skb_queue_len(&dev->fragment_skb) == 0) {
+               rc = pn533_send_cmd_direct_async(dev, PN533_CMD_TG_SET_DATA,
+                                       skb, pn533_tm_send_complete, NULL);
+       } else
+               rc = pn533_send_cmd_direct_async(dev,
+                                       PN533_CMD_TG_SET_META_DATA,
+                                       skb, pn533_tm_send_complete, NULL);
+
+       if (rc == 0) /* success */
+               return;
+
+       dev_err(&dev->interface->dev,
+               "Error %d when trying to perform set meta data_exchange", rc);
+
+       dev_kfree_skb(skb);
+
+error:
+       pn533_send_ack(dev, GFP_KERNEL);
+       queue_work(dev->wq, &dev->cmd_work);
 }
 
 static void pn533_wq_tg_get_data(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, tg_work);
-
        struct sk_buff *skb;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, 0);
        if (!skb)
@@ -1676,7 +1772,7 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
        size_t gb_len;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (resp->len < ATR_REQ_GB_OFFSET + 1)
                return -EINVAL;
@@ -1684,8 +1780,8 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
        mode = resp->data[0];
        cmd = &resp->data[1];
 
-       nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n",
-                   mode, resp->len);
+       dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n",
+               mode, resp->len);
 
        if ((mode & PN533_INIT_TARGET_RESP_FRAME_MASK) ==
            PN533_INIT_TARGET_RESP_ACTIVE)
@@ -1700,8 +1796,8 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
        rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
                              comm_mode, gb, gb_len);
        if (rc < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error when signaling target activation");
+               nfc_err(&dev->interface->dev,
+                       "Error when signaling target activation\n");
                return rc;
        }
 
@@ -1715,7 +1811,7 @@ static void pn533_listen_mode_timer(unsigned long data)
 {
        struct pn533 *dev = (struct pn533 *)data;
 
-       nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout");
+       dev_dbg(&dev->interface->dev, "Listen mode timeout\n");
 
        dev->cancel_listen = 1;
 
@@ -1730,13 +1826,12 @@ static int pn533_rf_complete(struct pn533 *dev, void *arg,
 {
        int rc = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
 
-               nfc_dev_err(&dev->interface->dev, "%s RF setting error %d",
-                           __func__, rc);
+               nfc_err(&dev->interface->dev, "RF setting error %d", rc);
 
                return rc;
        }
@@ -1754,7 +1849,7 @@ static void pn533_wq_rf(struct work_struct *work)
        struct sk_buff *skb;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, 2);
        if (!skb)
@@ -1767,25 +1862,136 @@ static void pn533_wq_rf(struct work_struct *work)
                                  pn533_rf_complete, NULL);
        if (rc < 0) {
                dev_kfree_skb(skb);
-               nfc_dev_err(&dev->interface->dev, "RF setting error %d", rc);
+               nfc_err(&dev->interface->dev, "RF setting error %d\n", rc);
        }
 
        return;
 }
 
+static int pn533_poll_dep_complete(struct pn533 *dev, void *arg,
+                                  struct sk_buff *resp)
+{
+       struct pn533_cmd_jump_dep_response *rsp;
+       struct nfc_target nfc_target;
+       u8 target_gt_len;
+       int rc;
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rsp = (struct pn533_cmd_jump_dep_response *)resp->data;
+
+       rc = rsp->status & PN533_CMD_RET_MASK;
+       if (rc != PN533_CMD_RET_SUCCESS) {
+               /* Not target found, turn radio off */
+               queue_work(dev->wq, &dev->rf_work);
+
+               dev_kfree_skb(resp);
+               return 0;
+       }
+
+       dev_dbg(&dev->interface->dev, "Creating new target");
+
+       nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+       nfc_target.nfcid1_len = 10;
+       memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len);
+       rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1);
+       if (rc)
+               goto error;
+
+       dev->tgt_available_prots = 0;
+       dev->tgt_active_prot = NFC_PROTO_NFC_DEP;
+
+       /* ATR_RES general bytes are located at offset 17 */
+       target_gt_len = resp->len - 17;
+       rc = nfc_set_remote_general_bytes(dev->nfc_dev,
+                                         rsp->gt, target_gt_len);
+       if (!rc) {
+               rc = nfc_dep_link_is_up(dev->nfc_dev,
+                                       dev->nfc_dev->targets[0].idx,
+                                       0, NFC_RF_INITIATOR);
+
+               if (!rc)
+                       pn533_poll_reset_mod_list(dev);
+       }
+error:
+       dev_kfree_skb(resp);
+       return rc;
+}
+
+#define PASSIVE_DATA_LEN 5
+static int pn533_poll_dep(struct nfc_dev *nfc_dev)
+{
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       struct sk_buff *skb;
+       int rc, skb_len;
+       u8 *next, nfcid3[NFC_NFCID3_MAXSIZE];
+       u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
+
+       dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (!dev->gb) {
+               dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
+
+               if (!dev->gb || !dev->gb_len) {
+                       dev->poll_dep = 0;
+                       queue_work(dev->wq, &dev->rf_work);
+               }
+       }
+
+       skb_len = 3 + dev->gb_len; /* ActPass + BR + Next */
+       skb_len += PASSIVE_DATA_LEN;
+
+       /* NFCID3 */
+       skb_len += NFC_NFCID3_MAXSIZE;
+       nfcid3[0] = 0x1;
+       nfcid3[1] = 0xfe;
+       get_random_bytes(nfcid3 + 2, 6);
+
+       skb = pn533_alloc_skb(dev, skb_len);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = 0x01;  /* Active */
+       *skb_put(skb, 1) = 0x02;  /* 424 kbps */
+
+       next = skb_put(skb, 1);  /* Next */
+       *next = 0;
+
+       /* Copy passive data */
+       memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN);
+       *next |= 1;
+
+       /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */
+       memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3,
+              NFC_NFCID3_MAXSIZE);
+       *next |= 2;
+
+       memcpy(skb_put(skb, dev->gb_len), dev->gb, dev->gb_len);
+       *next |= 4; /* We have some Gi */
+
+       rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
+                                 pn533_poll_dep_complete, NULL);
+
+       if (rc < 0)
+               dev_kfree_skb(skb);
+
+       return rc;
+}
+
 static int pn533_poll_complete(struct pn533 *dev, void *arg,
                               struct sk_buff *resp)
 {
        struct pn533_poll_modulations *cur_mod;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
 
-               nfc_dev_err(&dev->interface->dev, "%s  Poll complete error %d",
-                           __func__, rc);
+               nfc_err(&dev->interface->dev, "%s  Poll complete error %d\n",
+                       __func__, rc);
 
                if (rc == -ENOENT) {
                        if (dev->poll_mod_count != 0)
@@ -1793,8 +1999,8 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg,
                        else
                                goto stop_poll;
                } else if (rc < 0) {
-                       nfc_dev_err(&dev->interface->dev,
-                                   "Error %d when running poll", rc);
+                       nfc_err(&dev->interface->dev,
+                               "Error %d when running poll\n", rc);
                        goto stop_poll;
                }
        }
@@ -1813,7 +2019,7 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg,
                goto done;
 
        if (!dev->poll_mod_count) {
-               nfc_dev_dbg(&dev->interface->dev, "Polling has been stopped.");
+               dev_dbg(&dev->interface->dev, "Polling has been stopped\n");
                goto done;
        }
 
@@ -1826,7 +2032,7 @@ done:
        return rc;
 
 stop_poll:
-       nfc_dev_err(&dev->interface->dev, "Polling operation has been stopped");
+       nfc_err(&dev->interface->dev, "Polling operation has been stopped\n");
 
        pn533_poll_reset_mod_list(dev);
        dev->poll_protocols = 0;
@@ -1856,8 +2062,13 @@ static int pn533_send_poll_frame(struct pn533 *dev)
 
        mod = dev->poll_mod_active[dev->poll_mod_curr];
 
-       nfc_dev_dbg(&dev->interface->dev, "%s mod len %d\n",
-                   __func__, mod->len);
+       dev_dbg(&dev->interface->dev, "%s mod len %d\n",
+               __func__, mod->len);
+
+       if (dev->poll_dep)  {
+               dev->poll_dep = 0;
+               return pn533_poll_dep(dev->nfc_dev);
+       }
 
        if (mod->len == 0) {  /* Listen mode */
                cmd_code = PN533_CMD_TG_INIT_AS_TARGET;
@@ -1868,7 +2079,7 @@ static int pn533_send_poll_frame(struct pn533 *dev)
        }
 
        if (!skb) {
-               nfc_dev_err(&dev->interface->dev, "Failed to allocate skb.");
+               nfc_err(&dev->interface->dev, "Failed to allocate skb\n");
                return -ENOMEM;
        }
 
@@ -1876,7 +2087,7 @@ static int pn533_send_poll_frame(struct pn533 *dev)
                                  NULL);
        if (rc < 0) {
                dev_kfree_skb(skb);
-               nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc);
+               nfc_err(&dev->interface->dev, "Polling loop error %d\n", rc);
        }
 
        return rc;
@@ -1890,9 +2101,9 @@ static void pn533_wq_poll(struct work_struct *work)
 
        cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
 
-       nfc_dev_dbg(&dev->interface->dev,
-                   "%s cancel_listen %d modulation len %d",
-                   __func__, dev->cancel_listen, cur_mod->len);
+       dev_dbg(&dev->interface->dev,
+               "%s cancel_listen %d modulation len %d\n",
+               __func__, dev->cancel_listen, cur_mod->len);
 
        if (dev->cancel_listen == 1) {
                dev->cancel_listen = 0;
@@ -1913,21 +2124,23 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
                            u32 im_protocols, u32 tm_protocols)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       struct pn533_poll_modulations *cur_mod;
        u8 rand_mod;
+       int rc;
 
-       nfc_dev_dbg(&dev->interface->dev,
-                   "%s: im protocols 0x%x tm protocols 0x%x",
-                   __func__, im_protocols, tm_protocols);
+       dev_dbg(&dev->interface->dev,
+               "%s: im protocols 0x%x tm protocols 0x%x\n",
+               __func__, im_protocols, tm_protocols);
 
        if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Cannot poll with a target already activated");
+               nfc_err(&dev->interface->dev,
+                       "Cannot poll with a target already activated\n");
                return -EBUSY;
        }
 
        if (dev->tgt_mode) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Cannot poll while already being activated");
+               nfc_err(&dev->interface->dev,
+                       "Cannot poll while already being activated\n");
                return -EBUSY;
        }
 
@@ -1946,20 +2159,26 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
        rand_mod %= dev->poll_mod_count;
        dev->poll_mod_curr = rand_mod;
 
-       return pn533_send_poll_frame(dev);
+       cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+       rc = pn533_send_poll_frame(dev);
+
+       /* Start listen timer */
+       if (!rc && cur_mod->len == 0 && dev->poll_mod_count > 1)
+               mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ);
+
+       return rc;
 }
 
 static void pn533_stop_poll(struct nfc_dev *nfc_dev)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
-
        del_timer(&dev->listen_timer);
 
        if (!dev->poll_mod_count) {
-               nfc_dev_dbg(&dev->interface->dev,
-                           "Polling operation was not running");
+               dev_dbg(&dev->interface->dev,
+                       "Polling operation was not running\n");
                return;
        }
 
@@ -1973,11 +2192,10 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev)
        struct pn533_cmd_activate_response *rsp;
        u16 gt_len;
        int rc;
-
        struct sk_buff *skb;
        struct sk_buff *resp;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/
        if (!skb)
@@ -1993,8 +2211,8 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev)
        rsp = (struct pn533_cmd_activate_response *)resp->data;
        rc = rsp->status & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Target activation failed (error 0x%x)", rc);
+               nfc_err(&dev->interface->dev,
+                       "Target activation failed (error 0x%x)\n", rc);
                dev_kfree_skb(resp);
                return -EIO;
        }
@@ -2013,39 +2231,38 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev,
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s - protocol=%u", __func__,
-                   protocol);
+       dev_dbg(&dev->interface->dev, "%s: protocol=%u\n", __func__, protocol);
 
        if (dev->poll_mod_count) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Cannot activate while polling");
+               nfc_err(&dev->interface->dev,
+                       "Cannot activate while polling\n");
                return -EBUSY;
        }
 
        if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev,
-                           "There is already an active target");
+               nfc_err(&dev->interface->dev,
+                       "There is already an active target\n");
                return -EBUSY;
        }
 
        if (!dev->tgt_available_prots) {
-               nfc_dev_err(&dev->interface->dev,
-                           "There is no available target to activate");
+               nfc_err(&dev->interface->dev,
+                       "There is no available target to activate\n");
                return -EINVAL;
        }
 
        if (!(dev->tgt_available_prots & (1 << protocol))) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Target doesn't support requested proto %u",
-                           protocol);
+               nfc_err(&dev->interface->dev,
+                       "Target doesn't support requested proto %u\n",
+                       protocol);
                return -EINVAL;
        }
 
        if (protocol == NFC_PROTO_NFC_DEP) {
                rc = pn533_activate_target_nfcdep(dev);
                if (rc) {
-                       nfc_dev_err(&dev->interface->dev,
-                                   "Activating target with DEP failed %d", rc);
+                       nfc_err(&dev->interface->dev,
+                               "Activating target with DEP failed %d\n", rc);
                        return rc;
                }
        }
@@ -2060,16 +2277,14 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
                                    struct nfc_target *target)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-
        struct sk_buff *skb;
        struct sk_buff *resp;
-
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (!dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev, "There is no active target");
+               nfc_err(&dev->interface->dev, "There is no active target\n");
                return;
        }
 
@@ -2088,8 +2303,8 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
 
        rc = resp->data[0] & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS)
-               nfc_dev_err(&dev->interface->dev,
-                           "Error 0x%x when releasing the target", rc);
+               nfc_err(&dev->interface->dev,
+                       "Error 0x%x when releasing the target\n", rc);
 
        dev_kfree_skb(resp);
        return;
@@ -2111,8 +2326,8 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
 
        if (dev->tgt_available_prots &&
            !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) {
-               nfc_dev_err(&dev->interface->dev,
-                           "The target does not support DEP");
+               nfc_err(&dev->interface->dev,
+                       "The target does not support DEP\n");
                rc =  -EINVAL;
                goto error;
        }
@@ -2121,15 +2336,15 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
 
        rc = rsp->status & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Bringing DEP link up failed (error 0x%x)", rc);
+               nfc_err(&dev->interface->dev,
+                       "Bringing DEP link up failed (error 0x%x)\n", rc);
                goto error;
        }
 
        if (!dev->tgt_available_prots) {
                struct nfc_target nfc_target;
 
-               nfc_dev_dbg(&dev->interface->dev, "Creating new target");
+               dev_dbg(&dev->interface->dev, "Creating new target\n");
 
                nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
                nfc_target.nfcid1_len = 10;
@@ -2158,7 +2373,6 @@ error:
 }
 
 static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf);
-#define PASSIVE_DATA_LEN 5
 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                             u8 comm_mode, u8 *gb, size_t gb_len)
 {
@@ -2166,20 +2380,19 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
        struct sk_buff *skb;
        int rc, skb_len;
        u8 *next, *arg, nfcid3[NFC_NFCID3_MAXSIZE];
-
        u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (dev->poll_mod_count) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Cannot bring the DEP link up while polling");
+               nfc_err(&dev->interface->dev,
+                       "Cannot bring the DEP link up while polling\n");
                return -EBUSY;
        }
 
        if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev,
-                           "There is already an active target");
+               nfc_err(&dev->interface->dev,
+                       "There is already an active target\n");
                return -EBUSY;
        }
 
@@ -2249,7 +2462,7 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        pn533_poll_reset_mod_list(dev);
 
@@ -2274,7 +2487,7 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev)
        struct sk_buff *skb, *tmp, *t;
        unsigned int skb_len = 0, tmp_len = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (skb_queue_empty(&dev->resp_q))
                return NULL;
@@ -2287,8 +2500,8 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev)
        skb_queue_walk_safe(&dev->resp_q, tmp, t)
                skb_len += tmp->len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s total length %d\n",
-                   __func__, skb_len);
+       dev_dbg(&dev->interface->dev, "%s total length %d\n",
+               __func__, skb_len);
 
        skb = alloc_skb(skb_len, GFP_KERNEL);
        if (skb == NULL)
@@ -2315,7 +2528,7 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
        int rc = 0;
        u8 status, ret, mi;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
@@ -2329,8 +2542,8 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
        skb_pull(resp, sizeof(status));
 
        if (ret != PN533_CMD_RET_SUCCESS) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Exchanging data failed (error 0x%x)", ret);
+               nfc_err(&dev->interface->dev,
+                       "Exchanging data failed (error 0x%x)\n", ret);
                rc = -EIO;
                goto error;
        }
@@ -2388,14 +2601,17 @@ static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb)
                        break;
                }
 
-               /* Reserve the TG/MI byte */
-               skb_reserve(frag, 1);
+               if (!dev->tgt_mode) {
+                       /* Reserve the TG/MI byte */
+                       skb_reserve(frag, 1);
 
-               /* MI + TG */
-               if (frag_size  == PN533_CMD_DATAFRAME_MAXLEN)
-                       *skb_push(frag, sizeof(u8)) = (PN533_CMD_MI_MASK | 1);
-               else
-                       *skb_push(frag, sizeof(u8)) =  1; /* TG */
+                       /* MI + TG */
+                       if (frag_size  == PN533_CMD_DATAFRAME_MAXLEN)
+                               *skb_push(frag, sizeof(u8)) =
+                                                       (PN533_CMD_MI_MASK | 1);
+                       else
+                               *skb_push(frag, sizeof(u8)) =  1; /* TG */
+               }
 
                memcpy(skb_put(frag, frag_size), skb->data, frag_size);
 
@@ -2420,11 +2636,11 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
        struct pn533_data_exchange_arg *arg = NULL;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (!dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Can't exchange data if there is no active target");
+               nfc_err(&dev->interface->dev,
+                       "Can't exchange data if there is no active target\n");
                rc = -EINVAL;
                goto error;
        }
@@ -2487,13 +2703,18 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
 {
        u8 status;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (IS_ERR(resp))
                return PTR_ERR(resp);
 
        status = resp->data[0];
 
+       /* Prepare for the next round */
+       if (skb_queue_len(&dev->fragment_skb) > 0) {
+               queue_work(dev->wq, &dev->mi_tm_tx_work);
+               return -EINPROGRESS;
+       }
        dev_kfree_skb(resp);
 
        if (status != 0) {
@@ -2514,19 +2735,34 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
+       /* let's split in multiple chunks if size's too big */
        if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Data length greater than the max allowed: %d",
-                           PN533_CMD_DATAEXCH_DATA_MAXLEN);
-               return -ENOSYS;
+               rc = pn533_fill_fragment_skbs(dev, skb);
+               if (rc <= 0)
+                       goto error;
+
+               /* get the first skb */
+               skb = skb_dequeue(&dev->fragment_skb);
+               if (!skb) {
+                       rc = -EIO;
+                       goto error;
+               }
+
+               rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_META_DATA, skb,
+                                               pn533_tm_send_complete, NULL);
+       } else {
+               /* Send th skb */
+               rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
+                                               pn533_tm_send_complete, NULL);
        }
 
-       rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
-                                  pn533_tm_send_complete, NULL);
-       if (rc < 0)
+error:
+       if (rc < 0) {
                dev_kfree_skb(skb);
+               skb_queue_purge(&dev->fragment_skb);
+       }
 
        return rc;
 }
@@ -2534,11 +2770,10 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 static void pn533_wq_mi_recv(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, mi_rx_work);
-
        struct sk_buff *skb;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, PN533_CMD_DATAEXCH_HEAD_LEN);
        if (!skb)
@@ -2570,8 +2805,8 @@ static void pn533_wq_mi_recv(struct work_struct *work)
        if (rc == 0) /* success */
                return;
 
-       nfc_dev_err(&dev->interface->dev,
-                   "Error %d when trying to perform data_exchange", rc);
+       nfc_err(&dev->interface->dev,
+               "Error %d when trying to perform data_exchange\n", rc);
 
        dev_kfree_skb(skb);
        kfree(dev->cmd_complete_mi_arg);
@@ -2587,7 +2822,7 @@ static void pn533_wq_mi_send(struct work_struct *work)
        struct sk_buff *skb;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        /* Grab the first skb in the queue */
        skb = skb_dequeue(&dev->fragment_skb);
@@ -2625,8 +2860,8 @@ static void pn533_wq_mi_send(struct work_struct *work)
        if (rc == 0) /* success */
                return;
 
-       nfc_dev_err(&dev->interface->dev,
-                   "Error %d when trying to perform data_exchange", rc);
+       nfc_err(&dev->interface->dev,
+               "Error %d when trying to perform data_exchange\n", rc);
 
        dev_kfree_skb(skb);
        kfree(dev->cmd_complete_dep_arg);
@@ -2641,10 +2876,9 @@ static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
 {
        struct sk_buff *skb;
        struct sk_buff *resp;
-
        int skb_len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */
 
@@ -2691,7 +2925,7 @@ static int pn533_pasori_fw_reset(struct pn533 *dev)
        struct sk_buff *skb;
        struct sk_buff *resp;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, sizeof(u8));
        if (!skb)
@@ -2717,7 +2951,7 @@ static void pn533_acr122_poweron_rdr_resp(struct urb *urb)
 {
        struct pn533_acr122_poweron_rdr_arg *arg = urb->context;
 
-       nfc_dev_dbg(&urb->dev->dev, "%s", __func__);
+       dev_dbg(&urb->dev->dev, "%s\n", __func__);
 
        print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
                       urb->transfer_buffer, urb->transfer_buffer_length,
@@ -2737,7 +2971,7 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev)
        void *cntx;
        struct pn533_acr122_poweron_rdr_arg arg;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        init_completion(&arg.done);
        cntx = dev->in_urb->context;  /* backup context */
@@ -2755,16 +2989,15 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev)
 
        rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Reader power on cmd error %d", rc);
+               nfc_err(&dev->interface->dev,
+                       "Reader power on cmd error %d\n", rc);
                return rc;
        }
 
        rc =  usb_submit_urb(dev->in_urb, GFP_KERNEL);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Can't submit for reader power on cmd response %d",
-                           rc);
+               nfc_err(&dev->interface->dev,
+                       "Can't submit reader poweron cmd response %d\n", rc);
                return rc;
        }
 
@@ -2785,20 +3018,19 @@ static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf)
        rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD,
                                     (u8 *)&rf_field, 1);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error on setting RF field");
+               nfc_err(&dev->interface->dev, "Error on setting RF field\n");
                return rc;
        }
 
        return rc;
 }
 
-int pn533_dev_up(struct nfc_dev *nfc_dev)
+static int pn533_dev_up(struct nfc_dev *nfc_dev)
 {
        return pn533_rf_field(nfc_dev, 1);
 }
 
-int pn533_dev_down(struct nfc_dev *nfc_dev)
+static int pn533_dev_down(struct nfc_dev *nfc_dev)
 {
        return pn533_rf_field(nfc_dev, 0);
 }
@@ -2839,16 +3071,16 @@ static int pn533_setup(struct pn533 *dev)
                break;
 
        default:
-               nfc_dev_err(&dev->interface->dev, "Unknown device type %d\n",
-                           dev->device_type);
+               nfc_err(&dev->interface->dev, "Unknown device type %d\n",
+                       dev->device_type);
                return -EINVAL;
        }
 
        rc = pn533_set_configuration(dev, PN533_CFGITEM_MAX_RETRIES,
                                     (u8 *)&max_retries, sizeof(max_retries));
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error on setting MAX_RETRIES config");
+               nfc_err(&dev->interface->dev,
+                       "Error on setting MAX_RETRIES config\n");
                return rc;
        }
 
@@ -2856,8 +3088,7 @@ static int pn533_setup(struct pn533 *dev)
        rc = pn533_set_configuration(dev, PN533_CFGITEM_TIMING,
                                     (u8 *)&timing, sizeof(timing));
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error on setting RF timings");
+               nfc_err(&dev->interface->dev, "Error on setting RF timings\n");
                return rc;
        }
 
@@ -2871,8 +3102,8 @@ static int pn533_setup(struct pn533 *dev)
                rc = pn533_set_configuration(dev, PN533_CFGITEM_PASORI,
                                             pasori_cfg, 3);
                if (rc) {
-                       nfc_dev_err(&dev->interface->dev,
-                                   "Error while settings PASORI config");
+                       nfc_err(&dev->interface->dev,
+                               "Error while settings PASORI config\n");
                        return rc;
                }
 
@@ -2917,8 +3148,8 @@ static int pn533_probe(struct usb_interface *interface,
        }
 
        if (!in_endpoint || !out_endpoint) {
-               nfc_dev_err(&interface->dev,
-                           "Could not find bulk-in or bulk-out endpoint");
+               nfc_err(&interface->dev,
+                       "Could not find bulk-in or bulk-out endpoint\n");
                rc = -ENODEV;
                goto error;
        }
@@ -2941,6 +3172,8 @@ static int pn533_probe(struct usb_interface *interface,
        INIT_WORK(&dev->mi_rx_work, pn533_wq_mi_recv);
        INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send);
        INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
+       INIT_WORK(&dev->mi_tm_rx_work, pn533_wq_tm_mi_recv);
+       INIT_WORK(&dev->mi_tm_tx_work, pn533_wq_tm_mi_send);
        INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll);
        INIT_WORK(&dev->rf_work, pn533_wq_rf);
        dev->wq = alloc_ordered_workqueue("pn533", 0);
@@ -2978,16 +3211,15 @@ static int pn533_probe(struct usb_interface *interface,
 
                rc = pn533_acr122_poweron_rdr(dev);
                if (rc < 0) {
-                       nfc_dev_err(&dev->interface->dev,
-                                   "Couldn't poweron the reader (error %d)",
-                                   rc);
+                       nfc_err(&dev->interface->dev,
+                               "Couldn't poweron the reader (error %d)\n", rc);
                        goto destroy_wq;
                }
                break;
 
        default:
-               nfc_dev_err(&dev->interface->dev, "Unknown device type %d\n",
-                           dev->device_type);
+               nfc_err(&dev->interface->dev, "Unknown device type %d\n",
+                       dev->device_type);
                rc = -EINVAL;
                goto destroy_wq;
        }
@@ -2997,9 +3229,9 @@ static int pn533_probe(struct usb_interface *interface,
        if (rc < 0)
                goto destroy_wq;
 
-       nfc_dev_info(&dev->interface->dev,
-                    "NXP PN5%02X firmware ver %d.%d now attached",
-                    fw_ver.ic, fw_ver.ver, fw_ver.rev);
+       nfc_info(&dev->interface->dev,
+                "NXP PN5%02X firmware ver %d.%d now attached\n",
+                fw_ver.ic, fw_ver.ver, fw_ver.rev);
 
 
        dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
@@ -3070,7 +3302,7 @@ static void pn533_disconnect(struct usb_interface *interface)
        usb_free_urb(dev->out_urb);
        kfree(dev);
 
-       nfc_dev_info(&interface->dev, "NXP PN533 NFC device disconnected");
+       nfc_info(&interface->dev, "NXP PN533 NFC device disconnected\n");
 }
 
 static struct usb_driver pn533_driver = {
index 01e27d4bdd0d7abcfdc4d91c7f6963974f3b4e48..b158ee1c2ac69fc8ec0ff3fc667df10e5eb3f70e 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/crc-ccitt.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
@@ -151,8 +153,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
        char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 };
        int count = sizeof(rset_cmd);
 
-       pr_info(DRIVER_DESC ": %s\n", __func__);
-       dev_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
+       nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
 
        /* Disable fw download */
        gpio_set_value(phy->gpio_fw, 0);
@@ -173,7 +174,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
                        dev_dbg(&phy->i2c_dev->dev, "Sending reset cmd\n");
                        ret = i2c_master_send(phy->i2c_dev, rset_cmd, count);
                        if (ret == count) {
-                               dev_info(&phy->i2c_dev->dev,
+                               nfc_info(&phy->i2c_dev->dev,
                                         "nfc_en polarity : active %s\n",
                                         (polarity == 0 ? "low" : "high"));
                                goto out;
@@ -181,7 +182,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
                }
        }
 
-       dev_err(&phy->i2c_dev->dev,
+       nfc_err(&phy->i2c_dev->dev,
                "Could not detect nfc_en polarity, fallback to active high\n");
 
 out:
@@ -201,7 +202,7 @@ static int pn544_hci_i2c_enable(void *phy_id)
 {
        struct pn544_i2c_phy *phy = phy_id;
 
-       pr_info(DRIVER_DESC ": %s\n", __func__);
+       pr_info("%s\n", __func__);
 
        pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE);
 
@@ -214,8 +215,6 @@ static void pn544_hci_i2c_disable(void *phy_id)
 {
        struct pn544_i2c_phy *phy = phy_id;
 
-       pr_info(DRIVER_DESC ": %s\n", __func__);
-
        gpio_set_value(phy->gpio_fw, 0);
        gpio_set_value(phy->gpio_en, !phy->en_polarity);
        usleep_range(10000, 15000);
@@ -298,11 +297,9 @@ static int check_crc(u8 *buf, int buflen)
        crc = ~crc;
 
        if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) {
-               pr_err(PN544_HCI_I2C_DRIVER_NAME
-                      ": CRC error 0x%x != 0x%x 0x%x\n",
+               pr_err("CRC error 0x%x != 0x%x 0x%x\n",
                       crc, buf[len - 1], buf[len - 2]);
-
-               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
+               pr_info("%s: BAD CRC\n", __func__);
                print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
                               16, 2, buf, buflen, false);
                return -EPERM;
@@ -328,13 +325,13 @@ static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb)
 
        r = i2c_master_recv(client, &len, 1);
        if (r != 1) {
-               dev_err(&client->dev, "cannot read len byte\n");
+               nfc_err(&client->dev, "cannot read len byte\n");
                return -EREMOTEIO;
        }
 
        if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) ||
            (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) {
-               dev_err(&client->dev, "invalid len byte\n");
+               nfc_err(&client->dev, "invalid len byte\n");
                r = -EBADMSG;
                goto flush;
        }
@@ -386,7 +383,7 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
 
        r = i2c_master_recv(client, (char *) &response, sizeof(response));
        if (r != sizeof(response)) {
-               dev_err(&client->dev, "cannot read fw status\n");
+               nfc_err(&client->dev, "cannot read fw status\n");
                return -EIO;
        }
 
@@ -478,8 +475,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
 {
        struct pn544_i2c_phy *phy = phy_id;
 
-       pr_info(DRIVER_DESC ": Starting Firmware Download (%s)\n",
-               firmware_name);
+       pr_info("Starting Firmware Download (%s)\n", firmware_name);
 
        strcpy(phy->firmware_name, firmware_name);
 
@@ -493,7 +489,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
 static void pn544_hci_i2c_fw_work_complete(struct pn544_i2c_phy *phy,
                                           int result)
 {
-       pr_info(DRIVER_DESC ": Firmware Download Complete, result=%d\n", result);
+       pr_info("Firmware Download Complete, result=%d\n", result);
 
        pn544_hci_i2c_disable(phy);
 
@@ -694,14 +690,14 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
        dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
 
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-               dev_err(&client->dev, "Need I2C_FUNC_I2C\n");
+               nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
                return -ENODEV;
        }
 
        phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
                           GFP_KERNEL);
        if (!phy) {
-               dev_err(&client->dev,
+               nfc_err(&client->dev,
                        "Cannot allocate memory for pn544 i2c phy.\n");
                return -ENOMEM;
        }
@@ -714,18 +710,18 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
 
        pdata = client->dev.platform_data;
        if (pdata == NULL) {
-               dev_err(&client->dev, "No platform data\n");
+               nfc_err(&client->dev, "No platform data\n");
                return -EINVAL;
        }
 
        if (pdata->request_resources == NULL) {
-               dev_err(&client->dev, "request_resources() missing\n");
+               nfc_err(&client->dev, "request_resources() missing\n");
                return -EINVAL;
        }
 
        r = pdata->request_resources(client);
        if (r) {
-               dev_err(&client->dev, "Cannot get platform resources\n");
+               nfc_err(&client->dev, "Cannot get platform resources\n");
                return r;
        }
 
@@ -739,7 +735,7 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
                                 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
                                 PN544_HCI_I2C_DRIVER_NAME, phy);
        if (r < 0) {
-               dev_err(&client->dev, "Unable to register IRQ handler\n");
+               nfc_err(&client->dev, "Unable to register IRQ handler\n");
                goto err_rti;
        }
 
index 078e62feba1715e9c4cc3bfd246e6c947082735b..74cfa0a88b9e7c0ae6cdedd66d77deedd15e9872 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -41,6 +43,7 @@ enum pn544_state {
 
 /* Proprietary commands */
 #define PN544_WRITE            0x3f
+#define PN544_TEST_SWP         0x21
 
 /* Proprietary gates, events, commands and registers */
 
@@ -81,14 +84,17 @@ enum pn544_state {
 #define PN544_PL_NFCT_DEACTIVATED              0x09
 
 #define PN544_SWP_MGMT_GATE                    0xA0
+#define PN544_SWP_DEFAULT_MODE                 0x01
 
 #define PN544_NFC_WI_MGMT_GATE                 0xA1
+#define PN544_NFC_ESE_DEFAULT_MODE             0x01
 
 #define PN544_HCI_EVT_SND_DATA                 0x01
 #define PN544_HCI_EVT_ACTIVATED                        0x02
 #define PN544_HCI_EVT_DEACTIVATED              0x03
 #define PN544_HCI_EVT_RCV_DATA                 0x04
 #define PN544_HCI_EVT_CONTINUE_MI              0x05
+#define PN544_HCI_EVT_SWITCH_MODE              0x03
 
 #define PN544_HCI_CMD_ATTREQUEST               0x12
 #define PN544_HCI_CMD_CONTINUE_ACTIVATION      0x13
@@ -187,13 +193,6 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev)
 
                {{0x9e, 0xb4}, 0x00},
 
-               {{0x9e, 0xd9}, 0xff},
-               {{0x9e, 0xda}, 0xff},
-               {{0x9e, 0xdb}, 0x23},
-               {{0x9e, 0xdc}, 0x21},
-               {{0x9e, 0xdd}, 0x22},
-               {{0x9e, 0xde}, 0x24},
-
                {{0x9c, 0x01}, 0x08},
 
                {{0x9e, 0xaa}, 0x01},
@@ -394,7 +393,7 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
        if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
                hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
                                                        &hdev->gb_len);
-               pr_debug("generate local bytes %p", hdev->gb);
+               pr_debug("generate local bytes %p\n", hdev->gb);
                if (hdev->gb == NULL || hdev->gb_len == 0) {
                        im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
                        tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
@@ -696,7 +695,7 @@ static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
                                   struct nfc_target *target)
 {
-       pr_debug("supported protocol %d", target->supported_protocols);
+       pr_debug("supported protocol %d\b", target->supported_protocols);
        if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK |
                                        NFC_PROTO_ISO14443_B_MASK)) {
                return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
@@ -733,7 +732,7 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event,
        struct sk_buff *rgb_skb = NULL;
        int r;
 
-       pr_debug("hci event %d", event);
+       pr_debug("hci event %d\n", event);
        switch (event) {
        case PN544_HCI_EVT_ACTIVATED:
                if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) {
@@ -764,7 +763,7 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event,
                }
 
                if (skb->data[0] != 0) {
-                       pr_debug("data0 %d", skb->data[0]);
+                       pr_debug("data0 %d\n", skb->data[0]);
                        r = -EPROTO;
                        goto exit;
                }
@@ -792,6 +791,108 @@ static int pn544_hci_fw_download(struct nfc_hci_dev *hdev,
        return info->fw_download(info->phy_id, firmware_name);
 }
 
+static int pn544_hci_discover_se(struct nfc_hci_dev *hdev)
+{
+       u32 se_idx = 0;
+       u8 ese_mode = 0x01; /* Default mode */
+       struct sk_buff *res_skb;
+       int r;
+
+       r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_TEST_SWP,
+                            NULL, 0, &res_skb);
+
+       if (r == 0) {
+               if (res_skb->len == 2 && res_skb->data[0] == 0x00)
+                       nfc_add_se(hdev->ndev, se_idx++, NFC_SE_UICC);
+
+               kfree_skb(res_skb);
+       }
+
+       r = nfc_hci_send_event(hdev, PN544_NFC_WI_MGMT_GATE,
+                               PN544_HCI_EVT_SWITCH_MODE,
+                               &ese_mode, 1);
+       if (r == 0)
+               nfc_add_se(hdev->ndev, se_idx++, NFC_SE_EMBEDDED);
+
+       return !se_idx;
+}
+
+#define PN544_SE_MODE_OFF      0x00
+#define PN544_SE_MODE_ON       0x01
+static int pn544_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+       struct nfc_se *se;
+       u8 enable = PN544_SE_MODE_ON;
+       static struct uicc_gatelist {
+               u8 head;
+               u8 adr[2];
+               u8 value;
+       } uicc_gatelist[] = {
+               {0x00, {0x9e, 0xd9}, 0x23},
+               {0x00, {0x9e, 0xda}, 0x21},
+               {0x00, {0x9e, 0xdb}, 0x22},
+               {0x00, {0x9e, 0xdc}, 0x24},
+       };
+       struct uicc_gatelist *p = uicc_gatelist;
+       int count = ARRAY_SIZE(uicc_gatelist);
+       struct sk_buff *res_skb;
+       int r;
+
+       se = nfc_find_se(hdev->ndev, se_idx);
+
+       switch (se->type) {
+       case NFC_SE_UICC:
+               while (count--) {
+                       r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE,
+                                       PN544_WRITE, (u8 *)p, 4, &res_skb);
+                       if (r < 0)
+                               return r;
+
+                       if (res_skb->len != 1) {
+                               kfree_skb(res_skb);
+                               return -EPROTO;
+                       }
+
+                       if (res_skb->data[0] != p->value) {
+                               kfree_skb(res_skb);
+                               return -EIO;
+                       }
+
+                       kfree_skb(res_skb);
+
+                       p++;
+               }
+
+               return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE,
+                             PN544_SWP_DEFAULT_MODE, &enable, 1);
+       case NFC_SE_EMBEDDED:
+               return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE,
+                             PN544_NFC_ESE_DEFAULT_MODE, &enable, 1);
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int pn544_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+       struct nfc_se *se;
+       u8 disable = PN544_SE_MODE_OFF;
+
+       se = nfc_find_se(hdev->ndev, se_idx);
+
+       switch (se->type) {
+       case NFC_SE_UICC:
+               return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE,
+                             PN544_SWP_DEFAULT_MODE, &disable, 1);
+       case NFC_SE_EMBEDDED:
+               return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE,
+                             PN544_NFC_ESE_DEFAULT_MODE, &disable, 1);
+       default:
+               return -EINVAL;
+       }
+}
+
 static struct nfc_hci_ops pn544_hci_ops = {
        .open = pn544_hci_open,
        .close = pn544_hci_close,
@@ -807,6 +908,9 @@ static struct nfc_hci_ops pn544_hci_ops = {
        .check_presence = pn544_hci_check_presence,
        .event_received = pn544_hci_event_received,
        .fw_download = pn544_hci_fw_download,
+       .discover_se = pn544_hci_discover_se,
+       .enable_se = pn544_hci_enable_se,
+       .disable_se = pn544_hci_disable_se,
 };
 
 int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
@@ -820,7 +924,6 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
 
        info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL);
        if (!info) {
-               pr_err("Cannot allocate memory for pn544_hci_info.\n");
                r = -ENOMEM;
                goto err_info_alloc;
        }
@@ -853,7 +956,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                                             phy_headroom + PN544_CMDS_HEADROOM,
                                             phy_tailroom, phy_payload);
        if (!info->hdev) {
-               pr_err("Cannot allocate nfc hdev.\n");
+               pr_err("Cannot allocate nfc hdev\n");
                r = -ENOMEM;
                goto err_alloc_hdev;
        }
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
new file mode 100644 (file)
index 0000000..8a0571e
--- /dev/null
@@ -0,0 +1,1529 @@
+/*
+ * Sony NFC Port-100 Series driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * Partly based/Inspired by Stephen Tiedemann's nfcpy
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <net/nfc/digital.h>
+
+#define VERSION "0.1"
+
+#define SONY_VENDOR_ID    0x054c
+#define RCS380_PRODUCT_ID 0x06c1
+
+#define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK    | \
+                          NFC_PROTO_MIFARE_MASK   | \
+                          NFC_PROTO_FELICA_MASK   | \
+                          NFC_PROTO_NFC_DEP_MASK)
+
+#define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \
+                             NFC_DIGITAL_DRV_CAPS_TG_CRC)
+
+/* Standard port100 frame definitions */
+#define PORT100_FRAME_HEADER_LEN (sizeof(struct port100_frame) \
+                                 + 2) /* data[0] CC, data[1] SCC */
+#define PORT100_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
+
+#define PORT100_COMM_RF_HEAD_MAX_LEN (sizeof(struct port100_tg_comm_rf_cmd))
+
+/*
+ * Max extended frame payload len, excluding CC and SCC
+ * which are already in PORT100_FRAME_HEADER_LEN.
+ */
+#define PORT100_FRAME_MAX_PAYLOAD_LEN 1001
+
+#define PORT100_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2),
+                                   Postamble (1) */
+static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = {
+       0x00, 0x00, 0xff, 0x00, 0xff, 0x00
+};
+
+#define PORT100_FRAME_CHECKSUM(f) (f->data[le16_to_cpu(f->datalen)])
+#define PORT100_FRAME_POSTAMBLE(f) (f->data[le16_to_cpu(f->datalen) + 1])
+
+/* start of frame */
+#define PORT100_FRAME_SOF      0x00FF
+#define PORT100_FRAME_EXT      0xFFFF
+#define PORT100_FRAME_ACK      0x00FF
+
+/* Port-100 command: in or out */
+#define PORT100_FRAME_DIRECTION(f) (f->data[0]) /* CC */
+#define PORT100_FRAME_DIR_OUT 0xD6
+#define PORT100_FRAME_DIR_IN  0xD7
+
+/* Port-100 sub-command */
+#define PORT100_FRAME_CMD(f) (f->data[1]) /* SCC */
+
+#define PORT100_CMD_GET_FIRMWARE_VERSION 0x20
+#define PORT100_CMD_GET_COMMAND_TYPE     0x28
+#define PORT100_CMD_SET_COMMAND_TYPE     0x2A
+
+#define PORT100_CMD_IN_SET_RF       0x00
+#define PORT100_CMD_IN_SET_PROTOCOL 0x02
+#define PORT100_CMD_IN_COMM_RF      0x04
+
+#define PORT100_CMD_TG_SET_RF       0x40
+#define PORT100_CMD_TG_SET_PROTOCOL 0x42
+#define PORT100_CMD_TG_SET_RF_OFF   0x46
+#define PORT100_CMD_TG_COMM_RF      0x48
+
+#define PORT100_CMD_SWITCH_RF       0x06
+
+#define PORT100_CMD_RESPONSE(cmd) (cmd + 1)
+
+#define PORT100_CMD_TYPE_IS_SUPPORTED(mask, cmd_type) \
+       ((mask) & (0x01 << (cmd_type)))
+#define PORT100_CMD_TYPE_0     0
+#define PORT100_CMD_TYPE_1     1
+
+#define PORT100_CMD_STATUS_OK      0x00
+#define PORT100_CMD_STATUS_TIMEOUT 0x80
+
+#define PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK 0x01
+#define PORT100_MDAA_TGT_WAS_ACTIVATED_MASK      0x02
+
+struct port100;
+
+typedef void (*port100_send_async_complete_t)(struct port100 *dev, void *arg,
+                                             struct sk_buff *resp);
+
+/**
+ * Setting sets structure for in_set_rf command
+ *
+ * @in_*_set_number: Represent the entry indexes in the port-100 RF Base Table.
+ *              This table contains multiple RF setting sets required for RF
+ *              communication.
+ *
+ * @in_*_comm_type: Theses fields set the communication type to be used.
+ */
+struct port100_in_rf_setting {
+       u8 in_send_set_number;
+       u8 in_send_comm_type;
+       u8 in_recv_set_number;
+       u8 in_recv_comm_type;
+} __packed;
+
+#define PORT100_COMM_TYPE_IN_212F 0x01
+#define PORT100_COMM_TYPE_IN_424F 0x02
+#define PORT100_COMM_TYPE_IN_106A 0x03
+
+static const struct port100_in_rf_setting in_rf_settings[] = {
+       [NFC_DIGITAL_RF_TECH_212F] = {
+               .in_send_set_number = 1,
+               .in_send_comm_type  = PORT100_COMM_TYPE_IN_212F,
+               .in_recv_set_number = 15,
+               .in_recv_comm_type  = PORT100_COMM_TYPE_IN_212F,
+       },
+       [NFC_DIGITAL_RF_TECH_424F] = {
+               .in_send_set_number = 1,
+               .in_send_comm_type  = PORT100_COMM_TYPE_IN_424F,
+               .in_recv_set_number = 15,
+               .in_recv_comm_type  = PORT100_COMM_TYPE_IN_424F,
+       },
+       [NFC_DIGITAL_RF_TECH_106A] = {
+               .in_send_set_number = 2,
+               .in_send_comm_type  = PORT100_COMM_TYPE_IN_106A,
+               .in_recv_set_number = 15,
+               .in_recv_comm_type  = PORT100_COMM_TYPE_IN_106A,
+       },
+};
+
+/**
+ * Setting sets structure for tg_set_rf command
+ *
+ * @tg_set_number: Represents the entry index in the port-100 RF Base Table.
+ *                 This table contains multiple RF setting sets required for RF
+ *                 communication. this field is used for both send and receive
+ *                 settings.
+ *
+ * @tg_comm_type: Sets the communication type to be used to send and receive
+ *                data.
+ */
+struct port100_tg_rf_setting {
+       u8 tg_set_number;
+       u8 tg_comm_type;
+} __packed;
+
+#define PORT100_COMM_TYPE_TG_106A 0x0B
+#define PORT100_COMM_TYPE_TG_212F 0x0C
+#define PORT100_COMM_TYPE_TG_424F 0x0D
+
+static const struct port100_tg_rf_setting tg_rf_settings[] = {
+       [NFC_DIGITAL_RF_TECH_106A] = {
+               .tg_set_number = 8,
+               .tg_comm_type = PORT100_COMM_TYPE_TG_106A,
+       },
+       [NFC_DIGITAL_RF_TECH_212F] = {
+               .tg_set_number = 8,
+               .tg_comm_type = PORT100_COMM_TYPE_TG_212F,
+       },
+       [NFC_DIGITAL_RF_TECH_424F] = {
+               .tg_set_number = 8,
+               .tg_comm_type = PORT100_COMM_TYPE_TG_424F,
+       },
+};
+
+#define PORT100_IN_PROT_INITIAL_GUARD_TIME      0x00
+#define PORT100_IN_PROT_ADD_CRC                 0x01
+#define PORT100_IN_PROT_CHECK_CRC               0x02
+#define PORT100_IN_PROT_MULTI_CARD              0x03
+#define PORT100_IN_PROT_ADD_PARITY              0x04
+#define PORT100_IN_PROT_CHECK_PARITY            0x05
+#define PORT100_IN_PROT_BITWISE_AC_RECV_MODE    0x06
+#define PORT100_IN_PROT_VALID_BIT_NUMBER        0x07
+#define PORT100_IN_PROT_CRYPTO1                 0x08
+#define PORT100_IN_PROT_ADD_SOF                 0x09
+#define PORT100_IN_PROT_CHECK_SOF               0x0A
+#define PORT100_IN_PROT_ADD_EOF                 0x0B
+#define PORT100_IN_PROT_CHECK_EOF               0x0C
+#define PORT100_IN_PROT_DEAF_TIME               0x0E
+#define PORT100_IN_PROT_CRM                     0x0F
+#define PORT100_IN_PROT_CRM_MIN_LEN             0x10
+#define PORT100_IN_PROT_T1_TAG_FRAME            0x11
+#define PORT100_IN_PROT_RFCA                    0x12
+#define PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR 0x13
+#define PORT100_IN_PROT_END                     0x14
+
+#define PORT100_IN_MAX_NUM_PROTOCOLS            19
+
+#define PORT100_TG_PROT_TU           0x00
+#define PORT100_TG_PROT_RF_OFF       0x01
+#define PORT100_TG_PROT_CRM          0x02
+#define PORT100_TG_PROT_END          0x03
+
+#define PORT100_TG_MAX_NUM_PROTOCOLS 3
+
+struct port100_protocol {
+       u8 number;
+       u8 value;
+} __packed;
+
+static struct port100_protocol
+in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = {
+       [NFC_DIGITAL_FRAMING_NFCA_SHORT] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,      6 },
+               { PORT100_IN_PROT_ADD_CRC,                 0 },
+               { PORT100_IN_PROT_CHECK_CRC,               0 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              0 },
+               { PORT100_IN_PROT_CHECK_PARITY,            1 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        7 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,      6 },
+               { PORT100_IN_PROT_ADD_CRC,                 0 },
+               { PORT100_IN_PROT_CHECK_CRC,               0 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              1 },
+               { PORT100_IN_PROT_CHECK_PARITY,            1 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        8 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,      6 },
+               { PORT100_IN_PROT_ADD_CRC,                 1 },
+               { PORT100_IN_PROT_CHECK_CRC,               1 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              1 },
+               { PORT100_IN_PROT_CHECK_PARITY,            1 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        8 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_T1T] = {
+               /* nfc_digital_framing_nfca_short */
+               { PORT100_IN_PROT_ADD_CRC,          2 },
+               { PORT100_IN_PROT_CHECK_CRC,        2 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,     2 },
+               { PORT100_IN_PROT_END,              0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_T2T] = {
+               /* nfc_digital_framing_nfca_standard */
+               { PORT100_IN_PROT_ADD_CRC,   1 },
+               { PORT100_IN_PROT_CHECK_CRC, 0 },
+               { PORT100_IN_PROT_END,       0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = {
+               /* nfc_digital_framing_nfca_standard */
+               { PORT100_IN_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,     18 },
+               { PORT100_IN_PROT_ADD_CRC,                 1 },
+               { PORT100_IN_PROT_CHECK_CRC,               1 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              0 },
+               { PORT100_IN_PROT_CHECK_PARITY,            0 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        8 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF_T3T] = {
+               /* nfc_digital_framing_nfcf */
+               { PORT100_IN_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = {
+               /* nfc_digital_framing_nfcf */
+               { PORT100_IN_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
+               { PORT100_IN_PROT_END, 0 },
+       },
+};
+
+static struct port100_protocol
+tg_protocols[][PORT100_TG_MAX_NUM_PROTOCOLS + 1] = {
+       [NFC_DIGITAL_FRAMING_NFCA_SHORT] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_T1T] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_T2T] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = {
+               { PORT100_TG_PROT_TU,     1 },
+               { PORT100_TG_PROT_RF_OFF, 0 },
+               { PORT100_TG_PROT_CRM,    7 },
+               { PORT100_TG_PROT_END,    0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF_T3T] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = {
+               { PORT100_TG_PROT_TU,     1 },
+               { PORT100_TG_PROT_RF_OFF, 0 },
+               { PORT100_TG_PROT_CRM,    7 },
+               { PORT100_TG_PROT_END,    0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
+               { PORT100_TG_PROT_RF_OFF, 1 },
+               { PORT100_TG_PROT_END,    0 },
+       },
+};
+
+struct port100 {
+       struct nfc_digital_dev *nfc_digital_dev;
+
+       int skb_headroom;
+       int skb_tailroom;
+
+       struct usb_device *udev;
+       struct usb_interface *interface;
+
+       struct urb *out_urb;
+       struct urb *in_urb;
+
+       struct work_struct cmd_complete_work;
+
+       u8 cmd_type;
+
+       /* The digital stack serializes commands to be sent. There is no need
+        * for any queuing/locking mechanism at driver level.
+        */
+       struct port100_cmd *cmd;
+};
+
+struct port100_cmd {
+       u8 code;
+       int status;
+       struct sk_buff *req;
+       struct sk_buff *resp;
+       int resp_len;
+       port100_send_async_complete_t  complete_cb;
+       void *complete_cb_context;
+};
+
+struct port100_frame {
+       u8 preamble;
+       __be16 start_frame;
+       __be16 extended_frame;
+       __le16 datalen;
+       u8 datalen_checksum;
+       u8 data[];
+} __packed;
+
+struct port100_ack_frame {
+       u8 preamble;
+       __be16 start_frame;
+       __be16 ack_frame;
+       u8 postambule;
+} __packed;
+
+struct port100_cb_arg {
+       nfc_digital_cmd_complete_t complete_cb;
+       void *complete_arg;
+       u8 mdaa;
+};
+
+struct port100_tg_comm_rf_cmd {
+       __le16 guard_time;
+       __le16 send_timeout;
+       u8 mdaa;
+       u8 nfca_param[6];
+       u8 nfcf_param[18];
+       u8 mf_halted;
+       u8 arae_flag;
+       __le16 recv_timeout;
+       u8 data[];
+} __packed;
+
+struct port100_tg_comm_rf_res {
+       u8 comm_type;
+       u8 ar_status;
+       u8 target_activated;
+       __le32 status;
+       u8 data[];
+} __packed;
+
+/* The rule: value + checksum = 0 */
+static inline u8 port100_checksum(u16 value)
+{
+       return ~(((u8 *)&value)[0] + ((u8 *)&value)[1]) + 1;
+}
+
+/* The rule: sum(data elements) + checksum = 0 */
+static u8 port100_data_checksum(u8 *data, int datalen)
+{
+       u8 sum = 0;
+       int i;
+
+       for (i = 0; i < datalen; i++)
+               sum += data[i];
+
+       return port100_checksum(sum);
+}
+
+static void port100_tx_frame_init(void *_frame, u8 cmd_code)
+{
+       struct port100_frame *frame = _frame;
+
+       frame->preamble = 0;
+       frame->start_frame = cpu_to_be16(PORT100_FRAME_SOF);
+       frame->extended_frame = cpu_to_be16(PORT100_FRAME_EXT);
+       PORT100_FRAME_DIRECTION(frame) = PORT100_FRAME_DIR_OUT;
+       PORT100_FRAME_CMD(frame) = cmd_code;
+       frame->datalen = cpu_to_le16(2);
+}
+
+static void port100_tx_frame_finish(void *_frame)
+{
+       struct port100_frame *frame = _frame;
+
+       frame->datalen_checksum = port100_checksum(le16_to_cpu(frame->datalen));
+
+       PORT100_FRAME_CHECKSUM(frame) =
+               port100_data_checksum(frame->data, le16_to_cpu(frame->datalen));
+
+       PORT100_FRAME_POSTAMBLE(frame) = 0;
+}
+
+static void port100_tx_update_payload_len(void *_frame, int len)
+{
+       struct port100_frame *frame = _frame;
+
+       frame->datalen = cpu_to_le16(le16_to_cpu(frame->datalen) + len);
+}
+
+static bool port100_rx_frame_is_valid(void *_frame)
+{
+       u8 checksum;
+       struct port100_frame *frame = _frame;
+
+       if (frame->start_frame != cpu_to_be16(PORT100_FRAME_SOF) ||
+           frame->extended_frame != cpu_to_be16(PORT100_FRAME_EXT))
+               return false;
+
+       checksum = port100_checksum(le16_to_cpu(frame->datalen));
+       if (checksum != frame->datalen_checksum)
+               return false;
+
+       checksum = port100_data_checksum(frame->data,
+                                        le16_to_cpu(frame->datalen));
+       if (checksum != PORT100_FRAME_CHECKSUM(frame))
+               return false;
+
+       return true;
+}
+
+static bool port100_rx_frame_is_ack(struct port100_ack_frame *frame)
+{
+       return (frame->start_frame == cpu_to_be16(PORT100_FRAME_SOF) &&
+               frame->ack_frame == cpu_to_be16(PORT100_FRAME_ACK));
+}
+
+static inline int port100_rx_frame_size(void *frame)
+{
+       struct port100_frame *f = frame;
+
+       return sizeof(struct port100_frame) + le16_to_cpu(f->datalen) +
+              PORT100_FRAME_TAIL_LEN;
+}
+
+static bool port100_rx_frame_is_cmd_response(struct port100 *dev, void *frame)
+{
+       struct port100_frame *f = frame;
+
+       return (PORT100_FRAME_CMD(f) == PORT100_CMD_RESPONSE(dev->cmd->code));
+}
+
+static void port100_recv_response(struct urb *urb)
+{
+       struct port100 *dev = urb->context;
+       struct port100_cmd *cmd = dev->cmd;
+       u8 *in_frame;
+
+       cmd->status = urb->status;
+
+       switch (urb->status) {
+       case 0:
+               break; /* success */
+       case -ECONNRESET:
+       case -ENOENT:
+               nfc_err(&dev->interface->dev,
+                       "The urb has been canceled (status %d)", urb->status);
+               goto sched_wq;
+       case -ESHUTDOWN:
+       default:
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+                       urb->status);
+               goto sched_wq;
+       }
+
+       in_frame = dev->in_urb->transfer_buffer;
+
+       if (!port100_rx_frame_is_valid(in_frame)) {
+               nfc_err(&dev->interface->dev, "Received an invalid frame");
+               cmd->status = -EIO;
+               goto sched_wq;
+       }
+
+       print_hex_dump_debug("PORT100 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
+                            port100_rx_frame_size(in_frame), false);
+
+       if (!port100_rx_frame_is_cmd_response(dev, in_frame)) {
+               nfc_err(&dev->interface->dev,
+                       "It's not the response to the last command");
+               cmd->status = -EIO;
+               goto sched_wq;
+       }
+
+sched_wq:
+       schedule_work(&dev->cmd_complete_work);
+}
+
+static int port100_submit_urb_for_response(struct port100 *dev, gfp_t flags)
+{
+       dev->in_urb->complete = port100_recv_response;
+
+       return usb_submit_urb(dev->in_urb, flags);
+}
+
+static void port100_recv_ack(struct urb *urb)
+{
+       struct port100 *dev = urb->context;
+       struct port100_cmd *cmd = dev->cmd;
+       struct port100_ack_frame *in_frame;
+       int rc;
+
+       cmd->status = urb->status;
+
+       switch (urb->status) {
+       case 0:
+               break; /* success */
+       case -ECONNRESET:
+       case -ENOENT:
+               nfc_err(&dev->interface->dev,
+                       "The urb has been stopped (status %d)", urb->status);
+               goto sched_wq;
+       case -ESHUTDOWN:
+       default:
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+                       urb->status);
+               goto sched_wq;
+       }
+
+       in_frame = dev->in_urb->transfer_buffer;
+
+       if (!port100_rx_frame_is_ack(in_frame)) {
+               nfc_err(&dev->interface->dev, "Received an invalid ack");
+               cmd->status = -EIO;
+               goto sched_wq;
+       }
+
+       rc = port100_submit_urb_for_response(dev, GFP_ATOMIC);
+       if (rc) {
+               nfc_err(&dev->interface->dev,
+                       "usb_submit_urb failed with result %d", rc);
+               cmd->status = rc;
+               goto sched_wq;
+       }
+
+       return;
+
+sched_wq:
+       schedule_work(&dev->cmd_complete_work);
+}
+
+static int port100_submit_urb_for_ack(struct port100 *dev, gfp_t flags)
+{
+       dev->in_urb->complete = port100_recv_ack;
+
+       return usb_submit_urb(dev->in_urb, flags);
+}
+
+static int port100_send_ack(struct port100 *dev)
+{
+       int rc;
+
+       dev->out_urb->transfer_buffer = ack_frame;
+       dev->out_urb->transfer_buffer_length = sizeof(ack_frame);
+       rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
+
+       return rc;
+}
+
+static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out,
+                                   struct sk_buff *in, int in_len)
+{
+       int rc;
+
+       dev->out_urb->transfer_buffer = out->data;
+       dev->out_urb->transfer_buffer_length = out->len;
+
+       dev->in_urb->transfer_buffer = in->data;
+       dev->in_urb->transfer_buffer_length = in_len;
+
+       print_hex_dump_debug("PORT100 TX: ", DUMP_PREFIX_NONE, 16, 1,
+                            out->data, out->len, false);
+
+       rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
+       if (rc)
+               return rc;
+
+       rc = port100_submit_urb_for_ack(dev, GFP_KERNEL);
+       if (rc)
+               goto error;
+
+       return 0;
+
+error:
+       usb_unlink_urb(dev->out_urb);
+       return rc;
+}
+
+static void port100_build_cmd_frame(struct port100 *dev, u8 cmd_code,
+                                   struct sk_buff *skb)
+{
+       /* payload is already there, just update datalen */
+       int payload_len = skb->len;
+
+       skb_push(skb, PORT100_FRAME_HEADER_LEN);
+       skb_put(skb, PORT100_FRAME_TAIL_LEN);
+
+       port100_tx_frame_init(skb->data, cmd_code);
+       port100_tx_update_payload_len(skb->data, payload_len);
+       port100_tx_frame_finish(skb->data);
+}
+
+static void port100_send_async_complete(struct port100 *dev)
+{
+       struct port100_cmd *cmd = dev->cmd;
+       int status = cmd->status;
+
+       struct sk_buff *req = cmd->req;
+       struct sk_buff *resp = cmd->resp;
+
+       dev_kfree_skb(req);
+
+       dev->cmd = NULL;
+
+       if (status < 0) {
+               cmd->complete_cb(dev, cmd->complete_cb_context,
+                                ERR_PTR(status));
+               dev_kfree_skb(resp);
+               goto done;
+       }
+
+       skb_put(resp, port100_rx_frame_size(resp->data));
+       skb_pull(resp, PORT100_FRAME_HEADER_LEN);
+       skb_trim(resp, resp->len - PORT100_FRAME_TAIL_LEN);
+
+       cmd->complete_cb(dev, cmd->complete_cb_context, resp);
+
+done:
+       kfree(cmd);
+}
+
+static int port100_send_cmd_async(struct port100 *dev, u8 cmd_code,
+                               struct sk_buff *req,
+                               port100_send_async_complete_t complete_cb,
+                               void *complete_cb_context)
+{
+       struct port100_cmd *cmd;
+       struct sk_buff *resp;
+       int rc;
+       int  resp_len = PORT100_FRAME_HEADER_LEN +
+                       PORT100_FRAME_MAX_PAYLOAD_LEN +
+                       PORT100_FRAME_TAIL_LEN;
+
+       resp = alloc_skb(resp_len, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               dev_kfree_skb(resp);
+               return -ENOMEM;
+       }
+
+       cmd->code = cmd_code;
+       cmd->req = req;
+       cmd->resp = resp;
+       cmd->resp_len = resp_len;
+       cmd->complete_cb = complete_cb;
+       cmd->complete_cb_context = complete_cb_context;
+
+       port100_build_cmd_frame(dev, cmd_code, req);
+
+       dev->cmd = cmd;
+
+       rc = port100_send_frame_async(dev, req, resp, resp_len);
+       if (rc) {
+               kfree(cmd);
+               dev_kfree_skb(resp);
+               dev->cmd = NULL;
+       }
+
+       return rc;
+}
+
+struct port100_sync_cmd_response {
+       struct sk_buff *resp;
+       struct completion done;
+};
+
+static void port100_wq_cmd_complete(struct work_struct *work)
+{
+       struct port100 *dev = container_of(work, struct port100,
+                                          cmd_complete_work);
+
+       port100_send_async_complete(dev);
+}
+
+static void port100_send_sync_complete(struct port100 *dev, void *_arg,
+                                     struct sk_buff *resp)
+{
+       struct port100_sync_cmd_response *arg = _arg;
+
+       arg->resp = resp;
+       complete(&arg->done);
+}
+
+static struct sk_buff *port100_send_cmd_sync(struct port100 *dev, u8 cmd_code,
+                                            struct sk_buff *req)
+{
+       int rc;
+       struct port100_sync_cmd_response arg;
+
+       init_completion(&arg.done);
+
+       rc = port100_send_cmd_async(dev, cmd_code, req,
+                                   port100_send_sync_complete, &arg);
+       if (rc) {
+               dev_kfree_skb(req);
+               return ERR_PTR(rc);
+       }
+
+       wait_for_completion(&arg.done);
+
+       return arg.resp;
+}
+
+static void port100_send_complete(struct urb *urb)
+{
+       struct port100 *dev = urb->context;
+
+       switch (urb->status) {
+       case 0:
+               break; /* success */
+       case -ECONNRESET:
+       case -ENOENT:
+               nfc_err(&dev->interface->dev,
+                       "The urb has been stopped (status %d)", urb->status);
+               break;
+       case -ESHUTDOWN:
+       default:
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+                       urb->status);
+       }
+}
+
+static void port100_abort_cmd(struct nfc_digital_dev *ddev)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+
+       /* An ack will cancel the last issued command */
+       port100_send_ack(dev);
+
+       /* cancel the urb request */
+       usb_kill_urb(dev->in_urb);
+}
+
+static struct sk_buff *port100_alloc_skb(struct port100 *dev, unsigned int size)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(dev->skb_headroom + dev->skb_tailroom + size,
+                       GFP_KERNEL);
+       if (skb)
+               skb_reserve(skb, dev->skb_headroom);
+
+       return skb;
+}
+
+static int port100_set_command_type(struct port100 *dev, u8 command_type)
+{
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int rc;
+
+       skb = port100_alloc_skb(dev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, sizeof(u8)) = command_type;
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_SET_COMMAND_TYPE, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static u64 port100_get_command_type_mask(struct port100 *dev)
+{
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       u64 mask;
+
+       skb = port100_alloc_skb(dev, 0);
+       if (!skb)
+               return -ENOMEM;
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_COMMAND_TYPE, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       if (resp->len < 8)
+               mask = 0;
+       else
+               mask = be64_to_cpu(*(__be64 *)resp->data);
+
+       dev_kfree_skb(resp);
+
+       return mask;
+}
+
+static u16 port100_get_firmware_version(struct port100 *dev)
+{
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       u16 fw_ver;
+
+       skb = port100_alloc_skb(dev, 0);
+       if (!skb)
+               return 0;
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_FIRMWARE_VERSION,
+                                    skb);
+       if (IS_ERR(resp))
+               return 0;
+
+       fw_ver = le16_to_cpu(*(__le16 *)resp->data);
+
+       dev_kfree_skb(resp);
+
+       return fw_ver;
+}
+
+static int port100_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct sk_buff *skb, *resp;
+
+       skb = port100_alloc_skb(dev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = on ? 1 : 0;
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_SWITCH_RF, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       dev_kfree_skb(resp);
+
+       return 0;
+}
+
+static int port100_in_set_rf(struct nfc_digital_dev *ddev, u8 rf)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int rc;
+
+       if (rf >= NFC_DIGITAL_RF_TECH_LAST)
+               return -EINVAL;
+
+       skb = port100_alloc_skb(dev, sizeof(struct port100_in_rf_setting));
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, sizeof(struct port100_in_rf_setting)),
+              &in_rf_settings[rf],
+              sizeof(struct port100_in_rf_setting));
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_RF, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static int port100_in_set_framing(struct nfc_digital_dev *ddev, int param)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_protocol *protocols;
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int num_protocols;
+       size_t size;
+       int rc;
+
+       if (param >= NFC_DIGITAL_FRAMING_LAST)
+               return -EINVAL;
+
+       protocols = in_protocols[param];
+
+       num_protocols = 0;
+       while (protocols[num_protocols].number != PORT100_IN_PROT_END)
+               num_protocols++;
+
+       if (!num_protocols)
+               return 0;
+
+       size = sizeof(struct port100_protocol) * num_protocols;
+
+       skb = port100_alloc_skb(dev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, size), protocols, size);
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_PROTOCOL, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static int port100_in_configure_hw(struct nfc_digital_dev *ddev, int type,
+                                  int param)
+{
+       if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+               return port100_in_set_rf(ddev, param);
+
+       if (type == NFC_DIGITAL_CONFIG_FRAMING)
+               return port100_in_set_framing(ddev, param);
+
+       return -EINVAL;
+}
+
+static void port100_in_comm_rf_complete(struct port100 *dev, void *arg,
+                                      struct sk_buff *resp)
+{
+       struct port100_cb_arg *cb_arg = arg;
+       nfc_digital_cmd_complete_t cb = cb_arg->complete_cb;
+       u32 status;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc =  PTR_ERR(resp);
+               goto exit;
+       }
+
+       if (resp->len < 4) {
+               nfc_err(&dev->interface->dev,
+                       "Invalid packet length received.\n");
+               rc = -EIO;
+               goto error;
+       }
+
+       status = le32_to_cpu(*(__le32 *)resp->data);
+
+       skb_pull(resp, sizeof(u32));
+
+       if (status == PORT100_CMD_STATUS_TIMEOUT) {
+               rc = -ETIMEDOUT;
+               goto error;
+       }
+
+       if (status != PORT100_CMD_STATUS_OK) {
+               nfc_err(&dev->interface->dev,
+                       "in_comm_rf failed with status 0x%08x\n", status);
+               rc = -EIO;
+               goto error;
+       }
+
+       /* Remove collision bits byte */
+       skb_pull(resp, 1);
+
+       goto exit;
+
+error:
+       kfree_skb(resp);
+       resp = ERR_PTR(rc);
+
+exit:
+       cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp);
+
+       kfree(cb_arg);
+}
+
+static int port100_in_send_cmd(struct nfc_digital_dev *ddev,
+                              struct sk_buff *skb, u16 _timeout,
+                              nfc_digital_cmd_complete_t cb, void *arg)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_cb_arg *cb_arg;
+       __le16 timeout;
+
+       cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+       if (!cb_arg)
+               return -ENOMEM;
+
+       cb_arg->complete_cb = cb;
+       cb_arg->complete_arg = arg;
+
+       timeout = cpu_to_le16(_timeout * 10);
+
+       memcpy(skb_push(skb, sizeof(__le16)), &timeout, sizeof(__le16));
+
+       return port100_send_cmd_async(dev, PORT100_CMD_IN_COMM_RF, skb,
+                                     port100_in_comm_rf_complete, cb_arg);
+}
+
+static int port100_tg_set_rf(struct nfc_digital_dev *ddev, u8 rf)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int rc;
+
+       if (rf >= NFC_DIGITAL_RF_TECH_LAST)
+               return -EINVAL;
+
+       skb = port100_alloc_skb(dev, sizeof(struct port100_tg_rf_setting));
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, sizeof(struct port100_tg_rf_setting)),
+              &tg_rf_settings[rf],
+              sizeof(struct port100_tg_rf_setting));
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_RF, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static int port100_tg_set_framing(struct nfc_digital_dev *ddev, int param)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_protocol *protocols;
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int rc;
+       int num_protocols;
+       size_t size;
+
+       if (param >= NFC_DIGITAL_FRAMING_LAST)
+               return -EINVAL;
+
+       protocols = tg_protocols[param];
+
+       num_protocols = 0;
+       while (protocols[num_protocols].number != PORT100_TG_PROT_END)
+               num_protocols++;
+
+       if (!num_protocols)
+               return 0;
+
+       size = sizeof(struct port100_protocol) * num_protocols;
+
+       skb = port100_alloc_skb(dev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, size), protocols, size);
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_PROTOCOL, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static int port100_tg_configure_hw(struct nfc_digital_dev *ddev, int type,
+                                  int param)
+{
+       if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+               return port100_tg_set_rf(ddev, param);
+
+       if (type == NFC_DIGITAL_CONFIG_FRAMING)
+               return port100_tg_set_framing(ddev, param);
+
+       return -EINVAL;
+}
+
+static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated)
+{
+       u8 mask;
+
+       switch (dev->cmd_type) {
+       case PORT100_CMD_TYPE_0:
+               mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK;
+               break;
+       case PORT100_CMD_TYPE_1:
+               mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK |
+                      PORT100_MDAA_TGT_WAS_ACTIVATED_MASK;
+               break;
+       default:
+               nfc_err(&dev->interface->dev, "Unknonwn command type.\n");
+               return false;
+       }
+
+       return ((tgt_activated & mask) == mask);
+}
+
+static void port100_tg_comm_rf_complete(struct port100 *dev, void *arg,
+                                       struct sk_buff *resp)
+{
+       u32 status;
+       struct port100_cb_arg *cb_arg = arg;
+       nfc_digital_cmd_complete_t cb = cb_arg->complete_cb;
+       struct port100_tg_comm_rf_res *hdr;
+
+       if (IS_ERR(resp))
+               goto exit;
+
+       hdr = (struct port100_tg_comm_rf_res *)resp->data;
+
+       status = le32_to_cpu(hdr->status);
+
+       if (cb_arg->mdaa &&
+           !port100_tg_target_activated(dev, hdr->target_activated)) {
+               kfree_skb(resp);
+               resp = ERR_PTR(-ETIMEDOUT);
+
+               goto exit;
+       }
+
+       skb_pull(resp, sizeof(struct port100_tg_comm_rf_res));
+
+       if (status != PORT100_CMD_STATUS_OK) {
+               kfree_skb(resp);
+
+               if (status == PORT100_CMD_STATUS_TIMEOUT)
+                       resp = ERR_PTR(-ETIMEDOUT);
+               else
+                       resp = ERR_PTR(-EIO);
+       }
+
+exit:
+       cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp);
+
+       kfree(cb_arg);
+}
+
+static int port100_tg_send_cmd(struct nfc_digital_dev *ddev,
+                              struct sk_buff *skb, u16 timeout,
+                              nfc_digital_cmd_complete_t cb, void *arg)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_tg_comm_rf_cmd *hdr;
+       struct port100_cb_arg *cb_arg;
+
+       cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+       if (!cb_arg)
+               return -ENOMEM;
+
+       cb_arg->complete_cb = cb;
+       cb_arg->complete_arg = arg;
+
+       skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd));
+
+       hdr = (struct port100_tg_comm_rf_cmd *)skb->data;
+
+       memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd));
+       hdr->guard_time = cpu_to_le16(500);
+       hdr->send_timeout = cpu_to_le16(0xFFFF);
+       hdr->recv_timeout = cpu_to_le16(timeout);
+
+       return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb,
+                                     port100_tg_comm_rf_complete, cb_arg);
+}
+
+static int port100_listen_mdaa(struct nfc_digital_dev *ddev,
+                              struct digital_tg_mdaa_params *params,
+                              u16 timeout,
+                              nfc_digital_cmd_complete_t cb, void *arg)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_tg_comm_rf_cmd *hdr;
+       struct port100_cb_arg *cb_arg;
+       struct sk_buff *skb;
+       int rc;
+
+       rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+                                    NFC_DIGITAL_RF_TECH_106A);
+       if (rc)
+               return rc;
+
+       rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCA_NFC_DEP);
+       if (rc)
+               return rc;
+
+       cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+       if (!cb_arg)
+               return -ENOMEM;
+
+       cb_arg->complete_cb = cb;
+       cb_arg->complete_arg = arg;
+       cb_arg->mdaa = 1;
+
+       skb = port100_alloc_skb(dev, 0);
+       if (!skb) {
+               kfree(cb_arg);
+               return -ENOMEM;
+       }
+
+       skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd));
+       hdr = (struct port100_tg_comm_rf_cmd *)skb->data;
+
+       memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd));
+
+       hdr->guard_time = 0;
+       hdr->send_timeout = cpu_to_le16(0xFFFF);
+       hdr->mdaa = 1;
+       hdr->nfca_param[0] = (params->sens_res >> 8) & 0xFF;
+       hdr->nfca_param[1] = params->sens_res & 0xFF;
+       memcpy(hdr->nfca_param + 2, params->nfcid1, 3);
+       hdr->nfca_param[5] = params->sel_res;
+       memcpy(hdr->nfcf_param, params->nfcid2, 8);
+       hdr->nfcf_param[16] = (params->sc >> 8) & 0xFF;
+       hdr->nfcf_param[17] = params->sc & 0xFF;
+       hdr->recv_timeout = cpu_to_le16(timeout);
+
+       return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb,
+                                     port100_tg_comm_rf_complete, cb_arg);
+}
+
+static int port100_listen(struct nfc_digital_dev *ddev, u16 timeout,
+                         nfc_digital_cmd_complete_t cb, void *arg)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct sk_buff *skb;
+
+       skb = port100_alloc_skb(dev, 0);
+       if (!skb)
+               return -ENOMEM;
+
+       return port100_tg_send_cmd(ddev, skb, timeout, cb, arg);
+}
+
+static struct nfc_digital_ops port100_digital_ops = {
+       .in_configure_hw = port100_in_configure_hw,
+       .in_send_cmd = port100_in_send_cmd,
+
+       .tg_listen_mdaa = port100_listen_mdaa,
+       .tg_listen = port100_listen,
+       .tg_configure_hw = port100_tg_configure_hw,
+       .tg_send_cmd = port100_tg_send_cmd,
+
+       .switch_rf = port100_switch_rf,
+       .abort_cmd = port100_abort_cmd,
+};
+
+static const struct usb_device_id port100_table[] = {
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE,
+         .idVendor             = SONY_VENDOR_ID,
+         .idProduct            = RCS380_PRODUCT_ID,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, port100_table);
+
+static int port100_probe(struct usb_interface *interface,
+                        const struct usb_device_id *id)
+{
+       struct port100 *dev;
+       int rc;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int in_endpoint;
+       int out_endpoint;
+       u16 fw_version;
+       u64 cmd_type_mask;
+       int i;
+
+       dev = devm_kzalloc(&interface->dev, sizeof(struct port100), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dev->udev = usb_get_dev(interface_to_usbdev(interface));
+       dev->interface = interface;
+       usb_set_intfdata(interface, dev);
+
+       in_endpoint = out_endpoint = 0;
+       iface_desc = interface->cur_altsetting;
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+
+               if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint))
+                       in_endpoint = endpoint->bEndpointAddress;
+
+               if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint))
+                       out_endpoint = endpoint->bEndpointAddress;
+       }
+
+       if (!in_endpoint || !out_endpoint) {
+               nfc_err(&interface->dev,
+                       "Could not find bulk-in or bulk-out endpoint\n");
+               rc = -ENODEV;
+               goto error;
+       }
+
+       dev->in_urb = usb_alloc_urb(0, GFP_KERNEL);
+       dev->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+       if (!dev->in_urb || !dev->out_urb) {
+               nfc_err(&interface->dev, "Could not allocate USB URBs\n");
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       usb_fill_bulk_urb(dev->in_urb, dev->udev,
+                         usb_rcvbulkpipe(dev->udev, in_endpoint),
+                         NULL, 0, NULL, dev);
+       usb_fill_bulk_urb(dev->out_urb, dev->udev,
+                         usb_sndbulkpipe(dev->udev, out_endpoint),
+                         NULL, 0, port100_send_complete, dev);
+
+       dev->skb_headroom = PORT100_FRAME_HEADER_LEN +
+                           PORT100_COMM_RF_HEAD_MAX_LEN;
+       dev->skb_tailroom = PORT100_FRAME_TAIL_LEN;
+
+       INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete);
+
+       /* The first thing to do with the Port-100 is to set the command type
+        * to be used. If supported we use command type 1. 0 otherwise.
+        */
+       cmd_type_mask = port100_get_command_type_mask(dev);
+       if (!cmd_type_mask) {
+               nfc_err(&interface->dev,
+                       "Could not get supported command types.\n");
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (PORT100_CMD_TYPE_IS_SUPPORTED(cmd_type_mask, PORT100_CMD_TYPE_1))
+               dev->cmd_type = PORT100_CMD_TYPE_1;
+       else
+               dev->cmd_type = PORT100_CMD_TYPE_0;
+
+       rc = port100_set_command_type(dev, dev->cmd_type);
+       if (rc) {
+               nfc_err(&interface->dev,
+                       "The device does not support command type %u.\n",
+                       dev->cmd_type);
+               goto error;
+       }
+
+       fw_version = port100_get_firmware_version(dev);
+       if (!fw_version)
+               nfc_err(&interface->dev,
+                       "Could not get device firmware version.\n");
+
+       nfc_info(&interface->dev,
+                "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n",
+                (fw_version & 0xFF00) >> 8, fw_version & 0xFF);
+
+       dev->nfc_digital_dev = nfc_digital_allocate_device(&port100_digital_ops,
+                                                          PORT100_PROTOCOLS,
+                                                          PORT100_CAPABILITIES,
+                                                          dev->skb_headroom,
+                                                          dev->skb_tailroom);
+       if (!dev->nfc_digital_dev) {
+               nfc_err(&interface->dev,
+                       "Could not allocate nfc_digital_dev.\n");
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       nfc_digital_set_parent_dev(dev->nfc_digital_dev, &interface->dev);
+       nfc_digital_set_drvdata(dev->nfc_digital_dev, dev);
+
+       rc = nfc_digital_register_device(dev->nfc_digital_dev);
+       if (rc) {
+               nfc_err(&interface->dev,
+                       "Could not register digital device.\n");
+               goto free_nfc_dev;
+       }
+
+       return 0;
+
+free_nfc_dev:
+       nfc_digital_free_device(dev->nfc_digital_dev);
+
+error:
+       usb_free_urb(dev->in_urb);
+       usb_free_urb(dev->out_urb);
+       usb_put_dev(dev->udev);
+
+       return rc;
+}
+
+static void port100_disconnect(struct usb_interface *interface)
+{
+       struct port100 *dev;
+
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       nfc_digital_unregister_device(dev->nfc_digital_dev);
+       nfc_digital_free_device(dev->nfc_digital_dev);
+
+       usb_kill_urb(dev->in_urb);
+       usb_kill_urb(dev->out_urb);
+
+       usb_free_urb(dev->in_urb);
+       usb_free_urb(dev->out_urb);
+
+       kfree(dev->cmd);
+
+       nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected");
+}
+
+static struct usb_driver port100_driver = {
+       .name =         "port100",
+       .probe =        port100_probe,
+       .disconnect =   port100_disconnect,
+       .id_table =     port100_table,
+};
+
+module_usb_driver(port100_driver);
+
+MODULE_DESCRIPTION("NFC Port-100 series usb driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
index d66033f418c98bf1818ba2c5fcbdc7bcebe8021b..0333e605ea0d752c5aa306957a80aa72c5d4094f 100644 (file)
@@ -242,6 +242,7 @@ extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc,
                                 struct bcma_device *core, bool enable);
 extern void bcma_core_pci_up(struct bcma_bus *bus);
 extern void bcma_core_pci_down(struct bcma_bus *bus);
+extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up);
 
 extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
 extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
index 7c1e1ebc0e2396cc7697bccc31d18be5566dc944..8c3b26a215745b755a5018764e4c0ca1f1bd52b7 100644 (file)
@@ -696,6 +696,18 @@ struct ieee80211_sec_chan_offs_ie {
        u8 sec_chan_offs;
 } __packed;
 
+/**
+ * struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE
+ *
+ * This structure represents the "Mesh Channel Switch Paramters element"
+ */
+struct ieee80211_mesh_chansw_params_ie {
+       u8 mesh_ttl;
+       u8 mesh_flags;
+       __le16 mesh_reason;
+       __le16 mesh_pre_value;
+} __packed;
+
 /**
  * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
  */
@@ -750,6 +762,14 @@ enum mesh_config_capab_flags {
        IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL       = 0x40,
 };
 
+/**
+ * mesh channel switch parameters element's flag indicator
+ *
+ */
+#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0)
+#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1)
+#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2)
+
 /**
  * struct ieee80211_rann_ie
  *
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
deleted file mode 100644 (file)
index 487b54c..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-   Copyright (c) 2010,2011 Code Aurora Forum.  All rights reserved.
-   Copyright (c) 2011,2012 Intel Corp.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 and
-   only version 2 as published by the Free Software Foundation.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-*/
-
-#ifndef __A2MP_H
-#define __A2MP_H
-
-#include <net/bluetooth/l2cap.h>
-
-#define A2MP_FEAT_EXT  0x8000
-
-enum amp_mgr_state {
-       READ_LOC_AMP_INFO,
-       READ_LOC_AMP_ASSOC,
-       READ_LOC_AMP_ASSOC_FINAL,
-       WRITE_REMOTE_AMP_ASSOC,
-};
-
-struct amp_mgr {
-       struct list_head        list;
-       struct l2cap_conn       *l2cap_conn;
-       struct l2cap_chan       *a2mp_chan;
-       struct l2cap_chan       *bredr_chan;
-       struct kref             kref;
-       __u8                    ident;
-       __u8                    handle;
-       unsigned long           state;
-       unsigned long           flags;
-
-       struct list_head        amp_ctrls;
-       struct mutex            amp_ctrls_lock;
-};
-
-struct a2mp_cmd {
-       __u8    code;
-       __u8    ident;
-       __le16  len;
-       __u8    data[0];
-} __packed;
-
-/* A2MP command codes */
-#define A2MP_COMMAND_REJ         0x01
-struct a2mp_cmd_rej {
-       __le16  reason;
-       __u8    data[0];
-} __packed;
-
-#define A2MP_DISCOVER_REQ        0x02
-struct a2mp_discov_req {
-       __le16  mtu;
-       __le16  ext_feat;
-} __packed;
-
-struct a2mp_cl {
-       __u8    id;
-       __u8    type;
-       __u8    status;
-} __packed;
-
-#define A2MP_DISCOVER_RSP        0x03
-struct a2mp_discov_rsp {
-       __le16     mtu;
-       __le16     ext_feat;
-       struct a2mp_cl cl[0];
-} __packed;
-
-#define A2MP_CHANGE_NOTIFY       0x04
-#define A2MP_CHANGE_RSP          0x05
-
-#define A2MP_GETINFO_REQ         0x06
-struct a2mp_info_req {
-       __u8       id;
-} __packed;
-
-#define A2MP_GETINFO_RSP         0x07
-struct a2mp_info_rsp {
-       __u8    id;
-       __u8    status;
-       __le32  total_bw;
-       __le32  max_bw;
-       __le32  min_latency;
-       __le16  pal_cap;
-       __le16  assoc_size;
-} __packed;
-
-#define A2MP_GETAMPASSOC_REQ     0x08
-struct a2mp_amp_assoc_req {
-       __u8    id;
-} __packed;
-
-#define A2MP_GETAMPASSOC_RSP     0x09
-struct a2mp_amp_assoc_rsp {
-       __u8    id;
-       __u8    status;
-       __u8    amp_assoc[0];
-} __packed;
-
-#define A2MP_CREATEPHYSLINK_REQ  0x0A
-#define A2MP_DISCONNPHYSLINK_REQ 0x0C
-struct a2mp_physlink_req {
-       __u8    local_id;
-       __u8    remote_id;
-       __u8    amp_assoc[0];
-} __packed;
-
-#define A2MP_CREATEPHYSLINK_RSP  0x0B
-#define A2MP_DISCONNPHYSLINK_RSP 0x0D
-struct a2mp_physlink_rsp {
-       __u8    local_id;
-       __u8    remote_id;
-       __u8    status;
-} __packed;
-
-/* A2MP response status */
-#define A2MP_STATUS_SUCCESS                    0x00
-#define A2MP_STATUS_INVALID_CTRL_ID            0x01
-#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
-#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS    0x02
-#define A2MP_STATUS_COLLISION_OCCURED          0x03
-#define A2MP_STATUS_DISCONN_REQ_RECVD          0x04
-#define A2MP_STATUS_PHYS_LINK_EXISTS           0x05
-#define A2MP_STATUS_SECURITY_VIOLATION         0x06
-
-extern struct list_head amp_mgr_list;
-extern struct mutex amp_mgr_list_lock;
-
-struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
-int amp_mgr_put(struct amp_mgr *mgr);
-u8 __next_ident(struct amp_mgr *mgr);
-struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
-                                      struct sk_buff *skb);
-struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
-void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
-void a2mp_discover_amp(struct l2cap_chan *chan);
-void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
-void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
-void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
-void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
-
-#endif /* __A2MP_H */
diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h
deleted file mode 100644 (file)
index 7ea3db7..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-   Copyright (c) 2011,2012 Intel Corp.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 and
-   only version 2 as published by the Free Software Foundation.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-*/
-
-#ifndef __AMP_H
-#define __AMP_H
-
-struct amp_ctrl {
-       struct list_head        list;
-       struct kref             kref;
-       __u8                    id;
-       __u16                   assoc_len_so_far;
-       __u16                   assoc_rem_len;
-       __u16                   assoc_len;
-       __u8                    *assoc;
-};
-
-int amp_ctrl_put(struct amp_ctrl *ctrl);
-void amp_ctrl_get(struct amp_ctrl *ctrl);
-struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id);
-struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id);
-void amp_ctrl_list_flush(struct amp_mgr *mgr);
-
-struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
-                            u8 remote_id, bool out);
-
-int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
-
-void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr);
-void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
-void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
-void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
-                                  struct hci_conn *hcon);
-void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
-                       struct hci_conn *hcon);
-void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
-                       struct hci_conn *hcon);
-void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
-void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
-void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
-void amp_create_logical_link(struct l2cap_chan *chan);
-void amp_disconnect_logical_link(struct hci_chan *hchan);
-void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason);
-
-#endif /* __AMP_H */
index 5fd510675cfa6982dd150a0084112a541ed0f3f0..a707a0209df44fcf502d74a08b2895daaac39b01 100644 (file)
@@ -218,11 +218,10 @@ void baswap(bdaddr_t *dst, bdaddr_t *src);
 
 struct bt_sock {
        struct sock sk;
-       bdaddr_t    src;
-       bdaddr_t    dst;
        struct list_head accept_q;
        struct sock *parent;
        unsigned long flags;
+       void (*skb_msg_name)(struct sk_buff *, void *, int *);
 };
 
 enum {
@@ -283,8 +282,11 @@ struct bt_skb_cb {
        __u8 incoming;
        __u16 expect;
        __u8 force_active;
+       struct l2cap_chan *chan;
        struct l2cap_ctrl control;
        struct hci_req_ctrl req;
+       bdaddr_t bdaddr;
+       __le16 psm;
 };
 #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
 
index b90eec5e9c0649de8b255f1b5f079823ddde2645..1784c48699f04dd425027d2e51d0aabc3fd87f4a 100644 (file)
 #define HCI_AMP                0x01
 
 /* First BR/EDR Controller shall have ID = 0 */
-#define HCI_BREDR_ID   0
+#define AMP_ID_BREDR   0x00
+
+/* AMP controller types */
+#define AMP_TYPE_BREDR 0x00
+#define AMP_TYPE_80211 0x01
 
 /* AMP controller status */
-#define AMP_CTRL_POWERED_DOWN                  0x00
-#define AMP_CTRL_BLUETOOTH_ONLY                        0x01
-#define AMP_CTRL_NO_CAPACITY                   0x02
-#define AMP_CTRL_LOW_CAPACITY                  0x03
-#define AMP_CTRL_MEDIUM_CAPACITY               0x04
-#define AMP_CTRL_HIGH_CAPACITY                 0x05
-#define AMP_CTRL_FULL_CAPACITY                 0x06
+#define AMP_STATUS_POWERED_DOWN                        0x00
+#define AMP_STATUS_BLUETOOTH_ONLY              0x01
+#define AMP_STATUS_NO_CAPACITY                 0x02
+#define AMP_STATUS_LOW_CAPACITY                        0x03
+#define AMP_STATUS_MEDIUM_CAPACITY             0x04
+#define AMP_STATUS_HIGH_CAPACITY               0x05
+#define AMP_STATUS_FULL_CAPACITY               0x06
 
 /* HCI device quirks */
 enum {
@@ -111,6 +115,7 @@ enum {
        HCI_PAIRABLE,
        HCI_SERVICE_CACHE,
        HCI_DEBUG_KEYS,
+       HCI_DUT_MODE,
        HCI_UNREGISTER,
        HCI_USER_CHANNEL,
 
@@ -118,9 +123,10 @@ enum {
        HCI_SSP_ENABLED,
        HCI_HS_ENABLED,
        HCI_LE_ENABLED,
-       HCI_LE_PERIPHERAL,
+       HCI_ADVERTISING,
        HCI_CONNECTABLE,
        HCI_DISCOVERABLE,
+       HCI_LIMITED_DISCOVERABLE,
        HCI_LINK_SECURITY,
        HCI_PERIODIC_INQ,
        HCI_FAST_CONNECTABLE,
@@ -811,6 +817,20 @@ struct hci_cp_host_buffer_size {
        __le16   sco_max_pkt;
 } __packed;
 
+#define HCI_OP_READ_NUM_SUPPORTED_IAC  0x0c38
+struct hci_rp_read_num_supported_iac {
+       __u8    status;
+       __u8    num_iac;
+} __packed;
+
+#define HCI_OP_READ_CURRENT_IAC_LAP    0x0c39
+
+#define HCI_OP_WRITE_CURRENT_IAC_LAP   0x0c3a
+struct hci_cp_write_current_iac_lap {
+       __u8    num_iac;
+       __u8    iac_lap[6];
+} __packed;
+
 #define HCI_OP_WRITE_INQUIRY_MODE      0x0c45
 
 #define HCI_MAX_EIR_LENGTH             240
@@ -847,6 +867,8 @@ struct hci_rp_read_inq_rsp_tx_power {
 
 #define HCI_OP_SET_EVENT_MASK_PAGE_2   0x0c63
 
+#define HCI_OP_READ_LOCATION_DATA      0x0c64
+
 #define HCI_OP_READ_FLOW_CONTROL_MODE  0x0c66
 struct hci_rp_read_flow_control_mode {
        __u8     status;
@@ -1022,6 +1044,10 @@ struct hci_rp_write_remote_amp_assoc {
        __u8     phy_handle;
 } __packed;
 
+#define HCI_OP_ENABLE_DUT_MODE         0x1803
+
+#define HCI_OP_WRITE_SSP_DEBUG_MODE    0x1804
+
 #define HCI_OP_LE_SET_EVENT_MASK       0x2001
 struct hci_cp_le_set_event_mask {
        __u8     mask[8];
@@ -1042,6 +1068,18 @@ struct hci_rp_le_read_local_features {
 
 #define HCI_OP_LE_SET_RANDOM_ADDR      0x2005
 
+#define HCI_OP_LE_SET_ADV_PARAM                0x2006
+struct hci_cp_le_set_adv_param {
+       __le16   min_interval;
+       __le16   max_interval;
+       __u8     type;
+       __u8     own_address_type;
+       __u8     direct_addr_type;
+       bdaddr_t direct_addr;
+       __u8     channel_map;
+       __u8     filter_policy;
+} __packed;
+
 #define HCI_OP_LE_READ_ADV_TX_POWER    0x2007
 struct hci_rp_le_read_adv_tx_power {
        __u8    status;
@@ -1056,6 +1094,12 @@ struct hci_cp_le_set_adv_data {
        __u8    data[HCI_MAX_AD_LENGTH];
 } __packed;
 
+#define HCI_OP_LE_SET_SCAN_RSP_DATA    0x2009
+struct hci_cp_le_set_scan_rsp_data {
+       __u8    length;
+       __u8    data[HCI_MAX_AD_LENGTH];
+} __packed;
+
 #define HCI_OP_LE_SET_ADV_ENABLE       0x200a
 
 #define LE_SCAN_PASSIVE                        0x00
@@ -1536,11 +1580,11 @@ struct hci_ev_le_ltk_req {
 } __packed;
 
 /* Advertising report event types */
-#define ADV_IND                0x00
-#define ADV_DIRECT_IND 0x01
-#define ADV_SCAN_IND   0x02
-#define ADV_NONCONN_IND        0x03
-#define ADV_SCAN_RSP   0x04
+#define LE_ADV_IND             0x00
+#define LE_ADV_DIRECT_IND      0x01
+#define LE_ADV_SCAN_IND                0x02
+#define LE_ADV_NONCONN_IND     0x03
+#define LE_ADV_SCAN_RSP                0x04
 
 #define ADDR_LE_DEV_PUBLIC     0x00
 #define ADDR_LE_DEV_RANDOM     0x01
@@ -1748,6 +1792,4 @@ struct hci_inquiry_req {
 };
 #define IREQ_CACHE_FLUSH 0x0001
 
-extern bool enable_hs;
-
 #endif /* __HCI_H */
index e09c30577b3a071ede7f745f512885dd46a7b000..8c0ab3d86f957fdb9ff51b304b5d4511cd439ffa 100644 (file)
@@ -81,6 +81,7 @@ struct hci_conn_hash {
 struct bdaddr_list {
        struct list_head list;
        bdaddr_t bdaddr;
+       u8 bdaddr_type;
 };
 
 struct bt_uuid {
@@ -141,6 +142,7 @@ struct hci_dev {
        __u8            dev_type;
        bdaddr_t        bdaddr;
        bdaddr_t        static_addr;
+       __u8            own_addr_type;
        __u8            dev_name[HCI_MAX_NAME_LENGTH];
        __u8            short_name[HCI_MAX_SHORT_NAME_LENGTH];
        __u8            eir[HCI_MAX_EIR_LENGTH];
@@ -159,11 +161,17 @@ struct hci_dev {
        __u16           manufacturer;
        __u16           lmp_subver;
        __u16           voice_setting;
+       __u8            num_iac;
        __u8            io_capability;
        __s8            inq_tx_power;
        __u16           page_scan_interval;
        __u16           page_scan_window;
        __u8            page_scan_type;
+       __u16           le_scan_interval;
+       __u16           le_scan_window;
+       __u16           le_conn_min_interval;
+       __u16           le_conn_max_interval;
+       __u8            ssp_debug_mode;
 
        __u16           devid_source;
        __u16           devid_vendor;
@@ -280,14 +288,15 @@ struct hci_dev {
        __s8                    adv_tx_power;
        __u8                    adv_data[HCI_MAX_AD_LENGTH];
        __u8                    adv_data_len;
+       __u8                    scan_rsp_data[HCI_MAX_AD_LENGTH];
+       __u8                    scan_rsp_data_len;
 
        int (*open)(struct hci_dev *hdev);
        int (*close)(struct hci_dev *hdev);
        int (*flush)(struct hci_dev *hdev);
        int (*setup)(struct hci_dev *hdev);
-       int (*send)(struct sk_buff *skb);
+       int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
        void (*notify)(struct hci_dev *hdev, unsigned int evt);
-       int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
 };
 
 #define HCI_PHY_HANDLE(handle) (handle & 0xff)
@@ -299,6 +308,8 @@ struct hci_conn {
 
        bdaddr_t        dst;
        __u8            dst_type;
+       bdaddr_t        src;
+       __u8            src_type;
        __u16           handle;
        __u16           state;
        __u8            mode;
@@ -307,7 +318,6 @@ struct hci_conn {
        __u8            attempt;
        __u8            dev_class[3];
        __u8            features[HCI_MAX_PAGES][8];
-       __u16           interval;
        __u16           pkt_type;
        __u16           link_policy;
        __u32           link_mode;
@@ -335,8 +345,8 @@ struct hci_conn {
        struct list_head chan_list;
 
        struct delayed_work disc_work;
-       struct timer_list idle_timer;
-       struct timer_list auto_accept_timer;
+       struct delayed_work auto_accept_work;
+       struct delayed_work idle_work;
 
        struct device   dev;
 
@@ -645,7 +655,7 @@ static inline void hci_conn_drop(struct hci_conn *conn)
                switch (conn->type) {
                case ACL_LINK:
                case LE_LINK:
-                       del_timer(&conn->idle_timer);
+                       cancel_delayed_work(&conn->idle_work);
                        if (conn->state == BT_CONNECTED) {
                                timeo = conn->disc_timeout;
                                if (!conn->out)
@@ -704,19 +714,6 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
        dev_set_drvdata(&hdev->dev, data);
 }
 
-/* hci_dev_list shall be locked */
-static inline uint8_t __hci_num_ctrl(void)
-{
-       uint8_t count = 0;
-       struct list_head *p;
-
-       list_for_each(p, &hci_dev_list) {
-               count++;
-       }
-
-       return count;
-}
-
 struct hci_dev *hci_dev_get(int index);
 struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src);
 
@@ -739,7 +736,7 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg);
 int hci_inquiry(void __user *arg);
 
 struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
-                                        bdaddr_t *bdaddr);
+                                        bdaddr_t *bdaddr, u8 type);
 int hci_blacklist_clear(struct hci_dev *hdev);
 int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
@@ -769,13 +766,11 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
-int hci_recv_frame(struct sk_buff *skb);
+int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
 int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count);
 int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
 
 void hci_init_sysfs(struct hci_dev *hdev);
-int hci_add_sysfs(struct hci_dev *hdev);
-void hci_del_sysfs(struct hci_dev *hdev);
 void hci_conn_init_sysfs(struct hci_conn *conn);
 void hci_conn_add_sysfs(struct hci_conn *conn);
 void hci_conn_del_sysfs(struct hci_conn *conn);
@@ -808,22 +803,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 #define lmp_host_le_capable(dev)   (!!((dev)->features[1][0] & LMP_HOST_LE))
 #define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR))
 
-/* returns true if at least one AMP active */
-static inline bool hci_amp_capable(void)
-{
-       struct hci_dev *hdev;
-       bool ret = false;
-
-       read_lock(&hci_dev_list_lock);
-       list_for_each_entry(hdev, &hci_dev_list, list)
-               if (hdev->amp_type == HCI_AMP &&
-                   test_bit(HCI_UP, &hdev->flags))
-                       ret = true;
-       read_unlock(&hci_dev_list_lock);
-
-       return ret;
-}
-
 /* ----- HCI protocols ----- */
 #define HCI_PROTO_DEFER             0x01
 
@@ -1034,34 +1013,6 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
        return false;
 }
 
-static inline size_t eir_get_length(u8 *eir, size_t eir_len)
-{
-       size_t parsed = 0;
-
-       while (parsed < eir_len) {
-               u8 field_len = eir[0];
-
-               if (field_len == 0)
-                       return parsed;
-
-               parsed += field_len + 1;
-               eir += field_len + 1;
-       }
-
-       return eir_len;
-}
-
-static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
-                                 u8 data_len)
-{
-       eir[eir_len++] = sizeof(type) + data_len;
-       eir[eir_len++] = type;
-       memcpy(&eir[eir_len], data, data_len);
-       eir_len += data_len;
-
-       return eir_len;
-}
-
 int hci_register_cb(struct hci_cb *hcb);
 int hci_unregister_cb(struct hci_cb *hcb);
 
@@ -1121,29 +1072,30 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event);
 #define DISCOV_BREDR_INQUIRY_LEN       0x08
 
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
-int mgmt_index_added(struct hci_dev *hdev);
-int mgmt_index_removed(struct hci_dev *hdev);
-int mgmt_set_powered_failed(struct hci_dev *hdev, int err);
+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_discoverable(struct hci_dev *hdev, u8 discoverable);
-int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
-int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
-                     bool persistent);
-int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                         u8 addr_type, u32 flags, u8 *name, u8 name_len,
-                         u8 *dev_class);
-int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                            u8 link_type, u8 addr_type, u8 reason);
-int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                          u8 link_type, u8 addr_type, u8 status);
-int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                       u8 addr_type, u8 status);
-int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
-int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                u8 status);
-int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 status);
+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,
+                          u8 addr_type, u32 flags, u8 *name, u8 name_len,
+                          u8 *dev_class);
+void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                             u8 link_type, u8 addr_type, u8 reason);
+void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 link_type, u8 addr_type, u8 status);
+void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                        u8 addr_type, u8 status);
+void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
+void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                 u8 status);
+void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                     u8 status);
 int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
                              u8 link_type, u8 addr_type, __le32 value,
                              u8 confirm_hint);
@@ -1160,25 +1112,25 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
 int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
                             u8 link_type, u8 addr_type, u32 passkey,
                             u8 entered);
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, u8 status);
-int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
-int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
-int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
-                                  u8 status);
-int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
-int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
-                                           u8 *randomizer, u8 status);
-int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                     u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
-                     u8 ssp, u8 *eir, u16 eir_len);
-int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, s8 rssi, u8 *name, u8 name_len);
-int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
+void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, u8 status);
+void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
+void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
+void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
+                                   u8 status);
+void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
+void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+                                            u8 *randomizer, u8 status);
+void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
+                      u8 ssp, u8 *eir, u16 eir_len);
+void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, s8 rssi, u8 *name, u8 name_len);
+void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
 int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-bool mgmt_valid_hdev(struct hci_dev *hdev);
-int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);
+void mgmt_reenable_advertising(struct hci_dev *hdev);
 
 /* HCI info for socket */
 #define hci_pi(sk) ((struct hci_pinfo *) sk)
@@ -1208,15 +1160,11 @@ struct hci_sec_filter {
 #define hci_req_lock(d)                mutex_lock(&d->req_lock)
 #define hci_req_unlock(d)      mutex_unlock(&d->req_lock)
 
-void hci_update_ad(struct hci_request *req);
-
 void 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, __u8 rand[8],
                                                        __u8 ltk[16]);
 
-u8 bdaddr_to_le(u8 bdaddr_type);
-
 #define SCO_AIRMODE_MASK       0x0003
 #define SCO_AIRMODE_CVSD       0x0000
 #define SCO_AIRMODE_TRANSP     0x0003
index f141b5f6e4f17c9447df0d1c93b1775109f7322c..51329905bfaafda1c290a26103914d5879144a01 100644 (file)
@@ -131,6 +131,7 @@ struct l2cap_conninfo {
 
 /* L2CAP fixed channels */
 #define L2CAP_FC_L2CAP         0x02
+#define L2CAP_FC_CONNLESS      0x04
 #define L2CAP_FC_A2MP          0x08
 
 /* L2CAP Control Field bit masks */
@@ -237,6 +238,7 @@ struct l2cap_conn_rsp {
 /* protocol/service multiplexer (PSM) */
 #define L2CAP_PSM_SDP          0x0001
 #define L2CAP_PSM_RFCOMM       0x0003
+#define L2CAP_PSM_3DSP         0x0021
 
 /* channel indentifier */
 #define L2CAP_CID_SIGNALING    0x0001
@@ -433,8 +435,6 @@ struct l2cap_seq_list {
 #define L2CAP_SEQ_LIST_TAIL    0x8000
 
 struct l2cap_chan {
-       struct sock *sk;
-
        struct l2cap_conn       *conn;
        struct hci_conn         *hs_hcon;
        struct hci_chan         *hs_hchan;
@@ -442,7 +442,12 @@ struct l2cap_chan {
 
        __u8            state;
 
+       bdaddr_t        dst;
+       __u8            dst_type;
+       bdaddr_t        src;
+       __u8            src_type;
        __le16          psm;
+       __le16          sport;
        __u16           dcid;
        __u16           scid;
 
@@ -453,8 +458,6 @@ struct l2cap_chan {
        __u8            chan_type;
        __u8            chan_policy;
 
-       __le16          sport;
-
        __u8            sec_level;
 
        __u8            ident;
@@ -546,9 +549,12 @@ struct l2cap_ops {
        void                    (*teardown) (struct l2cap_chan *chan, int err);
        void                    (*close) (struct l2cap_chan *chan);
        void                    (*state_change) (struct l2cap_chan *chan,
-                                                int state);
+                                                int state, int err);
        void                    (*ready) (struct l2cap_chan *chan);
        void                    (*defer) (struct l2cap_chan *chan);
+       void                    (*resume) (struct l2cap_chan *chan);
+       void                    (*set_shutdown) (struct l2cap_chan *chan);
+       long                    (*get_sndtimeo) (struct l2cap_chan *chan);
        struct sk_buff          *(*alloc_skb) (struct l2cap_chan *chan,
                                               unsigned long len, int nb);
 };
@@ -557,9 +563,6 @@ struct l2cap_conn {
        struct hci_conn         *hcon;
        struct hci_chan         *hchan;
 
-       bdaddr_t                *dst;
-       bdaddr_t                *src;
-
        unsigned int            mtu;
 
        __u32                   feat_mask;
@@ -650,6 +653,7 @@ enum {
        FLAG_FLUSHABLE,
        FLAG_EXT_CTRL,
        FLAG_EFS_ENABLE,
+       FLAG_DEFER_SETUP,
 };
 
 enum {
@@ -791,6 +795,19 @@ static inline void l2cap_chan_no_defer(struct l2cap_chan *chan)
 {
 }
 
+static inline void l2cap_chan_no_resume(struct l2cap_chan *chan)
+{
+}
+
+static inline void l2cap_chan_no_set_shutdown(struct l2cap_chan *chan)
+{
+}
+
+static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan)
+{
+       return 0;
+}
+
 extern bool disable_ertm;
 
 int l2cap_init_sockets(void);
@@ -798,7 +815,6 @@ void l2cap_cleanup_sockets(void);
 bool l2cap_is_socket(struct socket *sock);
 
 void __l2cap_connect_rsp_defer(struct l2cap_chan *chan);
-int __l2cap_wait_ack(struct sock *sk);
 
 int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
 int l2cap_add_scid(struct l2cap_chan *chan,  __u16 scid);
index 2ad433bb9a2e88e63dc30a56e0363fd69a1fce4d..518c5c84e39a67ef4c9789eacd8d88117bf6ccbd 100644 (file)
@@ -362,6 +362,13 @@ struct mgmt_cp_set_static_address {
 } __packed;
 #define MGMT_SET_STATIC_ADDRESS_SIZE   6
 
+#define MGMT_OP_SET_SCAN_PARAMS                0x002C
+struct mgmt_cp_set_scan_params {
+       __le16  interval;
+       __le16  window;
+} __packed;
+#define MGMT_SET_SCAN_PARAMS_SIZE      4
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
index 7afd4199d6b6cfab22c74fb6bf0544a887bbd5af..3588f48bfd354828ca6891f6306491fe83696291 100644 (file)
@@ -300,6 +300,8 @@ struct rfcomm_conninfo {
 
 struct rfcomm_pinfo {
        struct bt_sock bt;
+       bdaddr_t src;
+       bdaddr_t dst;
        struct rfcomm_dlc   *dlc;
        u8     channel;
        u8     sec_level;
index e252a31ee6b6389f54d1a2483a5f143d6032d57f..2019d1a0996a80d9d2fc9ef29fb33e102f2864bd 100644 (file)
@@ -55,9 +55,6 @@ struct sco_conninfo {
 struct sco_conn {
        struct hci_conn *hcon;
 
-       bdaddr_t        *dst;
-       bdaddr_t        *src;
-
        spinlock_t      lock;
        struct sock     *sk;
 
@@ -72,6 +69,8 @@ struct sco_conn {
 
 struct sco_pinfo {
        struct bt_sock  bt;
+       bdaddr_t        src;
+       bdaddr_t        dst;
        __u32           flags;
        __u16           setting;
        struct sco_conn *conn;
diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
deleted file mode 100644 (file)
index f8ba07f..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
-   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 __SMP_H
-#define __SMP_H
-
-struct smp_command_hdr {
-       __u8    code;
-} __packed;
-
-#define SMP_CMD_PAIRING_REQ    0x01
-#define SMP_CMD_PAIRING_RSP    0x02
-struct smp_cmd_pairing {
-       __u8    io_capability;
-       __u8    oob_flag;
-       __u8    auth_req;
-       __u8    max_key_size;
-       __u8    init_key_dist;
-       __u8    resp_key_dist;
-} __packed;
-
-#define SMP_IO_DISPLAY_ONLY    0x00
-#define SMP_IO_DISPLAY_YESNO   0x01
-#define SMP_IO_KEYBOARD_ONLY   0x02
-#define SMP_IO_NO_INPUT_OUTPUT 0x03
-#define SMP_IO_KEYBOARD_DISPLAY        0x04
-
-#define SMP_OOB_NOT_PRESENT    0x00
-#define SMP_OOB_PRESENT                0x01
-
-#define SMP_DIST_ENC_KEY       0x01
-#define SMP_DIST_ID_KEY                0x02
-#define SMP_DIST_SIGN          0x04
-
-#define SMP_AUTH_NONE          0x00
-#define SMP_AUTH_BONDING       0x01
-#define SMP_AUTH_MITM          0x04
-
-#define SMP_CMD_PAIRING_CONFIRM        0x03
-struct smp_cmd_pairing_confirm {
-       __u8    confirm_val[16];
-} __packed;
-
-#define SMP_CMD_PAIRING_RANDOM 0x04
-struct smp_cmd_pairing_random {
-       __u8    rand_val[16];
-} __packed;
-
-#define SMP_CMD_PAIRING_FAIL   0x05
-struct smp_cmd_pairing_fail {
-       __u8    reason;
-} __packed;
-
-#define SMP_CMD_ENCRYPT_INFO   0x06
-struct smp_cmd_encrypt_info {
-       __u8    ltk[16];
-} __packed;
-
-#define SMP_CMD_MASTER_IDENT   0x07
-struct smp_cmd_master_ident {
-       __le16  ediv;
-       __u8    rand[8];
-} __packed;
-
-#define SMP_CMD_IDENT_INFO     0x08
-struct smp_cmd_ident_info {
-       __u8    irk[16];
-} __packed;
-
-#define SMP_CMD_IDENT_ADDR_INFO        0x09
-struct smp_cmd_ident_addr_info {
-       __u8    addr_type;
-       bdaddr_t bdaddr;
-} __packed;
-
-#define SMP_CMD_SIGN_INFO      0x0a
-struct smp_cmd_sign_info {
-       __u8    csrk[16];
-} __packed;
-
-#define SMP_CMD_SECURITY_REQ   0x0b
-struct smp_cmd_security_req {
-       __u8    auth_req;
-} __packed;
-
-#define SMP_PASSKEY_ENTRY_FAILED       0x01
-#define SMP_OOB_NOT_AVAIL              0x02
-#define SMP_AUTH_REQUIREMENTS          0x03
-#define SMP_CONFIRM_FAILED             0x04
-#define SMP_PAIRING_NOTSUPP            0x05
-#define SMP_ENC_KEY_SIZE               0x06
-#define SMP_CMD_NOTSUPP                        0x07
-#define SMP_UNSPECIFIED                        0x08
-#define SMP_REPEATED_ATTEMPTS          0x09
-
-#define SMP_MIN_ENC_KEY_SIZE           7
-#define SMP_MAX_ENC_KEY_SIZE           16
-
-#define SMP_FLAG_TK_VALID      1
-#define SMP_FLAG_CFM_PENDING   2
-#define SMP_FLAG_MITM_AUTH     3
-
-struct smp_chan {
-       struct l2cap_conn *conn;
-       u8              preq[7]; /* SMP Pairing Request */
-       u8              prsp[7]; /* SMP Pairing Response */
-       u8              prnd[16]; /* SMP Pairing Random (local) */
-       u8              rrnd[16]; /* SMP Pairing Random (remote) */
-       u8              pcnf[16]; /* SMP Pairing Confirm */
-       u8              tk[16]; /* SMP Temporary Key */
-       u8              enc_key_size;
-       unsigned long   smp_flags;
-       struct crypto_blkcipher *tfm;
-       struct work_struct confirm;
-       struct work_struct random;
-
-};
-
-/* SMP Commands */
-int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
-int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
-int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
-int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
-
-void smp_chan_destroy(struct l2cap_conn *conn);
-
-#endif /* __SMP_H */
index 45f6bf5911042885a999292adb4001f0c93da02a..3eae46cb1acfecadbff979ee17e17aeab40971e7 100644 (file)
@@ -744,6 +744,10 @@ enum station_parameters_apply_mask {
  * @capability: station capability
  * @ext_capab: extended capabilities of the station
  * @ext_capab_len: number of extended capabilities
+ * @supported_channels: supported channels in IEEE 802.11 format
+ * @supported_channels_len: number of supported channels
+ * @supported_oper_classes: supported oper classes in IEEE 802.11 format
+ * @supported_oper_classes_len: number of supported operating classes
  */
 struct station_parameters {
        const u8 *supported_rates;
@@ -763,6 +767,10 @@ struct station_parameters {
        u16 capability;
        const u8 *ext_capab;
        u8 ext_capab_len;
+       const u8 *supported_channels;
+       u8 supported_channels_len;
+       const u8 *supported_oper_classes;
+       u8 supported_oper_classes_len;
 };
 
 /**
@@ -1656,6 +1664,9 @@ struct cfg80211_disassoc_request {
  *     sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
  *     required to assume that the port is unauthorized until authorized by
  *     user space. Otherwise, port is marked authorized by default.
+ * @userspace_handles_dfs: whether user space controls DFS operation, i.e.
+ *     changes the channel when a radar is detected. This is required
+ *     to operate on DFS channels.
  * @basic_rates: bitmap of basic rates to use when creating the IBSS
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
@@ -1673,6 +1684,7 @@ struct cfg80211_ibss_params {
        bool channel_fixed;
        bool privacy;
        bool control_port;
+       bool userspace_handles_dfs;
        int mcast_rate[IEEE80211_NUM_BANDS];
        struct ieee80211_ht_cap ht_capa;
        struct ieee80211_ht_cap ht_capa_mask;
@@ -3053,6 +3065,7 @@ struct cfg80211_cached_keys;
  * @conn: (private) cfg80211 software SME connection state machine data
  * @connect_keys: (private) keys to set after connection is established
  * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
  * @event_lock: (private) lock for event list
  */
@@ -3091,6 +3104,7 @@ struct wireless_dev {
        struct ieee80211_channel *channel;
 
        bool ibss_fixed;
+       bool ibss_dfs_possible;
 
        bool ps;
        int ps_timeout;
@@ -3483,6 +3497,15 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
 const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
                                               u32 center_freq);
 
+/**
+ * reg_initiator_name - map regulatory request initiator enum to name
+ * @initiator: the regulatory request initiator
+ *
+ * You can use this to map the regulatory request initiator enum to a
+ * proper string representation.
+ */
+const char *reg_initiator_name(enum nl80211_reg_initiator initiator);
+
 /*
  * callbacks for asynchronous cfg80211 methods, notification
  * functions and BSS handling helpers
index f386c480e1341dd70308d02300dc26f60af275f0..7ceed99a05bc79218a841352a454a980df48741b 100644 (file)
@@ -1503,6 +1503,10 @@ struct ieee80211_tx_control {
  * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
  *     only, to allow getting TBTT of a DTIM beacon.
  *
+ * @IEEE80211_HW_SUPPORTS_HT_CCK_RATES: Hardware supports mixing HT/CCK rates
+ *     and can cope with CCK rates in an aggregation session (e.g. by not
+ *     using aggregation for such frames.)
+ *
  * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
  *     for a single active channel while using channel contexts. When support
  *     is not enabled the default action is to disconnect when getting the
@@ -4567,4 +4571,18 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
                                    struct cfg80211_wowlan_wakeup *wakeup,
                                    gfp_t gfp);
 
+/**
+ * ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface
+ * @skb: frame to be sent from within the driver
+ * @band: the band to transmit on
+ * @sta: optional pointer to get the station to send the frame to
+ *
+ * Note: must be called under RCU lock
+ */
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, struct sk_buff *skb,
+                             int band, struct ieee80211_sta **sta);
+
 #endif /* MAC80211_H */
diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h
new file mode 100644 (file)
index 0000000..36acecd
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __NFC_DIGITAL_H
+#define __NFC_DIGITAL_H
+
+#include <linux/skbuff.h>
+#include <net/nfc/nfc.h>
+
+/**
+ * Configuration types for in_configure_hw and tg_configure_hw.
+ */
+enum {
+       NFC_DIGITAL_CONFIG_RF_TECH = 0,
+       NFC_DIGITAL_CONFIG_FRAMING,
+};
+
+/**
+ * RF technology values passed as param argument to in_configure_hw and
+ * tg_configure_hw for NFC_DIGITAL_CONFIG_RF_TECH configuration type.
+ */
+enum {
+       NFC_DIGITAL_RF_TECH_106A = 0,
+       NFC_DIGITAL_RF_TECH_212F,
+       NFC_DIGITAL_RF_TECH_424F,
+
+       NFC_DIGITAL_RF_TECH_LAST,
+};
+
+/**
+ * Framing configuration passed as param argument to in_configure_hw and
+ * tg_configure_hw for NFC_DIGITAL_CONFIG_FRAMING configuration type.
+ */
+enum {
+       NFC_DIGITAL_FRAMING_NFCA_SHORT = 0,
+       NFC_DIGITAL_FRAMING_NFCA_STANDARD,
+       NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A,
+
+       NFC_DIGITAL_FRAMING_NFCA_T1T,
+       NFC_DIGITAL_FRAMING_NFCA_T2T,
+       NFC_DIGITAL_FRAMING_NFCA_NFC_DEP,
+
+       NFC_DIGITAL_FRAMING_NFCF,
+       NFC_DIGITAL_FRAMING_NFCF_T3T,
+       NFC_DIGITAL_FRAMING_NFCF_NFC_DEP,
+       NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED,
+
+       NFC_DIGITAL_FRAMING_LAST,
+};
+
+#define DIGITAL_MDAA_NFCID1_SIZE 3
+
+struct digital_tg_mdaa_params {
+       u16 sens_res;
+       u8 nfcid1[DIGITAL_MDAA_NFCID1_SIZE];
+       u8 sel_res;
+
+       u8 nfcid2[NFC_NFCID2_MAXSIZE];
+       u16 sc;
+};
+
+struct nfc_digital_dev;
+
+/**
+ * nfc_digital_cmd_complete_t - Definition of command result callback
+ *
+ * @ddev: nfc_digital_device ref
+ * @arg: user data
+ * @resp: response data
+ *
+ * resp pointer can be an error code and will be checked with IS_ERR() macro.
+ * The callback is responsible for freeing resp sk_buff.
+ */
+typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev,
+                                          void *arg, struct sk_buff *resp);
+
+/**
+ * Device side NFC Digital operations
+ *
+ * Initiator mode:
+ * @in_configure_hw: Hardware configuration for RF technology and communication
+ *     framing in initiator mode. This is a synchronous function.
+ * @in_send_cmd: Initiator mode data exchange using RF technology and framing
+ *     previously set with in_configure_hw. The peer response is returned
+ *     through callback cb. If an io error occurs or the peer didn't reply
+ *     within the specified timeout (ms), the error code is passed back through
+ *     the resp pointer. This is an asynchronous function.
+ *
+ * Target mode: Only NFC-DEP protocol is supported in target mode.
+ * @tg_configure_hw: Hardware configuration for RF technology and communication
+ *     framing in target mode. This is a synchronous function.
+ * @tg_send_cmd: Target mode data exchange using RF technology and framing
+ *     previously set with tg_configure_hw. The peer next command is returned
+ *     through callback cb. If an io error occurs or the peer didn't reply
+ *     within the specified timeout (ms), the error code is passed back through
+ *     the resp pointer. This is an asynchronous function.
+ * @tg_listen: Put the device in listen mode waiting for data from the peer
+ *     device. This is an asynchronous function.
+ * @tg_listen_mdaa: If supported, put the device in automatic listen mode with
+ *     mode detection and automatic anti-collision. In this mode, the device
+ *     automatically detects the RF technology and executes the anti-collision
+ *     detection using the command responses specified in mdaa_params. The
+ *     mdaa_params structure contains SENS_RES, NFCID1, and SEL_RES for 106A RF
+ *     tech. NFCID2 and system code (sc) for 212F and 424F. The driver returns
+ *     the NFC-DEP ATR_REQ command through cb. The digital stack deducts the RF
+ *     tech by analyzing the SoD of the frame containing the ATR_REQ command.
+ *     This is an asynchronous function.
+ *
+ * @switch_rf: Turns device radio on or off. The stack does not call explicitly
+ *     switch_rf to turn the radio on. A call to in|tg_configure_hw must turn
+ *     the device radio on.
+ * @abort_cmd: Discard the last sent command.
+ */
+struct nfc_digital_ops {
+       int (*in_configure_hw)(struct nfc_digital_dev *ddev, int type,
+                              int param);
+       int (*in_send_cmd)(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+                          u16 timeout, nfc_digital_cmd_complete_t cb,
+                          void *arg);
+
+       int (*tg_configure_hw)(struct nfc_digital_dev *ddev, int type,
+                              int param);
+       int (*tg_send_cmd)(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+                          u16 timeout, nfc_digital_cmd_complete_t cb,
+                          void *arg);
+       int (*tg_listen)(struct nfc_digital_dev *ddev, u16 timeout,
+                        nfc_digital_cmd_complete_t cb, void *arg);
+       int (*tg_listen_mdaa)(struct nfc_digital_dev *ddev,
+                             struct digital_tg_mdaa_params *mdaa_params,
+                             u16 timeout, nfc_digital_cmd_complete_t cb,
+                             void *arg);
+
+       int (*switch_rf)(struct nfc_digital_dev *ddev, bool on);
+       void (*abort_cmd)(struct nfc_digital_dev *ddev);
+};
+
+#define NFC_DIGITAL_POLL_MODE_COUNT_MAX        6 /* 106A, 212F, and 424F in & tg */
+
+typedef int (*digital_poll_t)(struct nfc_digital_dev *ddev, u8 rf_tech);
+
+struct digital_poll_tech {
+       u8 rf_tech;
+       digital_poll_t poll_func;
+};
+
+/**
+ * Driver capabilities - bit mask made of the following values
+ *
+ * @NFC_DIGITAL_DRV_CAPS_IN_CRC: The driver handles CRC calculation in initiator
+ *     mode.
+ * @NFC_DIGITAL_DRV_CAPS_TG_CRC: The driver handles CRC calculation in target
+ *     mode.
+ */
+#define NFC_DIGITAL_DRV_CAPS_IN_CRC    0x0001
+#define NFC_DIGITAL_DRV_CAPS_TG_CRC    0x0002
+
+struct nfc_digital_dev {
+       struct nfc_dev *nfc_dev;
+       struct nfc_digital_ops *ops;
+
+       u32 protocols;
+
+       int tx_headroom;
+       int tx_tailroom;
+
+       u32 driver_capabilities;
+       void *driver_data;
+
+       struct digital_poll_tech poll_techs[NFC_DIGITAL_POLL_MODE_COUNT_MAX];
+       u8 poll_tech_count;
+       u8 poll_tech_index;
+       struct mutex poll_lock;
+
+       struct work_struct cmd_work;
+       struct work_struct cmd_complete_work;
+       struct list_head cmd_queue;
+       struct mutex cmd_lock;
+
+       struct work_struct poll_work;
+
+       u8 curr_protocol;
+       u8 curr_rf_tech;
+       u8 curr_nfc_dep_pni;
+
+       int (*skb_check_crc)(struct sk_buff *skb);
+       void (*skb_add_crc)(struct sk_buff *skb);
+};
+
+struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops,
+                                                   __u32 supported_protocols,
+                                                   __u32 driver_capabilities,
+                                                   int tx_headroom,
+                                                   int tx_tailroom);
+void nfc_digital_free_device(struct nfc_digital_dev *ndev);
+int nfc_digital_register_device(struct nfc_digital_dev *ndev);
+void nfc_digital_unregister_device(struct nfc_digital_dev *ndev);
+
+static inline void nfc_digital_set_parent_dev(struct nfc_digital_dev *ndev,
+                                             struct device *dev)
+{
+       nfc_set_parent_dev(ndev->nfc_dev, dev);
+}
+
+static inline void nfc_digital_set_drvdata(struct nfc_digital_dev *dev,
+                                          void *data)
+{
+       dev->driver_data = data;
+}
+
+static inline void *nfc_digital_get_drvdata(struct nfc_digital_dev *dev)
+{
+       return dev->driver_data;
+}
+
+#endif /* __NFC_DIGITAL_H */
index b64b7bce4b94302a8edb923bc54bc1e4b2c8b997..2eca2960ca9c54116bd187b6e1e18b70060f0282 100644 (file)
 
 #include <net/nfc/nfc.h>
 
-struct nfc_phy_ops {
-       int (*write)(void *dev_id, struct sk_buff *skb);
-       int (*enable)(void *dev_id);
-       void (*disable)(void *dev_id);
-};
-
 struct nfc_hci_dev;
 
 struct nfc_hci_ops {
index 88785e5c6b2cf6d2c32a8af553259a9d21678025..e5aa5acafea0e0e176e83ce50d58d33529be4669 100644 (file)
 #define NCI_GID_NFCEE_MGMT                                     0x2
 #define NCI_GID_PROPRIETARY                                    0xf
 
+/* ----- NCI over SPI head/crc(tail) room needed for outgoing frames ----- */
+#define NCI_SPI_HDR_LEN                                                4
+#define NCI_SPI_CRC_LEN                                                2
+
 /* ---- NCI Packet structures ---- */
 #define NCI_CTRL_HDR_SIZE                                      3
 #define NCI_DATA_HDR_SIZE                                      3
index 99fc1f3a392af172b2c3d5b5b499898f5cfbae39..6126f1f992b40068923771cf0a7967395f88017e 100644 (file)
@@ -207,19 +207,9 @@ int nci_to_errno(__u8 code);
 #define NCI_SPI_CRC_ENABLED    0x01
 
 /* ----- NCI SPI structures ----- */
-struct nci_spi_dev;
-
-struct nci_spi_ops {
-       int (*open)(struct nci_spi_dev *ndev);
-       int (*close)(struct nci_spi_dev *ndev);
-       void (*assert_int)(struct nci_spi_dev *ndev);
-       void (*deassert_int)(struct nci_spi_dev *ndev);
-};
-
-struct nci_spi_dev {
-       struct nci_dev          *nci_dev;
+struct nci_spi {
+       struct nci_dev          *ndev;
        struct spi_device       *spi;
-       struct nci_spi_ops      *ops;
 
        unsigned int            xfer_udelay;    /* microseconds delay between
                                                  transactions */
@@ -227,31 +217,15 @@ struct nci_spi_dev {
 
        struct completion       req_completion;
        u8                      req_result;
-
-       void                    *driver_data;
 };
 
-/* ----- NCI SPI Devices ----- */
-struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
-                                               struct nci_spi_ops *ops,
-                                               u32 supported_protocols,
-                                               u32 supported_se,
-                                               u8 acknowledge_mode,
-                                               unsigned int delay);
-void nci_spi_free_device(struct nci_spi_dev *ndev);
-int nci_spi_register_device(struct nci_spi_dev *ndev);
-void nci_spi_unregister_device(struct nci_spi_dev *ndev);
-int nci_spi_recv_frame(struct nci_spi_dev *ndev);
-
-static inline void nci_spi_set_drvdata(struct nci_spi_dev *ndev,
-                                           void *data)
-{
-       ndev->driver_data = data;
-}
-
-static inline void *nci_spi_get_drvdata(struct nci_spi_dev *ndev)
-{
-       return ndev->driver_data;
-}
+/* ----- NCI SPI ----- */
+struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
+                                    u8 acknowledge_mode, unsigned int delay,
+                                    struct nci_dev *ndev);
+int nci_spi_send(struct nci_spi *nspi,
+                struct completion *write_handshake_completion,
+                struct sk_buff *skb);
+struct sk_buff *nci_spi_read(struct nci_spi *nspi);
 
 #endif /* __NCI_CORE_H */
index f68ee68e4e3e97cd055bc8cc760ab6bba9a11a4b..82fc4e43fc6e9ec156e19e18dddc4d3ec1fbe037 100644 (file)
 #include <linux/device.h>
 #include <linux/skbuff.h>
 
-#define nfc_dev_info(dev, fmt, arg...) dev_info((dev), "NFC: " fmt "\n", ## arg)
-#define nfc_dev_err(dev, fmt, arg...) dev_err((dev), "NFC: " fmt "\n", ## arg)
-#define nfc_dev_dbg(dev, fmt, arg...) dev_dbg((dev), fmt "\n", ## arg)
+#define nfc_info(dev, fmt, ...) dev_info((dev), "NFC: " fmt, ##__VA_ARGS__)
+#define nfc_err(dev, fmt, ...) dev_err((dev), "NFC: " fmt, ##__VA_ARGS__)
+
+struct nfc_phy_ops {
+       int (*write)(void *dev_id, struct sk_buff *skb);
+       int (*enable)(void *dev_id);
+       void (*disable)(void *dev_id);
+};
 
 struct nfc_dev;
 
@@ -48,6 +53,8 @@ struct nfc_dev;
 typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb,
                                                                int err);
 
+typedef void (*se_io_cb_t)(void *context, u8 *apdu, size_t apdu_len, int err);
+
 struct nfc_target;
 
 struct nfc_ops {
@@ -74,12 +81,23 @@ struct nfc_ops {
        int (*discover_se)(struct nfc_dev *dev);
        int (*enable_se)(struct nfc_dev *dev, u32 se_idx);
        int (*disable_se)(struct nfc_dev *dev, u32 se_idx);
+       int (*se_io) (struct nfc_dev *dev, u32 se_idx,
+                     u8 *apdu, size_t apdu_length,
+                     se_io_cb_t cb, void *cb_context);
 };
 
 #define NFC_TARGET_IDX_ANY -1
 #define NFC_MAX_GT_LEN 48
 #define NFC_ATR_RES_GT_OFFSET 15
 
+/**
+ * struct nfc_target - NFC target descriptiom
+ *
+ * @sens_res: 2 bytes describing the target SENS_RES response, if the target
+ *     is a type A one. The %sens_res most significant byte must be byte 2
+ *     as described by the NFC Forum digital specification (i.e. the platform
+ *     configuration one) while %sens_res least significant byte is byte 1.
+ */
 struct nfc_target {
        u32 idx;
        u32 supported_protocols;
@@ -243,5 +261,6 @@ void nfc_driver_failure(struct nfc_dev *dev, int err);
 
 int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
 int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
+struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx);
 
 #endif /* __NET_NFC_H */
index 29bed72a4ac43e4bd141b87a8b5e9d410ab21cff..6ad6cc03ccd3aeec4074f187836efac62ed31a0f 100644 (file)
@@ -85,6 +85,7 @@
  *     a specific SE notifies us about the end of a transaction. The parameter
  *     for this event is the application ID (AID).
  * @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller.
+ * @NFC_CMD_SE_IO: Send/Receive APDUs to/from the selected secure element.
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -114,6 +115,7 @@ enum nfc_commands {
        NFC_EVENT_SE_CONNECTIVITY,
        NFC_EVENT_SE_TRANSACTION,
        NFC_CMD_GET_SE,
+       NFC_CMD_SE_IO,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -147,6 +149,7 @@ enum nfc_commands {
  * @NFC_ATTR_SE_INDEX: Secure element index
  * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
  * @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status
+ * @NFC_ATTR_APDU: Secure element APDU
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -174,6 +177,7 @@ enum nfc_attrs {
        NFC_ATTR_SE_TYPE,
        NFC_ATTR_SE_AID,
        NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS,
+       NFC_ATTR_SE_APDU,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
index fde2c021b26dbe3be5e00a9c47de5dbdb7d60b80..f752e9821e717ee68066f4d8467775a8bee2fd78 100644 (file)
@@ -988,7 +988,7 @@ enum nl80211_commands {
  *     to query the CRDA to retrieve one regulatory domain. This attribute can
  *     also be used by userspace to query the kernel for the currently set
  *     regulatory domain. We chose an alpha2 as that is also used by the
- *     IEEE-802.11d country information element to identify a country.
+ *     IEEE-802.11 country information element to identify a country.
  *     Users can also simply ask the wireless core to set regulatory domain
  *     to a specific alpha2.
  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
@@ -1496,6 +1496,18 @@ enum nl80211_commands {
  * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
  *     As specified in the &enum nl80211_rxmgmt_flags.
  *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ *      supported operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ *     controls DFS operation in IBSS mode. If the flag is included in
+ *     %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ *     channels and reports radar events to userspace. Userspace is required
+ *     to react to radar events, e.g. initiate a channel switch or leave the
+ *     IBSS network.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1806,6 +1818,12 @@ enum nl80211_attrs {
 
        NL80211_ATTR_RXMGMT_FLAGS,
 
+       NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+       NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+       NL80211_ATTR_HANDLE_DFS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3860,13 +3878,12 @@ enum nl80211_radar_event {
  *
  * Channel states used by the DFS code.
  *
- * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
  *     check (CAC) must be performed before using it for AP or IBSS.
- * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
  *     is therefore marked as not available.
- * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
  */
-
 enum nl80211_dfs_state {
        NL80211_DFS_USABLE,
        NL80211_DFS_UNAVAILABLE,
index 17f33a62f6db559824d9dc1b3973acb779fa9162..efcd108822c43134e3d64755e24c16410f0598d5 100644 (file)
@@ -15,8 +15,9 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "a2mp.h"
+#include "amp.h"
 
 /* Global AMP Manager list */
 LIST_HEAD(amp_mgr_list);
@@ -75,33 +76,26 @@ u8 __next_ident(struct amp_mgr *mgr)
        return mgr->ident;
 }
 
-static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
-{
-       cl->id = 0;
-       cl->type = 0;
-       cl->status = 1;
-}
-
 /* hci_dev_list shall be locked */
-static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
+static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl)
 {
-       int i = 0;
        struct hci_dev *hdev;
+       int i = 1;
 
-       __a2mp_cl_bredr(cl);
+       cl[0].id = AMP_ID_BREDR;
+       cl[0].type = AMP_TYPE_BREDR;
+       cl[0].status = AMP_STATUS_BLUETOOTH_ONLY;
 
        list_for_each_entry(hdev, &hci_dev_list, list) {
-               /* Iterate through AMP controllers */
-               if (hdev->id == HCI_BREDR_ID)
-                       continue;
-
-               /* Starting from second entry */
-               if (++i >= num_ctrl)
-                       return;
-
-               cl[i].id = hdev->id;
-               cl[i].type = hdev->amp_type;
-               cl[i].status = hdev->amp_status;
+               if (hdev->dev_type == HCI_AMP) {
+                       cl[i].id = hdev->id;
+                       cl[i].type = hdev->amp_type;
+                       if (test_bit(HCI_UP, &hdev->flags))
+                               cl[i].status = hdev->amp_status;
+                       else
+                               cl[i].status = AMP_STATUS_POWERED_DOWN;
+                       i++;
+               }
        }
 }
 
@@ -129,6 +123,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
        struct a2mp_discov_rsp *rsp;
        u16 ext_feat;
        u8 num_ctrl;
+       struct hci_dev *hdev;
 
        if (len < sizeof(*req))
                return -EINVAL;
@@ -152,7 +147,14 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
 
        read_lock(&hci_dev_list_lock);
 
-       num_ctrl = __hci_num_ctrl();
+       /* at minimum the BR/EDR needs to be listed */
+       num_ctrl = 1;
+
+       list_for_each_entry(hdev, &hci_dev_list, list) {
+               if (hdev->dev_type == HCI_AMP)
+                       num_ctrl++;
+       }
+
        len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
        rsp = kmalloc(len, GFP_ATOMIC);
        if (!rsp) {
@@ -163,7 +165,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
        rsp->mtu = __constant_cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
        rsp->ext_feat = 0;
 
-       __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
+       __a2mp_add_cl(mgr, rsp->cl);
 
        read_unlock(&hci_dev_list_lock);
 
@@ -208,7 +210,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
                BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type,
                       cl->status);
 
-               if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) {
+               if (cl->id != AMP_ID_BREDR && cl->type != AMP_TYPE_BREDR) {
                        struct a2mp_info_req req;
 
                        found = true;
@@ -344,7 +346,7 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
        tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
 
        hdev = hci_dev_get(req->id);
-       if (!hdev || hdev->amp_type == HCI_BREDR || tmp) {
+       if (!hdev || hdev->amp_type == AMP_TYPE_BREDR || tmp) {
                struct a2mp_amp_assoc_rsp rsp;
                rsp.id = req->id;
 
@@ -451,7 +453,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
        rsp.remote_id = req->local_id;
 
        hdev = hci_dev_get(req->remote_id);
-       if (!hdev || hdev->amp_type != HCI_AMP) {
+       if (!hdev || hdev->amp_type == AMP_TYPE_BREDR) {
                rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
                goto send_rsp;
        }
@@ -535,7 +537,8 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
                goto send_rsp;
        }
 
-       hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, mgr->l2cap_conn->dst);
+       hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
+                                      &mgr->l2cap_conn->hcon->dst);
        if (!hcon) {
                BT_ERR("No phys link exist");
                rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS;
@@ -669,7 +672,8 @@ static void a2mp_chan_close_cb(struct l2cap_chan *chan)
        l2cap_chan_put(chan);
 }
 
-static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state)
+static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state,
+                                     int err)
 {
        struct amp_mgr *mgr = chan->data;
 
@@ -706,6 +710,9 @@ static struct l2cap_ops a2mp_chan_ops = {
        .teardown = l2cap_chan_no_teardown,
        .ready = l2cap_chan_no_ready,
        .defer = l2cap_chan_no_defer,
+       .resume = l2cap_chan_no_resume,
+       .set_shutdown = l2cap_chan_no_set_shutdown,
+       .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
 };
 
 static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
@@ -829,6 +836,9 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
 {
        struct amp_mgr *mgr;
 
+       if (conn->hcon->type != ACL_LINK)
+               return NULL;
+
        mgr = amp_mgr_create(conn, false);
        if (!mgr) {
                BT_ERR("Could not create AMP manager");
@@ -871,7 +881,7 @@ void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
        rsp.id = hdev->id;
        rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
 
-       if (hdev->amp_type != HCI_BREDR) {
+       if (hdev->amp_type != AMP_TYPE_BREDR) {
                rsp.status = 0;
                rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
                rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
diff --git a/net/bluetooth/a2mp.h b/net/bluetooth/a2mp.h
new file mode 100644 (file)
index 0000000..487b54c
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+   Copyright (c) 2010,2011 Code Aurora Forum.  All rights reserved.
+   Copyright (c) 2011,2012 Intel Corp.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 and
+   only version 2 as published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
+
+#ifndef __A2MP_H
+#define __A2MP_H
+
+#include <net/bluetooth/l2cap.h>
+
+#define A2MP_FEAT_EXT  0x8000
+
+enum amp_mgr_state {
+       READ_LOC_AMP_INFO,
+       READ_LOC_AMP_ASSOC,
+       READ_LOC_AMP_ASSOC_FINAL,
+       WRITE_REMOTE_AMP_ASSOC,
+};
+
+struct amp_mgr {
+       struct list_head        list;
+       struct l2cap_conn       *l2cap_conn;
+       struct l2cap_chan       *a2mp_chan;
+       struct l2cap_chan       *bredr_chan;
+       struct kref             kref;
+       __u8                    ident;
+       __u8                    handle;
+       unsigned long           state;
+       unsigned long           flags;
+
+       struct list_head        amp_ctrls;
+       struct mutex            amp_ctrls_lock;
+};
+
+struct a2mp_cmd {
+       __u8    code;
+       __u8    ident;
+       __le16  len;
+       __u8    data[0];
+} __packed;
+
+/* A2MP command codes */
+#define A2MP_COMMAND_REJ         0x01
+struct a2mp_cmd_rej {
+       __le16  reason;
+       __u8    data[0];
+} __packed;
+
+#define A2MP_DISCOVER_REQ        0x02
+struct a2mp_discov_req {
+       __le16  mtu;
+       __le16  ext_feat;
+} __packed;
+
+struct a2mp_cl {
+       __u8    id;
+       __u8    type;
+       __u8    status;
+} __packed;
+
+#define A2MP_DISCOVER_RSP        0x03
+struct a2mp_discov_rsp {
+       __le16     mtu;
+       __le16     ext_feat;
+       struct a2mp_cl cl[0];
+} __packed;
+
+#define A2MP_CHANGE_NOTIFY       0x04
+#define A2MP_CHANGE_RSP          0x05
+
+#define A2MP_GETINFO_REQ         0x06
+struct a2mp_info_req {
+       __u8       id;
+} __packed;
+
+#define A2MP_GETINFO_RSP         0x07
+struct a2mp_info_rsp {
+       __u8    id;
+       __u8    status;
+       __le32  total_bw;
+       __le32  max_bw;
+       __le32  min_latency;
+       __le16  pal_cap;
+       __le16  assoc_size;
+} __packed;
+
+#define A2MP_GETAMPASSOC_REQ     0x08
+struct a2mp_amp_assoc_req {
+       __u8    id;
+} __packed;
+
+#define A2MP_GETAMPASSOC_RSP     0x09
+struct a2mp_amp_assoc_rsp {
+       __u8    id;
+       __u8    status;
+       __u8    amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_REQ  0x0A
+#define A2MP_DISCONNPHYSLINK_REQ 0x0C
+struct a2mp_physlink_req {
+       __u8    local_id;
+       __u8    remote_id;
+       __u8    amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_RSP  0x0B
+#define A2MP_DISCONNPHYSLINK_RSP 0x0D
+struct a2mp_physlink_rsp {
+       __u8    local_id;
+       __u8    remote_id;
+       __u8    status;
+} __packed;
+
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS                    0x00
+#define A2MP_STATUS_INVALID_CTRL_ID            0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS    0x02
+#define A2MP_STATUS_COLLISION_OCCURED          0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD          0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS           0x05
+#define A2MP_STATUS_SECURITY_VIOLATION         0x06
+
+extern struct list_head amp_mgr_list;
+extern struct mutex amp_mgr_list_lock;
+
+struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
+int amp_mgr_put(struct amp_mgr *mgr);
+u8 __next_ident(struct amp_mgr *mgr);
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+                                      struct sk_buff *skb);
+struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
+void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
+void a2mp_discover_amp(struct l2cap_chan *chan);
+void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
+void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
+void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
+void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
+
+#endif /* __A2MP_H */
index e6e1278dca89750815c9562f92c77f58518225e5..f6a1671ea2ff793bfca0efdd1541765803fb4d03 100644 (file)
 /* Bluetooth address family and sockets. */
 
 #include <linux/module.h>
+#include <linux/debugfs.h>
 #include <asm/ioctls.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <linux/proc_fs.h>
 
-#define VERSION "2.16"
+#define VERSION "2.17"
 
 /* Bluetooth sockets */
 #define BT_MAX_PROTO   8
@@ -221,12 +222,12 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (flags & (MSG_OOB))
                return -EOPNOTSUPP;
 
-       msg->msg_namelen = 0;
-
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb) {
-               if (sk->sk_shutdown & RCV_SHUTDOWN)
+               if (sk->sk_shutdown & RCV_SHUTDOWN) {
+                       msg->msg_namelen = 0;
                        return 0;
+               }
                return err;
        }
 
@@ -238,9 +239,16 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        skb_reset_transport_header(skb);
        err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-       if (err == 0)
+       if (err == 0) {
                sock_recv_ts_and_drops(msg, sk, skb);
 
+               if (bt_sk(sk)->skb_msg_name)
+                       bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
+                                               &msg->msg_namelen);
+               else
+                       msg->msg_namelen = 0;
+       }
+
        skb_free_datagram(sk, skb);
 
        return err ? : copied;
@@ -604,7 +612,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
        struct bt_sock_list *l = s->l;
 
        if (v == SEQ_START_TOKEN) {
-               seq_puts(seq ,"sk               RefCnt Rmem   Wmem   User   Inode  Src Dst Parent");
+               seq_puts(seq ,"sk               RefCnt Rmem   Wmem   User   Inode  Parent");
 
                if (l->custom_seq_show) {
                        seq_putc(seq, ' ');
@@ -617,15 +625,13 @@ static int bt_seq_show(struct seq_file *seq, void *v)
                struct bt_sock *bt = bt_sk(sk);
 
                seq_printf(seq,
-                          "%pK %-6d %-6u %-6u %-6u %-6lu %pMR %pMR %-6lu",
+                          "%pK %-6d %-6u %-6u %-6u %-6lu %-6lu",
                           sk,
                           atomic_read(&sk->sk_refcnt),
                           sk_rmem_alloc_get(sk),
                           sk_wmem_alloc_get(sk),
                           from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
                           sock_i_ino(sk),
-                          &bt->src,
-                          &bt->dst,
                           bt->parent? sock_i_ino(bt->parent): 0LU);
 
                if (l->custom_seq_show) {
@@ -703,12 +709,17 @@ static struct net_proto_family bt_sock_family_ops = {
        .create = bt_sock_create,
 };
 
+struct dentry *bt_debugfs;
+EXPORT_SYMBOL_GPL(bt_debugfs);
+
 static int __init bt_init(void)
 {
        int err;
 
        BT_INFO("Core ver %s", VERSION);
 
+       bt_debugfs = debugfs_create_dir("bluetooth", NULL);
+
        err = bt_sysfs_init();
        if (err < 0)
                return err;
@@ -749,7 +760,6 @@ error:
 
 static void __exit bt_exit(void)
 {
-
        sco_exit();
 
        l2cap_exit();
@@ -759,6 +769,8 @@ static void __exit bt_exit(void)
        sock_unregister(PF_BLUETOOTH);
 
        bt_sysfs_cleanup();
+
+       debugfs_remove_recursive(bt_debugfs);
 }
 
 subsys_initcall(bt_init);
index d459ed43c779d776e453634db290d08f48d5a7c7..bb39509b3f065e2a0d18e1a53cfcfabc8bfe779e 100644 (file)
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
 #include <crypto/hash.h>
 
+#include "a2mp.h"
+#include "amp.h"
+
 /* Remote AMP Controllers interface */
 void amp_ctrl_get(struct amp_ctrl *ctrl)
 {
@@ -110,7 +111,7 @@ static u8 __next_handle(struct amp_mgr *mgr)
 struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
                             u8 remote_id, bool out)
 {
-       bdaddr_t *dst = mgr->l2cap_conn->dst;
+       bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst;
        struct hci_conn *hcon;
 
        hcon = hci_conn_add(hdev, AMP_LINK, dst);
@@ -409,7 +410,8 @@ void amp_create_logical_link(struct l2cap_chan *chan)
        struct hci_cp_create_accept_logical_link cp;
        struct hci_dev *hdev;
 
-       BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst);
+       BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon,
+              &chan->conn->hcon->dst);
 
        if (!hs_hcon)
                return;
diff --git a/net/bluetooth/amp.h b/net/bluetooth/amp.h
new file mode 100644 (file)
index 0000000..7ea3db7
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+   Copyright (c) 2011,2012 Intel Corp.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 and
+   only version 2 as published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
+
+#ifndef __AMP_H
+#define __AMP_H
+
+struct amp_ctrl {
+       struct list_head        list;
+       struct kref             kref;
+       __u8                    id;
+       __u16                   assoc_len_so_far;
+       __u16                   assoc_rem_len;
+       __u16                   assoc_len;
+       __u8                    *assoc;
+};
+
+int amp_ctrl_put(struct amp_ctrl *ctrl);
+void amp_ctrl_get(struct amp_ctrl *ctrl);
+struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id);
+struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id);
+void amp_ctrl_list_flush(struct amp_mgr *mgr);
+
+struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
+                            u8 remote_id, bool out);
+
+int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
+
+void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr);
+void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
+void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
+void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
+                                  struct hci_conn *hcon);
+void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
+                       struct hci_conn *hcon);
+void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
+                       struct hci_conn *hcon);
+void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
+void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
+void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
+void amp_create_logical_link(struct l2cap_chan *chan);
+void amp_disconnect_logical_link(struct hci_chan *hchan);
+void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason);
+
+#endif /* __AMP_H */
index e430b1abcd2fabf102d15cd4f99399c983fcf9f2..a841d3e776c5e091c19efea8423750edd018e946 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/hci_core.h>
 
 #include "bnep.h"
@@ -510,20 +511,13 @@ static int bnep_session(void *arg)
 
 static struct device *bnep_get_device(struct bnep_session *session)
 {
-       bdaddr_t *src = &bt_sk(session->sock->sk)->src;
-       bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
-       struct hci_dev *hdev;
        struct hci_conn *conn;
 
-       hdev = hci_get_route(dst, src);
-       if (!hdev)
+       conn = l2cap_pi(session->sock->sk)->chan->conn->hcon;
+       if (!conn)
                return NULL;
 
-       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
-
-       hci_dev_put(hdev);
-
-       return conn ? &conn->dev : NULL;
+       return &conn->dev;
 }
 
 static struct device_type bnep_type = {
@@ -539,8 +533,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
 
        BT_DBG("");
 
-       baswap((void *) dst, &bt_sk(sock->sk)->dst);
-       baswap((void *) src, &bt_sk(sock->sk)->src);
+       baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
+       baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
 
        /* session struct allocated as private part of net_device */
        dev = alloc_netdev(sizeof(struct bnep_session),
index e0a6ebf2baa6fecd58a0cc13ea6f2906c6614a46..67fe5e84e68f0bffb166bbcfb6cdb736f15ab05e 100644 (file)
@@ -340,20 +340,20 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
 
        down_write(&cmtp_session_sem);
 
-       s = __cmtp_get_session(&bt_sk(sock->sk)->dst);
+       s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst);
        if (s && s->state == BT_CONNECTED) {
                err = -EEXIST;
                goto failed;
        }
 
-       bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
+       bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst);
 
        session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
                                        l2cap_pi(sock->sk)->chan->imtu);
 
        BT_DBG("mtu %d", session->mtu);
 
-       sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst);
+       sprintf(session->name, "%pMR", &session->bdaddr);
 
        session->sock  = sock;
        session->state = BT_CONFIG;
index 514148b7a66b7f6f07e460fb5ca84dc5fae0c271..ba5366c320dacc7d4db4659aa144051baf1b035a 100644 (file)
@@ -28,8 +28,9 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
+#include "a2mp.h"
 
 struct sco_param {
        u16 pkt_type;
@@ -49,30 +50,6 @@ static const struct sco_param sco_param_wideband[] = {
        { EDR_ESCO_MASK | ESCO_EV3,   0x0008 }, /* T1 */
 };
 
-static void hci_le_create_connection(struct hci_conn *conn)
-{
-       struct hci_dev *hdev = conn->hdev;
-       struct hci_cp_le_create_conn cp;
-
-       conn->state = BT_CONNECT;
-       conn->out = true;
-       conn->link_mode |= HCI_LM_MASTER;
-       conn->sec_level = BT_SECURITY_LOW;
-
-       memset(&cp, 0, sizeof(cp));
-       cp.scan_interval = __constant_cpu_to_le16(0x0060);
-       cp.scan_window = __constant_cpu_to_le16(0x0030);
-       bacpy(&cp.peer_addr, &conn->dst);
-       cp.peer_addr_type = conn->dst_type;
-       cp.conn_interval_min = __constant_cpu_to_le16(0x0028);
-       cp.conn_interval_max = __constant_cpu_to_le16(0x0038);
-       cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
-       cp.min_ce_len = __constant_cpu_to_le16(0x0000);
-       cp.max_ce_len = __constant_cpu_to_le16(0x0000);
-
-       hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
-}
-
 static void hci_le_create_connection_cancel(struct hci_conn *conn)
 {
        hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
@@ -340,8 +317,10 @@ static void hci_conn_timeout(struct work_struct *work)
 }
 
 /* Enter sniff mode */
-static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
+static void hci_conn_idle(struct work_struct *work)
 {
+       struct hci_conn *conn = container_of(work, struct hci_conn,
+                                            idle_work.work);
        struct hci_dev *hdev = conn->hdev;
 
        BT_DBG("hcon %p mode %d", conn, conn->mode);
@@ -375,21 +354,12 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
        }
 }
 
-static void hci_conn_idle(unsigned long arg)
+static void hci_conn_auto_accept(struct work_struct *work)
 {
-       struct hci_conn *conn = (void *) arg;
-
-       BT_DBG("hcon %p mode %d", conn, conn->mode);
-
-       hci_conn_enter_sniff_mode(conn);
-}
-
-static void hci_conn_auto_accept(unsigned long arg)
-{
-       struct hci_conn *conn = (void *) arg;
-       struct hci_dev *hdev = conn->hdev;
+       struct hci_conn *conn = container_of(work, struct hci_conn,
+                                            auto_accept_work.work);
 
-       hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
+       hci_send_cmd(conn->hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
                     &conn->dst);
 }
 
@@ -404,6 +374,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
                return NULL;
 
        bacpy(&conn->dst, dst);
+       bacpy(&conn->src, &hdev->bdaddr);
        conn->hdev  = hdev;
        conn->type  = type;
        conn->mode  = HCI_CM_ACTIVE;
@@ -437,9 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        INIT_LIST_HEAD(&conn->chan_list);
 
        INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
-       setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
-       setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
-                   (unsigned long) conn);
+       INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
+       INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
 
        atomic_set(&conn->refcnt, 0);
 
@@ -460,11 +430,9 @@ int hci_conn_del(struct hci_conn *conn)
 
        BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
 
-       del_timer(&conn->idle_timer);
-
        cancel_delayed_work_sync(&conn->disc_work);
-
-       del_timer(&conn->auto_accept_timer);
+       cancel_delayed_work_sync(&conn->auto_accept_work);
+       cancel_delayed_work_sync(&conn->idle_work);
 
        if (conn->type == ACL_LINK) {
                struct hci_conn *sco = conn->link;
@@ -546,34 +514,124 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
 }
 EXPORT_SYMBOL(hci_get_route);
 
+static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
+{
+       struct hci_conn *conn;
+
+       if (status == 0)
+               return;
+
+       BT_ERR("HCI request failed to create LE connection: status 0x%2.2x",
+              status);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+       if (!conn)
+               goto done;
+
+       conn->state = BT_CLOSED;
+
+       mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+                           status);
+
+       hci_proto_connect_cfm(conn, status);
+
+       hci_conn_del(conn);
+
+done:
+       hci_dev_unlock(hdev);
+}
+
+static int hci_create_le_conn(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_cp_le_create_conn cp;
+       struct hci_request req;
+       int err;
+
+       hci_req_init(&req, hdev);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.scan_interval = cpu_to_le16(hdev->le_scan_interval);
+       cp.scan_window = cpu_to_le16(hdev->le_scan_window);
+       bacpy(&cp.peer_addr, &conn->dst);
+       cp.peer_addr_type = conn->dst_type;
+       cp.own_address_type = conn->src_type;
+       cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval);
+       cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval);
+       cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
+       cp.min_ce_len = __constant_cpu_to_le16(0x0000);
+       cp.max_ce_len = __constant_cpu_to_le16(0x0000);
+
+       hci_req_add(&req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
+
+       err = hci_req_run(&req, create_le_conn_complete);
+       if (err) {
+               hci_conn_del(conn);
+               return err;
+       }
+
+       return 0;
+}
+
 static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                                    u8 dst_type, u8 sec_level, u8 auth_type)
 {
-       struct hci_conn *le;
+       struct hci_conn *conn;
+       int err;
 
-       if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags))
+       if (test_bit(HCI_ADVERTISING, &hdev->flags))
                return ERR_PTR(-ENOTSUPP);
 
-       le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
-       if (!le) {
-               le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
-               if (le)
-                       return ERR_PTR(-EBUSY);
+       /* Some devices send ATT messages as soon as the physical link is
+        * established. To be able to handle these ATT messages, the user-
+        * space first establishes the connection and then starts the pairing
+        * process.
+        *
+        * So if a hci_conn object already exists for the following connection
+        * attempt, we simply update pending_sec_level and auth_type fields
+        * and return the object found.
+        */
+       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+       if (conn) {
+               conn->pending_sec_level = sec_level;
+               conn->auth_type = auth_type;
+               goto done;
+       }
 
-               le = hci_conn_add(hdev, LE_LINK, dst);
-               if (!le)
-                       return ERR_PTR(-ENOMEM);
+       /* Since the controller supports only one LE connection attempt at a
+        * time, we return -EBUSY if there is any connection attempt running.
+        */
+       conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+       if (conn)
+               return ERR_PTR(-EBUSY);
 
-               le->dst_type = bdaddr_to_le(dst_type);
-               hci_le_create_connection(le);
-       }
+       conn = hci_conn_add(hdev, LE_LINK, dst);
+       if (!conn)
+               return ERR_PTR(-ENOMEM);
+
+       if (dst_type == BDADDR_LE_PUBLIC)
+               conn->dst_type = ADDR_LE_DEV_PUBLIC;
+       else
+               conn->dst_type = ADDR_LE_DEV_RANDOM;
 
-       le->pending_sec_level = sec_level;
-       le->auth_type = auth_type;
+       conn->src_type = hdev->own_addr_type;
+
+       conn->state = BT_CONNECT;
+       conn->out = true;
+       conn->link_mode |= HCI_LM_MASTER;
+       conn->sec_level = BT_SECURITY_LOW;
+       conn->pending_sec_level = sec_level;
+       conn->auth_type = auth_type;
 
-       hci_conn_hold(le);
+       err = hci_create_le_conn(conn);
+       if (err)
+               return ERR_PTR(err);
 
-       return le;
+done:
+       hci_conn_hold(conn);
+       return conn;
 }
 
 static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
@@ -850,8 +908,8 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
 
 timer:
        if (hdev->idle_timeout > 0)
-               mod_timer(&conn->idle_timer,
-                         jiffies + msecs_to_jiffies(hdev->idle_timeout));
+               queue_delayed_work(hdev->workqueue, &conn->idle_work,
+                                  msecs_to_jiffies(hdev->idle_timeout));
 }
 
 /* Drop all connection on the device */
index 82dbdc6a7e9eadfd359b86b6c7a0c1658362b52a..6ccc4eb9e55e4958f3eb070894a7668b05c98b5b 100644 (file)
@@ -27,8 +27,9 @@
 
 #include <linux/export.h>
 #include <linux/idr.h>
-
 #include <linux/rfkill.h>
+#include <linux/debugfs.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -55,6 +56,586 @@ static void hci_notify(struct hci_dev *hdev, int event)
        hci_sock_dev_event(hdev, event);
 }
 
+/* ---- HCI debugfs entries ---- */
+
+static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
+                            size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       struct sk_buff *skb;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+       int err;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return -ENETDOWN;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags))
+               return -EALREADY;
+
+       hci_req_lock(hdev);
+       if (enable)
+               skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
+                                    HCI_CMD_TIMEOUT);
+       else
+               skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
+                                    HCI_CMD_TIMEOUT);
+       hci_req_unlock(hdev);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       err = -bt_to_errno(skb->data[0]);
+       kfree_skb(skb);
+
+       if (err < 0)
+               return err;
+
+       change_bit(HCI_DUT_MODE, &hdev->dev_flags);
+
+       return count;
+}
+
+static const struct file_operations dut_mode_fops = {
+       .open           = simple_open,
+       .read           = dut_mode_read,
+       .write          = dut_mode_write,
+       .llseek         = default_llseek,
+};
+
+static int features_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       u8 p;
+
+       hci_dev_lock(hdev);
+       for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
+               seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
+                          hdev->features[p][0], hdev->features[p][1],
+                          hdev->features[p][2], hdev->features[p][3],
+                          hdev->features[p][4], hdev->features[p][5],
+                          hdev->features[p][6], hdev->features[p][7]);
+       }
+       if (lmp_le_capable(hdev))
+               seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+                          hdev->le_features[0], hdev->le_features[1],
+                          hdev->le_features[2], hdev->le_features[3],
+                          hdev->le_features[4], hdev->le_features[5],
+                          hdev->le_features[6], hdev->le_features[7]);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int features_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, features_show, inode->i_private);
+}
+
+static const struct file_operations features_fops = {
+       .open           = features_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int blacklist_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bdaddr_list *b;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(b, &hdev->blacklist, list)
+               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int blacklist_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blacklist_show, inode->i_private);
+}
+
+static const struct file_operations blacklist_fops = {
+       .open           = blacklist_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int uuids_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bt_uuid *uuid;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               u8 i, val[16];
+
+               /* The Bluetooth UUID values are stored in big endian,
+                * but with reversed byte order. So convert them into
+                * the right order for the %pUb modifier.
+                */
+               for (i = 0; i < 16; i++)
+                       val[i] = uuid->uuid[15 - i];
+
+               seq_printf(f, "%pUb\n", val);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int uuids_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, uuids_show, inode->i_private);
+}
+
+static const struct file_operations uuids_fops = {
+       .open           = uuids_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int inquiry_cache_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct discovery_state *cache = &hdev->discovery;
+       struct inquiry_entry *e;
+
+       hci_dev_lock(hdev);
+
+       list_for_each_entry(e, &cache->all, all) {
+               struct inquiry_data *data = &e->data;
+               seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+                          &data->bdaddr,
+                          data->pscan_rep_mode, data->pscan_period_mode,
+                          data->pscan_mode, data->dev_class[2],
+                          data->dev_class[1], data->dev_class[0],
+                          __le16_to_cpu(data->clock_offset),
+                          data->rssi, data->ssp_mode, e->timestamp);
+       }
+
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int inquiry_cache_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, inquiry_cache_show, inode->i_private);
+}
+
+static const struct file_operations inquiry_cache_fops = {
+       .open           = inquiry_cache_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int link_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct list_head *p, *n;
+
+       hci_dev_lock(hdev);
+       list_for_each_safe(p, n, &hdev->link_keys) {
+               struct link_key *key = list_entry(p, struct link_key, list);
+               seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
+                          HCI_LINK_KEY_SIZE, key->val, key->pin_len);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int link_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, link_keys_show, inode->i_private);
+}
+
+static const struct file_operations link_keys_fops = {
+       .open           = link_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations use_debug_keys_fops = {
+       .open           = simple_open,
+       .read           = use_debug_keys_read,
+       .llseek         = default_llseek,
+};
+
+static int dev_class_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
+                  hdev->dev_class[1], hdev->dev_class[0]);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int dev_class_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dev_class_show, inode->i_private);
+}
+
+static const struct file_operations dev_class_fops = {
+       .open           = dev_class_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int voice_setting_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->voice_setting;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
+                       NULL, "0x%4.4llx\n");
+
+static int auto_accept_delay_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       hdev->auto_accept_delay = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int auto_accept_delay_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->auto_accept_delay;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
+                       auto_accept_delay_set, "%llu\n");
+
+static int ssp_debug_mode_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+       struct sk_buff *skb;
+       __u8 mode;
+       int err;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return -ENETDOWN;
+
+       hci_req_lock(hdev);
+       mode = val;
+       skb = __hci_cmd_sync(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode),
+                            &mode, HCI_CMD_TIMEOUT);
+       hci_req_unlock(hdev);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       err = -bt_to_errno(skb->data[0]);
+       kfree_skb(skb);
+
+       if (err < 0)
+               return err;
+
+       hci_dev_lock(hdev);
+       hdev->ssp_debug_mode = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int ssp_debug_mode_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->ssp_debug_mode;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get,
+                       ssp_debug_mode_set, "%llu\n");
+
+static int idle_timeout_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val != 0 && (val < 500 || val > 3600000))
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->idle_timeout = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int idle_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->idle_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
+                       idle_timeout_set, "%llu\n");
+
+static int sniff_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
+                       sniff_min_interval_set, "%llu\n");
+
+static int sniff_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
+                       sniff_max_interval_set, "%llu\n");
+
+static int static_address_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "%pMR\n", &hdev->static_addr);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int static_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, static_address_show, inode->i_private);
+}
+
+static const struct file_operations static_address_fops = {
+       .open           = static_address_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int own_address_type_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->own_addr_type = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int own_address_type_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->own_addr_type;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(own_address_type_fops, own_address_type_get,
+                       own_address_type_set, "%llu\n");
+
+static int long_term_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct list_head *p, *n;
+
+       hci_dev_lock(hdev);
+       list_for_each_safe(p, n, &hdev->link_keys) {
+               struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
+               seq_printf(f, "%pMR (type %u) %u %u %u %.4x %*phN %*phN\\n",
+                          &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
+                          ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
+                          8, ltk->rand, 16, ltk->val);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int long_term_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, long_term_keys_show, inode->i_private);
+}
+
+static const struct file_operations long_term_keys_fops = {
+       .open           = long_term_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int conn_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
+                       conn_min_interval_set, "%llu\n");
+
+static int conn_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
+                       conn_max_interval_set, "%llu\n");
+
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -307,11 +888,23 @@ static void amp_init(struct hci_request *req)
        /* Read Local Version */
        hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
 
+       /* Read Local Supported Commands */
+       hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
+
+       /* Read Local Supported Features */
+       hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+
        /* Read Local AMP Info */
        hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
 
        /* Read Data Blk size */
        hci_req_add(req, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
+
+       /* Read Flow Control Mode */
+       hci_req_add(req, HCI_OP_READ_FLOW_CONTROL_MODE, 0, NULL);
+
+       /* Read Location Data */
+       hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
 }
 
 static void hci_init1_req(struct hci_request *req, unsigned long opt)
@@ -341,6 +934,8 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt)
 
 static void bredr_setup(struct hci_request *req)
 {
+       struct hci_dev *hdev = req->hdev;
+
        __le16 param;
        __u8 flt_type;
 
@@ -356,6 +951,12 @@ static void bredr_setup(struct hci_request *req)
        /* Read Voice Setting */
        hci_req_add(req, HCI_OP_READ_VOICE_SETTING, 0, NULL);
 
+       /* Read Number of Supported IAC */
+       hci_req_add(req, HCI_OP_READ_NUM_SUPPORTED_IAC, 0, NULL);
+
+       /* Read Current IAC LAP */
+       hci_req_add(req, HCI_OP_READ_CURRENT_IAC_LAP, 0, NULL);
+
        /* Clear Event Filters */
        flt_type = HCI_FLT_CLEAR_ALL;
        hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
@@ -364,8 +965,10 @@ static void bredr_setup(struct hci_request *req)
        param = __constant_cpu_to_le16(0x7d00);
        hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
 
-       /* Read page scan parameters */
-       if (req->hdev->hci_ver > BLUETOOTH_VER_1_1) {
+       /* AVM Berlin (31), aka "BlueFRITZ!", reports version 1.2,
+        * but it does not support page scan related HCI commands.
+        */
+       if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1) {
                hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL);
                hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL);
        }
@@ -534,6 +1137,14 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
                hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
 
        if (lmp_ssp_capable(hdev)) {
+               /* When SSP is available, then the host features page
+                * should also be available as well. However some
+                * controllers list the max_page as 0 as long as SSP
+                * has not been enabled. To achieve proper debugging
+                * output, force the minimum max_page to 1 at least.
+                */
+               hdev->max_page = 0x01;
+
                if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
                        u8 mode = 0x01;
                        hci_req_add(req, HCI_OP_WRITE_SSP_MODE,
@@ -664,8 +1275,17 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                hci_setup_link_policy(req);
 
        if (lmp_le_capable(hdev)) {
+               /* If the controller has a public BD_ADDR, then by
+                * default use that one. If this is a LE only
+                * controller without one, default to the random
+                * address.
+                */
+               if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+                       hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
+
                hci_set_le_support(req);
-               hci_update_ad(req);
        }
 
        /* Read features beyond page 1 if available */
@@ -699,6 +1319,14 @@ static int __hci_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
+       /* The Device Under Test (DUT) mode is special and available for
+        * all controller types. So just create it early on.
+        */
+       if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
+                                   &dut_mode_fops);
+       }
+
        /* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
         * BR/EDR/LE type controllers. AMP controllers only need the
         * first stage init.
@@ -714,7 +1342,71 @@ static int __hci_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
-       return __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+       err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+       if (err < 0)
+               return err;
+
+       /* Only create debugfs entries during the initial setup
+        * phase and not every time the controller gets powered on.
+        */
+       if (!test_bit(HCI_SETUP, &hdev->dev_flags))
+               return 0;
+
+       debugfs_create_file("features", 0444, hdev->debugfs, hdev,
+                           &features_fops);
+       debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
+                          &hdev->manufacturer);
+       debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
+       debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+       debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
+                           &blacklist_fops);
+       debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+
+       if (lmp_bredr_capable(hdev)) {
+               debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
+                                   hdev, &inquiry_cache_fops);
+               debugfs_create_file("link_keys", 0400, hdev->debugfs,
+                                   hdev, &link_keys_fops);
+               debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
+                                   hdev, &use_debug_keys_fops);
+               debugfs_create_file("dev_class", 0444, hdev->debugfs,
+                                   hdev, &dev_class_fops);
+               debugfs_create_file("voice_setting", 0444, hdev->debugfs,
+                                   hdev, &voice_setting_fops);
+       }
+
+       if (lmp_ssp_capable(hdev)) {
+               debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
+                                   hdev, &auto_accept_delay_fops);
+               debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs,
+                                   hdev, &ssp_debug_mode_fops);
+       }
+
+       if (lmp_sniff_capable(hdev)) {
+               debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
+                                   hdev, &idle_timeout_fops);
+               debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_min_interval_fops);
+               debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_max_interval_fops);
+       }
+
+       if (lmp_le_capable(hdev)) {
+               debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
+                                 &hdev->le_white_list_size);
+               debugfs_create_file("static_address", 0444, hdev->debugfs,
+                                  hdev, &static_address_fops);
+               debugfs_create_file("own_address_type", 0644, hdev->debugfs,
+                                   hdev, &own_address_type_fops);
+               debugfs_create_file("long_term_keys", 0400, hdev->debugfs,
+                                   hdev, &long_term_keys_fops);
+               debugfs_create_file("conn_min_interval", 0644, hdev->debugfs,
+                                   hdev, &conn_min_interval_fops);
+               debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
+                                   hdev, &conn_max_interval_fops);
+       }
+
+       return 0;
 }
 
 static void hci_scan_req(struct hci_request *req, unsigned long opt)
@@ -1036,6 +1728,11 @@ int hci_inquiry(void __user *arg)
                goto done;
        }
 
+       if (hdev->dev_type != HCI_BREDR) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
        if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
                err = -EOPNOTSUPP;
                goto done;
@@ -1100,89 +1797,6 @@ done:
        return err;
 }
 
-static u8 create_ad(struct hci_dev *hdev, u8 *ptr)
-{
-       u8 ad_len = 0, flags = 0;
-       size_t name_len;
-
-       if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
-               flags |= LE_AD_GENERAL;
-
-       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
-               if (lmp_le_br_capable(hdev))
-                       flags |= LE_AD_SIM_LE_BREDR_CTRL;
-               if (lmp_host_le_br_capable(hdev))
-                       flags |= LE_AD_SIM_LE_BREDR_HOST;
-       } else {
-               flags |= LE_AD_NO_BREDR;
-       }
-
-       if (flags) {
-               BT_DBG("adv flags 0x%02x", flags);
-
-               ptr[0] = 2;
-               ptr[1] = EIR_FLAGS;
-               ptr[2] = flags;
-
-               ad_len += 3;
-               ptr += 3;
-       }
-
-       if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
-               ptr[0] = 2;
-               ptr[1] = EIR_TX_POWER;
-               ptr[2] = (u8) hdev->adv_tx_power;
-
-               ad_len += 3;
-               ptr += 3;
-       }
-
-       name_len = strlen(hdev->dev_name);
-       if (name_len > 0) {
-               size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
-
-               if (name_len > max_len) {
-                       name_len = max_len;
-                       ptr[1] = EIR_NAME_SHORT;
-               } else
-                       ptr[1] = EIR_NAME_COMPLETE;
-
-               ptr[0] = name_len + 1;
-
-               memcpy(ptr + 2, hdev->dev_name, name_len);
-
-               ad_len += (name_len + 2);
-               ptr += (name_len + 2);
-       }
-
-       return ad_len;
-}
-
-void hci_update_ad(struct hci_request *req)
-{
-       struct hci_dev *hdev = req->hdev;
-       struct hci_cp_le_set_adv_data cp;
-       u8 len;
-
-       if (!lmp_le_capable(hdev))
-               return;
-
-       memset(&cp, 0, sizeof(cp));
-
-       len = create_ad(hdev, cp.data);
-
-       if (hdev->adv_data_len == len &&
-           memcmp(cp.data, hdev->adv_data, len) == 0)
-               return;
-
-       memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
-       hdev->adv_data_len = len;
-
-       cp.length = len;
-
-       hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
-}
-
 static int hci_dev_do_open(struct hci_dev *hdev)
 {
        int ret = 0;
@@ -1196,13 +1810,29 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                goto done;
        }
 
-       /* Check for rfkill but allow the HCI setup stage to proceed
-        * (which in itself doesn't cause any RF activity).
-        */
-       if (test_bit(HCI_RFKILLED, &hdev->dev_flags) &&
-           !test_bit(HCI_SETUP, &hdev->dev_flags)) {
-               ret = -ERFKILL;
-               goto done;
+       if (!test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               /* Check for rfkill but allow the HCI setup stage to
+                * proceed (which in itself doesn't cause any RF activity).
+                */
+               if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
+                       ret = -ERFKILL;
+                       goto done;
+               }
+
+               /* Check for valid public address or a configured static
+                * random adddress, but let the HCI setup proceed to
+                * be able to determine if there is a public address
+                * or not.
+                *
+                * This check is only valid for BR/EDR controllers
+                * since AMP controllers do not have an address.
+                */
+               if (hdev->dev_type == HCI_BREDR &&
+                   !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
+                   !bacmp(&hdev->static_addr, BDADDR_ANY)) {
+                       ret = -EADDRNOTAVAIL;
+                       goto done;
+               }
        }
 
        if (test_bit(HCI_UP, &hdev->flags)) {
@@ -1238,7 +1868,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                hci_notify(hdev, HCI_DEV_UP);
                if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
                    !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
-                   mgmt_valid_hdev(hdev)) {
+                   hdev->dev_type == HCI_BREDR) {
                        hci_dev_lock(hdev);
                        mgmt_powered(hdev, 1);
                        hci_dev_unlock(hdev);
@@ -1288,6 +1918,10 @@ int hci_dev_open(__u16 dev)
        if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
                cancel_delayed_work(&hdev->power_off);
 
+       /* After this call it is guaranteed that the setup procedure
+        * has finished. This means that error conditions like RFKILL
+        * or no valid public or static random address apply.
+        */
        flush_workqueue(hdev->req_workqueue);
 
        err = hci_dev_do_open(hdev);
@@ -1320,6 +1954,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
                cancel_delayed_work(&hdev->discov_off);
                hdev->discov_timeout = 0;
                clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
        }
 
        if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
@@ -1341,6 +1976,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        skb_queue_purge(&hdev->cmd_q);
        atomic_set(&hdev->cmd_cnt, 1);
        if (!test_bit(HCI_RAW, &hdev->flags) &&
+           !test_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
            test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) {
                set_bit(HCI_INIT, &hdev->flags);
                __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
@@ -1373,15 +2009,16 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        hdev->flags = 0;
        hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
 
-       if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
-           mgmt_valid_hdev(hdev)) {
-               hci_dev_lock(hdev);
-               mgmt_powered(hdev, 0);
-               hci_dev_unlock(hdev);
+       if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+               if (hdev->dev_type == HCI_BREDR) {
+                       hci_dev_lock(hdev);
+                       mgmt_powered(hdev, 0);
+                       hci_dev_unlock(hdev);
+               }
        }
 
        /* Controller radio is available but is currently powered down */
-       hdev->amp_status = 0;
+       hdev->amp_status = AMP_STATUS_POWERED_DOWN;
 
        memset(hdev->eir, 0, sizeof(hdev->eir));
        memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
@@ -1500,6 +2137,11 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
                goto done;
        }
 
+       if (hdev->dev_type != HCI_BREDR) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
        if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
                err = -EOPNOTSUPP;
                goto done;
@@ -1703,7 +2345,14 @@ static void hci_power_on(struct work_struct *work)
                return;
        }
 
-       if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
+       /* During the HCI setup phase, a few error conditions are
+        * ignored and they need to be checked now. If they are still
+        * valid, it is important to turn the device back off.
+        */
+       if (test_bit(HCI_RFKILLED, &hdev->dev_flags) ||
+           (hdev->dev_type == HCI_BREDR &&
+            !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
+            !bacmp(&hdev->static_addr, BDADDR_ANY))) {
                clear_bit(HCI_AUTO_OFF, &hdev->dev_flags);
                hci_dev_do_close(hdev);
        } else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
@@ -1728,19 +2377,12 @@ static void hci_power_off(struct work_struct *work)
 static void hci_discov_off(struct work_struct *work)
 {
        struct hci_dev *hdev;
-       u8 scan = SCAN_PAGE;
 
        hdev = container_of(work, struct hci_dev, discov_off.work);
 
        BT_DBG("%s", hdev->name);
 
-       hci_dev_lock(hdev);
-
-       hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
-
-       hdev->discov_timeout = 0;
-
-       hci_dev_unlock(hdev);
+       mgmt_discoverable_timeout(hdev);
 }
 
 int hci_uuids_clear(struct hci_dev *hdev)
@@ -2063,13 +2705,15 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
        return 0;
 }
 
-struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
+struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
+                                        bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *b;
 
-       list_for_each_entry(b, &hdev->blacklist, list)
-               if (bacmp(bdaddr, &b->bdaddr) == 0)
+       list_for_each_entry(b, &hdev->blacklist, list) {
+               if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type)
                        return b;
+       }
 
        return NULL;
 }
@@ -2079,9 +2723,7 @@ int hci_blacklist_clear(struct hci_dev *hdev)
        struct list_head *p, *n;
 
        list_for_each_safe(p, n, &hdev->blacklist) {
-               struct bdaddr_list *b;
-
-               b = list_entry(p, struct bdaddr_list, list);
+               struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
 
                list_del(p);
                kfree(b);
@@ -2094,10 +2736,10 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
-       if (bacmp(bdaddr, BDADDR_ANY) == 0)
+       if (!bacmp(bdaddr, BDADDR_ANY))
                return -EBADF;
 
-       if (hci_blacklist_lookup(hdev, bdaddr))
+       if (hci_blacklist_lookup(hdev, bdaddr, type))
                return -EEXIST;
 
        entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
@@ -2105,6 +2747,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
                return -ENOMEM;
 
        bacpy(&entry->bdaddr, bdaddr);
+       entry->bdaddr_type = type;
 
        list_add(&entry->list, &hdev->blacklist);
 
@@ -2115,10 +2758,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
-       if (bacmp(bdaddr, BDADDR_ANY) == 0)
+       if (!bacmp(bdaddr, BDADDR_ANY))
                return hci_blacklist_clear(hdev);
 
-       entry = hci_blacklist_lookup(hdev, bdaddr);
+       entry = hci_blacklist_lookup(hdev, bdaddr, type);
        if (!entry)
                return -ENOENT;
 
@@ -2216,13 +2859,19 @@ struct hci_dev *hci_alloc_dev(void)
        hdev->pkt_type  = (HCI_DM1 | HCI_DH1 | HCI_HV1);
        hdev->esco_type = (ESCO_HV1);
        hdev->link_mode = (HCI_LM_ACCEPT);
-       hdev->io_capability = 0x03; /* No Input No Output */
+       hdev->num_iac = 0x01;           /* One IAC support is mandatory */
+       hdev->io_capability = 0x03;     /* No Input No Output */
        hdev->inq_tx_power = HCI_TX_POWER_INVALID;
        hdev->adv_tx_power = HCI_TX_POWER_INVALID;
 
        hdev->sniff_max_interval = 800;
        hdev->sniff_min_interval = 80;
 
+       hdev->le_scan_interval = 0x0060;
+       hdev->le_scan_window = 0x0030;
+       hdev->le_conn_min_interval = 0x0028;
+       hdev->le_conn_max_interval = 0x0038;
+
        mutex_init(&hdev->lock);
        mutex_init(&hdev->req_lock);
 
@@ -2311,7 +2960,12 @@ int hci_register_dev(struct hci_dev *hdev)
                goto err;
        }
 
-       error = hci_add_sysfs(hdev);
+       if (!IS_ERR_OR_NULL(bt_debugfs))
+               hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
+
+       dev_set_name(&hdev->dev, "%s", hdev->name);
+
+       error = device_add(&hdev->dev);
        if (error < 0)
                goto err_wqueue;
 
@@ -2329,9 +2983,9 @@ int hci_register_dev(struct hci_dev *hdev)
                set_bit(HCI_RFKILLED, &hdev->dev_flags);
 
        set_bit(HCI_SETUP, &hdev->dev_flags);
+       set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
 
-       if (hdev->dev_type != HCI_AMP) {
-               set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+       if (hdev->dev_type == HCI_BREDR) {
                /* Assume BR/EDR support until proven otherwise (such as
                 * through reading supported features during init.
                 */
@@ -2399,7 +3053,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
                rfkill_destroy(hdev->rfkill);
        }
 
-       hci_del_sysfs(hdev);
+       device_del(&hdev->dev);
+
+       debugfs_remove_recursive(hdev->debugfs);
 
        destroy_workqueue(hdev->workqueue);
        destroy_workqueue(hdev->req_workqueue);
@@ -2435,9 +3091,8 @@ int hci_resume_dev(struct hci_dev *hdev)
 EXPORT_SYMBOL(hci_resume_dev);
 
 /* Receive frame from HCI drivers */
-int hci_recv_frame(struct sk_buff *skb)
+int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        if (!hdev || (!test_bit(HCI_UP, &hdev->flags)
                      && !test_bit(HCI_INIT, &hdev->flags))) {
                kfree_skb(skb);
@@ -2496,7 +3151,6 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
                scb->expect = hlen;
                scb->pkt_type = type;
 
-               skb->dev = (void *) hdev;
                hdev->reassembly[index] = skb;
        }
 
@@ -2556,7 +3210,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
                        /* Complete frame */
 
                        bt_cb(skb)->pkt_type = type;
-                       hci_recv_frame(skb);
+                       hci_recv_frame(hdev, skb);
 
                        hdev->reassembly[index] = NULL;
                        return remain;
@@ -2647,15 +3301,8 @@ int hci_unregister_cb(struct hci_cb *cb)
 }
 EXPORT_SYMBOL(hci_unregister_cb);
 
-static int hci_send_frame(struct sk_buff *skb)
+static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-
-       if (!hdev) {
-               kfree_skb(skb);
-               return -ENODEV;
-       }
-
        BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 
        /* Time stamp */
@@ -2672,7 +3319,8 @@ static int hci_send_frame(struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
-       return hdev->send(skb);
+       if (hdev->send(hdev, skb) < 0)
+               BT_ERR("%s sending frame failed", hdev->name);
 }
 
 void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
@@ -2735,7 +3383,6 @@ static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
        BT_DBG("skb len %d", skb->len);
 
        bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
-       skb->dev = (void *) hdev;
 
        return skb;
 }
@@ -2879,7 +3526,6 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
                do {
                        skb = list; list = list->next;
 
-                       skb->dev = (void *) hdev;
                        bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
                        hci_add_acl_hdr(skb, conn->handle, flags);
 
@@ -2898,8 +3544,6 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
 
        BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
 
-       skb->dev = (void *) hdev;
-
        hci_queue_acl(chan, &chan->data_q, skb, flags);
 
        queue_work(hdev->workqueue, &hdev->tx_work);
@@ -2920,7 +3564,6 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
        skb_reset_transport_header(skb);
        memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE);
 
-       skb->dev = (void *) hdev;
        bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
 
        skb_queue_tail(&conn->data_q, skb);
@@ -3185,7 +3828,7 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
                        hci_conn_enter_active_mode(chan->conn,
                                                   bt_cb(skb)->force_active);
 
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
                        hdev->acl_last_tx = jiffies;
 
                        hdev->acl_cnt--;
@@ -3237,7 +3880,7 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
                        hci_conn_enter_active_mode(chan->conn,
                                                   bt_cb(skb)->force_active);
 
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
                        hdev->acl_last_tx = jiffies;
 
                        hdev->block_cnt -= blocks;
@@ -3290,7 +3933,7 @@ static void hci_sched_sco(struct hci_dev *hdev)
        while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
                while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
                        BT_DBG("skb %p len %d", skb, skb->len);
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
 
                        conn->sent++;
                        if (conn->sent == ~0)
@@ -3314,7 +3957,7 @@ static void hci_sched_esco(struct hci_dev *hdev)
                                                     &quote))) {
                while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
                        BT_DBG("skb %p len %d", skb, skb->len);
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
 
                        conn->sent++;
                        if (conn->sent == ~0)
@@ -3356,7 +3999,7 @@ static void hci_sched_le(struct hci_dev *hdev)
 
                        skb = skb_dequeue(&chan->data_q);
 
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
                        hdev->le_last_tx = jiffies;
 
                        cnt--;
@@ -3392,7 +4035,7 @@ static void hci_tx_work(struct work_struct *work)
 
        /* Send next queued raw (unknown type) packet */
        while ((skb = skb_dequeue(&hdev->raw_q)))
-               hci_send_frame(skb);
+               hci_send_frame(hdev, skb);
 }
 
 /* ----- HCI RX task (incoming data processing) ----- */
@@ -3638,7 +4281,7 @@ static void hci_cmd_work(struct work_struct *work)
                hdev->sent_cmd = skb_clone(skb, GFP_KERNEL);
                if (hdev->sent_cmd) {
                        atomic_dec(&hdev->cmd_cnt);
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
                        if (test_bit(HCI_RESET, &hdev->flags))
                                del_timer(&hdev->cmd_timer);
                        else
@@ -3650,15 +4293,3 @@ static void hci_cmd_work(struct work_struct *work)
                }
        }
 }
-
-u8 bdaddr_to_le(u8 bdaddr_type)
-{
-       switch (bdaddr_type) {
-       case BDADDR_LE_PUBLIC:
-               return ADDR_LE_DEV_PUBLIC;
-
-       default:
-               /* Fallback to LE Random address type */
-               return ADDR_LE_DEV_RANDOM;
-       }
-}
index 4785ab0795f5f14b092acc0416f2496e82ca7a73..5935f748c0f9a6fe71cb3c0fe8baeaedb7019c37 100644 (file)
@@ -29,8 +29,9 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "a2mp.h"
+#include "amp.h"
 
 /* Handle HCI Event packets */
 
@@ -194,6 +195,11 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
 
        memset(hdev->adv_data, 0, sizeof(hdev->adv_data));
        hdev->adv_data_len = 0;
+
+       memset(hdev->scan_rsp_data, 0, sizeof(hdev->scan_rsp_data));
+       hdev->scan_rsp_data_len = 0;
+
+       hdev->ssp_debug_mode = 0;
 }
 
 static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -309,11 +315,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
                set_bit(HCI_ISCAN, &hdev->flags);
                if (!old_iscan)
                        mgmt_discoverable(hdev, 1);
-               if (hdev->discov_timeout > 0) {
-                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
-                       queue_delayed_work(hdev->workqueue, &hdev->discov_off,
-                                          to);
-               }
        } else if (old_iscan)
                mgmt_discoverable(hdev, 0);
 
@@ -417,6 +418,21 @@ static void hci_cc_write_voice_setting(struct hci_dev *hdev,
                hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
 }
 
+static void hci_cc_read_num_supported_iac(struct hci_dev *hdev,
+                                         struct sk_buff *skb)
+{
+       struct hci_rp_read_num_supported_iac *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hdev->num_iac = rp->num_iac;
+
+       BT_DBG("%s num iac %d", hdev->name, hdev->num_iac);
+}
+
 static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
@@ -454,14 +470,13 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
        if (rp->status)
                return;
 
-       hdev->hci_ver = rp->hci_ver;
-       hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
-       hdev->lmp_ver = rp->lmp_ver;
-       hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
-       hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
-
-       BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name,
-              hdev->manufacturer, hdev->hci_ver, hdev->hci_rev);
+       if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               hdev->hci_ver = rp->hci_ver;
+               hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
+               hdev->lmp_ver = rp->lmp_ver;
+               hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
+               hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
+       }
 }
 
 static void hci_cc_read_local_commands(struct hci_dev *hdev,
@@ -541,7 +556,8 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
        if (rp->status)
                return;
 
-       hdev->max_page = rp->max_page;
+       if (hdev->max_page < rp->max_page)
+               hdev->max_page = rp->max_page;
 
        if (rp->page < HCI_MAX_PAGES)
                memcpy(hdev->features[rp->page], rp->features, 8);
@@ -918,17 +934,9 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
 
        if (!status) {
                if (*sent)
-                       set_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+                       set_bit(HCI_ADVERTISING, &hdev->dev_flags);
                else
-                       clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
-       }
-
-       if (!test_bit(HCI_INIT, &hdev->flags)) {
-               struct hci_request req;
-
-               hci_req_init(&req, hdev);
-               hci_update_ad(&req);
-               hci_req_run(&req, NULL);
+                       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
        }
 
        hci_dev_unlock(hdev);
@@ -1005,7 +1013,7 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
                } else {
                        hdev->features[1][0] &= ~LMP_HOST_LE;
                        clear_bit(HCI_LE_ENABLED, &hdev->dev_flags);
-                       clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+                       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
                }
 
                if (sent->simul)
@@ -1296,9 +1304,11 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
                goto unlock;
 
        if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
-               struct hci_cp_auth_requested cp;
-               cp.handle = __cpu_to_le16(conn->handle);
-               hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
+               struct hci_cp_auth_requested auth_cp;
+
+               auth_cp.handle = __cpu_to_le16(conn->handle);
+               hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
+                            sizeof(auth_cp), &auth_cp);
        }
 
 unlock:
@@ -1470,33 +1480,6 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
        hci_dev_unlock(hdev);
 }
 
-static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
-{
-       struct hci_conn *conn;
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       if (status) {
-               hci_dev_lock(hdev);
-
-               conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
-               if (!conn) {
-                       hci_dev_unlock(hdev);
-                       return;
-               }
-
-               BT_DBG("%s bdaddr %pMR conn %p", hdev->name, &conn->dst, conn);
-
-               conn->state = BT_CLOSED;
-               mgmt_connect_failed(hdev, &conn->dst, conn->type,
-                                   conn->dst_type, status);
-               hci_proto_connect_cfm(conn, status);
-               hci_conn_del(conn);
-
-               hci_dev_unlock(hdev);
-       }
-}
-
 static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status)
 {
        struct hci_cp_create_phy_link *cp;
@@ -1711,7 +1694,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                                      &flags);
 
        if ((mask & HCI_LM_ACCEPT) &&
-           !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
+           !hci_blacklist_lookup(hdev, &ev->bdaddr, BDADDR_BREDR)) {
                /* Connection accepted */
                struct inquiry_entry *ie;
                struct hci_conn *conn;
@@ -1826,10 +1809,25 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        }
 
        if (ev->status == 0) {
-               if (conn->type == ACL_LINK && conn->flush_key)
+               u8 type = conn->type;
+
+               if (type == ACL_LINK && conn->flush_key)
                        hci_remove_link_key(hdev, &conn->dst);
                hci_proto_disconn_cfm(conn, ev->reason);
                hci_conn_del(conn);
+
+               /* Re-enable advertising if necessary, since it might
+                * have been disabled by the connection. From the
+                * HCI_LE_Set_Advertise_Enable command description in
+                * the core specification (v4.0):
+                * "The Controller shall continue advertising until the Host
+                * issues an LE_Set_Advertise_Enable command with
+                * Advertising_Enable set to 0x00 (Advertising is disabled)
+                * or until a connection is created or until the Advertising
+                * is timed out due to Directed Advertising."
+                */
+               if (type == LE_LINK)
+                       mgmt_reenable_advertising(hdev);
        }
 
 unlock:
@@ -2144,6 +2142,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_write_voice_setting(hdev, skb);
                break;
 
+       case HCI_OP_READ_NUM_SUPPORTED_IAC:
+               hci_cc_read_num_supported_iac(hdev, skb);
+               break;
+
        case HCI_OP_WRITE_SSP_MODE:
                hci_cc_write_ssp_mode(hdev, skb);
                break;
@@ -2347,10 +2349,6 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_disconnect(hdev, ev->status);
                break;
 
-       case HCI_OP_LE_CREATE_CONN:
-               hci_cs_le_create_conn(hdev, ev->status);
-               break;
-
        case HCI_OP_CREATE_PHY_LINK:
                hci_cs_create_phylink(hdev, ev->status);
                break;
@@ -2553,7 +2551,6 @@ static void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
                conn->mode = ev->mode;
-               conn->interval = __le16_to_cpu(ev->interval);
 
                if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND,
                                        &conn->flags)) {
@@ -2935,6 +2932,23 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static inline size_t eir_get_length(u8 *eir, size_t eir_len)
+{
+       size_t parsed = 0;
+
+       while (parsed < eir_len) {
+               u8 field_len = eir[0];
+
+               if (field_len == 0)
+                       return parsed;
+
+               parsed += field_len + 1;
+               eir += field_len + 1;
+       }
+
+       return eir_len;
+}
+
 static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
                                            struct sk_buff *skb)
 {
@@ -3175,7 +3189,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
 
                if (hdev->auto_accept_delay > 0) {
                        int delay = msecs_to_jiffies(hdev->auto_accept_delay);
-                       mod_timer(&conn->auto_accept_timer, jiffies + delay);
+                       queue_delayed_work(conn->hdev->workqueue,
+                                          &conn->auto_accept_work, delay);
                        goto unlock;
                }
 
@@ -3490,6 +3505,17 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                conn->dst_type = ev->bdaddr_type;
 
+               /* The advertising parameters for own address type
+                * define which source address and source address
+                * type this connections has.
+                */
+               if (bacmp(&conn->src, BDADDR_ANY)) {
+                       conn->src_type = ADDR_LE_DEV_PUBLIC;
+               } else {
+                       bacpy(&conn->src, &hdev->static_addr);
+                       conn->src_type = ADDR_LE_DEV_RANDOM;
+               }
+
                if (ev->role == LE_CONN_ROLE_MASTER) {
                        conn->out = true;
                        conn->link_mode |= HCI_LM_MASTER;
@@ -3645,8 +3671,8 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
        skb_pull(skb, HCI_EVENT_HDR_SIZE);
 
        if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
-               struct hci_command_hdr *hdr = (void *) hdev->sent_cmd->data;
-               u16 opcode = __le16_to_cpu(hdr->opcode);
+               struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
+               u16 opcode = __le16_to_cpu(cmd_hdr->opcode);
 
                hci_req_cmd_complete(hdev, opcode, 0);
        }
index 579886186c3a826f126f19929541a0c0788d4e12..71f0be1730801a615191a9badc2bd2f588bd4fbb 100644 (file)
@@ -387,7 +387,6 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
        __net_timestamp(skb);
 
        bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
-       skb->dev = (void *) hdev;
        hci_send_to_sock(hdev, skb);
        kfree_skb(skb);
 }
@@ -482,7 +481,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_add(hdev, &bdaddr, 0);
+       err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR);
 
        hci_dev_unlock(hdev);
 
@@ -499,7 +498,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_del(hdev, &bdaddr, 0);
+       err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR);
 
        hci_dev_unlock(hdev);
 
@@ -518,6 +517,9 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
        if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
                return -EBUSY;
 
+       if (hdev->dev_type != HCI_BREDR)
+               return -EOPNOTSUPP;
+
        switch (cmd) {
        case HCISETRAW:
                if (!capable(CAP_NET_ADMIN))
@@ -550,10 +552,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
                return hci_sock_blacklist_del(hdev, (void __user *) arg);
        }
 
-       if (hdev->ioctl)
-               return hdev->ioctl(hdev, cmd, arg);
-
-       return -EINVAL;
+       return -ENOIOCTLCMD;
 }
 
 static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
@@ -942,7 +941,6 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        bt_cb(skb)->pkt_type = *((unsigned char *) skb->data);
        skb_pull(skb, 1);
-       skb->dev = (void *) hdev;
 
        if (hci_pi(sk)->channel == HCI_CHANNEL_RAW &&
            bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
index edf623a29043117c4be9cf255b37af3ddf22d988..0b61250cfdf90c9e3a488c9ca3cce41ac79d6a84 100644 (file)
@@ -1,17 +1,12 @@
 /* Bluetooth HCI driver model support. */
 
-#include <linux/debugfs.h>
 #include <linux/module.h>
-#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
 static struct class *bt_class;
 
-struct dentry *bt_debugfs;
-EXPORT_SYMBOL_GPL(bt_debugfs);
-
 static inline char *link_typetostr(int type)
 {
        switch (type) {
@@ -42,29 +37,15 @@ static ssize_t show_link_address(struct device *dev,
        return sprintf(buf, "%pMR\n", &conn->dst);
 }
 
-static ssize_t show_link_features(struct device *dev,
-                                 struct device_attribute *attr, char *buf)
-{
-       struct hci_conn *conn = to_hci_conn(dev);
-
-       return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
-                      conn->features[0][0], conn->features[0][1],
-                      conn->features[0][2], conn->features[0][3],
-                      conn->features[0][4], conn->features[0][5],
-                      conn->features[0][6], conn->features[0][7]);
-}
-
 #define LINK_ATTR(_name, _mode, _show, _store) \
 struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store)
 
 static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
 static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
-static LINK_ATTR(features, S_IRUGO, show_link_features, NULL);
 
 static struct attribute *bt_link_attrs[] = {
        &link_attr_type.attr,
        &link_attr_address.attr,
-       &link_attr_features.attr,
        NULL
 };
 
@@ -150,28 +131,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn)
        hci_dev_put(hdev);
 }
 
-static inline char *host_bustostr(int bus)
-{
-       switch (bus) {
-       case HCI_VIRTUAL:
-               return "VIRTUAL";
-       case HCI_USB:
-               return "USB";
-       case HCI_PCCARD:
-               return "PCCARD";
-       case HCI_UART:
-               return "UART";
-       case HCI_RS232:
-               return "RS232";
-       case HCI_PCI:
-               return "PCI";
-       case HCI_SDIO:
-               return "SDIO";
-       default:
-               return "UNKNOWN";
-       }
-}
-
 static inline char *host_typetostr(int type)
 {
        switch (type) {
@@ -184,13 +143,6 @@ static inline char *host_typetostr(int type)
        }
 }
 
-static ssize_t show_bus(struct device *dev,
-                       struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%s\n", host_bustostr(hdev->bus));
-}
-
 static ssize_t show_type(struct device *dev,
                         struct device_attribute *attr, char *buf)
 {
@@ -212,14 +164,6 @@ static ssize_t show_name(struct device *dev,
        return sprintf(buf, "%s\n", name);
 }
 
-static ssize_t show_class(struct device *dev,
-                         struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
-                      hdev->dev_class[1], hdev->dev_class[0]);
-}
-
 static ssize_t show_address(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
@@ -227,150 +171,14 @@ static ssize_t show_address(struct device *dev,
        return sprintf(buf, "%pMR\n", &hdev->bdaddr);
 }
 
-static ssize_t show_features(struct device *dev,
-                            struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-
-       return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
-                      hdev->features[0][0], hdev->features[0][1],
-                      hdev->features[0][2], hdev->features[0][3],
-                      hdev->features[0][4], hdev->features[0][5],
-                      hdev->features[0][6], hdev->features[0][7]);
-}
-
-static ssize_t show_manufacturer(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->manufacturer);
-}
-
-static ssize_t show_hci_version(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->hci_ver);
-}
-
-static ssize_t show_hci_revision(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->hci_rev);
-}
-
-static ssize_t show_idle_timeout(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->idle_timeout);
-}
-
-static ssize_t store_idle_timeout(struct device *dev,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       unsigned int val;
-       int rv;
-
-       rv = kstrtouint(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val != 0 && (val < 500 || val > 3600000))
-               return -EINVAL;
-
-       hdev->idle_timeout = val;
-
-       return count;
-}
-
-static ssize_t show_sniff_max_interval(struct device *dev,
-                                      struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->sniff_max_interval);
-}
-
-static ssize_t store_sniff_max_interval(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       u16 val;
-       int rv;
-
-       rv = kstrtou16(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
-               return -EINVAL;
-
-       hdev->sniff_max_interval = val;
-
-       return count;
-}
-
-static ssize_t show_sniff_min_interval(struct device *dev,
-                                      struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->sniff_min_interval);
-}
-
-static ssize_t store_sniff_min_interval(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       u16 val;
-       int rv;
-
-       rv = kstrtou16(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
-               return -EINVAL;
-
-       hdev->sniff_min_interval = val;
-
-       return count;
-}
-
-static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL);
 static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-static DEVICE_ATTR(class, S_IRUGO, show_class, NULL);
 static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
-static DEVICE_ATTR(features, S_IRUGO, show_features, NULL);
-static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL);
-static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL);
-static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL);
-
-static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
-                  show_idle_timeout, store_idle_timeout);
-static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
-                  show_sniff_max_interval, store_sniff_max_interval);
-static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
-                  show_sniff_min_interval, store_sniff_min_interval);
 
 static struct attribute *bt_host_attrs[] = {
-       &dev_attr_bus.attr,
        &dev_attr_type.attr,
        &dev_attr_name.attr,
-       &dev_attr_class.attr,
        &dev_attr_address.attr,
-       &dev_attr_features.attr,
-       &dev_attr_manufacturer.attr,
-       &dev_attr_hci_version.attr,
-       &dev_attr_hci_revision.attr,
-       &dev_attr_idle_timeout.attr,
-       &dev_attr_sniff_max_interval.attr,
-       &dev_attr_sniff_min_interval.attr,
        NULL
 };
 
@@ -396,141 +204,6 @@ static struct device_type bt_host = {
        .release = bt_host_release,
 };
 
-static int inquiry_cache_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct discovery_state *cache = &hdev->discovery;
-       struct inquiry_entry *e;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(e, &cache->all, all) {
-               struct inquiry_data *data = &e->data;
-               seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
-                          &data->bdaddr,
-                          data->pscan_rep_mode, data->pscan_period_mode,
-                          data->pscan_mode, data->dev_class[2],
-                          data->dev_class[1], data->dev_class[0],
-                          __le16_to_cpu(data->clock_offset),
-                          data->rssi, data->ssp_mode, e->timestamp);
-       }
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int inquiry_cache_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, inquiry_cache_show, inode->i_private);
-}
-
-static const struct file_operations inquiry_cache_fops = {
-       .open           = inquiry_cache_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int blacklist_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct bdaddr_list *b;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(b, &hdev->blacklist, list)
-               seq_printf(f, "%pMR\n", &b->bdaddr);
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int blacklist_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, blacklist_show, inode->i_private);
-}
-
-static const struct file_operations blacklist_fops = {
-       .open           = blacklist_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static void print_bt_uuid(struct seq_file *f, u8 *uuid)
-{
-       u32 data0, data5;
-       u16 data1, data2, data3, data4;
-
-       data5 = get_unaligned_le32(uuid);
-       data4 = get_unaligned_le16(uuid + 4);
-       data3 = get_unaligned_le16(uuid + 6);
-       data2 = get_unaligned_le16(uuid + 8);
-       data1 = get_unaligned_le16(uuid + 10);
-       data0 = get_unaligned_le32(uuid + 12);
-
-       seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
-                  data0, data1, data2, data3, data4, data5);
-}
-
-static int uuids_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct bt_uuid *uuid;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(uuid, &hdev->uuids, list)
-               print_bt_uuid(f, uuid->uuid);
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int uuids_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, uuids_show, inode->i_private);
-}
-
-static const struct file_operations uuids_fops = {
-       .open           = uuids_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int auto_accept_delay_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-
-       hdev->auto_accept_delay = val;
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int auto_accept_delay_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-
-       *val = hdev->auto_accept_delay;
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
-                       auto_accept_delay_set, "%llu\n");
-
 void hci_init_sysfs(struct hci_dev *hdev)
 {
        struct device *dev = &hdev->dev;
@@ -542,52 +215,8 @@ void hci_init_sysfs(struct hci_dev *hdev)
        device_initialize(dev);
 }
 
-int hci_add_sysfs(struct hci_dev *hdev)
-{
-       struct device *dev = &hdev->dev;
-       int err;
-
-       BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
-
-       dev_set_name(dev, "%s", hdev->name);
-
-       err = device_add(dev);
-       if (err < 0)
-               return err;
-
-       if (!bt_debugfs)
-               return 0;
-
-       hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
-       if (!hdev->debugfs)
-               return 0;
-
-       debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
-                           hdev, &inquiry_cache_fops);
-
-       debugfs_create_file("blacklist", 0444, hdev->debugfs,
-                           hdev, &blacklist_fops);
-
-       debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
-
-       debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev,
-                           &auto_accept_delay_fops);
-       return 0;
-}
-
-void hci_del_sysfs(struct hci_dev *hdev)
-{
-       BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
-
-       debugfs_remove_recursive(hdev->debugfs);
-
-       device_del(&hdev->dev);
-}
-
 int __init bt_sysfs_init(void)
 {
-       bt_debugfs = debugfs_create_dir("bluetooth", NULL);
-
        bt_class = class_create(THIS_MODULE, "bluetooth");
 
        return PTR_ERR_OR_ZERO(bt_class);
@@ -596,6 +225,4 @@ int __init bt_sysfs_init(void)
 void bt_sysfs_cleanup(void)
 {
        class_destroy(bt_class);
-
-       debugfs_remove_recursive(bt_debugfs);
 }
index bdc35a7a7feeaf4ac7a918547e82d5d19a7ef537..292e619db8961c82e7c3aa7f3280cb4236176ab8 100644 (file)
@@ -767,10 +767,10 @@ static int hidp_setup_hid(struct hidp_session *session,
        strncpy(hid->name, req->name, sizeof(req->name) - 1);
 
        snprintf(hid->phys, sizeof(hid->phys), "%pMR",
-                &bt_sk(session->ctrl_sock->sk)->src);
+                &l2cap_pi(session->ctrl_sock->sk)->chan->src);
 
        snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
-                &bt_sk(session->ctrl_sock->sk)->dst);
+                &l2cap_pi(session->ctrl_sock->sk)->chan->dst);
 
        hid->dev.parent = &session->conn->hcon->dev;
        hid->ll_driver = &hidp_hid_driver;
@@ -1283,23 +1283,29 @@ static int hidp_session_thread(void *arg)
 static int hidp_verify_sockets(struct socket *ctrl_sock,
                               struct socket *intr_sock)
 {
+       struct l2cap_chan *ctrl_chan, *intr_chan;
        struct bt_sock *ctrl, *intr;
        struct hidp_session *session;
 
        if (!l2cap_is_socket(ctrl_sock) || !l2cap_is_socket(intr_sock))
                return -EINVAL;
 
+       ctrl_chan = l2cap_pi(ctrl_sock->sk)->chan;
+       intr_chan = l2cap_pi(intr_sock->sk)->chan;
+
+       if (bacmp(&ctrl_chan->src, &intr_chan->src) ||
+           bacmp(&ctrl_chan->dst, &intr_chan->dst))
+               return -ENOTUNIQ;
+
        ctrl = bt_sk(ctrl_sock->sk);
        intr = bt_sk(intr_sock->sk);
 
-       if (bacmp(&ctrl->src, &intr->src) || bacmp(&ctrl->dst, &intr->dst))
-               return -ENOTUNIQ;
        if (ctrl->sk.sk_state != BT_CONNECTED ||
            intr->sk.sk_state != BT_CONNECTED)
                return -EBADFD;
 
        /* early session check, we check again during session registration */
-       session = hidp_session_find(&ctrl->dst);
+       session = hidp_session_find(&ctrl_chan->dst);
        if (session) {
                hidp_session_put(session);
                return -EEXIST;
@@ -1332,7 +1338,7 @@ int hidp_connection_add(struct hidp_connadd_req *req,
        if (!conn)
                return -EBADFD;
 
-       ret = hidp_session_new(&session, &bt_sk(ctrl_sock->sk)->dst, ctrl_sock,
+       ret = hidp_session_new(&session, &chan->dst, ctrl_sock,
                               intr_sock, req, conn);
        if (ret)
                goto out_conn;
index 02dba4e6df9628c7d7dc454dd043503b5f2f4d60..0cef677078381315c7ce3e58abb6573136bc227b 100644 (file)
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/smp.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "smp.h"
+#include "a2mp.h"
+#include "amp.h"
 
 bool disable_ertm;
 
-static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
-static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
+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 LIST_HEAD(chan_list);
 static DEFINE_RWLOCK(chan_list_lock);
@@ -58,6 +59,18 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
 static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
                     struct sk_buff_head *skbs, u8 event);
 
+static inline __u8 bdaddr_type(struct hci_conn *hcon, __u8 type)
+{
+       if (hcon->type == LE_LINK) {
+               if (type == ADDR_LE_DEV_PUBLIC)
+                       return BDADDR_LE_PUBLIC;
+               else
+                       return BDADDR_LE_RANDOM;
+       }
+
+       return BDADDR_BREDR;
+}
+
 /* ---- L2CAP channels ---- */
 
 static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
@@ -148,7 +161,7 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
        struct l2cap_chan *c;
 
        list_for_each_entry(c, &chan_list, global_l) {
-               if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
+               if (c->sport == psm && !bacmp(&c->src, src))
                        return c;
        }
        return NULL;
@@ -210,38 +223,25 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
        return 0;
 }
 
-static void __l2cap_state_change(struct l2cap_chan *chan, int state)
+static void l2cap_state_change(struct l2cap_chan *chan, int state)
 {
        BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
               state_to_string(state));
 
        chan->state = state;
-       chan->ops->state_change(chan, state);
-}
-
-static void l2cap_state_change(struct l2cap_chan *chan, int state)
-{
-       struct sock *sk = chan->sk;
-
-       lock_sock(sk);
-       __l2cap_state_change(chan, state);
-       release_sock(sk);
+       chan->ops->state_change(chan, state, 0);
 }
 
-static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
+static inline void l2cap_state_change_and_error(struct l2cap_chan *chan,
+                                               int state, int err)
 {
-       struct sock *sk = chan->sk;
-
-       sk->sk_err = err;
+       chan->state = state;
+       chan->ops->state_change(chan, chan->state, err);
 }
 
 static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
 {
-       struct sock *sk = chan->sk;
-
-       lock_sock(sk);
-       __l2cap_chan_set_err(chan, err);
-       release_sock(sk);
+       chan->ops->state_change(chan, chan->state, err);
 }
 
 static void __set_retrans_timer(struct l2cap_chan *chan)
@@ -620,10 +620,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
 void l2cap_chan_close(struct l2cap_chan *chan, int reason)
 {
        struct l2cap_conn *conn = chan->conn;
-       struct sock *sk = chan->sk;
 
-       BT_DBG("chan %p state %s sk %p", chan, state_to_string(chan->state),
-              sk);
+       BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
 
        switch (chan->state) {
        case BT_LISTEN:
@@ -634,7 +632,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
        case BT_CONFIG:
                if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
                    conn->hcon->type == ACL_LINK) {
-                       __set_chan_timer(chan, sk->sk_sndtimeo);
+                       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
                        l2cap_send_disconn_req(chan, reason);
                } else
                        l2cap_chan_del(chan, reason);
@@ -646,10 +644,11 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
                        struct l2cap_conn_rsp rsp;
                        __u16 result;
 
-                       if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+                       if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
                                result = L2CAP_CR_SEC_BLOCK;
                        else
                                result = L2CAP_CR_BAD_PSM;
+
                        l2cap_state_change(chan, BT_DISCONN);
 
                        rsp.scid   = cpu_to_le16(chan->dcid);
@@ -676,7 +675,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
 
 static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
 {
-       if (chan->chan_type == L2CAP_CHAN_RAW) {
+       switch (chan->chan_type) {
+       case L2CAP_CHAN_RAW:
                switch (chan->sec_level) {
                case BT_SECURITY_HIGH:
                        return HCI_AT_DEDICATED_BONDING_MITM;
@@ -685,15 +685,29 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
                default:
                        return HCI_AT_NO_BONDING;
                }
-       } else if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
-               if (chan->sec_level == BT_SECURITY_LOW)
-                       chan->sec_level = BT_SECURITY_SDP;
-
+               break;
+       case L2CAP_CHAN_CONN_LESS:
+               if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_3DSP)) {
+                       if (chan->sec_level == BT_SECURITY_LOW)
+                               chan->sec_level = BT_SECURITY_SDP;
+               }
                if (chan->sec_level == BT_SECURITY_HIGH)
                        return HCI_AT_NO_BONDING_MITM;
                else
                        return HCI_AT_NO_BONDING;
-       } else {
+               break;
+       case L2CAP_CHAN_CONN_ORIENTED:
+               if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
+                       if (chan->sec_level == BT_SECURITY_LOW)
+                               chan->sec_level = BT_SECURITY_SDP;
+
+                       if (chan->sec_level == BT_SECURITY_HIGH)
+                               return HCI_AT_NO_BONDING_MITM;
+                       else
+                               return HCI_AT_NO_BONDING;
+               }
+               /* fall through */
+       default:
                switch (chan->sec_level) {
                case BT_SECURITY_HIGH:
                        return HCI_AT_GENERAL_BONDING_MITM;
@@ -702,6 +716,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
                default:
                        return HCI_AT_NO_BONDING;
                }
+               break;
        }
 }
 
@@ -1015,11 +1030,27 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
 static bool __amp_capable(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
+       struct hci_dev *hdev;
+       bool amp_available = false;
+
+       if (!conn->hs_enabled)
+               return false;
+
+       if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP))
+               return false;
+
+       read_lock(&hci_dev_list_lock);
+       list_for_each_entry(hdev, &hci_dev_list, list) {
+               if (hdev->amp_type != AMP_TYPE_BREDR &&
+                   test_bit(HCI_UP, &hdev->flags)) {
+                       amp_available = true;
+                       break;
+               }
+       }
+       read_unlock(&hci_dev_list_lock);
 
-       if (conn->hs_enabled && hci_amp_capable() &&
-           chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED &&
-           conn->fixed_chan_mask & L2CAP_FC_A2MP)
-               return true;
+       if (chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED)
+               return amp_available;
 
        return false;
 }
@@ -1185,7 +1216,6 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
 
 static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
 {
-       struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
        struct l2cap_disconn_req req;
 
@@ -1208,10 +1238,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
        l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ,
                       sizeof(req), &req);
 
-       lock_sock(sk);
-       __l2cap_state_change(chan, BT_DISCONN);
-       __l2cap_chan_set_err(chan, err);
-       release_sock(sk);
+       l2cap_state_change_and_error(chan, BT_DISCONN, err);
 }
 
 /* ---- L2CAP connections ---- */
@@ -1224,8 +1251,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
        mutex_lock(&conn->chan_lock);
 
        list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
-               struct sock *sk = chan->sk;
-
                l2cap_chan_lock(chan);
 
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1257,19 +1282,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                        rsp.dcid = cpu_to_le16(chan->scid);
 
                        if (l2cap_chan_check_security(chan)) {
-                               lock_sock(sk);
-                               if (test_bit(BT_SK_DEFER_SETUP,
-                                            &bt_sk(sk)->flags)) {
+                               if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                                        rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
                                        rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
                                        chan->ops->defer(chan);
 
                                } else {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
                                        rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
                                }
-                               release_sock(sk);
                        } else {
                                rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
                                rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
@@ -1308,8 +1330,6 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
        read_lock(&chan_list_lock);
 
        list_for_each_entry(c, &chan_list, global_l) {
-               struct sock *sk = c->sk;
-
                if (state && c->state != state)
                        continue;
 
@@ -1318,16 +1338,16 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
                        int src_any, dst_any;
 
                        /* Exact match. */
-                       src_match = !bacmp(&bt_sk(sk)->src, src);
-                       dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+                       src_match = !bacmp(&c->src, src);
+                       dst_match = !bacmp(&c->dst, dst);
                        if (src_match && dst_match) {
                                read_unlock(&chan_list_lock);
                                return c;
                        }
 
                        /* Closest match */
-                       src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
-                       dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+                       src_any = !bacmp(&c->src, BDADDR_ANY);
+                       dst_any = !bacmp(&c->dst, BDADDR_ANY);
                        if ((src_match && dst_any) || (src_any && dst_match) ||
                            (src_any && dst_any))
                                c1 = c;
@@ -1341,14 +1361,15 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
 
 static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 {
-       struct sock *parent;
+       struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan, *pchan;
+       u8 dst_type;
 
        BT_DBG("");
 
        /* Check if we have socket listening on cid */
        pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
-                                         conn->src, conn->dst);
+                                         &hcon->src, &hcon->dst);
        if (!pchan)
                return;
 
@@ -1356,9 +1377,13 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
        if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
                return;
 
-       parent = pchan->sk;
+       dst_type = bdaddr_type(hcon, hcon->dst_type);
 
-       lock_sock(parent);
+       /* If device is blocked, do not create a channel for it */
+       if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type))
+               return;
+
+       l2cap_chan_lock(pchan);
 
        chan = pchan->ops->new_connection(pchan);
        if (!chan)
@@ -1366,13 +1391,15 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 
        chan->dcid = L2CAP_CID_ATT;
 
-       bacpy(&bt_sk(chan->sk)->src, conn->src);
-       bacpy(&bt_sk(chan->sk)->dst, conn->dst);
+       bacpy(&chan->src, &hcon->src);
+       bacpy(&chan->dst, &hcon->dst);
+       chan->src_type = bdaddr_type(hcon, hcon->src_type);
+       chan->dst_type = dst_type;
 
        __l2cap_chan_add(conn, chan);
 
 clean:
-       release_sock(parent);
+       l2cap_chan_unlock(pchan);
 }
 
 static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -1407,12 +1434,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
                                l2cap_chan_ready(chan);
 
                } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
-                       struct sock *sk = chan->sk;
-                       __clear_chan_timer(chan);
-                       lock_sock(sk);
-                       __l2cap_state_change(chan, BT_CONNECTED);
-                       sk->sk_state_change(sk);
-                       release_sock(sk);
+                       l2cap_chan_ready(chan);
 
                } else if (chan->state == BT_CONNECT) {
                        l2cap_do_start(chan);
@@ -1632,9 +1654,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
                break;
        }
 
-       conn->src = &hcon->hdev->bdaddr;
-       conn->dst = &hcon->dst;
-
        conn->feat_mask = 0;
 
        if (hcon->type == ACL_LINK)
@@ -1691,8 +1710,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
        read_lock(&chan_list_lock);
 
        list_for_each_entry(c, &chan_list, global_l) {
-               struct sock *sk = c->sk;
-
                if (state && c->state != state)
                        continue;
 
@@ -1701,16 +1718,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
                        int src_any, dst_any;
 
                        /* Exact match. */
-                       src_match = !bacmp(&bt_sk(sk)->src, src);
-                       dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+                       src_match = !bacmp(&c->src, src);
+                       dst_match = !bacmp(&c->dst, dst);
                        if (src_match && dst_match) {
                                read_unlock(&chan_list_lock);
                                return c;
                        }
 
                        /* Closest match */
-                       src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
-                       dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+                       src_any = !bacmp(&c->src, BDADDR_ANY);
+                       dst_any = !bacmp(&c->dst, BDADDR_ANY);
                        if ((src_match && dst_any) || (src_any && dst_match) ||
                            (src_any && dst_any))
                                c1 = c;
@@ -1725,18 +1742,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
 int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                       bdaddr_t *dst, u8 dst_type)
 {
-       struct sock *sk = chan->sk;
-       bdaddr_t *src = &bt_sk(sk)->src;
        struct l2cap_conn *conn;
        struct hci_conn *hcon;
        struct hci_dev *hdev;
        __u8 auth_type;
        int err;
 
-       BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", src, dst,
+       BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
               dst_type, __le16_to_cpu(psm));
 
-       hdev = hci_get_route(dst, src);
+       hdev = hci_get_route(dst, &chan->src);
        if (!hdev)
                return -EHOSTUNREACH;
 
@@ -1793,9 +1808,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        }
 
        /* Set destination address and psm */
-       lock_sock(sk);
-       bacpy(&bt_sk(sk)->dst, dst);
-       release_sock(sk);
+       bacpy(&chan->dst, dst);
+       chan->dst_type = dst_type;
 
        chan->psm = psm;
        chan->dcid = cid;
@@ -1828,7 +1842,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        }
 
        /* Update source addr of the socket */
-       bacpy(src, conn->src);
+       bacpy(&chan->src, &hcon->src);
+       chan->src_type = bdaddr_type(hcon, hcon->src_type);
 
        l2cap_chan_unlock(chan);
        l2cap_chan_add(conn, chan);
@@ -1838,7 +1853,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        hci_conn_drop(hcon);
 
        l2cap_state_change(chan, BT_CONNECT);
-       __set_chan_timer(chan, sk->sk_sndtimeo);
+       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
 
        if (hcon->state == BT_CONNECTED) {
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1858,38 +1873,6 @@ done:
        return err;
 }
 
-int __l2cap_wait_ack(struct sock *sk)
-{
-       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
-       DECLARE_WAITQUEUE(wait, current);
-       int err = 0;
-       int timeo = HZ/5;
-
-       add_wait_queue(sk_sleep(sk), &wait);
-       set_current_state(TASK_INTERRUPTIBLE);
-       while (chan->unacked_frames > 0 && chan->conn) {
-               if (!timeo)
-                       timeo = HZ/5;
-
-               if (signal_pending(current)) {
-                       err = sock_intr_errno(timeo);
-                       break;
-               }
-
-               release_sock(sk);
-               timeo = schedule_timeout(timeo);
-               lock_sock(sk);
-               set_current_state(TASK_INTERRUPTIBLE);
-
-               err = sock_error(sk);
-               if (err)
-                       break;
-       }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(sk_sleep(sk), &wait);
-       return err;
-}
-
 static void l2cap_monitor_timeout(struct work_struct *work)
 {
        struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -2266,7 +2249,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
        int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
        struct l2cap_hdr *lh;
 
-       BT_DBG("chan %p len %zu priority %u", chan, len, priority);
+       BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan,
+              __le16_to_cpu(chan->psm), len, priority);
 
        count = min_t(unsigned int, (conn->mtu - hlen), len);
 
@@ -2281,7 +2265,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
        lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
-       put_unaligned(chan->psm, skb_put(skb, L2CAP_PSMLEN_SIZE));
+       put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE));
 
        err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
        if (unlikely(err < 0)) {
@@ -2829,17 +2813,16 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
        mutex_lock(&conn->chan_lock);
 
        list_for_each_entry(chan, &conn->chan_l, list) {
-               struct sock *sk = chan->sk;
                if (chan->chan_type != L2CAP_CHAN_RAW)
                        continue;
 
-               /* Don't send frame to the socket it came from */
-               if (skb->sk == sk)
+               /* Don't send frame to the channel it came from */
+               if (bt_cb(skb)->chan == chan)
                        continue;
+
                nskb = skb_clone(skb, GFP_KERNEL);
                if (!nskb)
                        continue;
-
                if (chan->ops->recv(chan, nskb))
                        kfree_skb(nskb);
        }
@@ -3046,8 +3029,8 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
 
        skb_queue_head_init(&chan->tx_q);
 
-       chan->local_amp_id = 0;
-       chan->move_id = 0;
+       chan->local_amp_id = AMP_ID_BREDR;
+       chan->move_id = AMP_ID_BREDR;
        chan->move_state = L2CAP_MOVE_STABLE;
        chan->move_role = L2CAP_MOVE_ROLE_NONE;
 
@@ -3100,7 +3083,7 @@ static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
 static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
                                      struct l2cap_conf_rfc *rfc)
 {
-       if (chan->local_amp_id && chan->hs_hcon) {
+       if (chan->local_amp_id != AMP_ID_BREDR && chan->hs_hcon) {
                u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
 
                /* Class 1 devices have must have ERTM timeouts
@@ -3718,7 +3701,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
        struct l2cap_conn_rsp rsp;
        struct l2cap_chan *chan = NULL, *pchan;
-       struct sock *parent, *sk = NULL;
        int result, status = L2CAP_CS_NO_INFO;
 
        u16 dcid = 0, scid = __le16_to_cpu(req->scid);
@@ -3727,16 +3709,15 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid);
 
        /* Check if we have socket listening on psm */
-       pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src, conn->dst);
+       pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
+                                        &conn->hcon->dst);
        if (!pchan) {
                result = L2CAP_CR_BAD_PSM;
                goto sendresp;
        }
 
-       parent = pchan->sk;
-
        mutex_lock(&conn->chan_lock);
-       lock_sock(parent);
+       l2cap_chan_lock(pchan);
 
        /* Check if the ACL is secure enough (if not SDP) */
        if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) &&
@@ -3756,8 +3737,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        if (!chan)
                goto response;
 
-       sk = chan->sk;
-
        /* For certain devices (ex: HID mouse), support for authentication,
         * pairing and bonding is optional. For such devices, inorder to avoid
         * the ACL alive for too long after L2CAP disconnection, reset the ACL
@@ -3765,8 +3744,10 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
         */
        conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
 
-       bacpy(&bt_sk(sk)->src, conn->src);
-       bacpy(&bt_sk(sk)->dst, conn->dst);
+       bacpy(&chan->src, &conn->hcon->src);
+       bacpy(&chan->dst, &conn->hcon->dst);
+       chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
+       chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
        chan->psm  = psm;
        chan->dcid = scid;
        chan->local_amp_id = amp_id;
@@ -3775,14 +3756,14 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
 
        dcid = chan->scid;
 
-       __set_chan_timer(chan, sk->sk_sndtimeo);
+       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
 
        chan->ident = cmd->ident;
 
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
                if (l2cap_chan_check_security(chan)) {
-                       if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
-                               __l2cap_state_change(chan, BT_CONNECT2);
+                       if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
+                               l2cap_state_change(chan, BT_CONNECT2);
                                result = L2CAP_CR_PEND;
                                status = L2CAP_CS_AUTHOR_PEND;
                                chan->ops->defer(chan);
@@ -3791,28 +3772,28 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
                                 * The connection will succeed after the
                                 * physical link is up.
                                 */
-                               if (amp_id) {
-                                       __l2cap_state_change(chan, BT_CONNECT2);
-                                       result = L2CAP_CR_PEND;
-                               } else {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                               if (amp_id == AMP_ID_BREDR) {
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        result = L2CAP_CR_SUCCESS;
+                               } else {
+                                       l2cap_state_change(chan, BT_CONNECT2);
+                                       result = L2CAP_CR_PEND;
                                }
                                status = L2CAP_CS_NO_INFO;
                        }
                } else {
-                       __l2cap_state_change(chan, BT_CONNECT2);
+                       l2cap_state_change(chan, BT_CONNECT2);
                        result = L2CAP_CR_PEND;
                        status = L2CAP_CS_AUTHEN_PEND;
                }
        } else {
-               __l2cap_state_change(chan, BT_CONNECT2);
+               l2cap_state_change(chan, BT_CONNECT2);
                result = L2CAP_CR_PEND;
                status = L2CAP_CS_NO_INFO;
        }
 
 response:
-       release_sock(parent);
+       l2cap_chan_unlock(pchan);
        mutex_unlock(&conn->chan_lock);
 
 sendresp:
@@ -3968,6 +3949,18 @@ static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data,
                                            L2CAP_CONF_SUCCESS, flags), data);
 }
 
+static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident,
+                                  u16 scid, u16 dcid)
+{
+       struct l2cap_cmd_rej_cid rej;
+
+       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
+       rej.scid = __cpu_to_le16(scid);
+       rej.dcid = __cpu_to_le16(dcid);
+
+       l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
+}
+
 static inline int l2cap_config_req(struct l2cap_conn *conn,
                                   struct l2cap_cmd_hdr *cmd, u16 cmd_len,
                                   u8 *data)
@@ -3987,18 +3980,14 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
        BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
 
        chan = l2cap_get_chan_by_scid(conn, dcid);
-       if (!chan)
-               return -EBADSLT;
+       if (!chan) {
+               cmd_reject_invalid_cid(conn, cmd->ident, dcid, 0);
+               return 0;
+       }
 
        if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
-               struct l2cap_cmd_rej_cid rej;
-
-               rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
-               rej.scid = cpu_to_le16(chan->scid);
-               rej.dcid = cpu_to_le16(chan->dcid);
-
-               l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
-                              sizeof(rej), &rej);
+               cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+                                      chan->dcid);
                goto unlock;
        }
 
@@ -4201,7 +4190,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
        struct l2cap_disconn_rsp rsp;
        u16 dcid, scid;
        struct l2cap_chan *chan;
-       struct sock *sk;
 
        if (cmd_len != sizeof(*req))
                return -EPROTO;
@@ -4216,20 +4204,17 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
        chan = __l2cap_get_chan_by_scid(conn, dcid);
        if (!chan) {
                mutex_unlock(&conn->chan_lock);
-               return -EBADSLT;
+               cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
+               return 0;
        }
 
        l2cap_chan_lock(chan);
 
-       sk = chan->sk;
-
        rsp.dcid = cpu_to_le16(chan->scid);
        rsp.scid = cpu_to_le16(chan->dcid);
        l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
 
-       lock_sock(sk);
-       sk->sk_shutdown = SHUTDOWN_MASK;
-       release_sock(sk);
+       chan->ops->set_shutdown(chan);
 
        l2cap_chan_hold(chan);
        l2cap_chan_del(chan, ECONNRESET);
@@ -4423,7 +4408,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
        BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
 
        /* For controller id 0 make BR/EDR connection */
-       if (req->amp_id == HCI_BREDR_ID) {
+       if (req->amp_id == AMP_ID_BREDR) {
                l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
                              req->amp_id);
                return 0;
@@ -4445,10 +4430,13 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
                struct amp_mgr *mgr = conn->hcon->amp_mgr;
                struct hci_conn *hs_hcon;
 
-               hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
+               hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
+                                                 &conn->hcon->dst);
                if (!hs_hcon) {
                        hci_dev_put(hdev);
-                       return -EBADSLT;
+                       cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+                                              chan->dcid);
+                       return 0;
                }
 
                BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
@@ -4658,7 +4646,7 @@ void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
 
        if (chan->state != BT_CONNECTED) {
                /* Ignore logical link if channel is on BR/EDR */
-               if (chan->local_amp_id)
+               if (chan->local_amp_id != AMP_ID_BREDR)
                        l2cap_logical_finish_create(chan, hchan);
        } else {
                l2cap_logical_finish_move(chan, hchan);
@@ -4669,7 +4657,7 @@ void l2cap_move_start(struct l2cap_chan *chan)
 {
        BT_DBG("chan %p", chan);
 
-       if (chan->local_amp_id == HCI_BREDR_ID) {
+       if (chan->local_amp_id == AMP_ID_BREDR) {
                if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
                        return;
                chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
@@ -4726,7 +4714,7 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result,
                               sizeof(rsp), &rsp);
 
                if (result == L2CAP_CR_SUCCESS) {
-                       __l2cap_state_change(chan, BT_CONFIG);
+                       l2cap_state_change(chan, BT_CONFIG);
                        set_bit(CONF_REQ_SENT, &chan->conf_state);
                        l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
                                       L2CAP_CONF_REQ,
@@ -4868,7 +4856,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
                goto send_move_response;
        }
 
-       if (req->dest_amp_id) {
+       if (req->dest_amp_id != AMP_ID_BREDR) {
                struct hci_dev *hdev;
                hdev = hci_dev_get(req->dest_amp_id);
                if (!hdev || hdev->dev_type != HCI_AMP ||
@@ -4888,7 +4876,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
         */
        if ((__chan_is_moving(chan) ||
             chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
-           bacmp(conn->src, conn->dst) > 0) {
+           bacmp(&conn->hcon->src, &conn->hcon->dst) > 0) {
                result = L2CAP_MR_COLLISION;
                goto send_move_response;
        }
@@ -4898,7 +4886,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
        chan->move_id = req->dest_amp_id;
        icid = chan->dcid;
 
-       if (!req->dest_amp_id) {
+       if (req->dest_amp_id == AMP_ID_BREDR) {
                /* Moving to BR/EDR */
                if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
                        chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
@@ -5090,7 +5078,7 @@ static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
        if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
                if (result == L2CAP_MC_CONFIRMED) {
                        chan->local_amp_id = chan->move_id;
-                       if (!chan->local_amp_id)
+                       if (chan->local_amp_id == AMP_ID_BREDR)
                                __release_logical_link(chan);
                } else {
                        chan->move_id = chan->local_amp_id;
@@ -5130,7 +5118,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
        if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
                chan->local_amp_id = chan->move_id;
 
-               if (!chan->local_amp_id && chan->hs_hchan)
+               if (chan->local_amp_id == AMP_ID_BREDR && chan->hs_hchan)
                        __release_logical_link(chan);
 
                l2cap_move_done(chan);
@@ -5304,20 +5292,6 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
        }
 }
 
-static __le16 l2cap_err_to_reason(int err)
-{
-       switch (err) {
-       case -EBADSLT:
-               return __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
-       case -EMSGSIZE:
-               return __constant_cpu_to_le16(L2CAP_REJ_MTU_EXCEEDED);
-       case -EINVAL:
-       case -EPROTO:
-       default:
-               return __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
-       }
-}
-
 static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
                                        struct sk_buff *skb)
 {
@@ -5350,7 +5324,7 @@ static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
 
                BT_ERR("Wrong link type (%d)", err);
 
-               rej.reason = l2cap_err_to_reason(err);
+               rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
                l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
                               sizeof(rej), &rej);
        }
@@ -5395,7 +5369,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
 
                        BT_ERR("Wrong link type (%d)", err);
 
-                       rej.reason = l2cap_err_to_reason(err);
+                       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
                        l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
                                       sizeof(rej), &rej);
                }
@@ -6403,7 +6377,7 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
        if (hcon->type != ACL_LINK)
                goto drop;
 
-       chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst);
+       chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst);
        if (!chan)
                goto drop;
 
@@ -6415,6 +6389,10 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
        if (chan->imtu < skb->len)
                goto drop;
 
+       /* Store remote BD_ADDR and PSM for msg_name */
+       bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
+       bt_cb(skb)->psm = psm;
+
        if (!chan->ops->recv(chan, skb))
                return;
 
@@ -6432,12 +6410,15 @@ static void l2cap_att_channel(struct l2cap_conn *conn,
                goto drop;
 
        chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
-                                        conn->src, conn->dst);
+                                        &hcon->src, &hcon->dst);
        if (!chan)
                goto drop;
 
        BT_DBG("chan %p, len %d", chan, skb->len);
 
+       if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
+               goto drop;
+
        if (chan->imtu < skb->len)
                goto drop;
 
@@ -6507,17 +6488,15 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
        /* Find listening sockets and check their link_mode */
        read_lock(&chan_list_lock);
        list_for_each_entry(c, &chan_list, global_l) {
-               struct sock *sk = c->sk;
-
                if (c->state != BT_LISTEN)
                        continue;
 
-               if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
+               if (!bacmp(&c->src, &hdev->bdaddr)) {
                        lm1 |= HCI_LM_ACCEPT;
                        if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
                                lm1 |= HCI_LM_MASTER;
                        exact++;
-               } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+               } else if (!bacmp(&c->src, BDADDR_ANY)) {
                        lm2 |= HCI_LM_ACCEPT;
                        if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
                                lm2 |= HCI_LM_MASTER;
@@ -6623,11 +6602,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 
                if (!status && (chan->state == BT_CONNECTED ||
                                chan->state == BT_CONFIG)) {
-                       struct sock *sk = chan->sk;
-
-                       clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
-                       sk->sk_state_change(sk);
-
+                       chan->ops->resume(chan);
                        l2cap_check_encryption(chan, encrypt);
                        l2cap_chan_unlock(chan);
                        continue;
@@ -6640,32 +6615,26 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                                __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                        }
                } else if (chan->state == BT_CONNECT2) {
-                       struct sock *sk = chan->sk;
                        struct l2cap_conn_rsp rsp;
                        __u16 res, stat;
 
-                       lock_sock(sk);
-
                        if (!status) {
-                               if (test_bit(BT_SK_DEFER_SETUP,
-                                            &bt_sk(sk)->flags)) {
+                               if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                                        res = L2CAP_CR_PEND;
                                        stat = L2CAP_CS_AUTHOR_PEND;
                                        chan->ops->defer(chan);
                                } else {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        res = L2CAP_CR_SUCCESS;
                                        stat = L2CAP_CS_NO_INFO;
                                }
                        } else {
-                               __l2cap_state_change(chan, BT_DISCONN);
+                               l2cap_state_change(chan, BT_DISCONN);
                                __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                                res = L2CAP_CR_SEC_BLOCK;
                                stat = L2CAP_CS_NO_INFO;
                        }
 
-                       release_sock(sk);
-
                        rsp.scid   = cpu_to_le16(chan->dcid);
                        rsp.dcid   = cpu_to_le16(chan->scid);
                        rsp.result = cpu_to_le16(res);
@@ -6782,9 +6751,13 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
                conn->rx_len -= skb->len;
 
                if (!conn->rx_len) {
-                       /* Complete frame received */
-                       l2cap_recv_frame(conn, conn->rx_skb);
+                       /* Complete frame received. l2cap_recv_frame
+                        * takes ownership of the skb so set the global
+                        * rx_skb pointer to NULL first.
+                        */
+                       struct sk_buff *rx_skb = conn->rx_skb;
                        conn->rx_skb = NULL;
+                       l2cap_recv_frame(conn, rx_skb);
                }
                break;
        }
@@ -6801,10 +6774,8 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
        read_lock(&chan_list_lock);
 
        list_for_each_entry(c, &chan_list, global_l) {
-               struct sock *sk = c->sk;
-
                seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
-                          &bt_sk(sk)->src, &bt_sk(sk)->dst,
+                          &c->src, &c->dst,
                           c->state, __le16_to_cpu(c->psm),
                           c->scid, c->dcid, c->imtu, c->omtu,
                           c->sec_level, c->mode);
@@ -6837,12 +6808,11 @@ int __init l2cap_init(void)
        if (err < 0)
                return err;
 
-       if (bt_debugfs) {
-               l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
-                                                   NULL, &l2cap_debugfs_fops);
-               if (!l2cap_debugfs)
-                       BT_ERR("Failed to create L2CAP debug file");
-       }
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
+                                           NULL, &l2cap_debugfs_fops);
 
        return 0;
 }
index 9119898ef0409ef144e9fa3f13773782c3648c32..7cc24d263caaab45af9d8bbe5be09ebda20e87a4 100644 (file)
@@ -32,7 +32,8 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
 
 static struct bt_sock_list l2cap_sk_list = {
        .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
@@ -68,6 +69,18 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        if (la.l2_cid && la.l2_psm)
                return -EINVAL;
 
+       if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
+               return -EINVAL;
+
+       if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Connection oriented channels are not supported on LE */
+               if (la.l2_psm)
+                       return -EINVAL;
+               /* We only allow ATT user space socket */
+               if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+       }
+
        lock_sock(sk);
 
        if (sk->sk_state != BT_OPEN) {
@@ -99,11 +112,20 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        if (err < 0)
                goto done;
 
-       if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP ||
-           __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)
-               chan->sec_level = BT_SECURITY_SDP;
+       switch (chan->chan_type) {
+       case L2CAP_CHAN_CONN_LESS:
+               if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_3DSP)
+                       chan->sec_level = BT_SECURITY_SDP;
+               break;
+       case L2CAP_CHAN_CONN_ORIENTED:
+               if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP ||
+                   __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)
+                       chan->sec_level = BT_SECURITY_SDP;
+               break;
+       }
 
-       bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
+       bacpy(&chan->src, &la.l2_bdaddr);
+       chan->src_type = la.l2_bdaddr_type;
 
        chan->state = BT_BOUND;
        sk->sk_state = BT_BOUND;
@@ -134,6 +156,47 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
        if (la.l2_cid && la.l2_psm)
                return -EINVAL;
 
+       if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
+               return -EINVAL;
+
+       /* Check that the socket wasn't bound to something that
+        * conflicts with the address given to connect(). If chan->src
+        * is BDADDR_ANY it means bind() was never used, in which case
+        * chan->src_type and la.l2_bdaddr_type do not need to match.
+        */
+       if (chan->src_type == BDADDR_BREDR && bacmp(&chan->src, BDADDR_ANY) &&
+           bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Old user space versions will try to incorrectly bind
+                * the ATT socket using BDADDR_BREDR. We need to accept
+                * this and fix up the source address type only when
+                * both the source CID and destination CID indicate
+                * ATT. Anything else is an invalid combination.
+                */
+               if (chan->scid != L2CAP_CID_ATT ||
+                   la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+
+               /* We don't have the hdev available here to make a
+                * better decision on random vs public, but since all
+                * user space versions that exhibit this issue anyway do
+                * not support random local addresses assuming public
+                * here is good enough.
+                */
+               chan->src_type = BDADDR_LE_PUBLIC;
+       }
+
+       if (chan->src_type != BDADDR_BREDR && la.l2_bdaddr_type == BDADDR_BREDR)
+               return -EINVAL;
+
+       if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Connection oriented channels are not supported on LE */
+               if (la.l2_psm)
+                       return -EINVAL;
+               /* We only allow ATT user space socket */
+               if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+       }
+
        err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
                                 &la.l2_bdaddr, la.l2_bdaddr_type);
        if (err)
@@ -265,12 +328,14 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
 
        if (peer) {
                la->l2_psm = chan->psm;
-               bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
+               bacpy(&la->l2_bdaddr, &chan->dst);
                la->l2_cid = cpu_to_le16(chan->dcid);
+               la->l2_bdaddr_type = chan->dst_type;
        } else {
                la->l2_psm = chan->sport;
-               bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
+               bacpy(&la->l2_bdaddr, &chan->src);
                la->l2_cid = cpu_to_le16(chan->scid);
+               la->l2_bdaddr_type = chan->src_type;
        }
 
        return 0;
@@ -660,10 +725,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                        break;
                }
 
-               if (opt)
+               if (opt) {
                        set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
-               else
+                       set_bit(FLAG_DEFER_SETUP, &chan->flags);
+               } else {
                        clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+                       clear_bit(FLAG_DEFER_SETUP, &chan->flags);
+               }
                break;
 
        case BT_FLUSHABLE:
@@ -678,7 +746,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                }
 
                if (opt == BT_FLUSHABLE_OFF) {
-                       struct l2cap_conn *conn = chan->conn;
+                       conn = chan->conn;
                        /* proceed further only when we have l2cap_conn and
                           No Flush support in the LM */
                        if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) {
@@ -852,6 +920,38 @@ static void l2cap_sock_kill(struct sock *sk)
        sock_put(sk);
 }
 
+static int __l2cap_wait_ack(struct sock *sk)
+{
+       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+       DECLARE_WAITQUEUE(wait, current);
+       int err = 0;
+       int timeo = HZ/5;
+
+       add_wait_queue(sk_sleep(sk), &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       while (chan->unacked_frames > 0 && chan->conn) {
+               if (!timeo)
+                       timeo = HZ/5;
+
+               if (signal_pending(current)) {
+                       err = sock_intr_errno(timeo);
+                       break;
+               }
+
+               release_sock(sk);
+               timeo = schedule_timeout(timeo);
+               lock_sock(sk);
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               err = sock_error(sk);
+               if (err)
+                       break;
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(sk_sleep(sk), &wait);
+       return err;
+}
+
 static int l2cap_sock_shutdown(struct socket *sock, int how)
 {
        struct sock *sk = sock->sk;
@@ -942,6 +1042,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 {
        struct sock *sk, *parent = chan->data;
 
+       lock_sock(parent);
+
        /* Check for backlog size */
        if (sk_acceptq_is_full(parent)) {
                BT_DBG("backlog full %d", parent->sk_ack_backlog);
@@ -959,18 +1061,19 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 
        bt_accept_enqueue(parent, sk);
 
+       release_sock(parent);
+
        return l2cap_pi(sk)->chan;
 }
 
 static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
 {
-       int err;
        struct sock *sk = chan->data;
-       struct l2cap_pinfo *pi = l2cap_pi(sk);
+       int err;
 
        lock_sock(sk);
 
-       if (pi->rx_busy_skb) {
+       if (l2cap_pi(sk)->rx_busy_skb) {
                err = -ENOMEM;
                goto done;
        }
@@ -986,9 +1089,9 @@ static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
         * acked and reassembled until there is buffer space
         * available.
         */
-       if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) {
-               pi->rx_busy_skb = skb;
-               l2cap_chan_busy(pi->chan, 1);
+       if (err < 0 && chan->mode == L2CAP_MODE_ERTM) {
+               l2cap_pi(sk)->rx_busy_skb = skb;
+               l2cap_chan_busy(chan, 1);
                err = 0;
        }
 
@@ -1046,26 +1149,33 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
        release_sock(sk);
 }
 
-static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state)
+static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
+                                      int err)
 {
        struct sock *sk = chan->data;
 
        sk->sk_state = state;
+
+       if (err)
+               sk->sk_err = err;
 }
 
 static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
                                               unsigned long len, int nb)
 {
+       struct sock *sk = chan->data;
        struct sk_buff *skb;
        int err;
 
        l2cap_chan_unlock(chan);
-       skb = bt_skb_send_alloc(chan->sk, len, nb, &err);
+       skb = bt_skb_send_alloc(sk, len, nb, &err);
        l2cap_chan_lock(chan);
 
        if (!skb)
                return ERR_PTR(err);
 
+       bt_cb(skb)->chan = chan;
+
        return skb;
 }
 
@@ -1091,11 +1201,39 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
 
 static void l2cap_sock_defer_cb(struct l2cap_chan *chan)
 {
-       struct sock *sk = chan->data;
-       struct sock *parent = bt_sk(sk)->parent;
+       struct sock *parent, *sk = chan->data;
 
+       lock_sock(sk);
+
+       parent = bt_sk(sk)->parent;
        if (parent)
                parent->sk_data_ready(parent, 0);
+
+       release_sock(sk);
+}
+
+static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->data;
+
+       clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
+       sk->sk_state_change(sk);
+}
+
+static void l2cap_sock_set_shutdown_cb(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->data;
+
+       lock_sock(sk);
+       sk->sk_shutdown = SHUTDOWN_MASK;
+       release_sock(sk);
+}
+
+static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->data;
+
+       return sk->sk_sndtimeo;
 }
 
 static struct l2cap_ops l2cap_chan_ops = {
@@ -1107,6 +1245,9 @@ static struct l2cap_ops l2cap_chan_ops = {
        .state_change   = l2cap_sock_state_change_cb,
        .ready          = l2cap_sock_ready_cb,
        .defer          = l2cap_sock_defer_cb,
+       .resume         = l2cap_sock_resume_cb,
+       .set_shutdown   = l2cap_sock_set_shutdown_cb,
+       .get_sndtimeo   = l2cap_sock_get_sndtimeo_cb,
        .alloc_skb      = l2cap_sock_alloc_skb_cb,
 };
 
@@ -1116,6 +1257,7 @@ static void l2cap_sock_destruct(struct sock *sk)
 
        if (l2cap_pi(sk)->chan)
                l2cap_chan_put(l2cap_pi(sk)->chan);
+
        if (l2cap_pi(sk)->rx_busy_skb) {
                kfree_skb(l2cap_pi(sk)->rx_busy_skb);
                l2cap_pi(sk)->rx_busy_skb = NULL;
@@ -1125,10 +1267,22 @@ static void l2cap_sock_destruct(struct sock *sk)
        skb_queue_purge(&sk->sk_write_queue);
 }
 
+static void l2cap_skb_msg_name(struct sk_buff *skb, void *msg_name,
+                              int *msg_namelen)
+{
+       struct sockaddr_l2 *la = (struct sockaddr_l2 *) msg_name;
+
+       memset(la, 0, sizeof(struct sockaddr_l2));
+       la->l2_family = AF_BLUETOOTH;
+       la->l2_psm = bt_cb(skb)->psm;
+       bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr);
+
+       *msg_namelen = sizeof(struct sockaddr_l2);
+}
+
 static void l2cap_sock_init(struct sock *sk, struct sock *parent)
 {
-       struct l2cap_pinfo *pi = l2cap_pi(sk);
-       struct l2cap_chan *chan = pi->chan;
+       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
 
        BT_DBG("sk %p", sk);
 
@@ -1152,13 +1306,13 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
 
                security_sk_clone(parent, sk);
        } else {
-
                switch (sk->sk_type) {
                case SOCK_RAW:
                        chan->chan_type = L2CAP_CHAN_RAW;
                        break;
                case SOCK_DGRAM:
                        chan->chan_type = L2CAP_CHAN_CONN_LESS;
+                       bt_sk(sk)->skb_msg_name = l2cap_skb_msg_name;
                        break;
                case SOCK_SEQPACKET:
                case SOCK_STREAM:
@@ -1220,8 +1374,6 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
 
        l2cap_chan_hold(chan);
 
-       chan->sk = sk;
-
        l2cap_pi(sk)->chan = chan;
 
        return sk;
index 16125ff918f10991f7633921f2d4cbb03861c8f1..074d83690a414c8e668499d7f7e7c86215c563ea 100644 (file)
@@ -30,7 +30,8 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
 
 #define MGMT_VERSION   1
 #define MGMT_REVISION  4
@@ -77,6 +78,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_SET_ADVERTISING,
        MGMT_OP_SET_BREDR,
        MGMT_OP_SET_STATIC_ADDRESS,
+       MGMT_OP_SET_SCAN_PARAMS,
 };
 
 static const u16 mgmt_events[] = {
@@ -182,11 +184,6 @@ static u8 mgmt_status_table[] = {
        MGMT_STATUS_CONNECT_FAILED,     /* MAC Connection Failed */
 };
 
-bool mgmt_valid_hdev(struct hci_dev *hdev)
-{
-       return hdev->dev_type == HCI_BREDR;
-}
-
 static u8 mgmt_status(u8 hci_status)
 {
        if (hci_status < ARRAY_SIZE(mgmt_status_table))
@@ -322,10 +319,8 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
 
        count = 0;
        list_for_each_entry(d, &hci_dev_list, list) {
-               if (!mgmt_valid_hdev(d))
-                       continue;
-
-               count++;
+               if (d->dev_type == HCI_BREDR)
+                       count++;
        }
 
        rp_len = sizeof(*rp) + (2 * count);
@@ -343,11 +338,10 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
                if (test_bit(HCI_USER_CHANNEL, &d->dev_flags))
                        continue;
 
-               if (!mgmt_valid_hdev(d))
-                       continue;
-
-               rp->index[count++] = cpu_to_le16(d->id);
-               BT_DBG("Added hci%u", d->id);
+               if (d->dev_type == HCI_BREDR) {
+                       rp->index[count++] = cpu_to_le16(d->id);
+                       BT_DBG("Added hci%u", d->id);
+               }
        }
 
        rp->num_controllers = cpu_to_le16(count);
@@ -370,9 +364,6 @@ static u32 get_supported_settings(struct hci_dev *hdev)
        settings |= MGMT_SETTING_POWERED;
        settings |= MGMT_SETTING_PAIRABLE;
 
-       if (lmp_ssp_capable(hdev))
-               settings |= MGMT_SETTING_SSP;
-
        if (lmp_bredr_capable(hdev)) {
                settings |= MGMT_SETTING_CONNECTABLE;
                if (hdev->hci_ver >= BLUETOOTH_VER_1_2)
@@ -380,7 +371,11 @@ static u32 get_supported_settings(struct hci_dev *hdev)
                settings |= MGMT_SETTING_DISCOVERABLE;
                settings |= MGMT_SETTING_BREDR;
                settings |= MGMT_SETTING_LINK_SECURITY;
-               settings |= MGMT_SETTING_HS;
+
+               if (lmp_ssp_capable(hdev)) {
+                       settings |= MGMT_SETTING_SSP;
+                       settings |= MGMT_SETTING_HS;
+               }
        }
 
        if (lmp_le_capable(hdev)) {
@@ -425,7 +420,7 @@ static u32 get_current_settings(struct hci_dev *hdev)
        if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
                settings |= MGMT_SETTING_HS;
 
-       if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
                settings |= MGMT_SETTING_ADVERTISING;
 
        return settings;
@@ -541,6 +536,156 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
        return ptr;
 }
 
+static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+
+       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+               if (cmd->opcode == opcode)
+                       return cmd;
+       }
+
+       return NULL;
+}
+
+static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+       u8 ad_len = 0;
+       size_t name_len;
+
+       name_len = strlen(hdev->dev_name);
+       if (name_len > 0) {
+               size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+               if (name_len > max_len) {
+                       name_len = max_len;
+                       ptr[1] = EIR_NAME_SHORT;
+               } else
+                       ptr[1] = EIR_NAME_COMPLETE;
+
+               ptr[0] = name_len + 1;
+
+               memcpy(ptr + 2, hdev->dev_name, name_len);
+
+               ad_len += (name_len + 2);
+               ptr += (name_len + 2);
+       }
+
+       return ad_len;
+}
+
+static void update_scan_rsp_data(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_scan_rsp_data cp;
+       u8 len;
+
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+
+       len = create_scan_rsp_data(hdev, cp.data);
+
+       if (hdev->scan_rsp_data_len == len &&
+           memcmp(cp.data, hdev->scan_rsp_data, len) == 0)
+               return;
+
+       memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
+       hdev->scan_rsp_data_len = len;
+
+       cp.length = len;
+
+       hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
+}
+
+static u8 get_adv_discov_flags(struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+
+       /* If there's a pending mgmt command the flags will not yet have
+        * their final values, so check for this first.
+        */
+       cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+       if (cmd) {
+               struct mgmt_mode *cp = cmd->param;
+               if (cp->val == 0x01)
+                       return LE_AD_GENERAL;
+               else if (cp->val == 0x02)
+                       return LE_AD_LIMITED;
+       } else {
+               if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+                       return LE_AD_LIMITED;
+               else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+                       return LE_AD_GENERAL;
+       }
+
+       return 0;
+}
+
+static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
+{
+       u8 ad_len = 0, flags = 0;
+
+       flags |= get_adv_discov_flags(hdev);
+
+       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               if (lmp_le_br_capable(hdev))
+                       flags |= LE_AD_SIM_LE_BREDR_CTRL;
+               if (lmp_host_le_br_capable(hdev))
+                       flags |= LE_AD_SIM_LE_BREDR_HOST;
+       } else {
+               flags |= LE_AD_NO_BREDR;
+       }
+
+       if (flags) {
+               BT_DBG("adv flags 0x%02x", flags);
+
+               ptr[0] = 2;
+               ptr[1] = EIR_FLAGS;
+               ptr[2] = flags;
+
+               ad_len += 3;
+               ptr += 3;
+       }
+
+       if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
+               ptr[0] = 2;
+               ptr[1] = EIR_TX_POWER;
+               ptr[2] = (u8) hdev->adv_tx_power;
+
+               ad_len += 3;
+               ptr += 3;
+       }
+
+       return ad_len;
+}
+
+static void update_adv_data(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_adv_data cp;
+       u8 len;
+
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+
+       len = create_adv_data(hdev, cp.data);
+
+       if (hdev->adv_data_len == len &&
+           memcmp(cp.data, hdev->adv_data, len) == 0)
+               return;
+
+       memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+       hdev->adv_data_len = len;
+
+       cp.length = len;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+}
+
 static void create_eir(struct hci_dev *hdev, u8 *data)
 {
        u8 *ptr = data;
@@ -639,6 +784,9 @@ static void update_class(struct hci_request *req)
        if (!hdev_is_powered(hdev))
                return;
 
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return;
+
        if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
                return;
 
@@ -646,6 +794,9 @@ static void update_class(struct hci_request *req)
        cod[1] = hdev->major_class;
        cod[2] = get_service_classes(hdev);
 
+       if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+               cod[1] |= 0x20;
+
        if (memcmp(cod, hdev->dev_class, 3) == 0)
                return;
 
@@ -770,18 +921,6 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
        }
 }
 
-static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
-{
-       struct pending_cmd *cmd;
-
-       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
-               if (cmd->opcode == opcode)
-                       return cmd;
-       }
-
-       return NULL;
-}
-
 static void mgmt_pending_remove(struct pending_cmd *cmd)
 {
        list_del(&cmd->list);
@@ -940,28 +1079,91 @@ static u8 mgmt_le_support(struct hci_dev *hdev)
                return MGMT_STATUS_SUCCESS;
 }
 
+static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
+{
+       struct pending_cmd *cmd;
+       struct mgmt_mode *cp;
+       struct hci_request req;
+       bool changed;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+       if (!cmd)
+               goto unlock;
+
+       if (status) {
+               u8 mgmt_err = mgmt_status(status);
+               cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+               goto remove_cmd;
+       }
+
+       cp = cmd->param;
+       if (cp->val) {
+               changed = !test_and_set_bit(HCI_DISCOVERABLE,
+                                           &hdev->dev_flags);
+
+               if (hdev->discov_timeout > 0) {
+                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+                       queue_delayed_work(hdev->workqueue, &hdev->discov_off,
+                                          to);
+               }
+       } else {
+               changed = test_and_clear_bit(HCI_DISCOVERABLE,
+                                            &hdev->dev_flags);
+       }
+
+       send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
+
+       if (changed)
+               new_settings(hdev, cmd->sk);
+
+       /* When the discoverable mode gets changed, make sure
+        * that class of device has the limited discoverable
+        * bit correctly set.
+        */
+       hci_req_init(&req, hdev);
+       update_class(&req);
+       hci_req_run(&req, NULL);
+
+remove_cmd:
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
 static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                            u16 len)
 {
        struct mgmt_cp_set_discoverable *cp = data;
        struct pending_cmd *cmd;
+       struct hci_request req;
        u16 timeout;
-       u8 scan, status;
+       u8 scan;
        int err;
 
        BT_DBG("request for %s", hdev->name);
 
-       status = mgmt_bredr_support(hdev);
-       if (status)
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+           !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-                                 status);
+                                 MGMT_STATUS_REJECTED);
 
-       if (cp->val != 0x00 && cp->val != 0x01)
+       if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
                                  MGMT_STATUS_INVALID_PARAMS);
 
        timeout = __le16_to_cpu(cp->timeout);
-       if (!cp->val && timeout > 0)
+
+       /* Disabling discoverable requires that no timeout is set,
+        * and enabling limited discoverable requires a timeout.
+        */
+       if ((cp->val == 0x00 && timeout > 0) ||
+           (cp->val == 0x02 && timeout == 0))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
                                  MGMT_STATUS_INVALID_PARAMS);
 
@@ -989,6 +1191,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
        if (!hdev_is_powered(hdev)) {
                bool changed = false;
 
+               /* Setting limited discoverable when powered off is
+                * not a valid operation since it requires a timeout
+                * and so no need to check HCI_LIMITED_DISCOVERABLE.
+                */
                if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
                        change_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
                        changed = true;
@@ -1004,16 +1210,20 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
-       if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
-               if (hdev->discov_timeout > 0) {
-                       cancel_delayed_work(&hdev->discov_off);
-                       hdev->discov_timeout = 0;
-               }
+       /* If the current mode is the same, then just update the timeout
+        * value with the new value. And if only the timeout gets updated,
+        * then no need for any HCI transactions.
+        */
+       if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags) &&
+           (cp->val == 0x02) == test_bit(HCI_LIMITED_DISCOVERABLE,
+                                         &hdev->dev_flags)) {
+               cancel_delayed_work(&hdev->discov_off);
+               hdev->discov_timeout = timeout;
 
-               if (cp->val && timeout > 0) {
-                       hdev->discov_timeout = timeout;
+               if (cp->val && hdev->discov_timeout > 0) {
+                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
                        queue_delayed_work(hdev->workqueue, &hdev->discov_off,
-                               msecs_to_jiffies(hdev->discov_timeout * 1000));
+                                          to);
                }
 
                err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
@@ -1026,20 +1236,66 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
+       /* Cancel any potential discoverable timeout that might be
+        * still active and store new timeout value. The arming of
+        * the timeout happens in the complete handler.
+        */
+       cancel_delayed_work(&hdev->discov_off);
+       hdev->discov_timeout = timeout;
+
+       /* Limited discoverable mode */
+       if (cp->val == 0x02)
+               set_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       else
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+
+       hci_req_init(&req, hdev);
+
+       /* The procedure for LE-only controllers is much simpler - just
+        * update the advertising data.
+        */
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               goto update_ad;
+
        scan = SCAN_PAGE;
 
-       if (cp->val)
+       if (cp->val) {
+               struct hci_cp_write_current_iac_lap hci_cp;
+
+               if (cp->val == 0x02) {
+                       /* Limited discoverable mode */
+                       hci_cp.num_iac = 2;
+                       hci_cp.iac_lap[0] = 0x00;       /* LIAC */
+                       hci_cp.iac_lap[1] = 0x8b;
+                       hci_cp.iac_lap[2] = 0x9e;
+                       hci_cp.iac_lap[3] = 0x33;       /* GIAC */
+                       hci_cp.iac_lap[4] = 0x8b;
+                       hci_cp.iac_lap[5] = 0x9e;
+               } else {
+                       /* General discoverable mode */
+                       hci_cp.num_iac = 1;
+                       hci_cp.iac_lap[0] = 0x33;       /* GIAC */
+                       hci_cp.iac_lap[1] = 0x8b;
+                       hci_cp.iac_lap[2] = 0x9e;
+               }
+
+               hci_req_add(&req, HCI_OP_WRITE_CURRENT_IAC_LAP,
+                           (hci_cp.num_iac * 3) + 1, &hci_cp);
+
                scan |= SCAN_INQUIRY;
-       else
-               cancel_delayed_work(&hdev->discov_off);
+       } else {
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       }
+
+       hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
 
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+update_ad:
+       update_adv_data(&req);
+
+       err = hci_req_run(&req, set_discoverable_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
-       if (cp->val)
-               hdev->discov_timeout = timeout;
-
 failed:
        hci_dev_unlock(hdev);
        return err;
@@ -1051,6 +1307,9 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
        struct hci_cp_write_page_scan_activity acp;
        u8 type;
 
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return;
+
        if (hdev->hci_ver < BLUETOOTH_VER_1_2)
                return;
 
@@ -1077,9 +1336,55 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
                hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
 }
 
+static u8 get_adv_type(struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+       bool connectable;
+
+       /* If there's a pending mgmt command the flag will not yet have
+        * it's final value, so check for this first.
+        */
+       cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+       if (cmd) {
+               struct mgmt_mode *cp = cmd->param;
+               connectable = !!cp->val;
+       } else {
+               connectable = test_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       }
+
+       return connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND;
+}
+
+static void enable_advertising(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_adv_param cp;
+       u8 enable = 0x01;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.min_interval = __constant_cpu_to_le16(0x0800);
+       cp.max_interval = __constant_cpu_to_le16(0x0800);
+       cp.type = get_adv_type(hdev);
+       cp.own_address_type = hdev->own_addr_type;
+       cp.channel_map = 0x07;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static void disable_advertising(struct hci_request *req)
+{
+       u8 enable = 0x00;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
 static void set_connectable_complete(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;
+       struct mgmt_mode *cp;
+       bool changed;
 
        BT_DBG("status 0x%02x", status);
 
@@ -1089,29 +1394,71 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
        if (!cmd)
                goto unlock;
 
+       if (status) {
+               u8 mgmt_err = mgmt_status(status);
+               cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+               goto remove_cmd;
+       }
+
+       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);
+
        send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
 
+       if (changed)
+               new_settings(hdev, cmd->sk);
+
+remove_cmd:
        mgmt_pending_remove(cmd);
 
 unlock:
        hci_dev_unlock(hdev);
 }
 
+static int set_connectable_update_settings(struct hci_dev *hdev,
+                                          struct sock *sk, u8 val)
+{
+       bool changed = false;
+       int err;
+
+       if (!!val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+               changed = true;
+
+       if (val) {
+               set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       } else {
+               clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+               clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+       }
+
+       err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
+       if (err < 0)
+               return err;
+
+       if (changed)
+               return new_settings(hdev, sk);
+
+       return 0;
+}
+
 static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                           u16 len)
 {
        struct mgmt_mode *cp = data;
        struct pending_cmd *cmd;
        struct hci_request req;
-       u8 scan, status;
+       u8 scan;
        int err;
 
        BT_DBG("request for %s", hdev->name);
 
-       status = mgmt_bredr_support(hdev);
-       if (status)
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+           !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
-                                 status);
+                                 MGMT_STATUS_REJECTED);
 
        if (cp->val != 0x00 && cp->val != 0x01)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
@@ -1120,25 +1467,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
-               bool changed = false;
-
-               if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-
-               if (cp->val) {
-                       set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-               } else {
-                       clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-                       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
-               }
-
-               err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
-               if (err < 0)
-                       goto failed;
-
-               if (changed)
-                       err = new_settings(hdev, sk);
-
+               err = set_connectable_update_settings(hdev, sk, cp->val);
                goto failed;
        }
 
@@ -1149,30 +1478,37 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
-       if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
-               err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
-               goto failed;
-       }
-
        cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        }
 
-       if (cp->val) {
-               scan = SCAN_PAGE;
-       } else {
-               scan = 0;
+       hci_req_init(&req, hdev);
 
-               if (test_bit(HCI_ISCAN, &hdev->flags) &&
-                   hdev->discov_timeout > 0)
-                       cancel_delayed_work(&hdev->discov_off);
-       }
+       /* If BR/EDR is not enabled and we disable advertising as a
+        * by-product of disabling connectable, we need to update the
+        * advertising flags.
+        */
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               if (!cp->val) {
+                       clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+                       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+               }
+               update_adv_data(&req);
+       } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
+               if (cp->val) {
+                       scan = SCAN_PAGE;
+               } else {
+                       scan = 0;
 
-       hci_req_init(&req, hdev);
+                       if (test_bit(HCI_ISCAN, &hdev->flags) &&
+                           hdev->discov_timeout > 0)
+                               cancel_delayed_work(&hdev->discov_off);
+               }
 
-       hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+               hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       }
 
        /* If we're going from non-connectable to connectable or
         * vice-versa when fast connectable is enabled ensure that fast
@@ -1183,9 +1519,20 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
        if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags))
                write_fast_connectable(&req, false);
 
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) &&
+           hci_conn_num(hdev, LE_LINK) == 0) {
+               disable_advertising(&req);
+               enable_advertising(&req);
+       }
+
        err = hci_req_run(&req, set_connectable_complete);
-       if (err < 0)
+       if (err < 0) {
                mgmt_pending_remove(cmd);
+               if (err == -ENODATA)
+                       err = set_connectable_update_settings(hdev, sk,
+                                                             cp->val);
+               goto failed;
+       }
 
 failed:
        hci_dev_unlock(hdev);
@@ -1196,6 +1543,7 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
                        u16 len)
 {
        struct mgmt_mode *cp = data;
+       bool changed;
        int err;
 
        BT_DBG("request for %s", hdev->name);
@@ -1207,17 +1555,18 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
        hci_dev_lock(hdev);
 
        if (cp->val)
-               set_bit(HCI_PAIRABLE, &hdev->dev_flags);
+               changed = !test_and_set_bit(HCI_PAIRABLE, &hdev->dev_flags);
        else
-               clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
+               changed = test_and_clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
 
        err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
        if (err < 0)
-               goto failed;
+               goto unlock;
 
-       err = new_settings(hdev, sk);
+       if (changed)
+               err = new_settings(hdev, sk);
 
-failed:
+unlock:
        hci_dev_unlock(hdev);
        return err;
 }
@@ -1296,7 +1645,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
        struct mgmt_mode *cp = data;
        struct pending_cmd *cmd;
-       u8 val, status;
+       u8 status;
        int err;
 
        BT_DBG("request for %s", hdev->name);
@@ -1315,14 +1664,20 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        hci_dev_lock(hdev);
 
-       val = !!cp->val;
-
        if (!hdev_is_powered(hdev)) {
-               bool changed = false;
+               bool changed;
 
-               if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
-                       change_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
-                       changed = true;
+               if (cp->val) {
+                       changed = !test_and_set_bit(HCI_SSP_ENABLED,
+                                                   &hdev->dev_flags);
+               } else {
+                       changed = test_and_clear_bit(HCI_SSP_ENABLED,
+                                                    &hdev->dev_flags);
+                       if (!changed)
+                               changed = test_and_clear_bit(HCI_HS_ENABLED,
+                                                            &hdev->dev_flags);
+                       else
+                               clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
                }
 
                err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
@@ -1335,13 +1690,14 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto failed;
        }
 
-       if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
+       if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev) ||
+           mgmt_pending_find(MGMT_OP_SET_HS, hdev)) {
                err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
                                 MGMT_STATUS_BUSY);
                goto failed;
        }
 
-       if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) {
+       if (!!cp->val == test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
                err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
                goto failed;
        }
@@ -1352,7 +1708,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto failed;
        }
 
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
+       err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &cp->val);
        if (err < 0) {
                mgmt_pending_remove(cmd);
                goto failed;
@@ -1376,6 +1732,14 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
        if (status)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status);
 
+       if (!lmp_ssp_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+                                 MGMT_STATUS_REJECTED);
+
        if (cp->val != 0x00 && cp->val != 0x01)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
                                  MGMT_STATUS_INVALID_PARAMS);
@@ -1424,6 +1788,24 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
 
        if (match.sk)
                sock_put(match.sk);
+
+       /* Make sure the controller has a good default for
+        * advertising data. Restrict the update to when LE
+        * has actually been enabled. During power on, the
+        * update in powered_update_hci will take care of it.
+        */
+       if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+               struct hci_request req;
+
+               hci_dev_lock(hdev);
+
+               hci_req_init(&req, hdev);
+               update_adv_data(&req);
+               update_scan_rsp_data(&req);
+               hci_req_run(&req, NULL);
+
+               hci_dev_unlock(hdev);
+       }
 }
 
 static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
@@ -1463,8 +1845,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                        changed = true;
                }
 
-               if (!val && test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
-                       clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+               if (!val && test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+                       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
                        changed = true;
                }
 
@@ -1491,18 +1873,18 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto unlock;
        }
 
+       hci_req_init(&req, hdev);
+
        memset(&hci_cp, 0, sizeof(hci_cp));
 
        if (val) {
                hci_cp.le = val;
                hci_cp.simul = lmp_le_br_capable(hdev);
+       } else {
+               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+                       disable_advertising(&req);
        }
 
-       hci_req_init(&req, hdev);
-
-       if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags) && !val)
-               hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(val), &val);
-
        hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
                    &hci_cp);
 
@@ -2640,8 +3022,11 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
                update_eir(&req);
        }
 
+       /* The name is stored in the scan response data and so
+        * no need to udpate the advertising data here.
+        */
        if (lmp_le_capable(hdev))
-               hci_update_ad(&req);
+               update_scan_rsp_data(&req);
 
        err = hci_req_run(&req, set_name_complete);
        if (err < 0)
@@ -2888,7 +3273,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                        goto failed;
                }
 
-               if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
+               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
                                         MGMT_STATUS_REJECTED);
                        mgmt_pending_remove(cmd);
@@ -2906,6 +3291,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                param_cp.type = LE_SCAN_ACTIVE;
                param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
                param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+               param_cp.own_address_type = hdev->own_addr_type;
                hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
                            &param_cp);
 
@@ -3214,7 +3600,8 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status)
                sock_put(match.sk);
 }
 
-static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
+static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
+                          u16 len)
 {
        struct mgmt_mode *cp = data;
        struct pending_cmd *cmd;
@@ -3236,13 +3623,19 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, u1
        hci_dev_lock(hdev);
 
        val = !!cp->val;
-       enabled = test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+       enabled = test_bit(HCI_ADVERTISING, &hdev->dev_flags);
 
-       if (!hdev_is_powered(hdev) || val == enabled) {
+       /* The following conditions are ones which mean that we should
+        * not do any HCI communication but directly send a mgmt
+        * response to user space (after toggling the flag if
+        * necessary).
+        */
+       if (!hdev_is_powered(hdev) || val == enabled ||
+           hci_conn_num(hdev, LE_LINK) > 0) {
                bool changed = false;
 
-               if (val != test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
-                       change_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+               if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+                       change_bit(HCI_ADVERTISING, &hdev->dev_flags);
                        changed = true;
                }
 
@@ -3271,7 +3664,10 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, u1
 
        hci_req_init(&req, hdev);
 
-       hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(val), &val);
+       if (val)
+               enable_advertising(&req);
+       else
+               disable_advertising(&req);
 
        err = hci_req_run(&req, set_advertising_complete);
        if (err < 0)
@@ -3322,6 +3718,47 @@ static int set_static_address(struct sock *sk, struct hci_dev *hdev,
        return err;
 }
 
+static int set_scan_params(struct sock *sk, struct hci_dev *hdev,
+                          void *data, u16 len)
+{
+       struct mgmt_cp_set_scan_params *cp = data;
+       __u16 interval, window;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       interval = __le16_to_cpu(cp->interval);
+
+       if (interval < 0x0004 || interval > 0x4000)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       window = __le16_to_cpu(cp->window);
+
+       if (window < 0x0004 || window > 0x4000)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       if (window > interval)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       hdev->le_scan_interval = interval;
+       hdev->le_scan_window = window;
+
+       err = cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0, NULL, 0);
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
 static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;
@@ -3420,6 +3857,26 @@ unlock:
        return err;
 }
 
+static void set_bredr_scan(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       u8 scan = 0;
+
+       /* Ensure that fast connectable is disabled. This function will
+        * not do anything if the page scan parameters are already what
+        * they should be.
+        */
+       write_fast_connectable(req, false);
+
+       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+               scan |= SCAN_PAGE;
+       if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+               scan |= SCAN_INQUIRY;
+
+       if (scan)
+               hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+}
+
 static void set_bredr_complete(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;
@@ -3482,7 +3939,6 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        if (!hdev_is_powered(hdev)) {
                if (!cp->val) {
-                       clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
                        clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
                        clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
                        clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
@@ -3519,13 +3975,21 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto unlock;
        }
 
-       /* We need to flip the bit already here so that hci_update_ad
+       /* We need to flip the bit already here so that update_adv_data
         * generates the correct flags.
         */
        set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
 
        hci_req_init(&req, hdev);
-       hci_update_ad(&req);
+
+       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+               set_bredr_scan(&req);
+
+       /* Since only the advertising data flags will change, there
+        * is no need to update the scan response data.
+        */
+       update_adv_data(&req);
+
        err = hci_req_run(&req, set_bredr_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
@@ -3587,15 +4051,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
 
        for (i = 0; i < key_count; i++) {
                struct mgmt_ltk_info *key = &cp->keys[i];
-               u8 type;
+               u8 type, addr_type;
+
+               if (key->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
 
                if (key->master)
                        type = HCI_SMP_LTK;
                else
                        type = HCI_SMP_LTK_SLAVE;
 
-               hci_add_ltk(hdev, &key->addr.bdaddr,
-                           bdaddr_to_le(key->addr.type),
+               hci_add_ltk(hdev, &key->addr.bdaddr, addr_type,
                            type, 0, key->authenticated, key->val,
                            key->enc_size, key->ediv, key->rand);
        }
@@ -3658,6 +4126,7 @@ static const struct mgmt_handler {
        { set_advertising,        false, MGMT_SETTING_SIZE },
        { set_bredr,              false, MGMT_SETTING_SIZE },
        { set_static_address,     false, MGMT_SET_STATIC_ADDRESS_SIZE },
+       { set_scan_params,        false, MGMT_SET_SCAN_PARAMS_SIZE },
 };
 
 
@@ -3703,7 +4172,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                        goto done;
                }
 
-               if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               if (test_bit(HCI_SETUP, &hdev->dev_flags) ||
+                   test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
                        err = cmd_status(sk, index, opcode,
                                         MGMT_STATUS_INVALID_INDEX);
                        goto done;
@@ -3753,44 +4223,24 @@ done:
        return err;
 }
 
-int mgmt_index_added(struct hci_dev *hdev)
+void mgmt_index_added(struct hci_dev *hdev)
 {
-       if (!mgmt_valid_hdev(hdev))
-               return -ENOTSUPP;
+       if (hdev->dev_type != HCI_BREDR)
+               return;
 
-       return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
+       mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
 }
 
-int mgmt_index_removed(struct hci_dev *hdev)
+void mgmt_index_removed(struct hci_dev *hdev)
 {
        u8 status = MGMT_STATUS_INVALID_INDEX;
 
-       if (!mgmt_valid_hdev(hdev))
-               return -ENOTSUPP;
+       if (hdev->dev_type != HCI_BREDR)
+               return;
 
        mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
 
-       return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
-}
-
-static void set_bredr_scan(struct hci_request *req)
-{
-       struct hci_dev *hdev = req->hdev;
-       u8 scan = 0;
-
-       /* Ensure that fast connectable is disabled. This function will
-        * not do anything if the page scan parameters are already what
-        * they should be.
-        */
-       write_fast_connectable(req, false);
-
-       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-               scan |= SCAN_PAGE;
-       if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-               scan |= SCAN_INQUIRY;
-
-       if (scan)
-               hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
 }
 
 static void powered_complete(struct hci_dev *hdev, u8 status)
@@ -3839,9 +4289,6 @@ static int powered_update_hci(struct hci_dev *hdev)
                    cp.simul != lmp_host_le_br_capable(hdev))
                        hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
                                    sizeof(cp), &cp);
-
-               /* In case BR/EDR was toggled during the AUTO_OFF phase */
-               hci_update_ad(&req);
        }
 
        if (lmp_le_capable(hdev)) {
@@ -3849,12 +4296,18 @@ static int powered_update_hci(struct hci_dev *hdev)
                if (bacmp(&hdev->static_addr, BDADDR_ANY))
                        hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
                                    &hdev->static_addr);
-       }
 
-       if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
-               u8 adv = 0x01;
+               /* Make sure the controller has a good default for
+                * advertising data. This also applies to the case
+                * where BR/EDR was toggled during the AUTO_OFF phase.
+                */
+               if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+                       update_adv_data(&req);
+                       update_scan_rsp_data(&req);
+               }
 
-               hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(adv), &adv);
+               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+                       enable_advertising(&req);
        }
 
        link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
@@ -3908,76 +4361,110 @@ new_settings:
        return err;
 }
 
-int mgmt_set_powered_failed(struct hci_dev *hdev, int err)
+void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
 {
        struct pending_cmd *cmd;
        u8 status;
 
        cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        if (err == -ERFKILL)
                status = MGMT_STATUS_RFKILLED;
        else
                status = MGMT_STATUS_FAILED;
 
-       err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
+       cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
 
        mgmt_pending_remove(cmd);
+}
 
-       return err;
+void mgmt_discoverable_timeout(struct hci_dev *hdev)
+{
+       struct hci_request req;
+
+       hci_dev_lock(hdev);
+
+       /* When discoverable timeout triggers, then just make sure
+        * the limited discoverable flag is cleared. Even in the case
+        * of a timeout triggered from general discoverable, it is
+        * safe to unconditionally clear the flag.
+        */
+       clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+
+       hci_req_init(&req, hdev);
+       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               u8 scan = SCAN_PAGE;
+               hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE,
+                           sizeof(scan), &scan);
+       }
+       update_class(&req);
+       update_adv_data(&req);
+       hci_req_run(&req, NULL);
+
+       hdev->discov_timeout = 0;
+
+       new_settings(hdev, NULL);
+
+       hci_dev_unlock(hdev);
 }
 
-int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
+void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
 {
-       struct cmd_lookup match = { NULL, hdev };
-       bool changed = false;
-       int err = 0;
+       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;
 
        if (discoverable) {
-               if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-                       changed = true;
+               changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
        } else {
-               if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-                       changed = true;
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+               changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
        }
 
-       mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp,
-                            &match);
-
-       if (changed)
-               err = new_settings(hdev, match.sk);
+       if (changed) {
+               struct hci_request req;
 
-       if (match.sk)
-               sock_put(match.sk);
+               /* 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);
 
-       return err;
+               new_settings(hdev, NULL);
+       }
 }
 
-int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
+void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
 {
-       struct pending_cmd *cmd;
-       bool changed = false;
-       int err = 0;
+       bool changed;
 
-       if (connectable) {
-               if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-       } else {
-               if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-       }
+       /* 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;
 
-       cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+       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)
-               err = new_settings(hdev, cmd ? cmd->sk : NULL);
-
-       return err;
+               new_settings(hdev, NULL);
 }
 
-int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
+void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
 {
        u8 mgmt_err = mgmt_status(status);
 
@@ -3988,12 +4475,10 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
        if (scan & SCAN_INQUIRY)
                mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
                                     cmd_status_rsp, &mgmt_err);
-
-       return 0;
 }
 
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
-                     bool persistent)
+void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+                      bool persistent)
 {
        struct mgmt_ev_new_link_key ev;
 
@@ -4006,10 +4491,10 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
        memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE);
        ev.key.pin_len = key->pin_len;
 
-       return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
 {
        struct mgmt_ev_new_long_term_key ev;
 
@@ -4028,13 +4513,23 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
        memcpy(ev.key.rand, key->rand, sizeof(key->rand));
        memcpy(ev.key.val, key->val, sizeof(key->val));
 
-       return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev),
-                         NULL);
+       mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
+}
+
+static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
+                                 u8 data_len)
+{
+       eir[eir_len++] = sizeof(type) + data_len;
+       eir[eir_len++] = type;
+       memcpy(&eir[eir_len], data, data_len);
+       eir_len += data_len;
+
+       return eir_len;
 }
 
-int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                         u8 addr_type, u32 flags, u8 *name, u8 name_len,
-                         u8 *dev_class)
+void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                          u8 addr_type, u32 flags, u8 *name, u8 name_len,
+                          u8 *dev_class)
 {
        char buf[512];
        struct mgmt_ev_device_connected *ev = (void *) buf;
@@ -4055,8 +4550,8 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        ev->eir_len = cpu_to_le16(eir_len);
 
-       return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
-                         sizeof(*ev) + eir_len, NULL);
+       mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
+                   sizeof(*ev) + eir_len, NULL);
 }
 
 static void disconnect_rsp(struct pending_cmd *cmd, void *data)
@@ -4094,12 +4589,11 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
        mgmt_pending_remove(cmd);
 }
 
-int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                            u8 link_type, u8 addr_type, u8 reason)
+void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                             u8 link_type, u8 addr_type, u8 reason)
 {
        struct mgmt_ev_device_disconnected ev;
        struct sock *sk = NULL;
-       int err;
 
        mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
 
@@ -4107,45 +4601,39 @@ int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
        ev.addr.type = link_to_bdaddr(link_type, addr_type);
        ev.reason = reason;
 
-       err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
-                        sk);
+       mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
 
        if (sk)
                sock_put(sk);
 
        mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
                             hdev);
-
-       return err;
 }
 
-int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                          u8 link_type, u8 addr_type, u8 status)
+void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 link_type, u8 addr_type, u8 status)
 {
        struct mgmt_rp_disconnect rp;
        struct pending_cmd *cmd;
-       int err;
 
        mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
                             hdev);
 
        cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        bacpy(&rp.addr.bdaddr, bdaddr);
        rp.addr.type = link_to_bdaddr(link_type, addr_type);
 
-       err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
-                          mgmt_status(status), &rp, sizeof(rp));
+       cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
+                    mgmt_status(status), &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
-int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                       u8 addr_type, u8 status)
+void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                        u8 addr_type, u8 status)
 {
        struct mgmt_ev_connect_failed ev;
 
@@ -4153,10 +4641,10 @@ int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        ev.addr.type = link_to_bdaddr(link_type, addr_type);
        ev.status = mgmt_status(status);
 
-       return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
+void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
 {
        struct mgmt_ev_pin_code_request ev;
 
@@ -4164,52 +4652,45 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
        ev.addr.type = BDADDR_BREDR;
        ev.secure = secure;
 
-       return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
-                         NULL);
+       mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                u8 status)
+void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                 u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_pin_code_reply rp;
-       int err;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        bacpy(&rp.addr.bdaddr, bdaddr);
        rp.addr.type = BDADDR_BREDR;
 
-       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
-                          mgmt_status(status), &rp, sizeof(rp));
+       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+                    mgmt_status(status), &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
-int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 status)
+void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                     u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_pin_code_reply rp;
-       int err;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        bacpy(&rp.addr.bdaddr, bdaddr);
        rp.addr.type = BDADDR_BREDR;
 
-       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
-                          mgmt_status(status), &rp, sizeof(rp));
+       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
+                    mgmt_status(status), &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
 int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -4311,8 +4792,8 @@ int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
        return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, u8 status)
+void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, u8 status)
 {
        struct mgmt_ev_auth_failed ev;
 
@@ -4320,40 +4801,36 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        ev.addr.type = link_to_bdaddr(link_type, addr_type);
        ev.status = mgmt_status(status);
 
-       return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
+void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
-       bool changed = false;
-       int err = 0;
+       bool changed;
 
        if (status) {
                u8 mgmt_err = mgmt_status(status);
                mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
                                     cmd_status_rsp, &mgmt_err);
-               return 0;
+               return;
        }
 
-       if (test_bit(HCI_AUTH, &hdev->flags)) {
-               if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
-                       changed = true;
-       } else {
-               if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
-                       changed = true;
-       }
+       if (test_bit(HCI_AUTH, &hdev->flags))
+               changed = !test_and_set_bit(HCI_LINK_SECURITY,
+                                           &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_LINK_SECURITY,
+                                            &hdev->dev_flags);
 
        mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
                             &match);
 
        if (changed)
-               err = new_settings(hdev, match.sk);
+               new_settings(hdev, match.sk);
 
        if (match.sk)
                sock_put(match.sk);
-
-       return err;
 }
 
 static void clear_eir(struct hci_request *req)
@@ -4371,38 +4848,41 @@ static void clear_eir(struct hci_request *req)
        hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
 }
 
-int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
+void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
        struct hci_request req;
        bool changed = false;
-       int err = 0;
 
        if (status) {
                u8 mgmt_err = mgmt_status(status);
 
                if (enable && test_and_clear_bit(HCI_SSP_ENABLED,
-                                                &hdev->dev_flags))
-                       err = new_settings(hdev, NULL);
+                                                &hdev->dev_flags)) {
+                       clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+                       new_settings(hdev, NULL);
+               }
 
                mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp,
                                     &mgmt_err);
-
-               return err;
+               return;
        }
 
        if (enable) {
-               if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
-                       changed = true;
+               changed = !test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
        } else {
-               if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
-                       changed = true;
+               changed = test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+               if (!changed)
+                       changed = test_and_clear_bit(HCI_HS_ENABLED,
+                                                    &hdev->dev_flags);
+               else
+                       clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
        }
 
        mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
 
        if (changed)
-               err = new_settings(hdev, match.sk);
+               new_settings(hdev, match.sk);
 
        if (match.sk)
                sock_put(match.sk);
@@ -4415,8 +4895,6 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
                clear_eir(&req);
 
        hci_req_run(&req, NULL);
-
-       return err;
 }
 
 static void sk_lookup(struct pending_cmd *cmd, void *data)
@@ -4429,33 +4907,30 @@ static void sk_lookup(struct pending_cmd *cmd, void *data)
        }
 }
 
-int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
-                                  u8 status)
+void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
+                                   u8 status)
 {
        struct cmd_lookup match = { NULL, hdev, mgmt_status(status) };
-       int err = 0;
 
        mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match);
        mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match);
        mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
 
        if (!status)
-               err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class,
-                                3, NULL);
+               mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3,
+                          NULL);
 
        if (match.sk)
                sock_put(match.sk);
-
-       return err;
 }
 
-int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
+void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
 {
        struct mgmt_cp_set_local_name ev;
        struct pending_cmd *cmd;
 
        if (status)
-               return 0;
+               return;
 
        memset(&ev, 0, sizeof(ev));
        memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
@@ -4469,58 +4944,54 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
                 * HCI dev don't send any mgmt signals.
                 */
                if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
-                       return 0;
+                       return;
        }
 
-       return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
-                         cmd ? cmd->sk : NULL);
+       mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
+                  cmd ? cmd->sk : NULL);
 }
 
-int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
-                                           u8 *randomizer, u8 status)
+void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+                                            u8 *randomizer, u8 status)
 {
        struct pending_cmd *cmd;
-       int err;
 
        BT_DBG("%s status %u", hdev->name, status);
 
        cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        if (status) {
-               err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
-                                mgmt_status(status));
+               cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+                          mgmt_status(status));
        } else {
                struct mgmt_rp_read_local_oob_data rp;
 
                memcpy(rp.hash, hash, sizeof(rp.hash));
                memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
 
-               err = cmd_complete(cmd->sk, hdev->id,
-                                  MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp,
-                                  sizeof(rp));
+               cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+                            0, &rp, sizeof(rp));
        }
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
-int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                     u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
-                     ssp, u8 *eir, u16 eir_len)
+void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
+                      ssp, u8 *eir, u16 eir_len)
 {
        char buf[512];
        struct mgmt_ev_device_found *ev = (void *) buf;
        size_t ev_size;
 
        if (!hci_discovery_active(hdev))
-               return -EPERM;
+               return;
 
        /* Leave 5 bytes for a potential CoD field */
        if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
-               return -EINVAL;
+               return;
 
        memset(buf, 0, sizeof(buf));
 
@@ -4542,11 +5013,11 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        ev->eir_len = cpu_to_le16(eir_len);
        ev_size = sizeof(*ev) + eir_len;
 
-       return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
+       mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
 }
 
-int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, s8 rssi, u8 *name, u8 name_len)
+void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, s8 rssi, u8 *name, u8 name_len)
 {
        struct mgmt_ev_device_found *ev;
        char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
@@ -4565,11 +5036,10 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        ev->eir_len = cpu_to_le16(eir_len);
 
-       return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
-                         sizeof(*ev) + eir_len, NULL);
+       mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, sizeof(*ev) + eir_len, NULL);
 }
 
-int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
+void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
 {
        struct mgmt_ev_discovering ev;
        struct pending_cmd *cmd;
@@ -4593,7 +5063,7 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
        ev.type = hdev->discovery.type;
        ev.discovering = discovering;
 
-       return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
 }
 
 int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
@@ -4623,3 +5093,36 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
        return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
                          cmd ? cmd->sk : NULL);
 }
+
+static void adv_enable_complete(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("%s status %u", hdev->name, status);
+
+       /* Clear the advertising mgmt setting if we failed to re-enable it */
+       if (status) {
+               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+               new_settings(hdev, NULL);
+       }
+}
+
+void mgmt_reenable_advertising(struct hci_dev *hdev)
+{
+       struct hci_request req;
+
+       if (hci_conn_num(hdev, LE_LINK) > 0)
+               return;
+
+       if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+               return;
+
+       hci_req_init(&req, hdev);
+       enable_advertising(&req);
+
+       /* If this fails we have no option but to let user space know
+        * that we've disabled advertising.
+        */
+       if (hci_req_run(&req, adv_enable_complete) < 0) {
+               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+               new_settings(hdev, NULL);
+       }
+}
index ca957d34b0c89fa29341a179ee16e325ac226e55..94d06cbfbc184a6e827aa5c3cbd15d4693eca49a 100644 (file)
@@ -641,13 +641,13 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
 {
        struct rfcomm_session *s;
        struct list_head *p, *n;
-       struct bt_sock *sk;
+       struct l2cap_chan *chan;
        list_for_each_safe(p, n, &session_list) {
                s = list_entry(p, struct rfcomm_session, list);
-               sk = bt_sk(s->sock->sk);
+               chan = l2cap_pi(s->sock->sk)->chan;
 
-               if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) &&
-                               !bacmp(&sk->dst, dst))
+               if ((!bacmp(src, BDADDR_ANY) || !bacmp(&chan->src, src)) &&
+                   !bacmp(&chan->dst, dst))
                        return s;
        }
        return NULL;
@@ -732,11 +732,11 @@ failed:
 
 void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst)
 {
-       struct sock *sk = s->sock->sk;
+       struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan;
        if (src)
-               bacpy(src, &bt_sk(sk)->src);
+               bacpy(src, &chan->src);
        if (dst)
-               bacpy(dst, &bt_sk(sk)->dst);
+               bacpy(dst, &chan->dst);
 }
 
 /* ---- RFCOMM frame sending ---- */
@@ -2112,12 +2112,11 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
        rfcomm_lock();
 
        list_for_each_entry(s, &session_list, list) {
+               struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan;
                struct rfcomm_dlc *d;
                list_for_each_entry(d, &s->dlcs, list) {
-                       struct sock *sk = s->sock->sk;
-
                        seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n",
-                                  &bt_sk(sk)->src, &bt_sk(sk)->dst,
+                                  &chan->src, &chan->dst,
                                   d->state, d->dlci, d->mtu,
                                   d->rx_credits, d->tx_credits);
                }
@@ -2155,13 +2154,6 @@ static int __init rfcomm_init(void)
                goto unregister;
        }
 
-       if (bt_debugfs) {
-               rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
-                               bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops);
-               if (!rfcomm_dlc_debugfs)
-                       BT_ERR("Failed to create RFCOMM debug file");
-       }
-
        err = rfcomm_init_ttys();
        if (err < 0)
                goto stop;
@@ -2172,6 +2164,13 @@ static int __init rfcomm_init(void)
 
        BT_INFO("RFCOMM ver %s", VERSION);
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
+                                                bt_debugfs, NULL,
+                                                &rfcomm_dlc_debugfs_fops);
+
        return 0;
 
 cleanup:
index 072938dc527d5800af3cf90b9da970585b277c6b..c4d3d423f89b84d41b9ff0e6919e9f37786f4960 100644 (file)
@@ -87,7 +87,8 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
                parent->sk_data_ready(parent, 0);
        } else {
                if (d->state == BT_CONNECTED)
-                       rfcomm_session_getaddr(d->session, &bt_sk(sk)->src, NULL);
+                       rfcomm_session_getaddr(d->session,
+                                              &rfcomm_pi(sk)->src, NULL);
                sk->sk_state_change(sk);
        }
 
@@ -110,7 +111,7 @@ static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
 
        sk_for_each(sk, &rfcomm_sk_list.head) {
                if (rfcomm_pi(sk)->channel == channel &&
-                               !bacmp(&bt_sk(sk)->src, src))
+                               !bacmp(&rfcomm_pi(sk)->src, src))
                        break;
        }
 
@@ -132,11 +133,11 @@ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *
 
                if (rfcomm_pi(sk)->channel == channel) {
                        /* Exact match. */
-                       if (!bacmp(&bt_sk(sk)->src, src))
+                       if (!bacmp(&rfcomm_pi(sk)->src, src))
                                break;
 
                        /* Closest match */
-                       if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+                       if (!bacmp(&rfcomm_pi(sk)->src, BDADDR_ANY))
                                sk1 = sk;
                }
        }
@@ -355,7 +356,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
                err = -EADDRINUSE;
        } else {
                /* Save source address */
-               bacpy(&bt_sk(sk)->src, &sa->rc_bdaddr);
+               bacpy(&rfcomm_pi(sk)->src, &sa->rc_bdaddr);
                rfcomm_pi(sk)->channel = sa->rc_channel;
                sk->sk_state = BT_BOUND;
        }
@@ -393,13 +394,14 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
        }
 
        sk->sk_state = BT_CONNECT;
-       bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr);
+       bacpy(&rfcomm_pi(sk)->dst, &sa->rc_bdaddr);
        rfcomm_pi(sk)->channel = sa->rc_channel;
 
        d->sec_level = rfcomm_pi(sk)->sec_level;
        d->role_switch = rfcomm_pi(sk)->role_switch;
 
-       err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+       err = rfcomm_dlc_open(d, &rfcomm_pi(sk)->src, &sa->rc_bdaddr,
+                             sa->rc_channel);
        if (!err)
                err = bt_sock_wait_state(sk, BT_CONNECTED,
                                sock_sndtimeo(sk, flags & O_NONBLOCK));
@@ -429,7 +431,7 @@ static int rfcomm_sock_listen(struct socket *sock, int backlog)
        }
 
        if (!rfcomm_pi(sk)->channel) {
-               bdaddr_t *src = &bt_sk(sk)->src;
+               bdaddr_t *src = &rfcomm_pi(sk)->src;
                u8 channel;
 
                err = -EINVAL;
@@ -530,9 +532,9 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *
        sa->rc_family  = AF_BLUETOOTH;
        sa->rc_channel = rfcomm_pi(sk)->channel;
        if (peer)
-               bacpy(&sa->rc_bdaddr, &bt_sk(sk)->dst);
+               bacpy(&sa->rc_bdaddr, &rfcomm_pi(sk)->dst);
        else
-               bacpy(&sa->rc_bdaddr, &bt_sk(sk)->src);
+               bacpy(&sa->rc_bdaddr, &rfcomm_pi(sk)->src);
 
        *len = sizeof(struct sockaddr_rc);
        return 0;
@@ -951,8 +953,8 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
        bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM);
 
        rfcomm_sock_init(sk, parent);
-       bacpy(&bt_sk(sk)->src, &src);
-       bacpy(&bt_sk(sk)->dst, &dst);
+       bacpy(&rfcomm_pi(sk)->src, &src);
+       bacpy(&rfcomm_pi(sk)->dst, &dst);
        rfcomm_pi(sk)->channel = channel;
 
        sk->sk_state = BT_CONFIG;
@@ -979,7 +981,7 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p)
 
        sk_for_each(sk, &rfcomm_sk_list.head) {
                seq_printf(f, "%pMR %pMR %d %d\n",
-                          &bt_sk(sk)->src, &bt_sk(sk)->dst,
+                          &rfcomm_pi(sk)->src, &rfcomm_pi(sk)->dst,
                           sk->sk_state, rfcomm_pi(sk)->channel);
        }
 
@@ -1049,15 +1051,15 @@ int __init rfcomm_init_sockets(void)
                goto error;
        }
 
-       if (bt_debugfs) {
-               rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
-                               bt_debugfs, NULL, &rfcomm_sock_debugfs_fops);
-               if (!rfcomm_sock_debugfs)
-                       BT_ERR("Failed to create RFCOMM debug file");
-       }
-
        BT_INFO("RFCOMM socket layer initialized");
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
+                                                 bt_debugfs, NULL,
+                                                 &rfcomm_sock_debugfs_fops);
+
        return 0;
 
 error:
index 96bd388d93a4aae145bfb207d26fbc3a90b9e0a8..12a0e51e21e13631beeec14d97e64aa137fd0e99 100644 (file)
@@ -92,9 +92,6 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
        hcon->sco_data = conn;
        conn->hcon = hcon;
 
-       conn->src = &hdev->bdaddr;
-       conn->dst = &hcon->dst;
-
        if (hdev->sco_mtu > 0)
                conn->mtu = hdev->sco_mtu;
        else
@@ -156,16 +153,14 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
 
 static int sco_connect(struct sock *sk)
 {
-       bdaddr_t *src = &bt_sk(sk)->src;
-       bdaddr_t *dst = &bt_sk(sk)->dst;
        struct sco_conn *conn;
        struct hci_conn *hcon;
        struct hci_dev  *hdev;
        int err, type;
 
-       BT_DBG("%pMR -> %pMR", src, dst);
+       BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
 
-       hdev = hci_get_route(dst, src);
+       hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src);
        if (!hdev)
                return -EHOSTUNREACH;
 
@@ -182,7 +177,8 @@ static int sco_connect(struct sock *sk)
                goto done;
        }
 
-       hcon = hci_connect_sco(hdev, type, dst, sco_pi(sk)->setting);
+       hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
+                              sco_pi(sk)->setting);
        if (IS_ERR(hcon)) {
                err = PTR_ERR(hcon);
                goto done;
@@ -196,7 +192,7 @@ static int sco_connect(struct sock *sk)
        }
 
        /* Update source addr of the socket */
-       bacpy(src, conn->src);
+       bacpy(&sco_pi(sk)->src, &hcon->src);
 
        err = sco_chan_add(conn, sk, NULL);
        if (err)
@@ -270,7 +266,7 @@ static struct sock *__sco_get_sock_listen_by_addr(bdaddr_t *ba)
                if (sk->sk_state != BT_LISTEN)
                        continue;
 
-               if (!bacmp(&bt_sk(sk)->src, ba))
+               if (!bacmp(&sco_pi(sk)->src, ba))
                        return sk;
        }
 
@@ -291,11 +287,11 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src)
                        continue;
 
                /* Exact match. */
-               if (!bacmp(&bt_sk(sk)->src, src))
+               if (!bacmp(&sco_pi(sk)->src, src))
                        break;
 
                /* Closest match */
-               if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+               if (!bacmp(&sco_pi(sk)->src, BDADDR_ANY))
                        sk1 = sk;
        }
 
@@ -475,7 +471,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
                goto done;
        }
 
-       bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr);
+       bacpy(&sco_pi(sk)->src, &sa->sco_bdaddr);
 
        sk->sk_state = BT_BOUND;
 
@@ -505,7 +501,7 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
        lock_sock(sk);
 
        /* Set destination address and psm */
-       bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr);
+       bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
 
        err = sco_connect(sk);
        if (err)
@@ -522,7 +518,7 @@ done:
 static int sco_sock_listen(struct socket *sock, int backlog)
 {
        struct sock *sk = sock->sk;
-       bdaddr_t *src = &bt_sk(sk)->src;
+       bdaddr_t *src = &sco_pi(sk)->src;
        int err = 0;
 
        BT_DBG("sk %p backlog %d", sk, backlog);
@@ -626,9 +622,9 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len
        *len = sizeof(struct sockaddr_sco);
 
        if (peer)
-               bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst);
+               bacpy(&sa->sco_bdaddr, &sco_pi(sk)->dst);
        else
-               bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src);
+               bacpy(&sa->sco_bdaddr, &sco_pi(sk)->src);
 
        return 0;
 }
@@ -999,7 +995,7 @@ static void sco_conn_ready(struct sco_conn *conn)
        } else {
                sco_conn_lock(conn);
 
-               parent = sco_get_sock_listen(conn->src);
+               parent = sco_get_sock_listen(&conn->hcon->src);
                if (!parent) {
                        sco_conn_unlock(conn);
                        return;
@@ -1017,8 +1013,8 @@ static void sco_conn_ready(struct sco_conn *conn)
 
                sco_sock_init(sk, parent);
 
-               bacpy(&bt_sk(sk)->src, conn->src);
-               bacpy(&bt_sk(sk)->dst, conn->dst);
+               bacpy(&sco_pi(sk)->src, &conn->hcon->src);
+               bacpy(&sco_pi(sk)->dst, &conn->hcon->dst);
 
                hci_conn_hold(conn->hcon);
                __sco_chan_add(conn, sk, parent);
@@ -1051,8 +1047,8 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
                if (sk->sk_state != BT_LISTEN)
                        continue;
 
-               if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
-                   !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+               if (!bacmp(&sco_pi(sk)->src, &hdev->bdaddr) ||
+                   !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) {
                        lm |= HCI_LM_ACCEPT;
 
                        if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
@@ -1111,8 +1107,8 @@ static int sco_debugfs_show(struct seq_file *f, void *p)
        read_lock(&sco_sk_list.lock);
 
        sk_for_each(sk, &sco_sk_list.head) {
-               seq_printf(f, "%pMR %pMR %d\n", &bt_sk(sk)->src,
-                          &bt_sk(sk)->dst, sk->sk_state);
+               seq_printf(f, "%pMR %pMR %d\n", &sco_pi(sk)->src,
+                          &sco_pi(sk)->dst, sk->sk_state);
        }
 
        read_unlock(&sco_sk_list.lock);
@@ -1181,15 +1177,14 @@ int __init sco_init(void)
                goto error;
        }
 
-       if (bt_debugfs) {
-               sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs,
-                                                 NULL, &sco_debugfs_fops);
-               if (!sco_debugfs)
-                       BT_ERR("Failed to create SCO debug file");
-       }
-
        BT_INFO("SCO socket layer initialized");
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs,
+                                         NULL, &sco_debugfs_fops);
+
        return 0;
 
 error:
index 884b2081a262ae14e7b044d4e3b825c686cc42a4..85a2796cac61bcc423ef724f1911f2a794667aff 100644 (file)
@@ -28,7 +28,8 @@
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
 
 #define SMP_TIMEOUT    msecs_to_jiffies(30000)
 
@@ -85,8 +86,8 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
 }
 
 static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
-               u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
-               u8 _rat, bdaddr_t *ra, u8 res[16])
+                 u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
+                 u8 _rat, bdaddr_t *ra, u8 res[16])
 {
        u8 p1[16], p2[16];
        int err;
@@ -126,8 +127,8 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
        return err;
 }
 
-static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
-                       u8 r1[16], u8 r2[16], u8 _r[16])
+static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16],
+                 u8 r2[16], u8 _r[16])
 {
        int err;
 
@@ -150,7 +151,7 @@ static int smp_rand(u8 *buf)
 }
 
 static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
-                                               u16 dlen, void *data)
+                                    u16 dlen, void *data)
 {
        struct sk_buff *skb;
        struct l2cap_hdr *lh;
@@ -213,9 +214,8 @@ static __u8 seclevel_to_authreq(__u8 sec_level)
 }
 
 static void build_pairing_cmd(struct l2cap_conn *conn,
-                               struct smp_cmd_pairing *req,
-                               struct smp_cmd_pairing *rsp,
-                               __u8 authreq)
+                             struct smp_cmd_pairing *req,
+                             struct smp_cmd_pairing *rsp, __u8 authreq)
 {
        u8 dist_keys = 0;
 
@@ -249,7 +249,7 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
        struct smp_chan *smp = conn->smp_chan;
 
        if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
-                       (max_key_size < SMP_MIN_ENC_KEY_SIZE))
+           (max_key_size < SMP_MIN_ENC_KEY_SIZE))
                return SMP_ENC_KEY_SIZE;
 
        smp->enc_key_size = max_key_size;
@@ -263,15 +263,15 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
 
        if (send)
                smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
-                                                               &reason);
+                            &reason);
 
-       clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags);
-       mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type,
-                        hcon->dst_type, HCI_ERROR_AUTH_FAILURE);
+       clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags);
+       mgmt_auth_failed(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type,
+                        HCI_ERROR_AUTH_FAILURE);
 
        cancel_delayed_work_sync(&conn->security_timer);
 
-       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
+       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
                smp_chan_destroy(conn);
 }
 
@@ -309,8 +309,8 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        /* If either side has unknown io_caps, use JUST WORKS */
        /* Otherwise, look up method from the table */
        if (!(auth & SMP_AUTH_MITM) ||
-                       local_io > SMP_IO_KEYBOARD_DISPLAY ||
-                       remote_io > SMP_IO_KEYBOARD_DISPLAY)
+           local_io > SMP_IO_KEYBOARD_DISPLAY ||
+           remote_io > SMP_IO_KEYBOARD_DISPLAY)
                method = JUST_WORKS;
        else
                method = gen_method[remote_io][local_io];
@@ -354,10 +354,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        hci_dev_lock(hcon->hdev);
 
        if (method == REQ_PASSKEY)
-               ret = mgmt_user_passkey_request(hcon->hdev, conn->dst,
+               ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type);
        else
-               ret = mgmt_user_confirm_request(hcon->hdev, conn->dst,
+               ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type,
                                                cpu_to_le32(passkey), 0);
 
@@ -386,12 +386,13 @@ static void confirm_work(struct work_struct *work)
        smp->tfm = tfm;
 
        if (conn->hcon->out)
-               ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0,
-                            conn->src, conn->hcon->dst_type, conn->dst, res);
+               ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
+                            conn->hcon->src_type, &conn->hcon->src,
+                            conn->hcon->dst_type, &conn->hcon->dst, res);
        else
                ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
-                            conn->hcon->dst_type, conn->dst, 0, conn->src,
-                            res);
+                            conn->hcon->dst_type, &conn->hcon->dst,
+                            conn->hcon->src_type, &conn->hcon->src, res);
        if (ret) {
                reason = SMP_UNSPECIFIED;
                goto error;
@@ -425,11 +426,13 @@ static void random_work(struct work_struct *work)
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
        if (hcon->out)
-               ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0,
-                            conn->src, hcon->dst_type, conn->dst, res);
+               ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+                            hcon->src_type, &hcon->src,
+                            hcon->dst_type, &hcon->dst, res);
        else
                ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
-                            hcon->dst_type, conn->dst, 0, conn->src, res);
+                            hcon->dst_type, &hcon->dst,
+                            hcon->src_type, &hcon->src, res);
        if (ret) {
                reason = SMP_UNSPECIFIED;
                goto error;
@@ -477,9 +480,9 @@ static void random_work(struct work_struct *work)
                swap128(key, stk);
 
                memset(stk + smp->enc_key_size, 0,
-                               SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
+                      SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
 
-               hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type,
+               hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
                            HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size,
                            ediv, rand);
        }
@@ -494,7 +497,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
 {
        struct smp_chan *smp;
 
-       smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC);
+       smp = kzalloc(sizeof(*smp), GFP_ATOMIC);
        if (!smp)
                return NULL;
 
@@ -649,7 +652,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
 
        if ((req->auth_req & SMP_AUTH_BONDING) &&
-                       (rsp->auth_req & SMP_AUTH_BONDING))
+           (rsp->auth_req & SMP_AUTH_BONDING))
                auth = SMP_AUTH_BONDING;
 
        auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM;
@@ -684,7 +687,7 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 
                swap128(smp->prnd, random);
                smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
-                                                               random);
+                            random);
        } else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) {
                queue_work(hdev->workqueue, &smp->confirm);
        } else {
@@ -714,7 +717,7 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        struct smp_ltk *key;
        struct hci_conn *hcon = conn->hcon;
 
-       key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type);
+       key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type);
        if (!key)
                return 0;
 
@@ -728,8 +731,8 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        hcon->enc_key_size = key->enc_size;
 
        return 1;
-
 }
+
 static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_security_req *rp = (void *) skb->data;
@@ -835,9 +838,9 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
        skb_pull(skb, sizeof(*rp));
 
        hci_dev_lock(hdev);
-       authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH);
-       hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type,
-                   HCI_SMP_LTK, 1, authenticated, smp->tk, smp->enc_key_size,
+       authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
+       hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, 1,
+                   authenticated, smp->tk, smp->enc_key_size,
                    rp->ediv, rp->rand);
        smp_distribute_keys(conn, 1);
        hci_dev_unlock(hdev);
@@ -853,7 +856,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
 
        if (hcon->type != LE_LINK) {
                kfree_skb(skb);
-               return -ENOTSUPP;
+               return 0;
        }
 
        if (skb->len < 1) {
@@ -861,7 +864,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
                return -EILSEQ;
        }
 
-       if (!test_bit(HCI_LE_ENABLED, &conn->hcon->hdev->dev_flags)) {
+       if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
                err = -ENOTSUPP;
                reason = SMP_PAIRING_NOTSUPP;
                goto done;
@@ -985,7 +988,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
                smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
 
                authenticated = hcon->sec_level == BT_SECURITY_HIGH;
-               hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type,
+               hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
                            HCI_SMP_LTK_SLAVE, 1, authenticated,
                            enc.ltk, smp->enc_key_size, ediv, ident.rand);
 
@@ -1007,10 +1010,10 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
 
                /* Just public address */
                memset(&addrinfo, 0, sizeof(addrinfo));
-               bacpy(&addrinfo.bdaddr, conn->src);
+               bacpy(&addrinfo.bdaddr, &conn->hcon->src);
 
                smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
-                                                               &addrinfo);
+                            &addrinfo);
 
                *keydist &= ~SMP_DIST_ID_KEY;
        }
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
new file mode 100644 (file)
index 0000000..f8ba07f
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+   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 __SMP_H
+#define __SMP_H
+
+struct smp_command_hdr {
+       __u8    code;
+} __packed;
+
+#define SMP_CMD_PAIRING_REQ    0x01
+#define SMP_CMD_PAIRING_RSP    0x02
+struct smp_cmd_pairing {
+       __u8    io_capability;
+       __u8    oob_flag;
+       __u8    auth_req;
+       __u8    max_key_size;
+       __u8    init_key_dist;
+       __u8    resp_key_dist;
+} __packed;
+
+#define SMP_IO_DISPLAY_ONLY    0x00
+#define SMP_IO_DISPLAY_YESNO   0x01
+#define SMP_IO_KEYBOARD_ONLY   0x02
+#define SMP_IO_NO_INPUT_OUTPUT 0x03
+#define SMP_IO_KEYBOARD_DISPLAY        0x04
+
+#define SMP_OOB_NOT_PRESENT    0x00
+#define SMP_OOB_PRESENT                0x01
+
+#define SMP_DIST_ENC_KEY       0x01
+#define SMP_DIST_ID_KEY                0x02
+#define SMP_DIST_SIGN          0x04
+
+#define SMP_AUTH_NONE          0x00
+#define SMP_AUTH_BONDING       0x01
+#define SMP_AUTH_MITM          0x04
+
+#define SMP_CMD_PAIRING_CONFIRM        0x03
+struct smp_cmd_pairing_confirm {
+       __u8    confirm_val[16];
+} __packed;
+
+#define SMP_CMD_PAIRING_RANDOM 0x04
+struct smp_cmd_pairing_random {
+       __u8    rand_val[16];
+} __packed;
+
+#define SMP_CMD_PAIRING_FAIL   0x05
+struct smp_cmd_pairing_fail {
+       __u8    reason;
+} __packed;
+
+#define SMP_CMD_ENCRYPT_INFO   0x06
+struct smp_cmd_encrypt_info {
+       __u8    ltk[16];
+} __packed;
+
+#define SMP_CMD_MASTER_IDENT   0x07
+struct smp_cmd_master_ident {
+       __le16  ediv;
+       __u8    rand[8];
+} __packed;
+
+#define SMP_CMD_IDENT_INFO     0x08
+struct smp_cmd_ident_info {
+       __u8    irk[16];
+} __packed;
+
+#define SMP_CMD_IDENT_ADDR_INFO        0x09
+struct smp_cmd_ident_addr_info {
+       __u8    addr_type;
+       bdaddr_t bdaddr;
+} __packed;
+
+#define SMP_CMD_SIGN_INFO      0x0a
+struct smp_cmd_sign_info {
+       __u8    csrk[16];
+} __packed;
+
+#define SMP_CMD_SECURITY_REQ   0x0b
+struct smp_cmd_security_req {
+       __u8    auth_req;
+} __packed;
+
+#define SMP_PASSKEY_ENTRY_FAILED       0x01
+#define SMP_OOB_NOT_AVAIL              0x02
+#define SMP_AUTH_REQUIREMENTS          0x03
+#define SMP_CONFIRM_FAILED             0x04
+#define SMP_PAIRING_NOTSUPP            0x05
+#define SMP_ENC_KEY_SIZE               0x06
+#define SMP_CMD_NOTSUPP                        0x07
+#define SMP_UNSPECIFIED                        0x08
+#define SMP_REPEATED_ATTEMPTS          0x09
+
+#define SMP_MIN_ENC_KEY_SIZE           7
+#define SMP_MAX_ENC_KEY_SIZE           16
+
+#define SMP_FLAG_TK_VALID      1
+#define SMP_FLAG_CFM_PENDING   2
+#define SMP_FLAG_MITM_AUTH     3
+
+struct smp_chan {
+       struct l2cap_conn *conn;
+       u8              preq[7]; /* SMP Pairing Request */
+       u8              prsp[7]; /* SMP Pairing Response */
+       u8              prnd[16]; /* SMP Pairing Random (local) */
+       u8              rrnd[16]; /* SMP Pairing Random (remote) */
+       u8              pcnf[16]; /* SMP Pairing Confirm */
+       u8              tk[16]; /* SMP Temporary Key */
+       u8              enc_key_size;
+       unsigned long   smp_flags;
+       struct crypto_blkcipher *tfm;
+       struct work_struct confirm;
+       struct work_struct random;
+
+};
+
+/* SMP Commands */
+int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
+int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
+int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
+int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
+
+void smp_chan_destroy(struct l2cap_conn *conn);
+
+#endif /* __SMP_H */
index 62535fe9f570273d83597567e8fb360262f7f208..97b5dcad50250d22bb5c5ad7ac41c18558d967ce 100644 (file)
@@ -4,6 +4,7 @@ config MAC80211
        select CRYPTO
        select CRYPTO_ARC4
        select CRYPTO_AES
+       select CRYPTO_CCM
        select CRC32
        select AVERAGE
        ---help---
@@ -258,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG
 
          Do not select this option.
 
+config MAC80211_MESH_CSA_DEBUG
+       bool "Verbose mesh channel switch debugging"
+       depends on MAC80211_DEBUG_MENU
+       depends on MAC80211_MESH
+       ---help---
+         Selecting this option causes mac80211 to print out very verbose mesh
+         channel switch debugging messages (when mac80211 is taking part in a
+         mesh network).
+
+         Do not select this option.
+
 config MAC80211_MESH_PS_DEBUG
        bool "Verbose mesh powersave debugging"
        depends on MAC80211_DEBUG_MENU
index be7614b9ed27b7827cbae7a423e5957ec1302c25..7c7df475a401693dd44cb1a6b9d68dd255b2acd5 100644 (file)
@@ -2,6 +2,8 @@
  * Copyright 2003-2004, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  *
+ * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
+ *
  * 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 "key.h"
 #include "aes_ccm.h"
 
-static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
+void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                              u8 *data, size_t data_len, u8 *mic)
 {
-       int i;
-       u8 *b_0, *aad, *b, *s_0;
-
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-       aad = scratch + 4 * AES_BLOCK_SIZE;
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-
-       crypto_cipher_encrypt_one(tfm, b, b_0);
+       struct scatterlist assoc, pt, ct[2];
+       struct {
+               struct aead_request     req;
+               u8                      priv[crypto_aead_reqsize(tfm)];
+       } aead_req;
 
-       /* Extra Authenticate-only data (always two AES blocks) */
-       for (i = 0; i < AES_BLOCK_SIZE; i++)
-               aad[i] ^= b[i];
-       crypto_cipher_encrypt_one(tfm, b, aad);
+       memset(&aead_req, 0, sizeof(aead_req));
 
-       aad += AES_BLOCK_SIZE;
+       sg_init_one(&pt, data, data_len);
+       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_init_table(ct, 2);
+       sg_set_buf(&ct[0], data, data_len);
+       sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 
-       for (i = 0; i < AES_BLOCK_SIZE; i++)
-               aad[i] ^= b[i];
-       crypto_cipher_encrypt_one(tfm, a, aad);
+       aead_request_set_tfm(&aead_req.req, tfm);
+       aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
+       aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0);
 
-       /* Mask out bits from auth-only-b_0 */
-       b_0[0] &= 0x07;
-
-       /* S_0 is used to encrypt T (= MIC) */
-       b_0[14] = 0;
-       b_0[15] = 0;
-       crypto_cipher_encrypt_one(tfm, s_0, b_0);
+       crypto_aead_encrypt(&aead_req.req);
 }
 
-
-void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
-                              u8 *data, size_t data_len,
-                              u8 *cdata, u8 *mic)
+int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                             u8 *data, size_t data_len, u8 *mic)
 {
-       int i, j, last_len, num_blocks;
-       u8 *pos, *cpos, *b, *s_0, *e, *b_0;
-
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-       e = scratch + 2 * AES_BLOCK_SIZE;
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
-       num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-       last_len = data_len % AES_BLOCK_SIZE;
-       aes_ccm_prepare(tfm, scratch, b);
-
-       /* Process payload blocks */
-       pos = data;
-       cpos = cdata;
-       for (j = 1; j <= num_blocks; j++) {
-               int blen = (j == num_blocks && last_len) ?
-                       last_len : AES_BLOCK_SIZE;
-
-               /* Authentication followed by encryption */
-               for (i = 0; i < blen; i++)
-                       b[i] ^= pos[i];
-               crypto_cipher_encrypt_one(tfm, b, b);
-
-               b_0[14] = (j >> 8) & 0xff;
-               b_0[15] = j & 0xff;
-               crypto_cipher_encrypt_one(tfm, e, b_0);
-               for (i = 0; i < blen; i++)
-                       *cpos++ = *pos++ ^ e[i];
-       }
-
-       for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++)
-               mic[i] = b[i] ^ s_0[i];
+       struct scatterlist assoc, pt, ct[2];
+       struct {
+               struct aead_request     req;
+               u8                      priv[crypto_aead_reqsize(tfm)];
+       } aead_req;
+
+       memset(&aead_req, 0, sizeof(aead_req));
+
+       sg_init_one(&pt, data, data_len);
+       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_init_table(ct, 2);
+       sg_set_buf(&ct[0], data, data_len);
+       sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+
+       aead_request_set_tfm(&aead_req.req, tfm);
+       aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
+       aead_request_set_crypt(&aead_req.req, ct, &pt,
+                              data_len + IEEE80211_CCMP_MIC_LEN, b_0);
+
+       return crypto_aead_decrypt(&aead_req.req);
 }
 
-
-int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
-                             u8 *cdata, size_t data_len, u8 *mic, u8 *data)
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
 {
-       int i, j, last_len, num_blocks;
-       u8 *pos, *cpos, *b, *s_0, *a, *b_0;
-
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-       a = scratch + 2 * AES_BLOCK_SIZE;
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
-       num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-       last_len = data_len % AES_BLOCK_SIZE;
-       aes_ccm_prepare(tfm, scratch, a);
-
-       /* Process payload blocks */
-       cpos = cdata;
-       pos = data;
-       for (j = 1; j <= num_blocks; j++) {
-               int blen = (j == num_blocks && last_len) ?
-                       last_len : AES_BLOCK_SIZE;
-
-               /* Decryption followed by authentication */
-               b_0[14] = (j >> 8) & 0xff;
-               b_0[15] = j & 0xff;
-               crypto_cipher_encrypt_one(tfm, b, b_0);
-               for (i = 0; i < blen; i++) {
-                       *pos = *cpos++ ^ b[i];
-                       a[i] ^= *pos++;
-               }
-               crypto_cipher_encrypt_one(tfm, a, a);
-       }
-
-       for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) {
-               if ((mic[i] ^ s_0[i]) != a[i])
-                       return -1;
-       }
-
-       return 0;
-}
+       struct crypto_aead *tfm;
+       int err;
 
+       tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(tfm))
+               return tfm;
 
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
-{
-       struct crypto_cipher *tfm;
+       err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
+       if (!err)
+               err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
+       if (!err)
+               return tfm;
 
-       tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
-       if (!IS_ERR(tfm))
-               crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
-
-       return tfm;
+       crypto_free_aead(tfm);
+       return ERR_PTR(err);
 }
 
-
-void ieee80211_aes_key_free(struct crypto_cipher *tfm)
+void ieee80211_aes_key_free(struct crypto_aead *tfm)
 {
-       crypto_free_cipher(tfm);
+       crypto_free_aead(tfm);
 }
index 5b7d744e237032e7326bcd792bc2a17423e20f1f..2c7ab1948a2edba3964a5c0edfb7e941752719f6 100644 (file)
 
 #include <linux/crypto.h>
 
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]);
-void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
-                              u8 *data, size_t data_len,
-                              u8 *cdata, u8 *mic);
-int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
-                             u8 *cdata, size_t data_len,
-                             u8 *mic, u8 *data);
-void ieee80211_aes_key_free(struct crypto_cipher *tfm);
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
+void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                              u8 *data, size_t data_len, u8 *mic);
+int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                             u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_key_free(struct crypto_aead *tfm);
 
 #endif /* AES_CCM_H */
index ac28af74a41410abceed9884a9388d9cb37784cb..95667b088c5b73cd0e95e8c1753ed76acec9dca0 100644 (file)
@@ -1059,6 +1059,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        /* abort any running channel switch */
        sdata->vif.csa_active = false;
        cancel_work_sync(&sdata->csa_finalize_work);
+       cancel_work_sync(&sdata->u.ap.request_smps_work);
 
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
@@ -1342,8 +1343,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                sta->plink_state = params->plink_state;
 
                                ieee80211_mps_sta_status_update(sta);
-                               changed |=
-                                     ieee80211_mps_local_status_update(sdata);
+                               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                               NL80211_MESH_POWER_UNKNOWN);
                                break;
                        default:
                                /*  nothing  */
@@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        mutex_unlock(&local->sta_mtx);
 
+       if ((sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+           sta->known_smps_mode != sta->sdata->bss->req_smps &&
+           test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
+           sta_info_tx_streams(sta) != 1) {
+               ht_dbg(sta->sdata,
+                      "%pM just authorized and MIMO capable - update SMPS\n",
+                      sta->sta.addr);
+               ieee80211_send_smps_action(sta->sdata,
+                       sta->sdata->bss->req_smps,
+                       sta->sta.addr,
+                       sta->sdata->vif.bss_conf.bssid);
+       }
+
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
                ieee80211_recalc_ps(local, -1);
@@ -2337,8 +2352,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,
 }
 #endif
 
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode)
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode)
+{
+       struct sta_info *sta;
+       enum ieee80211_smps_mode old_req;
+       int i;
+
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
+               return -EINVAL;
+
+       if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+               return 0;
+
+       old_req = sdata->u.ap.req_smps;
+       sdata->u.ap.req_smps = smps_mode;
+
+       /* AUTOMATIC doesn't mean much for AP - don't allow it */
+       if (old_req == smps_mode ||
+           smps_mode == IEEE80211_SMPS_AUTOMATIC)
+               return 0;
+
+        /* If no associated stations, there's no need to do anything */
+       if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
+               sdata->smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+               return 0;
+       }
+
+       ht_dbg(sdata,
+              "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
+              smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
+
+       mutex_lock(&sdata->local->sta_mtx);
+       for (i = 0; i < STA_HASH_SIZE; i++) {
+               for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
+                               lockdep_is_held(&sdata->local->sta_mtx));
+                    sta;
+                    sta = rcu_dereference_protected(sta->hnext,
+                               lockdep_is_held(&sdata->local->sta_mtx))) {
+                       /*
+                        * Only stations associated to our AP and
+                        * associated VLANs
+                        */
+                       if (sta->sdata->bss != &sdata->u.ap)
+                               continue;
+
+                       /* This station doesn't support MIMO - skip it */
+                       if (sta_info_tx_streams(sta) == 1)
+                               continue;
+
+                       /*
+                        * Don't wake up a STA just to send the action frame
+                        * unless we are getting more restrictive.
+                        */
+                       if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+                           !ieee80211_smps_is_restrictive(sta->known_smps_mode,
+                                                          smps_mode)) {
+                               ht_dbg(sdata,
+                                      "Won't send SMPS to sleeping STA %pM\n",
+                                      sta->sta.addr);
+                               continue;
+                       }
+
+                       /*
+                        * If the STA is not authorized, wait until it gets
+                        * authorized and the action frame will be sent then.
+                        */
+                       if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                               continue;
+
+                       ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
+                       ieee80211_send_smps_action(sdata, smps_mode,
+                                                  sta->sta.addr,
+                                                  sdata->vif.bss_conf.bssid);
+               }
+       }
+       mutex_unlock(&sdata->local->sta_mtx);
+
+       sdata->smps_mode = smps_mode;
+       ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+
+       return 0;
+}
+
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode)
 {
        const u8 *ap;
        enum ieee80211_smps_mode old_req;
@@ -2346,6 +2445,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
 
        lockdep_assert_held(&sdata->wdev.mtx);
 
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
+               return -EINVAL;
+
        old_req = sdata->u.mgd.req_smps;
        sdata->u.mgd.req_smps = smps_mode;
 
@@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
 
        /* no change, but if automatic follow powersave */
        sdata_lock(sdata);
-       __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+       __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
        sdata_unlock(sdata);
 
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
@@ -2860,7 +2962,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data,
                             csa_finalize_work);
        struct ieee80211_local *local = sdata->local;
-       int err, changed;
+       int err, changed = 0;
 
        if (!ieee80211_sdata_running(sdata))
                return;
@@ -2892,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        case NL80211_IFTYPE_ADHOC:
                ieee80211_ibss_finish_csa(sdata);
                break;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               err = ieee80211_mesh_finish_csa(sdata);
+               if (err < 0)
+                       return;
+               break;
+#endif
        default:
                WARN_ON(1);
                return;
@@ -2912,6 +3021,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
+       struct ieee80211_if_mesh __maybe_unused *ifmsh;
        int err, num_chanctx;
 
        if (!list_empty(&local->roc_list) || local->scanning)
@@ -2995,6 +3105,26 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                if (err < 0)
                        return err;
                break;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               ifmsh = &sdata->u.mesh;
+
+               if (!ifmsh->mesh_id)
+                       return -EINVAL;
+
+               if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
+                       return -EINVAL;
+
+               /* changes into another band are not supported */
+               if (sdata->vif.bss_conf.chandef.chan->band !=
+                   params->chandef.chan->band)
+                       return -EINVAL;
+
+               err = ieee80211_mesh_csa_beacon(sdata, params, true);
+               if (err < 0)
+                       return err;
+               break;
+#endif
        default:
                return -EOPNOTSUPP;
        }
@@ -3564,7 +3694,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
                return -EINVAL;
        }
        band = chanctx_conf->def.chan->band;
-       sta = sta_info_get(sdata, peer);
+       sta = sta_info_get_bss(sdata, peer);
        if (sta) {
                qos = test_sta_flag(sta, WLAN_STA_WME);
        } else {
index 4ccc5ed6237dd00e5701c19908e55d413e83d506..493d68061f0c2717d8828809e8ec27d87d6d08b6 100644 (file)
 #define MAC80211_MESH_SYNC_DEBUG 0
 #endif
 
+#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG
+#define MAC80211_MESH_CSA_DEBUG 1
+#else
+#define MAC80211_MESH_CSA_DEBUG 0
+#endif
+
 #ifdef CONFIG_MAC80211_MESH_PS_DEBUG
 #define MAC80211_MESH_PS_DEBUG 1
 #else
@@ -157,6 +163,10 @@ do {                                                                       \
        _sdata_dbg(MAC80211_MESH_SYNC_DEBUG,                            \
                   sdata, fmt, ##__VA_ARGS__)
 
+#define mcsa_dbg(sdata, fmt, ...)                                      \
+       _sdata_dbg(MAC80211_MESH_CSA_DEBUG,                             \
+                  sdata, fmt, ##__VA_ARGS__)
+
 #define mps_dbg(sdata, fmt, ...)                                       \
        _sdata_dbg(MAC80211_MESH_PS_DEBUG,                              \
                   sdata, fmt, ##__VA_ARGS__)
index cafe614ef93d672087fb66a1d82aaff4110cc22c..04b5a14c8a054e33227b893de78912e5ee4a6f80 100644 (file)
@@ -224,12 +224,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
             smps_mode == IEEE80211_SMPS_AUTOMATIC))
                return -EINVAL;
 
-       /* supported only on managed interfaces for now */
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+           sdata->vif.type != NL80211_IFTYPE_AP)
                return -EOPNOTSUPP;
 
        sdata_lock(sdata);
-       err = __ieee80211_request_smps(sdata, smps_mode);
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               err = __ieee80211_request_smps_mgd(sdata, smps_mode);
+       else
+               err = __ieee80211_request_smps_ap(sdata, smps_mode);
        sdata_unlock(sdata);
 
        return err;
@@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
 static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
                                     char *buf, int buflen)
 {
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
-               return -EOPNOTSUPP;
-
-       return snprintf(buf, buflen, "request: %s\nused: %s\n",
-                       smps_modes[sdata->u.mgd.req_smps],
-                       smps_modes[sdata->smps_mode]);
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               return snprintf(buf, buflen, "request: %s\nused: %s\n",
+                               smps_modes[sdata->u.mgd.req_smps],
+                               smps_modes[sdata->smps_mode]);
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               return snprintf(buf, buflen, "request: %s\nused: %s\n",
+                               smps_modes[sdata->u.ap.req_smps],
+                               smps_modes[sdata->smps_mode]);
+       return -EINVAL;
 }
 
 static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
@@ -563,6 +569,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 static void add_ap_files(struct ieee80211_sub_if_data *sdata)
 {
        DEBUGFS_ADD(num_mcast_sta);
+       DEBUGFS_ADD_MODE(smps, 0600);
        DEBUGFS_ADD(num_sta_ps);
        DEBUGFS_ADD(dtim_count);
        DEBUGFS_ADD(num_buffered_multicast);
index 529bf58bc14511beae95c4ff250ec54eff5c9710..9a8be8f69224d0be11fe9b10a0176a2f5a0002f1 100644 (file)
@@ -448,14 +448,25 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-void ieee80211_request_smps_work(struct work_struct *work)
+void ieee80211_request_smps_mgd_work(struct work_struct *work)
 {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
                             u.mgd.request_smps_work);
 
        sdata_lock(sdata);
-       __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode);
+       __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
+       sdata_unlock(sdata);
+}
+
+void ieee80211_request_smps_ap_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            u.ap.request_smps_work);
+
+       sdata_lock(sdata);
+       __ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode);
        sdata_unlock(sdata);
 }
 
@@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 
-       if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
+       if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
+                        vif->type != NL80211_IFTYPE_AP))
                return;
 
        if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
-       if (sdata->u.mgd.driver_smps_mode == smps_mode)
-               return;
-
-       sdata->u.mgd.driver_smps_mode = smps_mode;
-
-       ieee80211_queue_work(&sdata->local->hw,
-                            &sdata->u.mgd.request_smps_work);
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               if (sdata->u.mgd.driver_smps_mode == smps_mode)
+                       return;
+               sdata->u.mgd.driver_smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.mgd.request_smps_work);
+       } else {
+               /* AUTOMATIC is meaningless in AP mode */
+               if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC))
+                       return;
+               if (sdata->u.ap.driver_smps_mode == smps_mode)
+                       return;
+               sdata->u.ap.driver_smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.ap.request_smps_work);
+       }
 }
 /* this might change ... don't want non-open drivers using it */
 EXPORT_SYMBOL_GPL(ieee80211_request_smps);
index 21a0b8835cb31d4a27dda9b2243038c2280b8f6a..531be040b9ae85972d61bf7df248ae28308d811b 100644 (file)
@@ -229,6 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        struct beacon_data *presp;
        enum nl80211_bss_scan_width scan_width;
        bool have_higher_than_11mbit;
+       bool radar_required = false;
        int err;
 
        sdata_assert_lock(sdata);
@@ -273,6 +274,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                }
                chandef.width = NL80211_CHAN_WIDTH_20;
                chandef.center_freq1 = chan->center_freq;
+               /* check again for downgraded chandef */
+               if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, beacons forbidden\n");
+                       return;
+               }
+       }
+
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &chandef);
+       if (err > 0) {
+               if (!ifibss->userspace_handles_dfs) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, DFS channel without control program\n");
+                       return;
+               }
+               radar_required = true;
        }
 
        ieee80211_vif_release_channel(sdata);
@@ -297,6 +315,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        rcu_assign_pointer(ifibss->presp, presp);
        mgmt = (void *)presp->head;
 
+       sdata->radar_required = radar_required;
        sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
@@ -445,60 +464,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                                  tsf, false);
 }
 
-static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
-                                    struct cfg80211_csa_settings *csa_settings)
-{
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-       struct ieee80211_local *local = sdata->local;
-       int freq;
-       int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
-                     sizeof(mgmt->u.action.u.chan_switch);
-       u8 *pos;
-
-       skb = dev_alloc_skb(local->tx_headroom + hdr_len +
-                           5 + /* channel switch announcement element */
-                           3); /* secondary channel offset element */
-       if (!skb)
-               return -1;
-
-       skb_reserve(skb, local->tx_headroom);
-       mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
-       memset(mgmt, 0, hdr_len);
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_ACTION);
-
-       eth_broadcast_addr(mgmt->da);
-       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-       memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
-       mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
-       mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
-       pos = skb_put(skb, 5);
-       *pos++ = WLAN_EID_CHANNEL_SWITCH;                       /* EID */
-       *pos++ = 3;                                             /* IE length */
-       *pos++ = csa_settings->block_tx ? 1 : 0;                /* CSA mode */
-       freq = csa_settings->chandef.chan->center_freq;
-       *pos++ = ieee80211_frequency_to_channel(freq);          /* channel */
-       *pos++ = csa_settings->count;                           /* count */
-
-       if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
-               enum nl80211_channel_type ch_type;
-
-               skb_put(skb, 3);
-               *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;     /* EID */
-               *pos++ = 1;                                     /* IE length */
-               ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
-               if (ch_type == NL80211_CHAN_HT40PLUS)
-                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
-               else
-                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
-       }
-
-       ieee80211_tx_skb(sdata, skb);
-       return 0;
-}
-
 int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
                              struct cfg80211_csa_settings *csa_settings)
 {
@@ -796,19 +761,34 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
 
+static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       int err;
+
+       /* if the current channel is a DFS channel, mark the channel as
+        * unavailable.
+        */
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &ifibss->chandef);
+       if (err > 0)
+               cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
+                                    GFP_ATOMIC);
+}
+
 static bool
 ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                                  struct ieee802_11_elems *elems,
                                  bool beacon)
 {
        struct cfg80211_csa_settings params;
+       struct ieee80211_csa_ie csa_ie;
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
        enum nl80211_channel_type ch_type;
        int err, num_chanctx;
        u32 sta_flags;
-       u8 mode;
 
        if (sdata->vif.csa_active)
                return true;
@@ -831,12 +811,10 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        memset(&params, 0, sizeof(params));
+       memset(&csa_ie, 0, sizeof(csa_ie));
        err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
                                           ifibss->chandef.chan->band,
-                                          sta_flags, ifibss->bssid,
-                                          &params.count, &mode,
-                                          &params.chandef);
-
+                                          sta_flags, ifibss->bssid, &csa_ie);
        /* can't switch to destination channel, fail */
        if (err < 0)
                goto disconnect;
@@ -845,6 +823,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (err)
                return false;
 
+       params.count = csa_ie.count;
+       params.chandef = csa_ie.chandef;
+
        if (ifibss->chandef.chan->band != params.chandef.chan->band)
                goto disconnect;
 
@@ -880,8 +861,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                goto disconnect;
        }
 
-       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
-                                    IEEE80211_CHAN_DISABLED)) {
+       if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef)) {
                sdata_info(sdata,
                           "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
                           ifibss->bssid,
@@ -897,10 +877,11 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (err < 0)
                goto disconnect;
        if (err) {
-               params.radar_required = true;
+               /* IBSS-DFS only allowed with a control program */
+               if (!ifibss->userspace_handles_dfs)
+                       goto disconnect;
 
-               /* TODO: IBSS-DFS not (yet) supported, disconnect. */
-               goto disconnect;
+               params.radar_required = true;
        }
 
        rcu_read_lock();
@@ -931,7 +912,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                 "received channel switch announcement to go to channel %d MHz\n",
                 params.chandef.chan->center_freq);
 
-       params.block_tx = !!mode;
+       params.block_tx = !!csa_ie.mode;
 
        ieee80211_ibss_csa_beacon(sdata, &params);
        sdata->csa_radar_required = params.radar_required;
@@ -947,12 +928,16 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        ieee80211_bss_info_change_notify(sdata, err);
        drv_channel_switch_beacon(sdata, &params.chandef);
 
+       ieee80211_ibss_csa_mark_radar(sdata);
+
        return true;
 disconnect:
        ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
        ieee80211_queue_work(&sdata->local->hw,
                             &ifibss->csa_connection_drop_work);
 
+       ieee80211_ibss_csa_mark_radar(sdata);
+
        return true;
 }
 
@@ -1688,6 +1673,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 
        sdata->u.ibss.privacy = params->privacy;
        sdata->u.ibss.control_port = params->control_port;
+       sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;
        sdata->u.ibss.basic_rates = params->basic_rates;
 
        /* fix basic_rates if channel does not support these rates */
index 3a87c8976a320473a5155f5963109b4319954f7f..29dc505be125c3c19737f8f6cee911492c52727c 100644 (file)
@@ -262,6 +262,10 @@ struct ieee80211_if_ap {
 
        struct ps_data ps;
        atomic_t num_mcast_sta; /* number of stations receiving multicast */
+       enum ieee80211_smps_mode req_smps, /* requested smps mode */
+                        driver_smps_mode; /* smps mode request */
+
+       struct work_struct request_smps_work;
 };
 
 struct ieee80211_if_wds {
@@ -334,6 +338,7 @@ enum ieee80211_sta_flags {
        IEEE80211_STA_DISABLE_VHT       = BIT(11),
        IEEE80211_STA_DISABLE_80P80MHZ  = BIT(12),
        IEEE80211_STA_DISABLE_160MHZ    = BIT(13),
+       IEEE80211_STA_DISABLE_WMM       = BIT(14),
 };
 
 struct ieee80211_mgd_auth_data {
@@ -497,6 +502,7 @@ struct ieee80211_if_ibss {
        bool privacy;
 
        bool control_port;
+       bool userspace_handles_dfs;
 
        u8 bssid[ETH_ALEN] __aligned(2);
        u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -538,6 +544,11 @@ struct ieee80211_mesh_sync_ops {
        /* add other framework functions here */
 };
 
+struct mesh_csa_settings {
+       struct rcu_head rcu_head;
+       struct cfg80211_csa_settings settings;
+};
+
 struct ieee80211_if_mesh {
        struct timer_list housekeeping_timer;
        struct timer_list mesh_path_timer;
@@ -598,6 +609,11 @@ struct ieee80211_if_mesh {
        int ps_peers_light_sleep;
        int ps_peers_deep_sleep;
        struct ps_data ps;
+       /* Channel Switching Support */
+       struct mesh_csa_settings __rcu *csa;
+       bool chsw_init;
+       u8 chsw_ttl;
+       u16 pre_value;
 };
 
 #ifdef CONFIG_MAC80211_MESH
@@ -893,6 +909,8 @@ struct tpt_led_trigger {
  *     that the scan completed.
  * @SCAN_ABORTED: Set for our scan work function when the driver reported
  *     a scan complete for an aborted scan.
+ * @SCAN_HW_CANCELLED: Set for our scan work function when the scan is being
+ *     cancelled.
  */
 enum {
        SCAN_SW_SCANNING,
@@ -900,6 +918,7 @@ enum {
        SCAN_ONCHANNEL_SCANNING,
        SCAN_COMPLETED,
        SCAN_ABORTED,
+       SCAN_HW_CANCELLED,
 };
 
 /**
@@ -1203,6 +1222,14 @@ struct ieee80211_ra_tid {
        u16 tid;
 };
 
+/* this struct holds the value parsing from channel switch IE  */
+struct ieee80211_csa_ie {
+       struct cfg80211_chan_def chandef;
+       u8 mode;
+       u8 count;
+       u8 ttl;
+};
+
 /* Parsed Information Elements */
 struct ieee802_11_elems {
        const u8 *ie_start;
@@ -1239,6 +1266,7 @@ struct ieee802_11_elems {
        const struct ieee80211_timeout_interval_ie *timeout_int;
        const u8 *opmode_notif;
        const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+       const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
 
        /* length of them, respectively */
        u8 ssid_len;
@@ -1339,6 +1367,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                   struct sk_buff *skb);
+int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings,
+                             bool csa_action);
+int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
 
 /* scan/BSS handling */
 void ieee80211_scan_work(struct work_struct *work);
@@ -1435,7 +1467,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
 int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
                               enum ieee80211_smps_mode smps, const u8 *da,
                               const u8 *bssid);
-void ieee80211_request_smps_work(struct work_struct *work);
+void ieee80211_request_smps_ap_work(struct work_struct *work);
+void ieee80211_request_smps_mgd_work(struct work_struct *work);
+bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+                                  enum ieee80211_smps_mode smps_mode_new);
 
 void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                     u16 initiator, u16 reason, bool stop);
@@ -1497,17 +1532,16 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
  *     %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
  *     %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
  *     %IEEE80211_STA_DISABLE_160MHZ.
- * @count: to be filled with the counter until the switch (on success only)
  * @bssid: the currently connected bssid (for reporting)
- * @mode: to be filled with CSA mode (on success only)
- * @new_chandef: to be filled with destination chandef (on success only)
+ * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
+       All of them will be filled with if success only.
  * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
  */
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                                 struct ieee802_11_elems *elems, bool beacon,
                                 enum ieee80211_band current_band,
-                                u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
-                                struct cfg80211_chan_def *new_chandef);
+                                u32 sta_flags, u8 *bssid,
+                                struct ieee80211_csa_ie *csa_ie);
 
 /* Suspend/resume and hw reconfiguration */
 int ieee80211_reconfig(struct ieee80211_local *local);
@@ -1653,8 +1687,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
 u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates);
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode);
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode);
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode);
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
 
 size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
@@ -1710,6 +1746,8 @@ void ieee80211_dfs_cac_timer(unsigned long data);
 void ieee80211_dfs_cac_timer_work(struct work_struct *work);
 void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
 void ieee80211_dfs_radar_detected_work(struct work_struct *work);
+int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings);
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index e48f103b9adeb5fd773f68c6d12cb633cea33fa5..ff101ea1d9ae1e208deb5d6fe1d67c1677b398c2 100644 (file)
@@ -1293,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        case NL80211_IFTYPE_AP:
                skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
                INIT_LIST_HEAD(&sdata->u.ap.vlans);
+               INIT_WORK(&sdata->u.ap.request_smps_work,
+                         ieee80211_request_smps_ap_work);
                sdata->vif.bss_conf.bssid = sdata->vif.addr;
+               sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
                type = NL80211_IFTYPE_STATION;
index 036d57e76a5e37d4156d5abedcd479e0aa0ffb6e..aaae0ed3700402433ae56244eb97baa2804aba5b 100644 (file)
@@ -83,7 +83,7 @@ struct ieee80211_key {
                         * Management frames.
                         */
                        u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
-                       struct crypto_cipher *tfm;
+                       struct crypto_aead *tfm;
                        u32 replays; /* dot11RSNAStatsCCMPReplays */
                } ccmp;
                struct {
index 707ac61d63e51a8a1528c8c65204c92c94400768..896fe3bd599e9bedd5db13dbc8ed04ab76e5bd88 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/unaligned.h>
 #include "ieee80211_i.h"
 #include "mesh.h"
+#include "driver-ops.h"
 
 static int mesh_allocated;
 static struct kmem_cache *rm_cache;
@@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       struct mesh_csa_settings *csa;
        enum ieee80211_band band;
        u8 *pos;
        struct ieee80211_sub_if_data *sdata;
@@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 
        head_len = hdr_len +
                   2 + /* NULL SSID */
+                  /* Channel Switch Announcement */
+                  2 + sizeof(struct ieee80211_channel_sw_ie) +
+                  /* Mesh Channel Swith Parameters */
+                  2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
                   2 + 8 + /* supported rates */
                   2 + 3; /* DS params */
        tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
@@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
        *pos++ = WLAN_EID_SSID;
        *pos++ = 0x0;
 
+       rcu_read_lock();
+       csa = rcu_dereference(ifmsh->csa);
+       if (csa) {
+               __le16 pre_value;
+
+               pos = skb_put(skb, 13);
+               memset(pos, 0, 13);
+               *pos++ = WLAN_EID_CHANNEL_SWITCH;
+               *pos++ = 3;
+               *pos++ = 0x0;
+               *pos++ = ieee80211_frequency_to_channel(
+                               csa->settings.chandef.chan->center_freq);
+               sdata->csa_counter_offset_beacon = hdr_len + 6;
+               *pos++ = csa->settings.count;
+               *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
+               *pos++ = 6;
+               if (ifmsh->chsw_init) {
+                       *pos++ = ifmsh->mshcfg.dot11MeshTTL;
+                       *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+               } else {
+                       *pos++ = ifmsh->chsw_ttl;
+               }
+               *pos++ |= csa->settings.block_tx ?
+                         WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
+               put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
+               pos += 2;
+               pre_value = cpu_to_le16(ifmsh->pre_value);
+               memcpy(pos, &pre_value, 2);
+               pos += 2;
+       }
+       rcu_read_unlock();
+
        if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
            mesh_add_ds_params_ie(sdata, skb))
                goto out_free;
@@ -812,6 +850,127 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        ieee80211_configure_filter(local);
 }
 
+static bool
+ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
+                                struct ieee802_11_elems *elems, bool beacon)
+{
+       struct cfg80211_csa_settings params;
+       struct ieee80211_csa_ie csa_ie;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx *chanctx;
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       int err, num_chanctx;
+       u32 sta_flags;
+
+       if (sdata->vif.csa_active)
+               return true;
+
+       if (!ifmsh->mesh_id)
+               return false;
+
+       sta_flags = IEEE80211_STA_DISABLE_VHT;
+       switch (sdata->vif.bss_conf.chandef.width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               sta_flags |= IEEE80211_STA_DISABLE_HT;
+       case NL80211_CHAN_WIDTH_20:
+               sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
+               break;
+       default:
+               break;
+       }
+
+       memset(&params, 0, sizeof(params));
+       memset(&csa_ie, 0, sizeof(csa_ie));
+       err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+                                          sta_flags, sdata->vif.addr,
+                                          &csa_ie);
+       if (err < 0)
+               return false;
+       if (err)
+               return false;
+
+       params.chandef = csa_ie.chandef;
+       params.count = csa_ie.count;
+
+       if (sdata->vif.bss_conf.chandef.chan->band !=
+           params.chandef.chan->band)
+               return false;
+
+       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
+                                    IEEE80211_CHAN_DISABLED)) {
+               sdata_info(sdata,
+                          "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n",
+                          sdata->vif.addr,
+                          params.chandef.chan->center_freq,
+                          params.chandef.width,
+                          params.chandef.center_freq1,
+                          params.chandef.center_freq2);
+               return false;
+       }
+
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &params.chandef);
+       if (err < 0)
+               return false;
+       if (err) {
+               params.radar_required = true;
+               /* TODO: DFS not (yet) supported */
+               return false;
+       }
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (!chanctx_conf)
+               goto failed_chswitch;
+
+       /* don't handle for multi-VIF cases */
+       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+       if (chanctx->refcount > 1)
+               goto failed_chswitch;
+
+       num_chanctx = 0;
+       list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
+               num_chanctx++;
+
+       if (num_chanctx > 1)
+               goto failed_chswitch;
+
+       rcu_read_unlock();
+
+       mcsa_dbg(sdata,
+                "received channel switch announcement to go to channel %d MHz\n",
+                params.chandef.chan->center_freq);
+
+       params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT;
+       if (beacon)
+               ifmsh->chsw_ttl = csa_ie.ttl - 1;
+       else
+               ifmsh->chsw_ttl = 0;
+
+       if (ifmsh->chsw_ttl > 0)
+               if (ieee80211_mesh_csa_beacon(sdata, &params, false) < 0)
+                       return false;
+
+       sdata->csa_radar_required = params.radar_required;
+
+       if (params.block_tx)
+               ieee80211_stop_queues_by_reason(&sdata->local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
+                               IEEE80211_QUEUE_STOP_REASON_CSA);
+
+       sdata->local->csa_chandef = params.chandef;
+       sdata->vif.csa_active = true;
+
+       ieee80211_bss_info_change_notify(sdata, err);
+       drv_channel_switch_beacon(sdata, &params.chandef);
+
+       return true;
+failed_chswitch:
+       rcu_read_unlock();
+       return false;
+}
+
 static void
 ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
                            struct ieee80211_mgmt *mgmt, size_t len)
@@ -918,6 +1077,142 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
        if (ifmsh->sync_ops)
                ifmsh->sync_ops->rx_bcn_presp(sdata,
                        stype, mgmt, &elems, rx_status);
+
+       if (!ifmsh->chsw_init)
+               ieee80211_mesh_process_chnswitch(sdata, &elems, true);
+}
+
+int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct mesh_csa_settings *tmp_csa_settings;
+       int ret = 0;
+
+       /* Reset the TTL value and Initiator flag */
+       ifmsh->chsw_init = false;
+       ifmsh->chsw_ttl = 0;
+
+       /* Remove the CSA and MCSP elements from the beacon */
+       tmp_csa_settings = rcu_dereference(ifmsh->csa);
+       rcu_assign_pointer(ifmsh->csa, NULL);
+       kfree_rcu(tmp_csa_settings, rcu_head);
+       ret = ieee80211_mesh_rebuild_beacon(sdata);
+       if (ret)
+               return -EINVAL;
+
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+
+       mcsa_dbg(sdata, "complete switching to center freq %d MHz",
+                sdata->vif.bss_conf.chandef.chan->center_freq);
+       return 0;
+}
+
+int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings,
+                             bool csa_action)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct mesh_csa_settings *tmp_csa_settings;
+       int ret = 0;
+
+       tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
+                                  GFP_ATOMIC);
+       if (!tmp_csa_settings)
+               return -ENOMEM;
+
+       memcpy(&tmp_csa_settings->settings, csa_settings,
+              sizeof(struct cfg80211_csa_settings));
+
+       rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
+
+       ret = ieee80211_mesh_rebuild_beacon(sdata);
+       if (ret) {
+               tmp_csa_settings = rcu_dereference(ifmsh->csa);
+               rcu_assign_pointer(ifmsh->csa, NULL);
+               kfree_rcu(tmp_csa_settings, rcu_head);
+               return ret;
+       }
+
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+
+       if (csa_action)
+               ieee80211_send_action_csa(sdata, csa_settings);
+
+       return 0;
+}
+
+static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
+                              struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_mgmt *mgmt_fwd;
+       struct sk_buff *skb;
+       struct ieee80211_local *local = sdata->local;
+       u8 *pos = mgmt->u.action.u.chan_switch.variable;
+       size_t offset_ttl;
+
+       skb = dev_alloc_skb(local->tx_headroom + len);
+       if (!skb)
+               return -ENOMEM;
+       skb_reserve(skb, local->tx_headroom);
+       mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
+
+       /* offset_ttl is based on whether the secondary channel
+        * offset is available or not. Substract 1 from the mesh TTL
+        * and disable the initiator flag before forwarding.
+        */
+       offset_ttl = (len < 42) ? 7 : 10;
+       *(pos + offset_ttl) -= 1;
+       *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+       sdata->u.mesh.chsw_ttl = *(pos + offset_ttl);
+
+       memcpy(mgmt_fwd, mgmt, len);
+       eth_broadcast_addr(mgmt_fwd->da);
+       memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN);
+
+       ieee80211_tx_skb(sdata, skb);
+       return 0;
+}
+
+static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
+                             struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct ieee802_11_elems elems;
+       u16 pre_value;
+       bool fwd_csa = true;
+       size_t baselen;
+       u8 *pos, ttl;
+
+       if (mgmt->u.action.u.measurement.action_code !=
+           WLAN_ACTION_SPCT_CHL_SWITCH)
+               return;
+
+       pos = mgmt->u.action.u.chan_switch.variable;
+       baselen = offsetof(struct ieee80211_mgmt,
+                          u.action.u.chan_switch.variable);
+       ieee802_11_parse_elems(pos, len - baselen, false, &elems);
+
+       ttl = elems.mesh_chansw_params_ie->mesh_ttl;
+       if (!--ttl)
+               fwd_csa = false;
+
+       pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
+       if (ifmsh->pre_value >= pre_value)
+               return;
+
+       ifmsh->pre_value = pre_value;
+
+       if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
+               mcsa_dbg(sdata, "Failed to process CSA action frame");
+               return;
+       }
+
+       /* forward or re-broadcast the CSA frame */
+       if (fwd_csa) {
+               if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0)
+                       mcsa_dbg(sdata, "Failed to forward the CSA frame");
+       }
 }
 
 static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
@@ -939,6 +1234,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
                if (mesh_action_is_path_sel(mgmt))
                        mesh_rx_path_sel_frame(sdata, mgmt, len);
                break;
+       case WLAN_CATEGORY_SPECTRUM_MGMT:
+               mesh_rx_csa_frame(sdata, mgmt, len);
+               break;
        }
 }
 
@@ -1056,13 +1354,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
                    (unsigned long) sdata);
 
        ifmsh->accepting_plinks = true;
-       ifmsh->preq_id = 0;
-       ifmsh->sn = 0;
-       ifmsh->num_gates = 0;
        atomic_set(&ifmsh->mpaths, 0);
        mesh_rmc_init(sdata);
        ifmsh->last_preq = jiffies;
        ifmsh->next_perr = jiffies;
+       ifmsh->chsw_init = false;
        /* Allocate all mesh structures when creating the first mesh interface. */
        if (!mesh_allocated)
                ieee80211s_init();
index 6b65d5055f5bf7572d29712c6345c88dd11b33a5..4301aa5aa227c3d539cd0bf89bdf5b004679976a 100644 (file)
@@ -222,7 +222,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
        mesh_path_flush_by_nexthop(sta);
 
        ieee80211_mps_sta_status_update(sta);
-       changed |= ieee80211_mps_local_status_update(sdata);
+       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                       NL80211_MESH_POWER_UNKNOWN);
 
        return changed;
 }
index 22290a929b94595445865baecdb692aff5e0be9c..0f79b78b5e86b45a6d7059de497b68b44944da09 100644 (file)
@@ -152,6 +152,9 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 
+       if (sta->local_pm == pm)
+               return 0;
+
        mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
                pm, sta->sta.addr);
 
@@ -245,6 +248,14 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
 
        do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
 
+       /* clear the MPSP flags for non-peers or active STA */
+       if (sta->plink_state != NL80211_PLINK_ESTAB) {
+               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+               clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
+       } else if (!do_buffer) {
+               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+       }
+
        /* Don't let the same PS state be set twice */
        if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
                return;
@@ -257,14 +268,6 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
        } else {
                ieee80211_sta_ps_deliver_wakeup(sta);
        }
-
-       /* clear the MPSP flags for non-peers or active STA */
-       if (sta->plink_state != NL80211_PLINK_ESTAB) {
-               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
-               clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
-       } else if (!do_buffer) {
-               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
-       }
 }
 
 static void mps_set_sta_peer_pm(struct sta_info *sta,
@@ -444,8 +447,7 @@ static void mpsp_qos_null_append(struct sta_info *sta,
  */
 static void mps_frame_deliver(struct sta_info *sta, int n_frames)
 {
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_local *local = sta->sdata->local;
        int ac;
        struct sk_buff_head frames;
        struct sk_buff *skb;
@@ -558,10 +560,10 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
 }
 
 /**
- * ieee80211_mps_frame_release - release buffered frames in response to beacon
+ * ieee80211_mps_frame_release - release frames buffered due to mesh power save
  *
  * @sta: mesh STA
- * @elems: beacon IEs
+ * @elems: IEs of beacon or probe response
  *
  * For peers if we have individually-addressed frames buffered or the peer
  * indicates buffered frames, send a corresponding MPSP trigger frame. Since
@@ -588,9 +590,10 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
            (!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
                return;
 
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-               buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
-                               skb_queue_len(&sta->tx_filtered[ac]);
+       if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER))
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
+                                       skb_queue_len(&sta->tx_filtered[ac]);
 
        if (!has_buffered && !buffer_local)
                return;
index 91cc8281e266cbfb7c51ea23c36e046e5f3c008a..d7504ab61a34c7ef6a51f8adce10e58c021408d0 100644 (file)
@@ -958,9 +958,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        struct cfg80211_bss *cbss = ifmgd->associated;
        struct ieee80211_chanctx *chanctx;
        enum ieee80211_band current_band;
-       u8 count;
-       u8 mode;
-       struct cfg80211_chan_def new_chandef = {};
+       struct ieee80211_csa_ie csa_ie;
        int res;
 
        sdata_assert_lock(sdata);
@@ -976,24 +974,24 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
 
        current_band = cbss->channel->band;
+       memset(&csa_ie, 0, sizeof(csa_ie));
        res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
                                           ifmgd->flags,
-                                          ifmgd->associated->bssid, &count,
-                                          &mode, &new_chandef);
+                                          ifmgd->associated->bssid, &csa_ie);
        if (res < 0)
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
        if (res)
                return;
 
-       if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
+       if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
                                     IEEE80211_CHAN_DISABLED)) {
                sdata_info(sdata,
                           "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
                           ifmgd->associated->bssid,
-                          new_chandef.chan->center_freq,
-                          new_chandef.width, new_chandef.center_freq1,
-                          new_chandef.center_freq2);
+                          csa_ie.chandef.chan->center_freq,
+                          csa_ie.chandef.width, csa_ie.chandef.center_freq1,
+                          csa_ie.chandef.center_freq2);
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
                return;
@@ -1037,9 +1035,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
        mutex_unlock(&local->chanctx_mtx);
 
-       local->csa_chandef = new_chandef;
+       local->csa_chandef = csa_ie.chandef;
 
-       if (mode)
+       if (csa_ie.mode)
                ieee80211_stop_queues_by_reason(&local->hw,
                                IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -1048,9 +1046,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                /* use driver's channel switch callback */
                struct ieee80211_channel_switch ch_switch = {
                        .timestamp = timestamp,
-                       .block_tx = mode,
-                       .chandef = new_chandef,
-                       .count = count,
+                       .block_tx = csa_ie.mode,
+                       .chandef = csa_ie.chandef,
+                       .count = csa_ie.count,
                };
 
                drv_channel_switch(local, &ch_switch);
@@ -1058,11 +1056,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        /* channel switch handled in software */
-       if (count <= 1)
+       if (csa_ie.count <= 1)
                ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
        else
                mod_timer(&ifmgd->chswitch_timer,
-                         TU_TO_EXP_TIME(count * cbss->beacon_interval));
+                         TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
 }
 
 static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@ -2527,7 +2525,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
         */
        ifmgd->wmm_last_param_set = -1;
 
-       if (elems.wmm_param)
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)
                ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
                                         elems.wmm_param_len);
        else
@@ -2955,7 +2953,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
                                         &elems, true);
 
-       if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
+           ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
                                     elems.wmm_param_len))
                changed |= BSS_CHANGED_QOS;
 
@@ -3499,7 +3498,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
                  ieee80211_beacon_connection_loss_work);
        INIT_WORK(&ifmgd->csa_connection_drop_work,
                  ieee80211_csa_connection_drop_work);
-       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
+       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -3937,6 +3936,44 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        return err;
 }
 
+static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
+                                       const u8 *wmm_param, int len)
+{
+       const u8 *pos;
+       size_t left;
+
+       if (len < 8)
+               return false;
+
+       if (wmm_param[5] != 1 /* version */)
+               return false;
+
+       pos = wmm_param + 8;
+       left = len - 8;
+
+       for (; left >= 4; left -= 4, pos += 4) {
+               u8 aifsn = pos[0] & 0x0f;
+               u8 ecwmin = pos[1] & 0x0f;
+               u8 ecwmax = (pos[1] & 0xf0) >> 4;
+               int aci = (pos[0] >> 5) & 0x03;
+
+               if (aifsn < 2) {
+                       sdata_info(sdata,
+                                  "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n",
+                                  aifsn, aci);
+                       return false;
+               }
+               if (ecwmin > ecwmax) {
+                       sdata_info(sdata,
+                                  "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
+                                  ecwmin, ecwmax, aci);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_assoc_request *req)
 {
@@ -3994,9 +4031,45 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        }
 
        /* prepare assoc data */
-       
+
        ifmgd->beacon_crc_valid = false;
 
+       assoc_data->wmm = bss->wmm_used &&
+                         (local->hw.queues >= IEEE80211_NUM_ACS);
+       if (assoc_data->wmm) {
+               /* try to check validity of WMM params IE */
+               const struct cfg80211_bss_ies *ies;
+               const u8 *wp, *start, *end;
+
+               rcu_read_lock();
+               ies = rcu_dereference(req->bss->ies);
+               start = ies->data;
+               end = start + ies->len;
+
+               while (true) {
+                       wp = cfg80211_find_vendor_ie(
+                               WLAN_OUI_MICROSOFT,
+                               WLAN_OUI_TYPE_MICROSOFT_WMM,
+                               start, end - start);
+                       if (!wp)
+                               break;
+                       start = wp + wp[1] + 2;
+                       /* if this IE is too short, try the next */
+                       if (wp[1] <= 4)
+                               continue;
+                       /* if this IE is WMM params, we found what we wanted */
+                       if (wp[6] == 1)
+                               break;
+               }
+
+               if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
+                                                       wp[1] - 2)) {
+                       assoc_data->wmm = false;
+                       ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
+               }
+               rcu_read_unlock();
+       }
+
        /*
         * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
         * We still associate in non-HT mode (11a/b/g) if any one of these
@@ -4026,18 +4099,22 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        /* Also disable HT if we don't support it or the AP doesn't use WMM */
        sband = local->hw.wiphy->bands[req->bss->channel->band];
        if (!sband->ht_cap.ht_supported ||
-           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
+           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
+           ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
-               if (!bss->wmm_used)
+               if (!bss->wmm_used &&
+                   !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
                        netdev_info(sdata->dev,
                                    "disabling HT as WMM/QoS is not supported by the AP\n");
        }
 
        /* disable VHT if we don't support it or the AP doesn't use WMM */
        if (!sband->vht_cap.vht_supported ||
-           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
+           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
+           ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
-               if (!bss->wmm_used)
+               if (!bss->wmm_used &&
+                   !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
                        netdev_info(sdata->dev,
                                    "disabling VHT as WMM/QoS is not supported by the AP\n");
        }
@@ -4066,8 +4143,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                sdata->smps_mode = ifmgd->req_smps;
 
        assoc_data->capability = req->bss->capability;
-       assoc_data->wmm = bss->wmm_used &&
-                         (local->hw.queues >= IEEE80211_NUM_ACS);
        assoc_data->supp_rates = bss->supp_rates;
        assoc_data->supp_rates_len = bss->supp_rates_len;
 
index acd1f71adc0386ab588f0939309e2367330f2b29..0c2a29484c07cdaaaf716b1f5ba0184d1047ee68 100644 (file)
@@ -394,6 +394,8 @@ void ieee80211_sw_roc_work(struct work_struct *work)
 
                if (started)
                        ieee80211_start_next_roc(local);
+               else if (list_empty(&local->roc_list))
+                       ieee80211_run_deferred_scan(local);
        }
 
  out_unlock:
index e126605cec66baf82aadb835856110d74105795f..22b223f13c9fa22994eba6f68769a27b2cfe4eb3 100644 (file)
@@ -235,7 +235,8 @@ static void rc_send_low_basicrate(s8 *idx, u32 basic_rates,
 static void __rate_control_send_low(struct ieee80211_hw *hw,
                                    struct ieee80211_supported_band *sband,
                                    struct ieee80211_sta *sta,
-                                   struct ieee80211_tx_info *info)
+                                   struct ieee80211_tx_info *info,
+                                   u32 rate_mask)
 {
        int i;
        u32 rate_flags =
@@ -247,6 +248,12 @@ static void __rate_control_send_low(struct ieee80211_hw *hw,
 
        info->control.rates[0].idx = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
+               if (!(rate_mask & BIT(i)))
+                       continue;
+
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                if (!rate_supported(sta, sband->band, i))
                        continue;
 
@@ -274,7 +281,8 @@ bool rate_control_send_low(struct ieee80211_sta *pubsta,
        bool use_basicrate = false;
 
        if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
-               __rate_control_send_low(txrc->hw, sband, pubsta, info);
+               __rate_control_send_low(txrc->hw, sband, pubsta, info,
+                                       txrc->rate_idx_mask);
 
                if (!pubsta && txrc->bss) {
                        mcast_rate = txrc->bss_conf->mcast_rate[sband->band];
@@ -656,7 +664,8 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
                rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates);
 
        if (dest[0].idx < 0)
-               __rate_control_send_low(&sdata->local->hw, sband, sta, info);
+               __rate_control_send_low(&sdata->local->hw, sband, sta, info,
+                                       sdata->rc_rateidx_mask[info->band]);
 
        if (sta)
                rate_fixup_ratelist(vif, sband, info, dest, max_rates);
index f0247a43a75c1d0825f2588cfbfbcd13fad9c28b..caecef870c0e44e3562cdc0a874bcfd233fc77a1 100644 (file)
@@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                                break;
 
                        if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-                           sdata->vif.type != NL80211_IFTYPE_ADHOC)
+                           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
                                break;
 
                        if (sdata->vif.type == NL80211_IFTYPE_STATION)
                                bssid = sdata->u.mgd.bssid;
                        else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
                                bssid = sdata->u.ibss.bssid;
+                       else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+                               bssid = mgmt->sa;
                        else
                                break;
 
@@ -3073,6 +3076,9 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
        case NL80211_IFTYPE_ADHOC:
                if (!bssid)
                        return 0;
+               if (ether_addr_equal(sdata->vif.addr, hdr->addr2) ||
+                   ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2))
+                       return 0;
                if (ieee80211_is_beacon(hdr->frame_control)) {
                        return 1;
                } else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) {
index ecb57b0bf74a19153ab0ed196fe1ae4f7e272c64..5ad66a83ef7f4d4525de163c2c2f1fe9d6931a04 100644 (file)
@@ -238,6 +238,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
        enum ieee80211_band band;
        int i, ielen, n_chans;
 
+       if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
+               return false;
+
        do {
                if (local->hw_scan_band == IEEE80211_NUM_BANDS)
                        return false;
@@ -939,7 +942,23 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
        if (!local->scan_req)
                goto out;
 
+       /*
+        * We have a scan running and the driver already reported completion,
+        * but the worker hasn't run yet or is stuck on the mutex - mark it as
+        * cancelled.
+        */
+       if (test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+           test_bit(SCAN_COMPLETED, &local->scanning)) {
+               set_bit(SCAN_HW_CANCELLED, &local->scanning);
+               goto out;
+       }
+
        if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
+               /*
+                * Make sure that __ieee80211_scan_completed doesn't trigger a
+                * scan on another band.
+                */
+               set_bit(SCAN_HW_CANCELLED, &local->scanning);
                if (local->ops->cancel_hw_scan)
                        drv_cancel_hw_scan(local,
                                rcu_dereference_protected(local->scan_sdata,
index 921597e279a307857cad5974a434fdd9252ec225..a40da20b32e074a3923f844edf104eca33a6134c 100644 (file)
@@ -24,8 +24,8 @@
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                                 struct ieee802_11_elems *elems, bool beacon,
                                 enum ieee80211_band current_band,
-                                u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
-                                struct cfg80211_chan_def *new_chandef)
+                                u32 sta_flags, u8 *bssid,
+                                struct ieee80211_csa_ie *csa_ie)
 {
        enum ieee80211_band new_band;
        int new_freq;
@@ -62,18 +62,24 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                        return -EINVAL;
                }
                new_chan_no = elems->ext_chansw_ie->new_ch_num;
-               *count = elems->ext_chansw_ie->count;
-               *mode = elems->ext_chansw_ie->mode;
+               csa_ie->count = elems->ext_chansw_ie->count;
+               csa_ie->mode = elems->ext_chansw_ie->mode;
        } else if (elems->ch_switch_ie) {
                new_band = current_band;
                new_chan_no = elems->ch_switch_ie->new_ch_num;
-               *count = elems->ch_switch_ie->count;
-               *mode = elems->ch_switch_ie->mode;
+               csa_ie->count = elems->ch_switch_ie->count;
+               csa_ie->mode = elems->ch_switch_ie->mode;
        } else {
                /* nothing here we understand */
                return 1;
        }
 
+       /* Mesh Channel Switch Parameters Element */
+       if (elems->mesh_chansw_params_ie) {
+               csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
+               csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags;
+       }
+
        new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
        new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
        if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
@@ -103,25 +109,26 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
        default:
                /* secondary_channel_offset was present but is invalid */
        case IEEE80211_HT_PARAM_CHA_SEC_NONE:
-               cfg80211_chandef_create(new_chandef, new_chan,
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
                                        NL80211_CHAN_HT20);
                break;
        case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-               cfg80211_chandef_create(new_chandef, new_chan,
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
                                        NL80211_CHAN_HT40PLUS);
                break;
        case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-               cfg80211_chandef_create(new_chandef, new_chan,
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
                                        NL80211_CHAN_HT40MINUS);
                break;
        case -1:
-               cfg80211_chandef_create(new_chandef, new_chan,
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
                                        NL80211_CHAN_NO_HT);
                /* keep width for 5/10 MHz channels */
                switch (sdata->vif.bss_conf.chandef.width) {
                case NL80211_CHAN_WIDTH_5:
                case NL80211_CHAN_WIDTH_10:
-                       new_chandef->width = sdata->vif.bss_conf.chandef.width;
+                       csa_ie->chandef.width =
+                               sdata->vif.bss_conf.chandef.width;
                        break;
                default:
                        break;
@@ -171,13 +178,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
        /* if VHT data is there validate & use it */
        if (new_vht_chandef.chan) {
                if (!cfg80211_chandef_compatible(&new_vht_chandef,
-                                                new_chandef)) {
+                                                &csa_ie->chandef)) {
                        sdata_info(sdata,
                                   "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
                                   bssid);
                        return -EINVAL;
                }
-               *new_chandef = new_vht_chandef;
+               csa_ie->chandef = new_vht_chandef;
        }
 
        return 0;
index aeb967a0aeed5ccc49b6c45c5ac13d0d97f97130..1eb66e26e49d0a02869ec5e5ff4789583e9760d1 100644 (file)
@@ -385,6 +385,30 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
 
        sta->sta.smps_mode = IEEE80211_SMPS_OFF;
+       if (sdata->vif.type == NL80211_IFTYPE_AP ||
+           sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+               struct ieee80211_supported_band *sband =
+                       local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+               u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
+                               IEEE80211_HT_CAP_SM_PS_SHIFT;
+               /*
+                * Assume that hostapd advertises our caps in the beacon and
+                * this is the known_smps_mode for a station that just assciated
+                */
+               switch (smps) {
+               case WLAN_HT_SMPS_CONTROL_DISABLED:
+                       sta->known_smps_mode = IEEE80211_SMPS_OFF;
+                       break;
+               case WLAN_HT_SMPS_CONTROL_STATIC:
+                       sta->known_smps_mode = IEEE80211_SMPS_STATIC;
+                       break;
+               case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+                       sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       break;
+               default:
+                       WARN_ON(1);
+               }
+       }
 
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 
@@ -1069,6 +1093,19 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
        ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
 
+       /* This station just woke up and isn't aware of our SMPS state */
+       if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,
+                                          sdata->smps_mode) &&
+           sta->known_smps_mode != sdata->bss->req_smps &&
+           sta_info_tx_streams(sta) != 1) {
+               ht_dbg(sdata,
+                      "%pM just woke up and MIMO capable - update SMPS\n",
+                      sta->sta.addr);
+               ieee80211_send_smps_action(sdata, sdata->bss->req_smps,
+                                          sta->sta.addr,
+                                          sdata->vif.bss_conf.bssid);
+       }
+
        local->total_ps_buffered -= buffered;
 
        sta_info_recalc_tim(sta);
@@ -1520,3 +1557,38 @@ int sta_info_move_state(struct sta_info *sta,
 
        return 0;
 }
+
+u8 sta_info_tx_streams(struct sta_info *sta)
+{
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap;
+       u8 rx_streams;
+
+       if (!sta->sta.ht_cap.ht_supported)
+               return 1;
+
+       if (sta->sta.vht_cap.vht_supported) {
+               int i;
+               u16 tx_mcs_map =
+                       le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map);
+
+               for (i = 7; i >= 0; i--)
+                       if ((tx_mcs_map & (0x3 << (i * 2))) !=
+                           IEEE80211_VHT_MCS_NOT_SUPPORTED)
+                               return i + 1;
+       }
+
+       if (ht_cap->mcs.rx_mask[3])
+               rx_streams = 4;
+       else if (ht_cap->mcs.rx_mask[2])
+               rx_streams = 3;
+       else if (ht_cap->mcs.rx_mask[1])
+               rx_streams = 2;
+       else
+               rx_streams = 1;
+
+       if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF))
+               return rx_streams;
+
+       return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+                       >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
+}
index 4208dbd5861f4d87cc8fa94aefc7c00f51e72eed..3ef06a26b9cb9feaddfd3d752dd9bd8ac971a11c 100644 (file)
@@ -301,6 +301,8 @@ struct sta_ampdu_mlme {
  * @chains: chains ever used for RX from this station
  * @chain_signal_last: last signal (per chain)
  * @chain_signal_avg: signal average (per chain)
+ * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
+ *     AP only.
  */
 struct sta_info {
        /* General information, mostly static */
@@ -411,6 +413,8 @@ struct sta_info {
        unsigned int lost_packets;
        unsigned int beacon_loss_count;
 
+       enum ieee80211_smps_mode known_smps_mode;
+
        /* keep last! */
        struct ieee80211_sta sta;
 };
@@ -613,6 +617,7 @@ void sta_set_rate_info_rx(struct sta_info *sta,
                          struct rate_info *rinfo);
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time);
+u8 sta_info_tx_streams(struct sta_info *sta);
 
 void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
index 368837fe3b800e87f408039ef4d36e84bfaa7069..52a152b01b063b226fb5a523b19a4e9863eeffaf 100644 (file)
@@ -180,6 +180,9 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 
+       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               sta->last_rx = jiffies;
+
        if (ieee80211_is_data_qos(mgmt->frame_control)) {
                struct ieee80211_hdr *hdr = (void *) skb->data;
                u8 *qc = ieee80211_get_qos_ctl(hdr);
@@ -191,29 +194,36 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
        if (ieee80211_is_action(mgmt->frame_control) &&
            mgmt->u.action.category == WLAN_CATEGORY_HT &&
            mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
-           sdata->vif.type == NL80211_IFTYPE_STATION &&
            ieee80211_sdata_running(sdata)) {
-               /*
-                * This update looks racy, but isn't -- if we come
-                * here we've definitely got a station that we're
-                * talking to, and on a managed interface that can
-                * only be the AP. And the only other place updating
-                * this variable in managed mode is before association.
-                */
+               enum ieee80211_smps_mode smps_mode;
+
                switch (mgmt->u.action.u.ht_smps.smps_control) {
                case WLAN_HT_SMPS_CONTROL_DYNAMIC:
-                       sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_STATIC:
-                       sdata->smps_mode = IEEE80211_SMPS_STATIC;
+                       smps_mode = IEEE80211_SMPS_STATIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_DISABLED:
                default: /* shouldn't happen since we don't send that */
-                       sdata->smps_mode = IEEE80211_SMPS_OFF;
+                       smps_mode = IEEE80211_SMPS_OFF;
                        break;
                }
 
-               ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       /*
+                        * This update looks racy, but isn't -- if we come
+                        * here we've definitely got a station that we're
+                        * talking to, and on a managed interface that can
+                        * only be the AP. And the only other place updating
+                        * this variable in managed mode is before association.
+                        */
+                       sdata->smps_mode = smps_mode;
+                       ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+               } else if (sdata->vif.type == NL80211_IFTYPE_AP ||
+                          sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+                       sta->known_smps_mode = smps_mode;
+               }
        }
 }
 
index 4fcbf634b54863387f85b42d5f47f82de861e995..c558b246ef0036c38e6c8a00b338b43c46913f78 100644 (file)
@@ -1120,7 +1120,8 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
                tx->sta = rcu_dereference(sdata->u.vlan.sta);
                if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
                        return TX_DROP;
-       } else if (info->flags & IEEE80211_TX_CTL_INJECTED ||
+       } else if (info->flags & (IEEE80211_TX_CTL_INJECTED |
+                                 IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||
                   tx->sdata->control_port_protocol == tx->skb->protocol) {
                tx->sta = sta_info_get_bss(sdata, hdr->addr1);
        }
@@ -1366,6 +1367,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
        return 0;
 }
 
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, struct sk_buff *skb,
+                             int band, struct ieee80211_sta **sta)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_tx_data tx;
+
+       if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
+               return false;
+
+       info->band = band;
+       info->control.vif = vif;
+       info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)];
+
+       if (invoke_tx_handlers(&tx))
+               return false;
+
+       if (sta) {
+               if (tx.sta)
+                       *sta = &tx.sta->sta;
+               else
+                       *sta = NULL;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
+
 /*
  * Returns false if the frame couldn't be transmitted but was queued instead.
  */
@@ -2369,6 +2399,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
                beacon_data = beacon->head;
                beacon_data_len = beacon->head_len;
                break;
+       case NL80211_IFTYPE_MESH_POINT:
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+               break;
        default:
                return;
        }
@@ -2423,6 +2457,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
                if (!beacon)
                        goto out;
 
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+       } else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+               beacon = rcu_dereference(ifmsh->beacon);
+               if (!beacon)
+                       goto out;
+
                beacon_data = beacon->head;
                beacon_data_len = beacon->head_len;
        } else {
@@ -2530,6 +2573,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                if (!bcn)
                        goto out;
 
+               if (sdata->vif.csa_active)
+                       ieee80211_update_csa(sdata, bcn);
+
                if (ifmsh->sync_ops)
                        ifmsh->sync_ops->adjust_tbtt(
                                                sdata);
index 550a6880625dd79cfd9741017ddb6e63f4c50274..592a18171f95e9ec5273b03307235dff2bd1c946 100644 (file)
@@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
                if (!sdata->dev)
                        continue;
 
-               if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
-                       continue;
-
                if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
                    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
                        continue;
@@ -743,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                case WLAN_EID_TIMEOUT_INTERVAL:
                case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
+               case WLAN_EID_CHAN_SWITCH_PARAM:
                /*
                 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
                 * that if the content gets bigger it might be needed more than once
@@ -908,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                        }
                        elems->sec_chan_offs = (void *)pos;
                        break;
+               case WLAN_EID_CHAN_SWITCH_PARAM:
+                       if (elen !=
+                           sizeof(*elems->mesh_chansw_params_ie)) {
+                               elem_parse_failed = true;
+                               break;
+                       }
+                       elems->mesh_chansw_params_ie = (void *)pos;
+                       break;
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
                        if (!action ||
                            elen != sizeof(*elems->wide_bw_chansw_ie)) {
@@ -2101,7 +2107,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
-       int rate, skip, shift;
+       int rate, shift;
        u8 i, exrates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
        u32 rate_flags;
@@ -2129,14 +2135,11 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
                pos = skb_put(skb, exrates + 2);
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
                *pos++ = exrates;
-               skip = 0;
                for (i = 8; i < sband->n_bitrates; i++) {
                        u8 basic = 0;
                        if ((rate_flags & sband->bitrates[i].flags)
                            != rate_flags)
                                continue;
-                       if (skip++ < 8)
-                               continue;
                        if (need_basic && basic_rates & BIT(i))
                                basic = 0x80;
                        rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
@@ -2239,6 +2242,10 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
        }
 
        rate = cfg80211_calculate_bitrate(&ri);
+       if (WARN_ONCE(!rate,
+                     "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n",
+                     status->flag, status->rate_idx, status->vht_nss))
+               return 0;
 
        /* rewind from end of MPDU */
        if (status->flag & RX_FLAG_MACTIME_END)
@@ -2353,3 +2360,115 @@ u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
 
        return ret;
 }
+
+/*
+ * Returns true if smps_mode_new is strictly more restrictive than
+ * smps_mode_old.
+ */
+bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+                                  enum ieee80211_smps_mode smps_mode_new)
+{
+       if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
+                        smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
+               return false;
+
+       switch (smps_mode_old) {
+       case IEEE80211_SMPS_STATIC:
+               return false;
+       case IEEE80211_SMPS_DYNAMIC:
+               return smps_mode_new == IEEE80211_SMPS_STATIC;
+       case IEEE80211_SMPS_OFF:
+               return smps_mode_new != IEEE80211_SMPS_OFF;
+       default:
+               WARN_ON(1);
+       }
+
+       return false;
+}
+
+int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings)
+{
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_local *local = sdata->local;
+       int freq;
+       int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
+                              sizeof(mgmt->u.action.u.chan_switch);
+       u8 *pos;
+
+       if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+               return -EOPNOTSUPP;
+
+       skb = dev_alloc_skb(local->tx_headroom + hdr_len +
+                           5 + /* channel switch announcement element */
+                           3 + /* secondary channel offset element */
+                           8); /* mesh channel switch parameters element */
+       if (!skb)
+               return -ENOMEM;
+
+       skb_reserve(skb, local->tx_headroom);
+       mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
+       memset(mgmt, 0, hdr_len);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       eth_broadcast_addr(mgmt->da);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+       } else {
+               struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+               memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
+       }
+       mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
+       mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
+       pos = skb_put(skb, 5);
+       *pos++ = WLAN_EID_CHANNEL_SWITCH;                       /* EID */
+       *pos++ = 3;                                             /* IE length */
+       *pos++ = csa_settings->block_tx ? 1 : 0;                /* CSA mode */
+       freq = csa_settings->chandef.chan->center_freq;
+       *pos++ = ieee80211_frequency_to_channel(freq);          /* channel */
+       *pos++ = csa_settings->count;                           /* count */
+
+       if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
+               enum nl80211_channel_type ch_type;
+
+               skb_put(skb, 3);
+               *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;     /* EID */
+               *pos++ = 1;                                     /* IE length */
+               ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
+               if (ch_type == NL80211_CHAN_HT40PLUS)
+                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+               else
+                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+       }
+
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+               __le16 pre_value;
+
+               skb_put(skb, 8);
+               *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;            /* EID */
+               *pos++ = 6;                                     /* IE length */
+               *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;     /* Mesh TTL */
+               *pos = 0x00;    /* Mesh Flag: Tx Restrict, Initiator, Reason */
+               *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+               *pos++ |= csa_settings->block_tx ?
+                         WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
+               put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
+               pos += 2;
+               if (!ifmsh->pre_value)
+                       ifmsh->pre_value = 1;
+               else
+                       ifmsh->pre_value++;
+               pre_value = cpu_to_le16(ifmsh->pre_value);
+               memcpy(pos, &pre_value, 2);             /* Precedence Value */
+               pos += 2;
+               ifmsh->chsw_init = true;
+       }
+
+       ieee80211_tx_skb(sdata, skb);
+       return 0;
+}
index c9edfcb7a13b65ad6d766b182e54cf6cfcc83c0b..d6572822076367cdf203207892a2880c7e4487c2 100644 (file)
@@ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
 }
 
 
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
                                int encrypted)
 {
        __le16 mask_fc;
        int a4_included, mgmt;
        u8 qos_tid;
-       u8 *b_0, *aad;
-       u16 data_len, len_a;
+       u16 len_a;
        unsigned int hdrlen;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-       memset(scratch, 0, 6 * AES_BLOCK_SIZE);
-
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-       aad = scratch + 4 * AES_BLOCK_SIZE;
-
        /*
         * Mask FC: zero subtype b4 b5 b6 (if not mgmt)
         * Retry, PwrMgt, MoreData; set Protected
@@ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
        else
                qos_tid = 0;
 
-       data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN;
-       if (encrypted)
-               data_len -= IEEE80211_CCMP_MIC_LEN;
+       /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
+        * mode authentication are not allowed to collide, yet both are derived
+        * from this vector b_0. We only set L := 1 here to indicate that the
+        * data size can be represented in (L+1) bytes. The CCM layer will take
+        * care of storing the data length in the top (L+1) bytes and setting
+        * and clearing the other bits as is required to derive the two IVs.
+        */
+       b_0[0] = 0x1;
 
-       /* First block, b_0 */
-       b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
        /* Nonce: Nonce Flags | A2 | PN
         * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
         */
        b_0[1] = qos_tid | (mgmt << 4);
        memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
        memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
-       /* l(m) */
-       put_unaligned_be16(data_len, &b_0[14]);
 
        /* AAD (extra authenticate-only data) / masked 802.11 header
         * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
@@ -407,7 +402,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
        u8 *pos;
        u8 pn[6];
        u64 pn64;
-       u8 scratch[6 * AES_BLOCK_SIZE];
+       u8 aad[2 * AES_BLOCK_SIZE];
+       u8 b_0[AES_BLOCK_SIZE];
 
        if (info->control.hw_key &&
            !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
@@ -460,9 +456,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
                return 0;
 
        pos += IEEE80211_CCMP_HDR_LEN;
-       ccmp_special_blocks(skb, pn, scratch, 0);
-       ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
-                                 pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN));
+       ccmp_special_blocks(skb, pn, b_0, aad, 0);
+       ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
+                                 skb_put(skb, IEEE80211_CCMP_MIC_LEN));
 
        return 0;
 }
@@ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
        }
 
        if (!(status->flag & RX_FLAG_DECRYPTED)) {
-               u8 scratch[6 * AES_BLOCK_SIZE];
+               u8 aad[2 * AES_BLOCK_SIZE];
+               u8 b_0[AES_BLOCK_SIZE];
                /* hardware didn't decrypt/verify MIC */
-               ccmp_special_blocks(skb, pn, scratch, 1);
+               ccmp_special_blocks(skb, pn, b_0, aad, 1);
 
                if (ieee80211_aes_ccm_decrypt(
-                           key->u.ccmp.tfm, scratch,
+                           key->u.ccmp.tfm, b_0, aad,
                            skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
                            data_len,
-                           skb->data + skb->len - IEEE80211_CCMP_MIC_LEN,
-                           skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN))
+                           skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
                        return RX_DROP_UNUSABLE;
        }
 
index 5948b2fc72f63d3a9994e962b1366f8913c61ed7..6e0fa0cce1982a8825d36e7966851a46fe1bcb2e 100644 (file)
@@ -14,6 +14,20 @@ menuconfig NFC
          To compile this support as a module, choose M here: the module will
          be called nfc.
 
+config NFC_DIGITAL
+       depends on NFC
+       select CRC_CCITT
+       select CRC_ITU_T
+       tristate "NFC Digital Protocol stack support"
+       default n
+       help
+         Say Y if you want to build NFC digital protocol stack support.
+         This is needed by NFC chipsets whose firmware only implement
+         the NFC analog layer.
+
+         To compile this support as a module, choose M here: the module will
+         be called nfc_digital.
+
 source "net/nfc/nci/Kconfig"
 source "net/nfc/hci/Kconfig"
 
index a76f4533cb6ce5a1b51e2199bbfd3c8bf91a86a4..2555ff8e7219b19ecdc06da62d5bef6ce555ae20 100644 (file)
@@ -5,7 +5,9 @@
 obj-$(CONFIG_NFC) += nfc.o
 obj-$(CONFIG_NFC_NCI) += nci/
 obj-$(CONFIG_NFC_HCI) += hci/
+obj-$(CONFIG_NFC_DIGITAL) += nfc_digital.o
 
 nfc-objs := core.o netlink.o af_nfc.o rawsock.o llcp_core.o llcp_commands.o \
                llcp_sock.o
 
+nfc_digital-objs := digital_core.o digital_technology.o digital_dep.o
index e92923cf3e0374f950417e5039ba2e61369c8e27..872529105abc7c3a2e41b9d579e934e019e8e5ff 100644 (file)
@@ -384,6 +384,19 @@ int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx,
 {
        dev->dep_link_up = true;
 
+       if (!dev->active_target) {
+               struct nfc_target *target;
+
+               target = nfc_find_target(dev, target_idx);
+               if (target == NULL)
+                       return -ENOTCONN;
+
+               dev->active_target = target;
+       }
+
+       dev->polling = false;
+       dev->rf_mode = rf_mode;
+
        nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode);
 
        return nfc_genl_dep_link_up_event(dev, target_idx, comm_mode, rf_mode);
@@ -536,7 +549,7 @@ error:
        return rc;
 }
 
-static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx)
+struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx)
 {
        struct nfc_se *se, *n;
 
@@ -546,6 +559,7 @@ static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx)
 
        return NULL;
 }
+EXPORT_SYMBOL(nfc_find_se);
 
 int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
 {
@@ -577,7 +591,7 @@ int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
                goto error;
        }
 
-       se = find_se(dev, se_idx);
+       se = nfc_find_se(dev, se_idx);
        if (!se) {
                rc = -EINVAL;
                goto error;
@@ -622,7 +636,7 @@ int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
                goto error;
        }
 
-       se = find_se(dev, se_idx);
+       se = nfc_find_se(dev, se_idx);
        if (!se) {
                rc = -EINVAL;
                goto error;
@@ -881,7 +895,7 @@ int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)
 
        pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
 
-       se = find_se(dev, se_idx);
+       se = nfc_find_se(dev, se_idx);
        if (se)
                return -EALREADY;
 
diff --git a/net/nfc/digital.h b/net/nfc/digital.h
new file mode 100644 (file)
index 0000000..08b29b5
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __DIGITAL_H
+#define __DIGITAL_H
+
+#include <net/nfc/nfc.h>
+#include <net/nfc/digital.h>
+
+#include <linux/crc-ccitt.h>
+#include <linux/crc-itu-t.h>
+
+#define PROTOCOL_ERR(req) pr_err("%d: NFC Digital Protocol error: %s\n", \
+                                __LINE__, req)
+
+#define DIGITAL_CMD_IN_SEND        0
+#define DIGITAL_CMD_TG_SEND        1
+#define DIGITAL_CMD_TG_LISTEN      2
+#define DIGITAL_CMD_TG_LISTEN_MDAA 3
+
+#define DIGITAL_MAX_HEADER_LEN 7
+#define DIGITAL_CRC_LEN        2
+
+#define DIGITAL_SENSF_NFCID2_NFC_DEP_B1 0x01
+#define DIGITAL_SENSF_NFCID2_NFC_DEP_B2 0xFE
+
+#define DIGITAL_SENS_RES_NFC_DEP 0x0100
+#define DIGITAL_SEL_RES_NFC_DEP  0x40
+#define DIGITAL_SENSF_FELICA_SC  0xFFFF
+
+#define DIGITAL_DRV_CAPS_IN_CRC(ddev) \
+       ((ddev)->driver_capabilities & NFC_DIGITAL_DRV_CAPS_IN_CRC)
+#define DIGITAL_DRV_CAPS_TG_CRC(ddev) \
+       ((ddev)->driver_capabilities & NFC_DIGITAL_DRV_CAPS_TG_CRC)
+
+struct digital_data_exch {
+       data_exchange_cb_t cb;
+       void *cb_context;
+};
+
+struct sk_buff *digital_skb_alloc(struct nfc_digital_dev *ddev,
+                                 unsigned int len);
+
+int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type,
+                    struct sk_buff *skb, struct digital_tg_mdaa_params *params,
+                    u16 timeout, nfc_digital_cmd_complete_t cmd_cb,
+                    void *cb_context);
+
+int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param);
+static inline int digital_in_send_cmd(struct nfc_digital_dev *ddev,
+                                     struct sk_buff *skb, u16 timeout,
+                                     nfc_digital_cmd_complete_t cmd_cb,
+                                     void *cb_context)
+{
+       return digital_send_cmd(ddev, DIGITAL_CMD_IN_SEND, skb, NULL, timeout,
+                               cmd_cb, cb_context);
+}
+
+void digital_poll_next_tech(struct nfc_digital_dev *ddev);
+
+int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech);
+int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech);
+
+int digital_target_found(struct nfc_digital_dev *ddev,
+                        struct nfc_target *target, u8 protocol);
+
+int digital_in_recv_mifare_res(struct sk_buff *resp);
+
+int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
+                           struct nfc_target *target, __u8 comm_mode, __u8 *gb,
+                           size_t gb_len);
+int digital_in_send_dep_req(struct nfc_digital_dev *ddev,
+                           struct nfc_target *target, struct sk_buff *skb,
+                           struct digital_data_exch *data_exch);
+
+int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param);
+static inline int digital_tg_send_cmd(struct nfc_digital_dev *ddev,
+                       struct sk_buff *skb, u16 timeout,
+                       nfc_digital_cmd_complete_t cmd_cb, void *cb_context)
+{
+       return digital_send_cmd(ddev, DIGITAL_CMD_TG_SEND, skb, NULL, timeout,
+                               cmd_cb, cb_context);
+}
+
+void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg,
+                             struct sk_buff *resp);
+
+void digital_tg_recv_sensf_req(struct nfc_digital_dev *ddev, void *arg,
+                              struct sk_buff *resp);
+
+static inline int digital_tg_listen(struct nfc_digital_dev *ddev, u16 timeout,
+                                   nfc_digital_cmd_complete_t cb, void *arg)
+{
+       return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN, NULL, NULL,
+                               timeout, cb, arg);
+}
+
+void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
+                            struct sk_buff *resp);
+
+int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb);
+
+int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech);
+int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech);
+
+typedef u16 (*crc_func_t)(u16, const u8 *, size_t);
+
+#define CRC_A_INIT 0x6363
+#define CRC_B_INIT 0xFFFF
+#define CRC_F_INIT 0x0000
+
+void digital_skb_add_crc(struct sk_buff *skb, crc_func_t crc_func, u16 init,
+                        u8 bitwise_inv, u8 msb_first);
+
+static inline void digital_skb_add_crc_a(struct sk_buff *skb)
+{
+       digital_skb_add_crc(skb, crc_ccitt, CRC_A_INIT, 0, 0);
+}
+
+static inline void digital_skb_add_crc_b(struct sk_buff *skb)
+{
+       digital_skb_add_crc(skb, crc_ccitt, CRC_B_INIT, 1, 0);
+}
+
+static inline void digital_skb_add_crc_f(struct sk_buff *skb)
+{
+       digital_skb_add_crc(skb, crc_itu_t, CRC_F_INIT, 0, 1);
+}
+
+static inline void digital_skb_add_crc_none(struct sk_buff *skb)
+{
+       return;
+}
+
+int digital_skb_check_crc(struct sk_buff *skb, crc_func_t crc_func,
+                         u16 crc_init, u8 bitwise_inv, u8 msb_first);
+
+static inline int digital_skb_check_crc_a(struct sk_buff *skb)
+{
+       return digital_skb_check_crc(skb, crc_ccitt, CRC_A_INIT, 0, 0);
+}
+
+static inline int digital_skb_check_crc_b(struct sk_buff *skb)
+{
+       return digital_skb_check_crc(skb, crc_ccitt, CRC_B_INIT, 1, 0);
+}
+
+static inline int digital_skb_check_crc_f(struct sk_buff *skb)
+{
+       return digital_skb_check_crc(skb, crc_itu_t, CRC_F_INIT, 0, 1);
+}
+
+static inline int digital_skb_check_crc_none(struct sk_buff *skb)
+{
+       return 0;
+}
+
+#endif /* __DIGITAL_H */
diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c
new file mode 100644 (file)
index 0000000..09fc954
--- /dev/null
@@ -0,0 +1,737 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#define pr_fmt(fmt) "digital: %s: " fmt, __func__
+
+#include <linux/module.h>
+
+#include "digital.h"
+
+#define DIGITAL_PROTO_NFCA_RF_TECH \
+       (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK)
+
+#define DIGITAL_PROTO_NFCF_RF_TECH \
+       (NFC_PROTO_FELICA_MASK | NFC_PROTO_NFC_DEP_MASK)
+
+struct digital_cmd {
+       struct list_head queue;
+
+       u8 type;
+       u8 pending;
+
+       u16 timeout;
+       struct sk_buff *req;
+       struct sk_buff *resp;
+       struct digital_tg_mdaa_params *mdaa_params;
+
+       nfc_digital_cmd_complete_t cmd_cb;
+       void *cb_context;
+};
+
+struct sk_buff *digital_skb_alloc(struct nfc_digital_dev *ddev,
+                                 unsigned int len)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(len + ddev->tx_headroom + ddev->tx_tailroom,
+                       GFP_KERNEL);
+       if (skb)
+               skb_reserve(skb, ddev->tx_headroom);
+
+       return skb;
+}
+
+void digital_skb_add_crc(struct sk_buff *skb, crc_func_t crc_func, u16 init,
+                        u8 bitwise_inv, u8 msb_first)
+{
+       u16 crc;
+
+       crc = crc_func(init, skb->data, skb->len);
+
+       if (bitwise_inv)
+               crc = ~crc;
+
+       if (msb_first)
+               crc = __fswab16(crc);
+
+       *skb_put(skb, 1) = crc & 0xFF;
+       *skb_put(skb, 1) = (crc >> 8) & 0xFF;
+}
+
+int digital_skb_check_crc(struct sk_buff *skb, crc_func_t crc_func,
+                         u16 crc_init, u8 bitwise_inv, u8 msb_first)
+{
+       int rc;
+       u16 crc;
+
+       if (skb->len <= 2)
+               return -EIO;
+
+       crc = crc_func(crc_init, skb->data, skb->len - 2);
+
+       if (bitwise_inv)
+               crc = ~crc;
+
+       if (msb_first)
+               crc = __swab16(crc);
+
+       rc = (skb->data[skb->len - 2] - (crc & 0xFF)) +
+            (skb->data[skb->len - 1] - ((crc >> 8) & 0xFF));
+
+       if (rc)
+               return -EIO;
+
+       skb_trim(skb, skb->len - 2);
+
+       return 0;
+}
+
+static inline void digital_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+       ddev->ops->switch_rf(ddev, on);
+}
+
+static inline void digital_abort_cmd(struct nfc_digital_dev *ddev)
+{
+       ddev->ops->abort_cmd(ddev);
+}
+
+static void digital_wq_cmd_complete(struct work_struct *work)
+{
+       struct digital_cmd *cmd;
+       struct nfc_digital_dev *ddev = container_of(work,
+                                                   struct nfc_digital_dev,
+                                                   cmd_complete_work);
+
+       mutex_lock(&ddev->cmd_lock);
+
+       cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd,
+                                      queue);
+       if (!cmd) {
+               mutex_unlock(&ddev->cmd_lock);
+               return;
+       }
+
+       list_del(&cmd->queue);
+
+       mutex_unlock(&ddev->cmd_lock);
+
+       if (!IS_ERR(cmd->resp))
+               print_hex_dump_debug("DIGITAL RX: ", DUMP_PREFIX_NONE, 16, 1,
+                                    cmd->resp->data, cmd->resp->len, false);
+
+       cmd->cmd_cb(ddev, cmd->cb_context, cmd->resp);
+
+       kfree(cmd->mdaa_params);
+       kfree(cmd);
+
+       schedule_work(&ddev->cmd_work);
+}
+
+static void digital_send_cmd_complete(struct nfc_digital_dev *ddev,
+                                     void *arg, struct sk_buff *resp)
+{
+       struct digital_cmd *cmd = arg;
+
+       cmd->resp = resp;
+
+       schedule_work(&ddev->cmd_complete_work);
+}
+
+static void digital_wq_cmd(struct work_struct *work)
+{
+       int rc;
+       struct digital_cmd *cmd;
+       struct digital_tg_mdaa_params *params;
+       struct nfc_digital_dev *ddev = container_of(work,
+                                                   struct nfc_digital_dev,
+                                                   cmd_work);
+
+       mutex_lock(&ddev->cmd_lock);
+
+       cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd,
+                                      queue);
+       if (!cmd || cmd->pending) {
+               mutex_unlock(&ddev->cmd_lock);
+               return;
+       }
+
+       mutex_unlock(&ddev->cmd_lock);
+
+       if (cmd->req)
+               print_hex_dump_debug("DIGITAL TX: ", DUMP_PREFIX_NONE, 16, 1,
+                                    cmd->req->data, cmd->req->len, false);
+
+       switch (cmd->type) {
+       case DIGITAL_CMD_IN_SEND:
+               rc = ddev->ops->in_send_cmd(ddev, cmd->req, cmd->timeout,
+                                           digital_send_cmd_complete, cmd);
+               break;
+
+       case DIGITAL_CMD_TG_SEND:
+               rc = ddev->ops->tg_send_cmd(ddev, cmd->req, cmd->timeout,
+                                           digital_send_cmd_complete, cmd);
+               break;
+
+       case DIGITAL_CMD_TG_LISTEN:
+               rc = ddev->ops->tg_listen(ddev, cmd->timeout,
+                                         digital_send_cmd_complete, cmd);
+               break;
+
+       case DIGITAL_CMD_TG_LISTEN_MDAA:
+               params = cmd->mdaa_params;
+
+               rc = ddev->ops->tg_listen_mdaa(ddev, params, cmd->timeout,
+                                              digital_send_cmd_complete, cmd);
+               break;
+
+       default:
+               pr_err("Unknown cmd type %d\n", cmd->type);
+               return;
+       }
+
+       if (!rc)
+               return;
+
+       pr_err("in_send_command returned err %d\n", rc);
+
+       mutex_lock(&ddev->cmd_lock);
+       list_del(&cmd->queue);
+       mutex_unlock(&ddev->cmd_lock);
+
+       kfree_skb(cmd->req);
+       kfree(cmd->mdaa_params);
+       kfree(cmd);
+
+       schedule_work(&ddev->cmd_work);
+}
+
+int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type,
+                    struct sk_buff *skb, struct digital_tg_mdaa_params *params,
+                    u16 timeout, nfc_digital_cmd_complete_t cmd_cb,
+                    void *cb_context)
+{
+       struct digital_cmd *cmd;
+
+       cmd = kzalloc(sizeof(struct digital_cmd), GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       cmd->type = cmd_type;
+       cmd->timeout = timeout;
+       cmd->req = skb;
+       cmd->mdaa_params = params;
+       cmd->cmd_cb = cmd_cb;
+       cmd->cb_context = cb_context;
+       INIT_LIST_HEAD(&cmd->queue);
+
+       mutex_lock(&ddev->cmd_lock);
+       list_add_tail(&cmd->queue, &ddev->cmd_queue);
+       mutex_unlock(&ddev->cmd_lock);
+
+       schedule_work(&ddev->cmd_work);
+
+       return 0;
+}
+
+int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param)
+{
+       int rc;
+
+       rc = ddev->ops->in_configure_hw(ddev, type, param);
+       if (rc)
+               pr_err("in_configure_hw failed: %d\n", rc);
+
+       return rc;
+}
+
+int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param)
+{
+       int rc;
+
+       rc = ddev->ops->tg_configure_hw(ddev, type, param);
+       if (rc)
+               pr_err("tg_configure_hw failed: %d\n", rc);
+
+       return rc;
+}
+
+static int digital_tg_listen_mdaa(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       struct digital_tg_mdaa_params *params;
+
+       params = kzalloc(sizeof(struct digital_tg_mdaa_params), GFP_KERNEL);
+       if (!params)
+               return -ENOMEM;
+
+       params->sens_res = DIGITAL_SENS_RES_NFC_DEP;
+       get_random_bytes(params->nfcid1, sizeof(params->nfcid1));
+       params->sel_res = DIGITAL_SEL_RES_NFC_DEP;
+
+       params->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1;
+       params->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2;
+       get_random_bytes(params->nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2);
+       params->sc = DIGITAL_SENSF_FELICA_SC;
+
+       return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN_MDAA, NULL, params,
+                               500, digital_tg_recv_atr_req, NULL);
+}
+
+int digital_target_found(struct nfc_digital_dev *ddev,
+                        struct nfc_target *target, u8 protocol)
+{
+       int rc;
+       u8 framing;
+       u8 rf_tech;
+       int (*check_crc)(struct sk_buff *skb);
+       void (*add_crc)(struct sk_buff *skb);
+
+       rf_tech = ddev->poll_techs[ddev->poll_tech_index].rf_tech;
+
+       switch (protocol) {
+       case NFC_PROTO_JEWEL:
+               framing = NFC_DIGITAL_FRAMING_NFCA_T1T;
+               check_crc = digital_skb_check_crc_b;
+               add_crc = digital_skb_add_crc_b;
+               break;
+
+       case NFC_PROTO_MIFARE:
+               framing = NFC_DIGITAL_FRAMING_NFCA_T2T;
+               check_crc = digital_skb_check_crc_a;
+               add_crc = digital_skb_add_crc_a;
+               break;
+
+       case NFC_PROTO_FELICA:
+               framing = NFC_DIGITAL_FRAMING_NFCF_T3T;
+               check_crc = digital_skb_check_crc_f;
+               add_crc = digital_skb_add_crc_f;
+               break;
+
+       case NFC_PROTO_NFC_DEP:
+               if (rf_tech == NFC_DIGITAL_RF_TECH_106A) {
+                       framing = NFC_DIGITAL_FRAMING_NFCA_NFC_DEP;
+                       check_crc = digital_skb_check_crc_a;
+                       add_crc = digital_skb_add_crc_a;
+               } else {
+                       framing = NFC_DIGITAL_FRAMING_NFCF_NFC_DEP;
+                       check_crc = digital_skb_check_crc_f;
+                       add_crc = digital_skb_add_crc_f;
+               }
+               break;
+
+       default:
+               pr_err("Invalid protocol %d\n", protocol);
+               return -EINVAL;
+       }
+
+       pr_debug("rf_tech=%d, protocol=%d\n", rf_tech, protocol);
+
+       ddev->curr_rf_tech = rf_tech;
+       ddev->curr_protocol = protocol;
+
+       if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
+               ddev->skb_add_crc = digital_skb_add_crc_none;
+               ddev->skb_check_crc = digital_skb_check_crc_none;
+       } else {
+               ddev->skb_add_crc = add_crc;
+               ddev->skb_check_crc = check_crc;
+       }
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, framing);
+       if (rc)
+               return rc;
+
+       target->supported_protocols = (1 << protocol);
+       rc = nfc_targets_found(ddev->nfc_dev, target, 1);
+       if (rc)
+               return rc;
+
+       ddev->poll_tech_count = 0;
+
+       return 0;
+}
+
+void digital_poll_next_tech(struct nfc_digital_dev *ddev)
+{
+       digital_switch_rf(ddev, 0);
+
+       mutex_lock(&ddev->poll_lock);
+
+       if (!ddev->poll_tech_count) {
+               mutex_unlock(&ddev->poll_lock);
+               return;
+       }
+
+       ddev->poll_tech_index = (ddev->poll_tech_index + 1) %
+                               ddev->poll_tech_count;
+
+       mutex_unlock(&ddev->poll_lock);
+
+       schedule_work(&ddev->poll_work);
+}
+
+static void digital_wq_poll(struct work_struct *work)
+{
+       int rc;
+       struct digital_poll_tech *poll_tech;
+       struct nfc_digital_dev *ddev = container_of(work,
+                                                   struct nfc_digital_dev,
+                                                   poll_work);
+       mutex_lock(&ddev->poll_lock);
+
+       if (!ddev->poll_tech_count) {
+               mutex_unlock(&ddev->poll_lock);
+               return;
+       }
+
+       poll_tech = &ddev->poll_techs[ddev->poll_tech_index];
+
+       mutex_unlock(&ddev->poll_lock);
+
+       rc = poll_tech->poll_func(ddev, poll_tech->rf_tech);
+       if (rc)
+               digital_poll_next_tech(ddev);
+}
+
+static void digital_add_poll_tech(struct nfc_digital_dev *ddev, u8 rf_tech,
+                                 digital_poll_t poll_func)
+{
+       struct digital_poll_tech *poll_tech;
+
+       if (ddev->poll_tech_count >= NFC_DIGITAL_POLL_MODE_COUNT_MAX)
+               return;
+
+       poll_tech = &ddev->poll_techs[ddev->poll_tech_count++];
+
+       poll_tech->rf_tech = rf_tech;
+       poll_tech->poll_func = poll_func;
+}
+
+/**
+ * start_poll operation
+ *
+ * For every supported protocol, the corresponding polling function is added
+ * to the table of polling technologies (ddev->poll_techs[]) using
+ * digital_add_poll_tech().
+ * When a polling function fails (by timeout or protocol error) the next one is
+ * schedule by digital_poll_next_tech() on the poll workqueue (ddev->poll_work).
+ */
+static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols,
+                             __u32 tm_protocols)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+       u32 matching_im_protocols, matching_tm_protocols;
+
+       pr_debug("protocols: im 0x%x, tm 0x%x, supported 0x%x\n", im_protocols,
+                tm_protocols, ddev->protocols);
+
+       matching_im_protocols = ddev->protocols & im_protocols;
+       matching_tm_protocols = ddev->protocols & tm_protocols;
+
+       if (!matching_im_protocols && !matching_tm_protocols) {
+               pr_err("Unknown protocol\n");
+               return -EINVAL;
+       }
+
+       if (ddev->poll_tech_count) {
+               pr_err("Already polling\n");
+               return -EBUSY;
+       }
+
+       if (ddev->curr_protocol) {
+               pr_err("A target is already active\n");
+               return -EBUSY;
+       }
+
+       ddev->poll_tech_count = 0;
+       ddev->poll_tech_index = 0;
+
+       if (matching_im_protocols & DIGITAL_PROTO_NFCA_RF_TECH)
+               digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A,
+                                     digital_in_send_sens_req);
+
+       if (im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) {
+               digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F,
+                                     digital_in_send_sensf_req);
+
+               digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F,
+                                     digital_in_send_sensf_req);
+       }
+
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               if (ddev->ops->tg_listen_mdaa) {
+                       digital_add_poll_tech(ddev, 0,
+                                             digital_tg_listen_mdaa);
+               } else {
+                       digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A,
+                                             digital_tg_listen_nfca);
+
+                       digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F,
+                                             digital_tg_listen_nfcf);
+
+                       digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F,
+                                             digital_tg_listen_nfcf);
+               }
+       }
+
+       if (!ddev->poll_tech_count) {
+               pr_err("Unsupported protocols: im=0x%x, tm=0x%x\n",
+                      matching_im_protocols, matching_tm_protocols);
+               return -EINVAL;
+       }
+
+       schedule_work(&ddev->poll_work);
+
+       return 0;
+}
+
+static void digital_stop_poll(struct nfc_dev *nfc_dev)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       mutex_lock(&ddev->poll_lock);
+
+       if (!ddev->poll_tech_count) {
+               pr_err("Polling operation was not running\n");
+               mutex_unlock(&ddev->poll_lock);
+               return;
+       }
+
+       ddev->poll_tech_count = 0;
+
+       mutex_unlock(&ddev->poll_lock);
+
+       cancel_work_sync(&ddev->poll_work);
+
+       digital_abort_cmd(ddev);
+}
+
+static int digital_dev_up(struct nfc_dev *nfc_dev)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       digital_switch_rf(ddev, 1);
+
+       return 0;
+}
+
+static int digital_dev_down(struct nfc_dev *nfc_dev)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       digital_switch_rf(ddev, 0);
+
+       return 0;
+}
+
+static int digital_dep_link_up(struct nfc_dev *nfc_dev,
+                              struct nfc_target *target,
+                              __u8 comm_mode, __u8 *gb, size_t gb_len)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       return digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len);
+}
+
+static int digital_dep_link_down(struct nfc_dev *nfc_dev)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       ddev->curr_protocol = 0;
+
+       return 0;
+}
+
+static int digital_activate_target(struct nfc_dev *nfc_dev,
+                                  struct nfc_target *target, __u32 protocol)
+{
+       return 0;
+}
+
+static void digital_deactivate_target(struct nfc_dev *nfc_dev,
+                                     struct nfc_target *target)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       ddev->curr_protocol = 0;
+}
+
+static int digital_tg_send(struct nfc_dev *dev, struct sk_buff *skb)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(dev);
+
+       return digital_tg_send_dep_res(ddev, skb);
+}
+
+static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg,
+                                    struct sk_buff *resp)
+{
+       struct digital_data_exch *data_exch = arg;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               goto done;
+       }
+
+       if (ddev->curr_protocol == NFC_PROTO_MIFARE)
+               rc = digital_in_recv_mifare_res(resp);
+       else
+               rc = ddev->skb_check_crc(resp);
+
+       if (rc) {
+               kfree_skb(resp);
+               resp = NULL;
+       }
+
+done:
+       data_exch->cb(data_exch->cb_context, resp, rc);
+
+       kfree(data_exch);
+}
+
+static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                          struct sk_buff *skb, data_exchange_cb_t cb,
+                          void *cb_context)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+       struct digital_data_exch *data_exch;
+
+       data_exch = kzalloc(sizeof(struct digital_data_exch), GFP_KERNEL);
+       if (!data_exch) {
+               pr_err("Failed to allocate data_exch struct\n");
+               return -ENOMEM;
+       }
+
+       data_exch->cb = cb;
+       data_exch->cb_context = cb_context;
+
+       if (ddev->curr_protocol == NFC_PROTO_NFC_DEP)
+               return digital_in_send_dep_req(ddev, target, skb, data_exch);
+
+       ddev->skb_add_crc(skb);
+
+       return digital_in_send_cmd(ddev, skb, 500, digital_in_send_complete,
+                                  data_exch);
+}
+
+static struct nfc_ops digital_nfc_ops = {
+       .dev_up = digital_dev_up,
+       .dev_down = digital_dev_down,
+       .start_poll = digital_start_poll,
+       .stop_poll = digital_stop_poll,
+       .dep_link_up = digital_dep_link_up,
+       .dep_link_down = digital_dep_link_down,
+       .activate_target = digital_activate_target,
+       .deactivate_target = digital_deactivate_target,
+       .tm_send = digital_tg_send,
+       .im_transceive = digital_in_send,
+};
+
+struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops,
+                                           __u32 supported_protocols,
+                                           __u32 driver_capabilities,
+                                           int tx_headroom, int tx_tailroom)
+{
+       struct nfc_digital_dev *ddev;
+
+       if (!ops->in_configure_hw || !ops->in_send_cmd || !ops->tg_listen ||
+           !ops->tg_configure_hw || !ops->tg_send_cmd || !ops->abort_cmd ||
+           !ops->switch_rf)
+               return NULL;
+
+       ddev = kzalloc(sizeof(struct nfc_digital_dev), GFP_KERNEL);
+       if (!ddev)
+               return NULL;
+
+       ddev->driver_capabilities = driver_capabilities;
+       ddev->ops = ops;
+
+       mutex_init(&ddev->cmd_lock);
+       INIT_LIST_HEAD(&ddev->cmd_queue);
+
+       INIT_WORK(&ddev->cmd_work, digital_wq_cmd);
+       INIT_WORK(&ddev->cmd_complete_work, digital_wq_cmd_complete);
+
+       mutex_init(&ddev->poll_lock);
+       INIT_WORK(&ddev->poll_work, digital_wq_poll);
+
+       if (supported_protocols & NFC_PROTO_JEWEL_MASK)
+               ddev->protocols |= NFC_PROTO_JEWEL_MASK;
+       if (supported_protocols & NFC_PROTO_MIFARE_MASK)
+               ddev->protocols |= NFC_PROTO_MIFARE_MASK;
+       if (supported_protocols & NFC_PROTO_FELICA_MASK)
+               ddev->protocols |= NFC_PROTO_FELICA_MASK;
+       if (supported_protocols & NFC_PROTO_NFC_DEP_MASK)
+               ddev->protocols |= NFC_PROTO_NFC_DEP_MASK;
+
+       ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN;
+       ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN;
+
+       ddev->nfc_dev = nfc_allocate_device(&digital_nfc_ops, ddev->protocols,
+                                           ddev->tx_headroom,
+                                           ddev->tx_tailroom);
+       if (!ddev->nfc_dev) {
+               pr_err("nfc_allocate_device failed\n");
+               goto free_dev;
+       }
+
+       nfc_set_drvdata(ddev->nfc_dev, ddev);
+
+       return ddev;
+
+free_dev:
+       kfree(ddev);
+
+       return NULL;
+}
+EXPORT_SYMBOL(nfc_digital_allocate_device);
+
+void nfc_digital_free_device(struct nfc_digital_dev *ddev)
+{
+       nfc_free_device(ddev->nfc_dev);
+       kfree(ddev);
+}
+EXPORT_SYMBOL(nfc_digital_free_device);
+
+int nfc_digital_register_device(struct nfc_digital_dev *ddev)
+{
+       return nfc_register_device(ddev->nfc_dev);
+}
+EXPORT_SYMBOL(nfc_digital_register_device);
+
+void nfc_digital_unregister_device(struct nfc_digital_dev *ddev)
+{
+       struct digital_cmd *cmd, *n;
+
+       nfc_unregister_device(ddev->nfc_dev);
+
+       mutex_lock(&ddev->poll_lock);
+       ddev->poll_tech_count = 0;
+       mutex_unlock(&ddev->poll_lock);
+
+       cancel_work_sync(&ddev->poll_work);
+       cancel_work_sync(&ddev->cmd_work);
+       cancel_work_sync(&ddev->cmd_complete_work);
+
+       list_for_each_entry_safe(cmd, n, &ddev->cmd_queue, queue) {
+               list_del(&cmd->queue);
+               kfree(cmd->mdaa_params);
+               kfree(cmd);
+       }
+}
+EXPORT_SYMBOL(nfc_digital_unregister_device);
+
+MODULE_LICENSE("GPL");
diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c
new file mode 100644 (file)
index 0000000..07bbc24
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#define pr_fmt(fmt) "digital: %s: " fmt, __func__
+
+#include "digital.h"
+
+#define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4
+#define DIGITAL_NFC_DEP_FRAME_DIR_IN  0xD5
+
+#define DIGITAL_NFC_DEP_NFCA_SOD_SB   0xF0
+
+#define DIGITAL_CMD_ATR_REQ 0x00
+#define DIGITAL_CMD_ATR_RES 0x01
+#define DIGITAL_CMD_PSL_REQ 0x04
+#define DIGITAL_CMD_PSL_RES 0x05
+#define DIGITAL_CMD_DEP_REQ 0x06
+#define DIGITAL_CMD_DEP_RES 0x07
+
+#define DIGITAL_ATR_REQ_MIN_SIZE 16
+#define DIGITAL_ATR_REQ_MAX_SIZE 64
+
+#define DIGITAL_NFCID3_LEN ((u8)8)
+#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30
+#define DIGITAL_GB_BIT 0x02
+
+#define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
+
+#define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10
+
+#define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
+                               ((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT)
+#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb)  ((pfb) & 0x10)
+#define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
+#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
+#define DIGITAL_NFC_DEP_PFB_PNI(pfb)     ((pfb) & 0x03)
+
+#define DIGITAL_NFC_DEP_PFB_I_PDU          0x00
+#define DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU   0x40
+#define DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU 0x80
+
+struct digital_atr_req {
+       u8 dir;
+       u8 cmd;
+       u8 nfcid3[10];
+       u8 did;
+       u8 bs;
+       u8 br;
+       u8 pp;
+       u8 gb[0];
+} __packed;
+
+struct digital_atr_res {
+       u8 dir;
+       u8 cmd;
+       u8 nfcid3[10];
+       u8 did;
+       u8 bs;
+       u8 br;
+       u8 to;
+       u8 pp;
+       u8 gb[0];
+} __packed;
+
+struct digital_psl_req {
+       u8 dir;
+       u8 cmd;
+       u8 did;
+       u8 brs;
+       u8 fsl;
+} __packed;
+
+struct digital_psl_res {
+       u8 dir;
+       u8 cmd;
+       u8 did;
+} __packed;
+
+struct digital_dep_req_res {
+       u8 dir;
+       u8 cmd;
+       u8 pfb;
+} __packed;
+
+static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp);
+
+static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev,
+                                    struct sk_buff *skb)
+{
+       skb_push(skb, sizeof(u8));
+
+       skb->data[0] = skb->len;
+
+       if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A)
+               *skb_push(skb, sizeof(u8)) = DIGITAL_NFC_DEP_NFCA_SOD_SB;
+}
+
+static int digital_skb_pull_dep_sod(struct nfc_digital_dev *ddev,
+                                   struct sk_buff *skb)
+{
+       u8 size;
+
+       if (skb->len < 2)
+               return -EIO;
+
+       if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A)
+               skb_pull(skb, sizeof(u8));
+
+       size = skb->data[0];
+       if (size != skb->len)
+               return -EIO;
+
+       skb_pull(skb, sizeof(u8));
+
+       return 0;
+}
+
+static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
+                                struct sk_buff *resp)
+{
+       struct nfc_target *target = arg;
+       struct digital_atr_res *atr_res;
+       u8 gb_len;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       if (resp->len < sizeof(struct digital_atr_res)) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       gb_len = resp->len - sizeof(struct digital_atr_res);
+
+       atr_res = (struct digital_atr_res *)resp->data;
+
+       rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len);
+       if (rc)
+               goto exit;
+
+       rc = nfc_dep_link_is_up(ddev->nfc_dev, target->idx, NFC_COMM_ACTIVE,
+                               NFC_RF_INITIATOR);
+
+       ddev->curr_nfc_dep_pni = 0;
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc)
+               ddev->curr_protocol = 0;
+}
+
+int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
+                           struct nfc_target *target, __u8 comm_mode, __u8 *gb,
+                           size_t gb_len)
+{
+       struct sk_buff *skb;
+       struct digital_atr_req *atr_req;
+       uint size;
+
+       size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len;
+
+       if (size > DIGITAL_ATR_REQ_MAX_SIZE) {
+               PROTOCOL_ERR("14.6.1.1");
+               return -EINVAL;
+       }
+
+       skb = digital_skb_alloc(ddev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_atr_req));
+
+       atr_req = (struct digital_atr_req *)skb->data;
+       memset(atr_req, 0, sizeof(struct digital_atr_req));
+
+       atr_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       atr_req->cmd = DIGITAL_CMD_ATR_REQ;
+       if (target->nfcid2_len)
+               memcpy(atr_req->nfcid3, target->nfcid2,
+                      max(target->nfcid2_len, DIGITAL_NFCID3_LEN));
+       else
+               get_random_bytes(atr_req->nfcid3, DIGITAL_NFCID3_LEN);
+
+       atr_req->did = 0;
+       atr_req->bs = 0;
+       atr_req->br = 0;
+
+       atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+
+       if (gb_len) {
+               atr_req->pp |= DIGITAL_GB_BIT;
+               memcpy(skb_put(skb, gb_len), gb, gb_len);
+       }
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, target);
+
+       return 0;
+}
+
+static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
+                               struct digital_data_exch *data_exch, u8 rtox)
+{
+       struct digital_dep_req_res *dep_req;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = rtox;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_req = (struct digital_dep_req_res *)skb->data;
+
+       dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+       dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU |
+                      DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+                                data_exch);
+
+       return rc;
+}
+
+static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       struct digital_data_exch *data_exch = arg;
+       struct digital_dep_req_res *dep_res;
+       u8 pfb;
+       uint size;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto error;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       dep_res = (struct digital_dep_req_res *)resp->data;
+
+       if (resp->len < sizeof(struct digital_dep_req_res) ||
+           dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
+           dep_res->cmd != DIGITAL_CMD_DEP_RES) {
+               rc = -EIO;
+               goto error;
+       }
+
+       pfb = dep_res->pfb;
+
+       switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
+       case DIGITAL_NFC_DEP_PFB_I_PDU:
+               if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
+                       PROTOCOL_ERR("14.12.3.3");
+                       rc = -EIO;
+                       goto error;
+               }
+
+               ddev->curr_nfc_dep_pni =
+                       DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+               rc = 0;
+               break;
+
+       case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
+               pr_err("Received a ACK/NACK PDU\n");
+               rc = -EIO;
+               goto error;
+
+       case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
+               if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
+                       rc = -EINVAL;
+                       goto error;
+               }
+
+               rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]);
+               if (rc)
+                       goto error;
+
+               kfree_skb(resp);
+               return;
+       }
+
+       if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
+               pr_err("MI bit set. Chained PDU not supported\n");
+               rc = -EIO;
+               goto error;
+       }
+
+       size = sizeof(struct digital_dep_req_res);
+
+       if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb))
+               size++;
+
+       if (size > resp->len) {
+               rc = -EIO;
+               goto error;
+       }
+
+       skb_pull(resp, size);
+
+exit:
+       data_exch->cb(data_exch->cb_context, resp, rc);
+
+error:
+       kfree(data_exch);
+
+       if (rc)
+               kfree_skb(resp);
+}
+
+int digital_in_send_dep_req(struct nfc_digital_dev *ddev,
+                           struct nfc_target *target, struct sk_buff *skb,
+                           struct digital_data_exch *data_exch)
+{
+       struct digital_dep_req_res *dep_req;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_req = (struct digital_dep_req_res *)skb->data;
+       dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+       dep_req->pfb = ddev->curr_nfc_dep_pni;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+                                  data_exch);
+}
+
+static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       int rc;
+       struct digital_dep_req_res *dep_req;
+       size_t size;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       size = sizeof(struct digital_dep_req_res);
+       dep_req = (struct digital_dep_req_res *)resp->data;
+
+       if (resp->len < size || dep_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
+           dep_req->cmd != DIGITAL_CMD_DEP_REQ) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb))
+               size++;
+
+       if (resp->len < size) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
+       case DIGITAL_NFC_DEP_PFB_I_PDU:
+               pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n");
+               ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb);
+               break;
+       case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
+               pr_err("Received a ACK/NACK PDU\n");
+               rc = -EINVAL;
+               goto exit;
+               break;
+       case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
+               pr_err("Received a SUPERVISOR PDU\n");
+               rc = -EINVAL;
+               goto exit;
+               break;
+       }
+
+       skb_pull(resp, size);
+
+       rc = nfc_tm_data_received(ddev->nfc_dev, resp);
+
+exit:
+       if (rc)
+               kfree_skb(resp);
+}
+
+int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb)
+{
+       struct digital_dep_req_res *dep_res;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+       dep_res = (struct digital_dep_req_res *)skb->data;
+
+       dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+       dep_res->cmd = DIGITAL_CMD_DEP_RES;
+       dep_res->pfb = ddev->curr_nfc_dep_pni;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+                                  NULL);
+}
+
+static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev,
+                                            void *arg, struct sk_buff *resp)
+{
+       u8 rf_tech = PTR_ERR(arg);
+
+       if (IS_ERR(resp))
+               return;
+
+       digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
+
+       digital_tg_listen(ddev, 1500, digital_tg_recv_dep_req, NULL);
+
+       dev_kfree_skb(resp);
+}
+
+static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did,
+                                  u8 rf_tech)
+{
+       struct digital_psl_res *psl_res;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, sizeof(struct digital_psl_res));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_psl_res));
+
+       psl_res = (struct digital_psl_res *)skb->data;
+
+       psl_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+       psl_res->cmd = DIGITAL_CMD_PSL_RES;
+       psl_res->did = did;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete,
+                                ERR_PTR(rf_tech));
+
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       int rc;
+       struct digital_psl_req *psl_req;
+       u8 rf_tech;
+       u8 dsi;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       psl_req = (struct digital_psl_req *)resp->data;
+
+       if (resp->len != sizeof(struct digital_psl_req) ||
+           psl_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
+           psl_req->cmd != DIGITAL_CMD_PSL_REQ) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       dsi = (psl_req->brs >> 3) & 0x07;
+       switch (dsi) {
+       case 0:
+               rf_tech = NFC_DIGITAL_RF_TECH_106A;
+               break;
+       case 1:
+               rf_tech = NFC_DIGITAL_RF_TECH_212F;
+               break;
+       case 2:
+               rf_tech = NFC_DIGITAL_RF_TECH_424F;
+               break;
+       default:
+               pr_err("Unsuported dsi value %d\n", dsi);
+               goto exit;
+       }
+
+       rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech);
+
+exit:
+       kfree_skb(resp);
+}
+
+static void digital_tg_send_atr_res_complete(struct nfc_digital_dev *ddev,
+                                            void *arg, struct sk_buff *resp)
+{
+       int offset;
+
+       if (IS_ERR(resp)) {
+               digital_poll_next_tech(ddev);
+               return;
+       }
+
+       offset = 2;
+       if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB)
+               offset++;
+
+       if (resp->data[offset] == DIGITAL_CMD_PSL_REQ)
+               digital_tg_recv_psl_req(ddev, arg, resp);
+       else
+               digital_tg_recv_dep_req(ddev, arg, resp);
+}
+
+static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
+                                  struct digital_atr_req *atr_req)
+{
+       struct digital_atr_res *atr_res;
+       struct sk_buff *skb;
+       u8 *gb;
+       size_t gb_len;
+       int rc;
+
+       gb = nfc_get_local_general_bytes(ddev->nfc_dev, &gb_len);
+       if (!gb)
+               gb_len = 0;
+
+       skb = digital_skb_alloc(ddev, sizeof(struct digital_atr_res) + gb_len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_atr_res));
+       atr_res = (struct digital_atr_res *)skb->data;
+
+       memset(atr_res, 0, sizeof(struct digital_atr_res));
+
+       atr_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+       atr_res->cmd = DIGITAL_CMD_ATR_RES;
+       memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3));
+       atr_res->to = 8;
+       atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+       if (gb_len) {
+               skb_put(skb, gb_len);
+
+               atr_res->pp |= DIGITAL_GB_BIT;
+               memcpy(atr_res->gb, gb, gb_len);
+       }
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 999,
+                                digital_tg_send_atr_res_complete, NULL);
+       if (rc) {
+               kfree_skb(skb);
+               return rc;
+       }
+
+       return rc;
+}
+
+void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
+                            struct sk_buff *resp)
+{
+       int rc;
+       struct digital_atr_req *atr_req;
+       size_t gb_len, min_size;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (!resp->len) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) {
+               min_size = DIGITAL_ATR_REQ_MIN_SIZE + 2;
+
+               ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_106A;
+               ddev->skb_add_crc = digital_skb_add_crc_a;
+               ddev->skb_check_crc = digital_skb_check_crc_a;
+       } else {
+               min_size = DIGITAL_ATR_REQ_MIN_SIZE + 1;
+
+               ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_212F;
+               ddev->skb_add_crc = digital_skb_add_crc_f;
+               ddev->skb_check_crc = digital_skb_check_crc_f;
+       }
+
+       if (resp->len < min_size) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) {
+               ddev->skb_add_crc = digital_skb_add_crc_none;
+               ddev->skb_check_crc = digital_skb_check_crc_none;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       atr_req = (struct digital_atr_req *)resp->data;
+
+       if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
+           atr_req->cmd != DIGITAL_CMD_ATR_REQ) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED);
+       if (rc)
+               goto exit;
+
+       rc = digital_tg_send_atr_res(ddev, atr_req);
+       if (rc)
+               goto exit;
+
+       gb_len = resp->len - sizeof(struct digital_atr_req);
+       rc = nfc_tm_activated(ddev->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
+                             NFC_COMM_PASSIVE, atr_req->gb, gb_len);
+       if (rc)
+               goto exit;
+
+       ddev->poll_tech_count = 0;
+
+       rc = 0;
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c
new file mode 100644 (file)
index 0000000..251c8c7
--- /dev/null
@@ -0,0 +1,770 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#define pr_fmt(fmt) "digital: %s: " fmt, __func__
+
+#include "digital.h"
+
+#define DIGITAL_CMD_SENS_REQ    0x26
+#define DIGITAL_CMD_ALL_REQ     0x52
+#define DIGITAL_CMD_SEL_REQ_CL1 0x93
+#define DIGITAL_CMD_SEL_REQ_CL2 0x95
+#define DIGITAL_CMD_SEL_REQ_CL3 0x97
+
+#define DIGITAL_SDD_REQ_SEL_PAR 0x20
+
+#define DIGITAL_SDD_RES_CT  0x88
+#define DIGITAL_SDD_RES_LEN 5
+
+#define DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res) (!((sel_res) & 0x04))
+#define DIGITAL_SEL_RES_IS_T2T(sel_res) (!((sel_res) & 0x60))
+#define DIGITAL_SEL_RES_IS_NFC_DEP(sel_res) ((sel_res) & 0x40)
+
+#define DIGITAL_SENS_RES_IS_T1T(sens_res) (((sens_res) & 0x0C00) == 0x0C00)
+#define DIGITAL_SENS_RES_IS_VALID(sens_res) \
+       ((!((sens_res) & 0x001F) && (((sens_res) & 0x0C00) == 0x0C00)) || \
+       (((sens_res) & 0x001F) && ((sens_res) & 0x0C00) != 0x0C00))
+
+#define DIGITAL_MIFARE_READ_RES_LEN 16
+#define DIGITAL_MIFARE_ACK_RES 0x0A
+
+#define DIGITAL_CMD_SENSF_REQ  0x00
+#define DIGITAL_CMD_SENSF_RES  0x01
+
+#define DIGITAL_SENSF_RES_MIN_LENGTH 17
+#define DIGITAL_SENSF_RES_RD_AP_B1   0x00
+#define DIGITAL_SENSF_RES_RD_AP_B2   0x8F
+
+#define DIGITAL_SENSF_REQ_RC_NONE 0
+#define DIGITAL_SENSF_REQ_RC_SC   1
+#define DIGITAL_SENSF_REQ_RC_AP   2
+
+struct digital_sdd_res {
+       u8 nfcid1[4];
+       u8 bcc;
+} __packed;
+
+struct digital_sel_req {
+       u8 sel_cmd;
+       u8 b2;
+       u8 nfcid1[4];
+       u8 bcc;
+} __packed;
+
+struct digital_sensf_req {
+       u8 cmd;
+       u8 sc1;
+       u8 sc2;
+       u8 rc;
+       u8 tsn;
+} __packed;
+
+struct digital_sensf_res {
+       u8 cmd;
+       u8 nfcid2[8];
+       u8 pad0[2];
+       u8 pad1[3];
+       u8 mrti_check;
+       u8 mrti_update;
+       u8 pad2;
+       u8 rd[2];
+} __packed;
+
+static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev,
+                                  struct nfc_target *target);
+
+static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       struct nfc_target *target = arg;
+       int rc;
+       u8 sel_res;
+       u8 nfc_proto;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
+               rc = digital_skb_check_crc_a(resp);
+               if (rc) {
+                       PROTOCOL_ERR("4.4.1.3");
+                       goto exit;
+               }
+       }
+
+       if (!resp->len) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       sel_res = resp->data[0];
+
+       if (!DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res)) {
+               rc = digital_in_send_sdd_req(ddev, target);
+               if (rc)
+                       goto exit;
+
+               goto exit_free_skb;
+       }
+
+       if (DIGITAL_SEL_RES_IS_T2T(sel_res)) {
+               nfc_proto = NFC_PROTO_MIFARE;
+       } else if (DIGITAL_SEL_RES_IS_NFC_DEP(sel_res)) {
+               nfc_proto = NFC_PROTO_NFC_DEP;
+       } else {
+               rc = -EOPNOTSUPP;
+               goto exit;
+       }
+
+       target->sel_res = sel_res;
+
+       rc = digital_target_found(ddev, target, nfc_proto);
+
+exit:
+       kfree(target);
+
+exit_free_skb:
+       dev_kfree_skb(resp);
+
+       if (rc)
+               digital_poll_next_tech(ddev);
+}
+
+static int digital_in_send_sel_req(struct nfc_digital_dev *ddev,
+                                  struct nfc_target *target,
+                                  struct digital_sdd_res *sdd_res)
+{
+       struct sk_buff *skb;
+       struct digital_sel_req *sel_req;
+       u8 sel_cmd;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, sizeof(struct digital_sel_req));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_sel_req));
+       sel_req = (struct digital_sel_req *)skb->data;
+
+       if (target->nfcid1_len <= 4)
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL1;
+       else if (target->nfcid1_len < 10)
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL2;
+       else
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL3;
+
+       sel_req->sel_cmd = sel_cmd;
+       sel_req->b2 = 0x70;
+       memcpy(sel_req->nfcid1, sdd_res->nfcid1, 4);
+       sel_req->bcc = sdd_res->bcc;
+
+       if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
+               rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                               NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A);
+               if (rc)
+                       goto exit;
+       } else {
+               digital_skb_add_crc_a(skb);
+       }
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sel_res,
+                                target);
+exit:
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_in_recv_sdd_res(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       struct nfc_target *target = arg;
+       struct digital_sdd_res *sdd_res;
+       int rc;
+       u8 offset, size;
+       u8 i, bcc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len < DIGITAL_SDD_RES_LEN) {
+               PROTOCOL_ERR("4.7.2.8");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       sdd_res = (struct digital_sdd_res *)resp->data;
+
+       for (i = 0, bcc = 0; i < 4; i++)
+               bcc ^= sdd_res->nfcid1[i];
+
+       if (bcc != sdd_res->bcc) {
+               PROTOCOL_ERR("4.7.2.6");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       if (sdd_res->nfcid1[0] == DIGITAL_SDD_RES_CT) {
+               offset = 1;
+               size = 3;
+       } else {
+               offset = 0;
+               size = 4;
+       }
+
+       memcpy(target->nfcid1 + target->nfcid1_len, sdd_res->nfcid1 + offset,
+              size);
+       target->nfcid1_len += size;
+
+       rc = digital_in_send_sel_req(ddev, target, sdd_res);
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc) {
+               kfree(target);
+               digital_poll_next_tech(ddev);
+       }
+}
+
+static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev,
+                                  struct nfc_target *target)
+{
+       int rc;
+       struct sk_buff *skb;
+       u8 sel_cmd;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCA_STANDARD);
+       if (rc)
+               return rc;
+
+       skb = digital_skb_alloc(ddev, 2);
+       if (!skb)
+               return -ENOMEM;
+
+       if (target->nfcid1_len == 0)
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL1;
+       else if (target->nfcid1_len == 3)
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL2;
+       else
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL3;
+
+       *skb_put(skb, sizeof(u8)) = sel_cmd;
+       *skb_put(skb, sizeof(u8)) = DIGITAL_SDD_REQ_SEL_PAR;
+
+       return digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sdd_res,
+                                  target);
+}
+
+static void digital_in_recv_sens_res(struct nfc_digital_dev *ddev, void *arg,
+                                    struct sk_buff *resp)
+{
+       struct nfc_target *target = NULL;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len < sizeof(u16)) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       target = kzalloc(sizeof(struct nfc_target), GFP_KERNEL);
+       if (!target) {
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       target->sens_res = __le16_to_cpu(*(__le16 *)resp->data);
+
+       if (!DIGITAL_SENS_RES_IS_VALID(target->sens_res)) {
+               PROTOCOL_ERR("4.6.3.3");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       if (DIGITAL_SENS_RES_IS_T1T(target->sens_res))
+               rc = digital_target_found(ddev, target, NFC_PROTO_JEWEL);
+       else
+               rc = digital_in_send_sdd_req(ddev, target);
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc) {
+               kfree(target);
+               digital_poll_next_tech(ddev);
+       }
+}
+
+int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       struct sk_buff *skb;
+       int rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+                                    NFC_DIGITAL_RF_TECH_106A);
+       if (rc)
+               return rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCA_SHORT);
+       if (rc)
+               return rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, sizeof(u8)) = DIGITAL_CMD_SENS_REQ;
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sens_res, NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+int digital_in_recv_mifare_res(struct sk_buff *resp)
+{
+       /* Successful READ command response is 16 data bytes + 2 CRC bytes long.
+        * Since the driver can't differentiate a ACK/NACK response from a valid
+        * READ response, the CRC calculation must be handled at digital level
+        * even if the driver supports it for this technology.
+        */
+       if (resp->len == DIGITAL_MIFARE_READ_RES_LEN + DIGITAL_CRC_LEN) {
+               if (digital_skb_check_crc_a(resp)) {
+                       PROTOCOL_ERR("9.4.1.2");
+                       return -EIO;
+               }
+
+               return 0;
+       }
+
+       /* ACK response (i.e. successful WRITE). */
+       if (resp->len == 1 && resp->data[0] == DIGITAL_MIFARE_ACK_RES) {
+               resp->data[0] = 0;
+               return 0;
+       }
+
+       /* NACK and any other responses are treated as error. */
+       return -EIO;
+}
+
+static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev, void *arg,
+                                  struct sk_buff *resp)
+{
+       int rc;
+       u8 proto;
+       struct nfc_target target;
+       struct digital_sensf_res *sensf_res;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len < DIGITAL_SENSF_RES_MIN_LENGTH) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
+               rc = digital_skb_check_crc_f(resp);
+               if (rc) {
+                       PROTOCOL_ERR("6.4.1.8");
+                       goto exit;
+               }
+       }
+
+       skb_pull(resp, 1);
+
+       memset(&target, 0, sizeof(struct nfc_target));
+
+       sensf_res = (struct digital_sensf_res *)resp->data;
+
+       memcpy(target.sensf_res, sensf_res, resp->len);
+       target.sensf_res_len = resp->len;
+
+       memcpy(target.nfcid2, sensf_res->nfcid2, NFC_NFCID2_MAXSIZE);
+       target.nfcid2_len = NFC_NFCID2_MAXSIZE;
+
+       if (target.nfcid2[0] == DIGITAL_SENSF_NFCID2_NFC_DEP_B1 &&
+           target.nfcid2[1] == DIGITAL_SENSF_NFCID2_NFC_DEP_B2)
+               proto = NFC_PROTO_NFC_DEP;
+       else
+               proto = NFC_PROTO_FELICA;
+
+       rc = digital_target_found(ddev, &target, proto);
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc)
+               digital_poll_next_tech(ddev);
+}
+
+int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       struct digital_sensf_req *sensf_req;
+       struct sk_buff *skb;
+       int rc;
+       u8 size;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
+       if (rc)
+               return rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCF);
+       if (rc)
+               return rc;
+
+       size = sizeof(struct digital_sensf_req);
+
+       skb = digital_skb_alloc(ddev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, size);
+
+       sensf_req = (struct digital_sensf_req *)skb->data;
+       sensf_req->cmd = DIGITAL_CMD_SENSF_REQ;
+       sensf_req->sc1 = 0xFF;
+       sensf_req->sc2 = 0xFF;
+       sensf_req->rc = 0;
+       sensf_req->tsn = 0;
+
+       *skb_push(skb, 1) = size + 1;
+
+       if (!DIGITAL_DRV_CAPS_IN_CRC(ddev))
+               digital_skb_add_crc_f(skb);
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sensf_res,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static int digital_tg_send_sel_res(struct nfc_digital_dev *ddev)
+{
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = DIGITAL_SEL_RES_NFC_DEP;
+
+       if (!DIGITAL_DRV_CAPS_TG_CRC(ddev))
+               digital_skb_add_crc_a(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_atr_req,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_tg_recv_sel_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) {
+               rc = digital_skb_check_crc_a(resp);
+               if (rc) {
+                       PROTOCOL_ERR("4.4.1.3");
+                       goto exit;
+               }
+       }
+
+       /* Silently ignore SEL_REQ content and send a SEL_RES for NFC-DEP */
+
+       rc = digital_tg_send_sel_res(ddev);
+
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
+
+static int digital_tg_send_sdd_res(struct nfc_digital_dev *ddev)
+{
+       struct sk_buff *skb;
+       struct digital_sdd_res *sdd_res;
+       int rc, i;
+
+       skb = digital_skb_alloc(ddev, sizeof(struct digital_sdd_res));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_sdd_res));
+       sdd_res = (struct digital_sdd_res *)skb->data;
+
+       sdd_res->nfcid1[0] = 0x08;
+       get_random_bytes(sdd_res->nfcid1 + 1, 3);
+
+       sdd_res->bcc = 0;
+       for (i = 0; i < 4; i++)
+               sdd_res->bcc ^= sdd_res->nfcid1[i];
+
+       rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sel_req,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_tg_recv_sdd_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       u8 *sdd_req;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       sdd_req = resp->data;
+
+       if (resp->len < 2 || sdd_req[0] != DIGITAL_CMD_SEL_REQ_CL1 ||
+           sdd_req[1] != DIGITAL_SDD_REQ_SEL_PAR) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = digital_tg_send_sdd_res(ddev);
+
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
+
+static int digital_tg_send_sens_res(struct nfc_digital_dev *ddev)
+{
+       struct sk_buff *skb;
+       u8 *sens_res;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 2);
+       if (!skb)
+               return -ENOMEM;
+
+       sens_res = skb_put(skb, 2);
+
+       sens_res[0] = (DIGITAL_SENS_RES_NFC_DEP >> 8) & 0xFF;
+       sens_res[1] = DIGITAL_SENS_RES_NFC_DEP & 0xFF;
+
+       rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sdd_req,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg,
+                             struct sk_buff *resp)
+{
+       u8 sens_req;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       sens_req = resp->data[0];
+
+       if (!resp->len || (sens_req != DIGITAL_CMD_SENS_REQ &&
+           sens_req != DIGITAL_CMD_ALL_REQ)) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = digital_tg_send_sens_res(ddev);
+
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
+
+static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev,
+                             struct digital_sensf_req *sensf_req)
+{
+       struct sk_buff *skb;
+       u8 size;
+       int rc;
+       struct digital_sensf_res *sensf_res;
+
+       size = sizeof(struct digital_sensf_res);
+
+       if (sensf_req->rc != DIGITAL_SENSF_REQ_RC_NONE)
+               size -= sizeof(sensf_res->rd);
+
+       skb = digital_skb_alloc(ddev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, size);
+
+       sensf_res = (struct digital_sensf_res *)skb->data;
+
+       memset(sensf_res, 0, size);
+
+       sensf_res->cmd = DIGITAL_CMD_SENSF_RES;
+       sensf_res->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1;
+       sensf_res->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2;
+       get_random_bytes(&sensf_res->nfcid2[2], 6);
+
+       switch (sensf_req->rc) {
+       case DIGITAL_SENSF_REQ_RC_SC:
+               sensf_res->rd[0] = sensf_req->sc1;
+               sensf_res->rd[1] = sensf_req->sc2;
+               break;
+       case DIGITAL_SENSF_REQ_RC_AP:
+               sensf_res->rd[0] = DIGITAL_SENSF_RES_RD_AP_B1;
+               sensf_res->rd[1] = DIGITAL_SENSF_RES_RD_AP_B2;
+               break;
+       }
+
+       *skb_push(skb, sizeof(u8)) = size + 1;
+
+       if (!DIGITAL_DRV_CAPS_TG_CRC(ddev))
+               digital_skb_add_crc_f(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 300,
+                                digital_tg_recv_atr_req, NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+void digital_tg_recv_sensf_req(struct nfc_digital_dev *ddev, void *arg,
+                              struct sk_buff *resp)
+{
+       struct digital_sensf_req *sensf_req;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) {
+               rc = digital_skb_check_crc_f(resp);
+               if (rc) {
+                       PROTOCOL_ERR("6.4.1.8");
+                       goto exit;
+               }
+       }
+
+       if (resp->len != sizeof(struct digital_sensf_req) + 1) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       skb_pull(resp, 1);
+       sensf_req = (struct digital_sensf_req *)resp->data;
+
+       if (sensf_req->cmd != DIGITAL_CMD_SENSF_REQ) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = digital_tg_send_sensf_res(ddev, sensf_req);
+
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
+
+int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       int rc;
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
+       if (rc)
+               return rc;
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCA_NFC_DEP);
+       if (rc)
+               return rc;
+
+       return digital_tg_listen(ddev, 300, digital_tg_recv_sens_req, NULL);
+}
+
+int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       int rc;
+       u8 *nfcid2;
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
+       if (rc)
+               return rc;
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCF_NFC_DEP);
+       if (rc)
+               return rc;
+
+       nfcid2 = kzalloc(NFC_NFCID2_MAXSIZE, GFP_KERNEL);
+       if (!nfcid2)
+               return -ENOMEM;
+
+       nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1;
+       nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2;
+       get_random_bytes(nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2);
+
+       return digital_tg_listen(ddev, 300, digital_tg_recv_sensf_req, nfcid2);
+}
index c7cf37ba729819ab49c22f5b462320f633cdab5c..f1d426f10cceac10b0befc9f1c98caa6ad5b7793 100644 (file)
 #include <linux/export.h>
 #include <linux/spi/spi.h>
 #include <linux/crc-ccitt.h>
-#include <linux/nfc.h>
 #include <net/nfc/nci_core.h>
 
-#define NCI_SPI_HDR_LEN                        4
-#define NCI_SPI_CRC_LEN                        2
 #define NCI_SPI_ACK_SHIFT              6
 #define NCI_SPI_MSB_PAYLOAD_MASK       0x3F
 
 
 #define CRC_INIT               0xFFFF
 
-static int nci_spi_open(struct nci_dev *nci_dev)
-{
-       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
-
-       return ndev->ops->open(ndev);
-}
-
-static int nci_spi_close(struct nci_dev *nci_dev)
-{
-       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
-
-       return ndev->ops->close(ndev);
-}
-
-static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb)
+static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb,
+                         int cs_change)
 {
        struct spi_message m;
        struct spi_transfer t;
 
-       t.tx_buf = skb->data;
-       t.len = skb->len;
-       t.cs_change = 0;
-       t.delay_usecs = ndev->xfer_udelay;
+       memset(&t, 0, sizeof(struct spi_transfer));
+       /* a NULL skb means we just want the SPI chip select line to raise */
+       if (skb) {
+               t.tx_buf = skb->data;
+               t.len = skb->len;
+       } else {
+               /* still set tx_buf non NULL to make the driver happy */
+               t.tx_buf = &t;
+               t.len = 0;
+       }
+       t.cs_change = cs_change;
+       t.delay_usecs = nspi->xfer_udelay;
 
        spi_message_init(&m);
        spi_message_add_tail(&t, &m);
 
-       return spi_sync(ndev->spi, &m);
+       return spi_sync(nspi->spi, &m);
 }
 
-static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)
+int nci_spi_send(struct nci_spi *nspi,
+                struct completion *write_handshake_completion,
+                struct sk_buff *skb)
 {
-       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
        unsigned int payload_len = skb->len;
        unsigned char *hdr;
        int ret;
        long completion_rc;
 
-       ndev->ops->deassert_int(ndev);
-
        /* add the NCI SPI header to the start of the buffer */
        hdr = skb_push(skb, NCI_SPI_HDR_LEN);
        hdr[0] = NCI_SPI_DIRECT_WRITE;
-       hdr[1] = ndev->acknowledge_mode;
+       hdr[1] = nspi->acknowledge_mode;
        hdr[2] = payload_len >> 8;
        hdr[3] = payload_len & 0xFF;
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
                u16 crc;
 
                crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
@@ -96,123 +87,77 @@ static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)
                *skb_put(skb, 1) = crc & 0xFF;
        }
 
-       ret = __nci_spi_send(ndev, skb);
+       if (write_handshake_completion) {
+               /* Trick SPI driver to raise chip select */
+               ret = __nci_spi_send(nspi, NULL, 1);
+               if (ret)
+                       goto done;
 
-       kfree_skb(skb);
-       ndev->ops->assert_int(ndev);
+               /* wait for NFC chip hardware handshake to complete */
+               if (wait_for_completion_timeout(write_handshake_completion,
+                                               msecs_to_jiffies(1000)) == 0) {
+                       ret = -ETIME;
+                       goto done;
+               }
+       }
 
-       if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED)
+       ret = __nci_spi_send(nspi, skb, 0);
+       if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
                goto done;
 
-       init_completion(&ndev->req_completion);
-       completion_rc =
-               wait_for_completion_interruptible_timeout(&ndev->req_completion,
-                                                         NCI_SPI_SEND_TIMEOUT);
+       init_completion(&nspi->req_completion);
+       completion_rc = wait_for_completion_interruptible_timeout(
+                                                       &nspi->req_completion,
+                                                       NCI_SPI_SEND_TIMEOUT);
 
-       if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK)
+       if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK)
                ret = -EIO;
 
 done:
+       kfree_skb(skb);
+
        return ret;
 }
-
-static struct nci_ops nci_spi_ops = {
-       .open = nci_spi_open,
-       .close = nci_spi_close,
-       .send = nci_spi_send,
-};
+EXPORT_SYMBOL_GPL(nci_spi_send);
 
 /* ---- Interface to NCI SPI drivers ---- */
 
 /**
- * nci_spi_allocate_device - allocate a new nci spi device
+ * nci_spi_allocate_spi - allocate a new nci spi
  *
  * @spi: SPI device
- * @ops: device operations
- * @supported_protocols: NFC protocols supported by the device
- * @supported_se: NFC Secure Elements supported by the device
- * @acknowledge_mode: Acknowledge mode used by the device
+ * @acknowledge_mode: Acknowledge mode used by the NFC device
  * @delay: delay between transactions in us
+ * @ndev: nci dev to send incoming nci frames to
  */
-struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
-                                               struct nci_spi_ops *ops,
-                                               u32 supported_protocols,
-                                               u32 supported_se,
-                                               u8 acknowledge_mode,
-                                               unsigned int delay)
+struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
+                                    u8 acknowledge_mode, unsigned int delay,
+                                    struct nci_dev *ndev)
 {
-       struct nci_spi_dev *ndev;
-       int tailroom = 0;
+       struct nci_spi *nspi;
 
-       if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int)
+       nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
+       if (!nspi)
                return NULL;
 
-       if (!supported_protocols)
-               return NULL;
-
-       ndev = devm_kzalloc(&spi->dev, sizeof(struct nci_dev), GFP_KERNEL);
-       if (!ndev)
-               return NULL;
+       nspi->acknowledge_mode = acknowledge_mode;
+       nspi->xfer_udelay = delay;
 
-       ndev->ops = ops;
-       ndev->acknowledge_mode = acknowledge_mode;
-       ndev->xfer_udelay = delay;
+       nspi->spi = spi;
+       nspi->ndev = ndev;
 
-       if (acknowledge_mode == NCI_SPI_CRC_ENABLED)
-               tailroom += NCI_SPI_CRC_LEN;
-
-       ndev->nci_dev = nci_allocate_device(&nci_spi_ops, supported_protocols,
-                                           NCI_SPI_HDR_LEN, tailroom);
-       if (!ndev->nci_dev)
-               return NULL;
-
-       nci_set_drvdata(ndev->nci_dev, ndev);
-
-       return ndev;
+       return nspi;
 }
-EXPORT_SYMBOL_GPL(nci_spi_allocate_device);
+EXPORT_SYMBOL_GPL(nci_spi_allocate_spi);
 
-/**
- * nci_spi_free_device - deallocate nci spi device
- *
- * @ndev: The nci spi device to deallocate
- */
-void nci_spi_free_device(struct nci_spi_dev *ndev)
-{
-       nci_free_device(ndev->nci_dev);
-}
-EXPORT_SYMBOL_GPL(nci_spi_free_device);
-
-/**
- * nci_spi_register_device - register a nci spi device in the nfc subsystem
- *
- * @pdev: The nci spi device to register
- */
-int nci_spi_register_device(struct nci_spi_dev *ndev)
-{
-       return nci_register_device(ndev->nci_dev);
-}
-EXPORT_SYMBOL_GPL(nci_spi_register_device);
-
-/**
- * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem
- *
- * @dev: The nci spi device to unregister
- */
-void nci_spi_unregister_device(struct nci_spi_dev *ndev)
-{
-       nci_unregister_device(ndev->nci_dev);
-}
-EXPORT_SYMBOL_GPL(nci_spi_unregister_device);
-
-static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
+static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
 {
        struct sk_buff *skb;
        unsigned char *hdr;
        u16 crc;
        int ret;
 
-       skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL);
+       skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL);
 
        /* add the NCI SPI header to the start of the buffer */
        hdr = skb_push(skb, NCI_SPI_HDR_LEN);
@@ -225,14 +170,14 @@ static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
        *skb_put(skb, 1) = crc >> 8;
        *skb_put(skb, 1) = crc & 0xFF;
 
-       ret = __nci_spi_send(ndev, skb);
+       ret = __nci_spi_send(nspi, skb, 0);
 
        kfree_skb(skb);
 
        return ret;
 }
 
-static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
+static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
 {
        struct sk_buff *skb;
        struct spi_message m;
@@ -242,43 +187,49 @@ static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
        int ret;
 
        spi_message_init(&m);
+
+       memset(&tx, 0, sizeof(struct spi_transfer));
        req[0] = NCI_SPI_DIRECT_READ;
-       req[1] = ndev->acknowledge_mode;
+       req[1] = nspi->acknowledge_mode;
        tx.tx_buf = req;
        tx.len = 2;
        tx.cs_change = 0;
        spi_message_add_tail(&tx, &m);
+
+       memset(&rx, 0, sizeof(struct spi_transfer));
        rx.rx_buf = resp_hdr;
        rx.len = 2;
        rx.cs_change = 1;
        spi_message_add_tail(&rx, &m);
-       ret = spi_sync(ndev->spi, &m);
 
+       ret = spi_sync(nspi->spi, &m);
        if (ret)
                return NULL;
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
                rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
                                resp_hdr[1] + NCI_SPI_CRC_LEN;
        else
                rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
 
-       skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL);
+       skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL);
        if (!skb)
                return NULL;
 
        spi_message_init(&m);
+
+       memset(&rx, 0, sizeof(struct spi_transfer));
        rx.rx_buf = skb_put(skb, rx_len);
        rx.len = rx_len;
        rx.cs_change = 0;
-       rx.delay_usecs = ndev->xfer_udelay;
+       rx.delay_usecs = nspi->xfer_udelay;
        spi_message_add_tail(&rx, &m);
-       ret = spi_sync(ndev->spi, &m);
 
+       ret = spi_sync(nspi->spi, &m);
        if (ret)
                goto receive_error;
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
                *skb_push(skb, 1) = resp_hdr[1];
                *skb_push(skb, 1) = resp_hdr[0];
        }
@@ -318,61 +269,53 @@ static u8 nci_spi_get_ack(struct sk_buff *skb)
 }
 
 /**
- * nci_spi_recv_frame - receive frame from NCI SPI drivers
+ * nci_spi_read - read frame from NCI SPI drivers
  *
- * @ndev: The nci spi device
+ * @nspi: The nci spi
  * Context: can sleep
  *
  * This call may only be used from a context that may sleep.  The sleep
  * is non-interruptible, and has no timeout.
  *
- * It returns zero on success, else a negative error code.
+ * It returns an allocated skb containing the frame on success, or NULL.
  */
-int nci_spi_recv_frame(struct nci_spi_dev *ndev)
+struct sk_buff *nci_spi_read(struct nci_spi *nspi)
 {
        struct sk_buff *skb;
-       int ret = 0;
-
-       ndev->ops->deassert_int(ndev);
 
        /* Retrieve frame from SPI */
-       skb = __nci_spi_recv_frame(ndev);
-       if (!skb) {
-               ret = -EIO;
+       skb = __nci_spi_read(nspi);
+       if (!skb)
                goto done;
-       }
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
                if (!nci_spi_check_crc(skb)) {
-                       send_acknowledge(ndev, ACKNOWLEDGE_NACK);
+                       send_acknowledge(nspi, ACKNOWLEDGE_NACK);
                        goto done;
                }
 
                /* In case of acknowledged mode: if ACK or NACK received,
                 * unblock completion of latest frame sent.
                 */
-               ndev->req_result = nci_spi_get_ack(skb);
-               if (ndev->req_result)
-                       complete(&ndev->req_completion);
+               nspi->req_result = nci_spi_get_ack(skb);
+               if (nspi->req_result)
+                       complete(&nspi->req_completion);
        }
 
        /* If there is no payload (ACK/NACK only frame),
         * free the socket buffer
         */
-       if (skb->len == 0) {
+       if (!skb->len) {
                kfree_skb(skb);
+               skb = NULL;
                goto done;
        }
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
-               send_acknowledge(ndev, ACKNOWLEDGE_ACK);
-
-       /* Forward skb to NCI core layer */
-       ret = nci_recv_frame(ndev->nci_dev, skb);
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               send_acknowledge(nspi, ACKNOWLEDGE_ACK);
 
 done:
-       ndev->ops->assert_int(ndev);
 
-       return ret;
+       return skb;
 }
-EXPORT_SYMBOL_GPL(nci_spi_recv_frame);
+EXPORT_SYMBOL_GPL(nci_spi_read);
index 68063b2025da2750519dac5a652cdbbea36dac74..84b7e3ea7b7ad7ce09e9256916589e4724d55969 100644 (file)
@@ -58,6 +58,7 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
        [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
        [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
                                     .len = NFC_FIRMWARE_NAME_MAXSIZE },
+       [NFC_ATTR_SE_APDU] = { .type = NLA_BINARY },
 };
 
 static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
@@ -1278,6 +1279,91 @@ static int nfc_genl_dump_ses_done(struct netlink_callback *cb)
        return 0;
 }
 
+struct se_io_ctx {
+       u32 dev_idx;
+       u32 se_idx;
+};
+
+static void se_io_cb(void *context, u8 *apdu, size_t apdu_len, int err)
+{
+       struct se_io_ctx *ctx = context;
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg) {
+               kfree(ctx);
+               return;
+       }
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_CMD_SE_IO);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, ctx->dev_idx) ||
+           nla_put_u32(msg, NFC_ATTR_SE_INDEX, ctx->se_idx) ||
+           nla_put(msg, NFC_ATTR_SE_APDU, apdu_len, apdu))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       kfree(ctx);
+
+       return;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       kfree(ctx);
+
+       return;
+}
+
+static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       struct se_io_ctx *ctx;
+       u32 dev_idx, se_idx;
+       u8 *apdu;
+       size_t apdu_len;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_APDU])
+               return -EINVAL;
+
+       dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+       dev = nfc_get_device(dev_idx);
+       if (!dev)
+               return -ENODEV;
+
+       if (!dev->ops || !dev->ops->se_io)
+               return -ENOTSUPP;
+
+       apdu_len = nla_len(info->attrs[NFC_ATTR_SE_APDU]);
+       if (apdu_len == 0)
+               return -EINVAL;
+
+       apdu = nla_data(info->attrs[NFC_ATTR_SE_APDU]);
+       if (!apdu)
+               return -EINVAL;
+
+       ctx = kzalloc(sizeof(struct se_io_ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->dev_idx = dev_idx;
+       ctx->se_idx = se_idx;
+
+       return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
+}
+
 static struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
@@ -1358,6 +1444,11 @@ static struct genl_ops nfc_genl_ops[] = {
                .done = nfc_genl_dump_ses_done,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_SE_IO,
+               .doit = nfc_genl_se_io,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index 313bf1bc848a8c45b755e838fc9cd595e1a833c2..cd958b381f9615911b1acfc6da6aceb39d373401 100644 (file)
@@ -142,11 +142,11 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,
 
        err = rawsock_add_header(skb);
        if (err)
-               goto error;
+               goto error_skb;
 
        err = sock_queue_rcv_skb(sk, skb);
        if (err)
-               goto error;
+               goto error_skb;
 
        spin_lock_bh(&sk->sk_write_queue.lock);
        if (!skb_queue_empty(&sk->sk_write_queue))
@@ -158,6 +158,9 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,
        sock_put(sk);
        return;
 
+error_skb:
+       kfree_skb(skb);
+
 error:
        rawsock_report_error(sk, err);
        sock_put(sk);
index 78efe895b6636c4a57af6d72f156501e2c778992..4c10e7e6c9f6ae53291d3f72d82b6c9b0539b552 100644 (file)
@@ -36,7 +36,7 @@ config RFKILL_REGULATOR
 
 config RFKILL_GPIO
        tristate "GPIO RFKILL driver"
-       depends on RFKILL && GPIOLIB && HAVE_CLK
+       depends on RFKILL && GPIOLIB
        default n
        help
          If you say yes here you get support of a generic gpio RFKILL
index fb076cd6f808ad9b409a9670bffa699a7435de34..5620d3c07479654e64fd0b7d1b325562b880b174 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/acpi_gpio.h>
 
 #include <linux/rfkill-gpio.h>
 
-enum rfkill_gpio_clk_state {
-       UNSPECIFIED = 0,
-       PWR_ENABLED,
-       PWR_DISABLED
-};
+struct rfkill_gpio_data {
+       const char              *name;
+       enum rfkill_type        type;
+       int                     reset_gpio;
+       int                     shutdown_gpio;
 
-#define PWR_CLK_SET(_RF, _EN) \
-       ((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED))
-#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED)
-#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED)
+       struct rfkill           *rfkill_dev;
+       char                    *reset_name;
+       char                    *shutdown_name;
+       struct clk              *clk;
 
-struct rfkill_gpio_data {
-       struct rfkill_gpio_platform_data        *pdata;
-       struct rfkill                           *rfkill_dev;
-       char                                    *reset_name;
-       char                                    *shutdown_name;
-       enum rfkill_gpio_clk_state              pwr_clk_enabled;
-       struct clk                              *pwr_clk;
+       bool                    clk_enabled;
 };
 
 static int rfkill_gpio_set_power(void *data, bool blocked)
@@ -52,23 +48,22 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
        struct rfkill_gpio_data *rfkill = data;
 
        if (blocked) {
-               if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-                       gpio_direction_output(rfkill->pdata->shutdown_gpio, 0);
-               if (gpio_is_valid(rfkill->pdata->reset_gpio))
-                       gpio_direction_output(rfkill->pdata->reset_gpio, 0);
-               if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
-                       clk_disable(rfkill->pwr_clk);
+               if (gpio_is_valid(rfkill->shutdown_gpio))
+                       gpio_set_value(rfkill->shutdown_gpio, 0);
+               if (gpio_is_valid(rfkill->reset_gpio))
+                       gpio_set_value(rfkill->reset_gpio, 0);
+               if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
+                       clk_disable(rfkill->clk);
        } else {
-               if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill))
-                       clk_enable(rfkill->pwr_clk);
-               if (gpio_is_valid(rfkill->pdata->reset_gpio))
-                       gpio_direction_output(rfkill->pdata->reset_gpio, 1);
-               if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-                       gpio_direction_output(rfkill->pdata->shutdown_gpio, 1);
+               if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
+                       clk_enable(rfkill->clk);
+               if (gpio_is_valid(rfkill->reset_gpio))
+                       gpio_set_value(rfkill->reset_gpio, 1);
+               if (gpio_is_valid(rfkill->shutdown_gpio))
+                       gpio_set_value(rfkill->shutdown_gpio, 1);
        }
 
-       if (rfkill->pwr_clk)
-               PWR_CLK_SET(rfkill, blocked);
+       rfkill->clk_enabled = blocked;
 
        return 0;
 }
@@ -77,117 +72,112 @@ static const struct rfkill_ops rfkill_gpio_ops = {
        .set_block = rfkill_gpio_set_power,
 };
 
+static int rfkill_gpio_acpi_probe(struct device *dev,
+                                 struct rfkill_gpio_data *rfkill)
+{
+       const struct acpi_device_id *id;
+
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return -ENODEV;
+
+       rfkill->name = dev_name(dev);
+       rfkill->type = (unsigned)id->driver_data;
+       rfkill->reset_gpio = acpi_get_gpio_by_index(dev, 0, NULL);
+       rfkill->shutdown_gpio = acpi_get_gpio_by_index(dev, 1, NULL);
+
+       return 0;
+}
+
 static int rfkill_gpio_probe(struct platform_device *pdev)
 {
-       struct rfkill_gpio_data *rfkill;
        struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct rfkill_gpio_data *rfkill;
+       const char *clk_name = NULL;
        int ret = 0;
        int len = 0;
 
-       if (!pdata) {
-               pr_warn("%s: No platform data specified\n", __func__);
-               return -EINVAL;
+       rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL);
+       if (!rfkill)
+               return -ENOMEM;
+
+       if (ACPI_HANDLE(&pdev->dev)) {
+               ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill);
+               if (ret)
+                       return ret;
+       } else if (pdata) {
+               clk_name = pdata->power_clk_name;
+               rfkill->name = pdata->name;
+               rfkill->type = pdata->type;
+               rfkill->reset_gpio = pdata->reset_gpio;
+               rfkill->shutdown_gpio = pdata->shutdown_gpio;
+       } else {
+               return -ENODEV;
        }
 
        /* make sure at-least one of the GPIO is defined and that
         * a name is specified for this instance */
-       if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) &&
-               !gpio_is_valid(pdata->shutdown_gpio))) {
+       if ((!gpio_is_valid(rfkill->reset_gpio) &&
+            !gpio_is_valid(rfkill->shutdown_gpio)) || !rfkill->name) {
                pr_warn("%s: invalid platform data\n", __func__);
                return -EINVAL;
        }
 
-       rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
-       if (!rfkill)
-               return -ENOMEM;
-
-       if (pdata->gpio_runtime_setup) {
+       if (pdata && pdata->gpio_runtime_setup) {
                ret = pdata->gpio_runtime_setup(pdev);
                if (ret) {
                        pr_warn("%s: can't set up gpio\n", __func__);
-                       goto fail_alloc;
+                       return ret;
                }
        }
 
-       rfkill->pdata = pdata;
-
-       len = strlen(pdata->name);
-       rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL);
-       if (!rfkill->reset_name) {
-               ret = -ENOMEM;
-               goto fail_alloc;
-       }
+       len = strlen(rfkill->name);
+       rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL);
+       if (!rfkill->reset_name)
+               return -ENOMEM;
 
-       rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL);
-       if (!rfkill->shutdown_name) {
-               ret = -ENOMEM;
-               goto fail_reset_name;
-       }
+       rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL);
+       if (!rfkill->shutdown_name)
+               return -ENOMEM;
 
-       snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name);
-       snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name);
+       snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
+       snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
 
-       if (pdata->power_clk_name) {
-               rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name);
-               if (IS_ERR(rfkill->pwr_clk)) {
-                       pr_warn("%s: can't find pwr_clk.\n", __func__);
-                       ret = PTR_ERR(rfkill->pwr_clk);
-                       goto fail_shutdown_name;
-               }
-       }
+       rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
 
-       if (gpio_is_valid(pdata->reset_gpio)) {
-               ret = gpio_request(pdata->reset_gpio, rfkill->reset_name);
+       if (gpio_is_valid(rfkill->reset_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, rfkill->reset_gpio,
+                                           0, rfkill->reset_name);
                if (ret) {
                        pr_warn("%s: failed to get reset gpio.\n", __func__);
-                       goto fail_clock;
+                       return ret;
                }
        }
 
-       if (gpio_is_valid(pdata->shutdown_gpio)) {
-               ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name);
+       if (gpio_is_valid(rfkill->shutdown_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, rfkill->shutdown_gpio,
+                                           0, rfkill->shutdown_name);
                if (ret) {
                        pr_warn("%s: failed to get shutdown gpio.\n", __func__);
-                       goto fail_reset;
+                       return ret;
                }
        }
 
-       rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type,
-                                         &rfkill_gpio_ops, rfkill);
-       if (!rfkill->rfkill_dev) {
-               ret = -ENOMEM;
-               goto fail_shutdown;
-       }
+       rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev,
+                                         rfkill->type, &rfkill_gpio_ops,
+                                         rfkill);
+       if (!rfkill->rfkill_dev)
+               return -ENOMEM;
 
        ret = rfkill_register(rfkill->rfkill_dev);
        if (ret < 0)
-               goto fail_rfkill;
+               return ret;
 
        platform_set_drvdata(pdev, rfkill);
 
-       dev_info(&pdev->dev, "%s device registered.\n", pdata->name);
+       dev_info(&pdev->dev, "%s device registered.\n", rfkill->name);
 
        return 0;
-
-fail_rfkill:
-       rfkill_destroy(rfkill->rfkill_dev);
-fail_shutdown:
-       if (gpio_is_valid(pdata->shutdown_gpio))
-               gpio_free(pdata->shutdown_gpio);
-fail_reset:
-       if (gpio_is_valid(pdata->reset_gpio))
-               gpio_free(pdata->reset_gpio);
-fail_clock:
-       if (rfkill->pwr_clk)
-               clk_put(rfkill->pwr_clk);
-fail_shutdown_name:
-       kfree(rfkill->shutdown_name);
-fail_reset_name:
-       kfree(rfkill->reset_name);
-fail_alloc:
-       kfree(rfkill);
-
-       return ret;
 }
 
 static int rfkill_gpio_remove(struct platform_device *pdev)
@@ -195,31 +185,26 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
        struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
        struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
 
-       if (pdata->gpio_runtime_close)
+       if (pdata && pdata->gpio_runtime_close)
                pdata->gpio_runtime_close(pdev);
        rfkill_unregister(rfkill->rfkill_dev);
        rfkill_destroy(rfkill->rfkill_dev);
-       if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-               gpio_free(rfkill->pdata->shutdown_gpio);
-       if (gpio_is_valid(rfkill->pdata->reset_gpio))
-               gpio_free(rfkill->pdata->reset_gpio);
-       if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
-               clk_disable(rfkill->pwr_clk);
-       if (rfkill->pwr_clk)
-               clk_put(rfkill->pwr_clk);
-       kfree(rfkill->shutdown_name);
-       kfree(rfkill->reset_name);
-       kfree(rfkill);
 
        return 0;
 }
 
+static const struct acpi_device_id rfkill_acpi_match[] = {
+       { "BCM4752", RFKILL_TYPE_GPS },
+       { },
+};
+
 static struct platform_driver rfkill_gpio_driver = {
        .probe = rfkill_gpio_probe,
        .remove = rfkill_gpio_remove,
        .driver = {
-                  .name = "rfkill_gpio",
-                  .owner = THIS_MODULE,
+               .name = "rfkill_gpio",
+               .owner = THIS_MODULE,
+               .acpi_match_table = ACPI_PTR(rfkill_acpi_match),
        },
 };
 
index 16f3c3a7b2c17747230f62d1318ed152490eac34..9b8cc877eb191a2a62370c9f2617bed513200d24 100644 (file)
@@ -504,7 +504,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
        case NL80211_IFTYPE_ADHOC:
                if (wdev->current_bss) {
                        *chan = wdev->current_bss->pub.channel;
-                       *chanmode = wdev->ibss_fixed
+                       *chanmode = (wdev->ibss_fixed &&
+                                    !wdev->ibss_dfs_possible)
                                  ? CHAN_MODE_SHARED
                                  : CHAN_MODE_EXCLUSIVE;
                        return;
index 67153964aad2059652ffd34d79c956585cc9c1e3..aff959e5a1b360e7cb467cade7f7d617544b3909 100644 (file)
@@ -566,18 +566,13 @@ int wiphy_register(struct wiphy *wiphy)
        /* check and set up bitrates */
        ieee80211_set_bitrate_flags(wiphy);
 
-
+       rtnl_lock();
        res = device_add(&rdev->wiphy.dev);
-       if (res)
-               return res;
-
-       res = rfkill_register(rdev->rfkill);
        if (res) {
-               device_del(&rdev->wiphy.dev);
+               rtnl_unlock();
                return res;
        }
 
-       rtnl_lock();
        /* set up regulatory info */
        wiphy_regulatory_register(wiphy);
 
@@ -606,6 +601,15 @@ int wiphy_register(struct wiphy *wiphy)
 
        rdev->wiphy.registered = true;
        rtnl_unlock();
+
+       res = rfkill_register(rdev->rfkill);
+       if (res) {
+               rfkill_destroy(rdev->rfkill);
+               rdev->rfkill = NULL;
+               wiphy_unregister(&rdev->wiphy);
+               return res;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(wiphy_register);
@@ -640,7 +644,8 @@ void wiphy_unregister(struct wiphy *wiphy)
                rtnl_unlock();
                __count == 0; }));
 
-       rfkill_unregister(rdev->rfkill);
+       if (rdev->rfkill)
+               rfkill_unregister(rdev->rfkill);
 
        rtnl_lock();
        rdev->wiphy.registered = false;
@@ -953,8 +958,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
        case NETDEV_PRE_UP:
                if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
                        return notifier_from_errno(-EOPNOTSUPP);
-               if (rfkill_blocked(rdev->rfkill))
-                       return notifier_from_errno(-ERFKILL);
                ret = cfg80211_can_add_interface(rdev, wdev->iftype);
                if (ret)
                        return notifier_from_errno(ret);
index b43efac4efca786d3078c45220747e88eea168e2..eb0f7a3a25a96ed7f635993d67dd13d1dc27ea16 100644 (file)
@@ -402,6 +402,9 @@ static inline int
 cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
                           enum nl80211_iftype iftype)
 {
+       if (rfkill_blocked(rdev->rfkill))
+               return -ERFKILL;
+
        return cfg80211_can_change_interface(rdev, NULL, iftype);
 }
 
index 39bff7d367687fbdd4d220d76e34d66e4e586a9c..9d797df56649c5a47fdf1f61e665ee31e4b77e7c 100644 (file)
@@ -83,6 +83,8 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
                         struct cfg80211_cached_keys *connkeys)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct ieee80211_channel *check_chan;
+       u8 radar_detect_width = 0;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -114,14 +116,28 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        wdev->connect_keys = connkeys;
 
        wdev->ibss_fixed = params->channel_fixed;
+       wdev->ibss_dfs_possible = params->userspace_handles_dfs;
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
+       check_chan = params->chandef.chan;
+       if (params->userspace_handles_dfs) {
+               /* use channel NULL to check for radar even if the current
+                * channel is not a radar channel - it might decide to change
+                * to DFS channel later.
+                */
+               radar_detect_width = BIT(params->chandef.width);
+               check_chan = NULL;
+       }
+
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          check_chan,
+                                          (params->channel_fixed &&
+                                           !radar_detect_width)
+                                          ? CHAN_MODE_SHARED
+                                          : CHAN_MODE_EXCLUSIVE,
+                                          radar_detect_width);
 
-       err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
-                                   params->channel_fixed
-                                   ? CHAN_MODE_SHARED
-                                   : CHAN_MODE_EXCLUSIVE);
        if (err) {
                wdev->connect_keys = NULL;
                return err;
@@ -263,6 +279,8 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
                                if (chan->flags & IEEE80211_CHAN_DISABLED)
                                        continue;
                                wdev->wext.ibss.chandef.chan = chan;
+                               wdev->wext.ibss.chandef.center_freq1 =
+                                       chan->center_freq;
                                break;
                        }
 
@@ -347,6 +365,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
        if (chan) {
                wdev->wext.ibss.chandef.chan = chan;
                wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+               wdev->wext.ibss.chandef.center_freq1 = freq;
                wdev->wext.ibss.channel_fixed = true;
        } else {
                /* cfg80211_ibss_wext_join will pick one if needed */
index 8d49c1ce3deacbbb7ec9fe534e081ef7953607f5..6a6b1c8e907df93e73b44a8912a3ae1fda5f80a5 100644 (file)
@@ -707,11 +707,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
                        if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
                                continue;
 
-                       timeout = c->dfs_state_entered +
-                                 IEEE80211_DFS_MIN_NOP_TIME_MS;
+                       timeout = c->dfs_state_entered + msecs_to_jiffies(
+                                       IEEE80211_DFS_MIN_NOP_TIME_MS);
 
                        if (time_after_eq(jiffies, timeout)) {
                                c->dfs_state = NL80211_DFS_USABLE;
+                               c->dfs_state_entered = jiffies;
+
                                cfg80211_chandef_create(&chandef, c,
                                                        NL80211_CHAN_NO_HT);
 
index 2838206ddad3b5b05ea2eb2a6bfbc281593f9b41..a7f4e7902104907adf86c9fffcfc0ab956d36095 100644 (file)
@@ -354,6 +354,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
        [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
        [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
+       [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+       [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
+       [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -2421,7 +2424,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                change = true;
        }
 
-       if (flags && (*flags & NL80211_MNTR_FLAG_ACTIVE) &&
+       if (flags && (*flags & MONITOR_FLAG_ACTIVE) &&
            !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
                return -EOPNOTSUPP;
 
@@ -2483,7 +2486,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
 
-       if (!err && (flags & NL80211_MNTR_FLAG_ACTIVE) &&
+       if (!err && (flags & MONITOR_FLAG_ACTIVE) &&
            !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
                return -EOPNOTSUPP;
 
@@ -3896,9 +3899,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info,
        return 0;
 }
 
+static int nl80211_parse_sta_channel_info(struct genl_info *info,
+                                     struct station_parameters *params)
+{
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+               params->supported_channels =
+                    nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               params->supported_channels_len =
+                    nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               /*
+                * Need to include at least one (first channel, number of
+                * channels) tuple for each subband, and must have proper
+                * tuples for the rest of the data as well.
+                */
+               if (params->supported_channels_len < 2)
+                       return -EINVAL;
+               if (params->supported_channels_len % 2)
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+               params->supported_oper_classes =
+                nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               params->supported_oper_classes_len =
+                 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               /*
+                * The value of the Length field of the Supported Operating
+                * Classes element is between 2 and 253.
+                */
+               if (params->supported_oper_classes_len < 2 ||
+                   params->supported_oper_classes_len > 253)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
 static int nl80211_set_station_tdls(struct genl_info *info,
                                    struct station_parameters *params)
 {
+       int err;
        /* Dummy STA entry gets updated once the peer capabilities are known */
        if (info->attrs[NL80211_ATTR_PEER_AID])
                params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
@@ -3909,6 +3948,10 @@ static int nl80211_set_station_tdls(struct genl_info *info,
                params->vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 
+       err = nl80211_parse_sta_channel_info(info, params);
+       if (err)
+               return err;
+
        return nl80211_parse_sta_wme(info, params);
 }
 
@@ -4089,6 +4132,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+       err = nl80211_parse_sta_channel_info(info, &params);
+       if (err)
+               return err;
+
        err = nl80211_parse_sta_wme(info, &params);
        if (err)
                return err;
@@ -5653,6 +5700,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
                break;
        default:
                return -EOPNOTSUPP;
@@ -5665,9 +5713,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        /* only important for AP, IBSS and mesh create IEs internally */
-       if (need_new_beacon &&
-           (!info->attrs[NL80211_ATTR_CSA_IES] ||
-            !info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
+       if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
                return -EINVAL;
 
        params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
@@ -5722,9 +5768,9 @@ skip_beacons:
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
                return -EINVAL;
 
-       /* DFS channels are only supported for AP/P2P GO ... for now. */
        if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
-           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
                err = cfg80211_chandef_dfs_required(wdev->wiphy,
                                                    &params.chandef);
                if (err < 0) {
@@ -6556,6 +6602,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        ibss.control_port =
                nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
 
+       ibss.userspace_handles_dfs =
+               nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
+
        err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
        if (err)
                kfree(connkeys);
@@ -10762,7 +10811,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
                    wdev->iftype != NL80211_IFTYPE_P2P_GO &&
-                   wdev->iftype != NL80211_IFTYPE_ADHOC))
+                   wdev->iftype != NL80211_IFTYPE_ADHOC &&
+                   wdev->iftype != NL80211_IFTYPE_MESH_POINT))
                goto out;
 
        wdev->channel = chandef->chan;
index 7d604c06c3dc38d1155366a52f184971be1197e3..a271c27fac774ce987c0db6f1330ffbfca6dc7f7 100644 (file)
@@ -97,6 +97,10 @@ int ieee80211_radiotap_iterator_init(
        struct ieee80211_radiotap_header *radiotap_header,
        int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
 {
+       /* check the radiotap header can actually be present */
+       if (max_length < sizeof(struct ieee80211_radiotap_header))
+               return -EINVAL;
+
        /* Linux only supports version 0 radiotap format */
        if (radiotap_header->it_version)
                return -EINVAL;
@@ -131,7 +135,8 @@ int ieee80211_radiotap_iterator_init(
                         */
 
                        if ((unsigned long)iterator->_arg -
-                           (unsigned long)iterator->_rtheader >
+                           (unsigned long)iterator->_rtheader +
+                           sizeof(uint32_t) >
                            (unsigned long)iterator->_max_length)
                                return -EINVAL;
                }
index d62cb1e91475e0adad49111cb615cc87af963e7c..7da67fd0b4188a7cf3e3ded0ae2a64d565274947 100644 (file)
@@ -768,24 +768,25 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(freq_reg_info);
 
-#ifdef CONFIG_CFG80211_REG_DEBUG
-static const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
+const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 {
        switch (initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
-               return "Set by core";
+               return "core";
        case NL80211_REGDOM_SET_BY_USER:
-               return "Set by user";
+               return "user";
        case NL80211_REGDOM_SET_BY_DRIVER:
-               return "Set by driver";
+               return "driver";
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-               return "Set by country IE";
+               return "country IE";
        default:
                WARN_ON(1);
-               return "Set by bug";
+               return "bug";
        }
 }
+EXPORT_SYMBOL(reg_initiator_name);
 
+#ifdef CONFIG_CFG80211_REG_DEBUG
 static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
                                    const struct ieee80211_reg_rule *reg_rule)
 {
@@ -972,6 +973,13 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
 }
 #endif
 
+static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
+{
+       if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
+           !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
+               return true;
+       return false;
+}
 
 static bool ignore_reg_update(struct wiphy *wiphy,
                              enum nl80211_reg_initiator initiator)
@@ -979,14 +987,17 @@ static bool ignore_reg_update(struct wiphy *wiphy,
        struct regulatory_request *lr = get_last_request();
 
        if (!lr) {
-               REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n",
+               REG_DBG_PRINT("Ignoring regulatory request set by %s "
+                             "since last_request is not set\n",
                              reg_initiator_name(initiator));
                return true;
        }
 
        if (initiator == NL80211_REGDOM_SET_BY_CORE &&
            wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
-               REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n",
+               REG_DBG_PRINT("Ignoring regulatory request set by %s "
+                             "since the driver uses its own custom "
+                             "regulatory domain\n",
                              reg_initiator_name(initiator));
                return true;
        }
@@ -995,10 +1006,12 @@ static bool ignore_reg_update(struct wiphy *wiphy,
         * wiphy->regd will be set once the device has its own
         * desired regulatory domain set
         */
-       if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
+       if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
            initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
            !is_world_regdom(lr->alpha2)) {
-               REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n",
+               REG_DBG_PRINT("Ignoring regulatory request set by %s "
+                             "since the driver requires its own regulatory "
+                             "domain to be set first\n",
                              reg_initiator_name(initiator));
                return true;
        }
@@ -1699,8 +1712,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 }
 EXPORT_SYMBOL(regulatory_hint);
 
-void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
-                        const u8 *country_ie, u8 country_ie_len)
+void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
+                               const u8 *country_ie, u8 country_ie_len)
 {
        char alpha2[2];
        enum environment_cap env = ENVIRON_ANY;
index af2d5f8a5d828481e4036cba15d7577674b0e303..9677e3c13da98da5b00f34e112b0562512b6fc65 100644 (file)
@@ -58,7 +58,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
                                 gfp_t gfp);
 
 /**
- * regulatory_hint_11d - hints a country IE as a regulatory domain
+ * regulatory_hint_country_ie - hints a country IE as a regulatory domain
  * @wiphy: the wireless device giving the hint (used only for reporting
  *     conflicts)
  * @band: the band on which the country IE was received on. This determines
@@ -78,7 +78,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
  * not observed. For this reason if a triplet is seen with channel
  * information for a band the BSS is not present in it will be ignored.
  */
-void regulatory_hint_11d(struct wiphy *wiphy,
+void regulatory_hint_country_ie(struct wiphy *wiphy,
                         enum ieee80211_band band,
                         const u8 *country_ie,
                         u8 country_ie_len);
index eeb71480f1af2cecb575780c29dff0c15de3f197..d4397eba5408ea4325ac3404a251b8617d878309 100644 (file)
@@ -254,10 +254,10 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
        rdev = container_of(wk, struct cfg80211_registered_device,
                            sched_scan_results_wk);
 
-       request = rdev->sched_scan_req;
-
        rtnl_lock();
 
+       request = rdev->sched_scan_req;
+
        /* we don't have sched_scan_req anymore if the scan is stopping */
        if (request) {
                if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
index 20e86a95dc4e0ed358485f04208c670297ee6517..65f800890d70d857c9b42d379caad887f5a98c74 100644 (file)
@@ -682,8 +682,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
         * - country_ie + 2, the start of the country ie data, and
         * - and country_ie[1] which is the IE length
         */
-       regulatory_hint_11d(wdev->wiphy, bss->channel->band,
-                           country_ie + 2, country_ie[1]);
+       regulatory_hint_country_ie(wdev->wiphy, bss->channel->band,
+                                  country_ie + 2, country_ie[1]);
        kfree(country_ie);
 }
 
index 3c8be6104ba407a9d0b5d997d8c2fe4d659a63e4..935dea9485da01f7a0fb82cacc6e187058ff1ce0 100644 (file)
@@ -1249,7 +1249,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        enum cfg80211_chan_mode chmode;
        int num_different_channels = 0;
        int total = 1;
-       bool radar_required;
+       bool radar_required = false;
        int i, j;
 
        ASSERT_RTNL();
@@ -1264,14 +1264,20 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_WDS:
-               radar_required = !!(chan &&
-                                   (chan->flags & IEEE80211_CHAN_RADAR));
+               /* if the interface could potentially choose a DFS channel,
+                * then mark DFS as required.
+                */
+               if (!chan) {
+                       if (chanmode != CHAN_MODE_UNDEFINED && radar_detect)
+                               radar_required = true;
+                       break;
+               }
+               radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_DEVICE:
        case NL80211_IFTYPE_MONITOR:
-               radar_required = false;
                break;
        case NUM_NL80211_IFTYPES:
        case NL80211_IFTYPE_UNSPECIFIED: