Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 8 Jul 2011 15:03:36 +0000 (11:03 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 8 Jul 2011 15:03:36 +0000 (11:03 -0400)
124 files changed:
Documentation/networking/nfc.txt [new file with mode: 0644]
drivers/Kconfig
drivers/Makefile
drivers/bcma/Kconfig
drivers/bcma/Makefile
drivers/bcma/bcma_private.h
drivers/bcma/driver_pci.c
drivers/bcma/driver_pci_host.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/carl9170.h
drivers/net/wireless/ath/carl9170/fw.c
drivers/net/wireless/ath/carl9170/fwcmd.h
drivers/net/wireless/ath/carl9170/fwdesc.h
drivers/net/wireless/ath/carl9170/hw.h
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/phy.c
drivers/net/wireless/ath/carl9170/version.h
drivers/net/wireless/ath/carl9170/wlan.h
drivers/net/wireless/b43/dma.c
drivers/net/wireless/ipw2x00/ipw2100.c
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/iwl-1000.c
drivers/net/wireless/iwlwifi/iwl-2000.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
drivers/net/wireless/iwlwifi/iwl-agn-lib.c
drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
drivers/net/wireless/iwlwifi/iwl-agn-tx.c
drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-agn.h
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-dev.h
drivers/net/wireless/iwlwifi/iwl-hcmd.c
drivers/net/wireless/iwlwifi/iwl-pci.c
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-rx.c
drivers/net/wireless/iwlwifi/iwl-testmode.h
drivers/net/wireless/iwlwifi/iwl-trans.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-trans.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-tx.c
drivers/net/wireless/mwifiex/sdio.h
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00crypto.c
drivers/net/wireless/rt2x00/rt2x00lib.h
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00queue.h
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtlwifi/pci.c
drivers/net/wireless/rtlwifi/pci.h
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
drivers/net/wireless/rtlwifi/rtl8192de/hw.c
drivers/net/wireless/rtlwifi/rtl8192de/led.c
drivers/net/wireless/rtlwifi/rtl8192de/phy.c
drivers/net/wireless/rtlwifi/rtl8192de/rf.c
drivers/net/wireless/rtlwifi/rtl8192de/trx.c
drivers/net/wireless/rtlwifi/rtl8192se/hw.c
drivers/net/wireless/wl12xx/Kconfig
drivers/net/wireless/wl12xx/acx.c
drivers/net/wireless/wl12xx/acx.h
drivers/net/wireless/wl12xx/boot.c
drivers/net/wireless/wl12xx/cmd.c
drivers/net/wireless/wl12xx/cmd.h
drivers/net/wireless/wl12xx/conf.h
drivers/net/wireless/wl12xx/debugfs.c
drivers/net/wireless/wl12xx/event.c
drivers/net/wireless/wl12xx/ini.h
drivers/net/wireless/wl12xx/init.c
drivers/net/wireless/wl12xx/io.c
drivers/net/wireless/wl12xx/io.h
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/ps.c
drivers/net/wireless/wl12xx/rx.c
drivers/net/wireless/wl12xx/rx.h
drivers/net/wireless/wl12xx/scan.c
drivers/net/wireless/wl12xx/scan.h
drivers/net/wireless/wl12xx/sdio.c
drivers/net/wireless/wl12xx/spi.c
drivers/net/wireless/wl12xx/testmode.c
drivers/net/wireless/wl12xx/tx.c
drivers/net/wireless/wl12xx/wl12xx.h
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/pn533.c [new file with mode: 0644]
drivers/ssb/pci.c
include/linux/nfc.h [new file with mode: 0644]
include/linux/nl80211.h
include/linux/socket.h
include/linux/ssb/ssb.h
include/net/cfg80211.h
include/net/mac80211.h
include/net/nfc.h [new file with mode: 0644]
net/Kconfig
net/Makefile
net/core/sock.c
net/mac80211/cfg.c
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/mac80211/ieee80211_i.h
net/mac80211/key.c
net/mac80211/mesh_pathtbl.c
net/mac80211/mlme.c
net/mac80211/pm.c
net/mac80211/tx.c
net/nfc/Kconfig [new file with mode: 0644]
net/nfc/Makefile [new file with mode: 0644]
net/nfc/af_nfc.c [new file with mode: 0644]
net/nfc/core.c [new file with mode: 0644]
net/nfc/netlink.c [new file with mode: 0644]
net/nfc/nfc.h [new file with mode: 0644]
net/nfc/rawsock.c [new file with mode: 0644]
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/scan.c

diff --git a/Documentation/networking/nfc.txt b/Documentation/networking/nfc.txt
new file mode 100644 (file)
index 0000000..b24c29b
--- /dev/null
@@ -0,0 +1,128 @@
+Linux NFC subsystem
+===================
+
+The Near Field Communication (NFC) subsystem is required to standardize the
+NFC device drivers development and to create an unified userspace interface.
+
+This document covers the architecture overview, the device driver interface
+description and the userspace interface description.
+
+Architecture overview
+---------------------
+
+The NFC subsystem is responsible for:
+      - NFC adapters management;
+      - Polling for targets;
+      - Low-level data exchange;
+
+The subsystem is divided in some parts. The 'core' is responsible for
+providing the device driver interface. On the other side, it is also
+responsible for providing an interface to control operations and low-level
+data exchange.
+
+The control operations are available to userspace via generic netlink.
+
+The low-level data exchange interface is provided by the new socket family
+PF_NFC. The NFC_SOCKPROTO_RAW performs raw communication with NFC targets.
+
+
+             +--------------------------------------+
+             |              USER SPACE              |
+             +--------------------------------------+
+                 ^                       ^
+                 | low-level             | control
+                 | data exchange         | operations
+                 |                       |
+                 |                       v
+                 |                  +-----------+
+                 | AF_NFC           |  netlink  |
+                 | socket           +-----------+
+                 | raw                   ^
+                 |                       |
+                 v                       v
+             +---------+            +-----------+
+             | rawsock | <--------> |   core    |
+             +---------+            +-----------+
+                                         ^
+                                         |
+                                         v
+                                    +-----------+
+                                    |  driver   |
+                                    +-----------+
+
+Device Driver Interface
+-----------------------
+
+When registering on the NFC subsystem, the device driver must inform the core
+of the set of supported NFC protocols and the set of ops callbacks. The ops
+callbacks that must be implemented are the following:
+
+* start_poll - setup the device to poll for targets
+* stop_poll - stop on progress polling operation
+* activate_target - select and initialize one of the targets found
+* deactivate_target - deselect and deinitialize the selected target
+* data_exchange - send data and receive the response (transceive operation)
+
+Userspace interface
+--------------------
+
+The userspace interface is divided in control operations and low-level data
+exchange operation.
+
+CONTROL OPERATIONS:
+
+Generic netlink is used to implement the interface to the control operations.
+The operations are composed by commands and events, all listed below:
+
+* NFC_CMD_GET_DEVICE - get specific device info or dump the device list
+* NFC_CMD_START_POLL - setup a specific device to polling for targets
+* NFC_CMD_STOP_POLL - stop the polling operation in a specific device
+* NFC_CMD_GET_TARGET - dump the list of targets found by a specific device
+
+* NFC_EVENT_DEVICE_ADDED - reports an NFC device addition
+* NFC_EVENT_DEVICE_REMOVED - reports an NFC device removal
+* NFC_EVENT_TARGETS_FOUND - reports START_POLL results when 1 or more targets
+are found
+
+The user must call START_POLL to poll for NFC targets, passing the desired NFC
+protocols through NFC_ATTR_PROTOCOLS attribute. The device remains in polling
+state until it finds any target. However, the user can stop the polling
+operation by calling STOP_POLL command. In this case, it will be checked if
+the requester of STOP_POLL is the same of START_POLL.
+
+If the polling operation finds one or more targets, the event TARGETS_FOUND is
+sent (including the device id). The user must call GET_TARGET to get the list of
+all targets found by such device. Each reply message has target attributes with
+relevant information such as the supported NFC protocols.
+
+All polling operations requested through one netlink socket are stopped when
+it's closed.
+
+LOW-LEVEL DATA EXCHANGE:
+
+The userspace must use PF_NFC sockets to perform any data communication with
+targets. All NFC sockets use AF_NFC:
+
+struct sockaddr_nfc {
+       sa_family_t sa_family;
+       __u32 dev_idx;
+       __u32 target_idx;
+       __u32 nfc_protocol;
+};
+
+To establish a connection with one target, the user must create an
+NFC_SOCKPROTO_RAW socket and call the 'connect' syscall with the sockaddr_nfc
+struct correctly filled. All information comes from NFC_EVENT_TARGETS_FOUND
+netlink event. As a target can support more than one NFC protocol, the user
+must inform which protocol it wants to use.
+
+Internally, 'connect' will result in an activate_target call to the driver.
+When the socket is closed, the target is deactivated.
+
+The data format exchanged through the sockets is NFC protocol dependent. For
+instance, when communicating with MIFARE tags, the data exchanged are MIFARE
+commands and their responses.
+
+The first received package is the response to the first sent package and so
+on. In order to allow valid "empty" responses, every data received has a NULL
+header of 1 byte.
index 3bb154d8c8cc778713c28e0d335282cb715594a2..258473ce8d01edacad70ac3238612a5298ba44f2 100644 (file)
@@ -94,8 +94,6 @@ source "drivers/memstick/Kconfig"
 
 source "drivers/leds/Kconfig"
 
-source "drivers/nfc/Kconfig"
-
 source "drivers/accessibility/Kconfig"
 
 source "drivers/infiniband/Kconfig"
index 09f3232bcdcddd2ff2ce042cd0a7a977320356c4..1bc896571a3a19762163d2cd215b87d8f55172d9 100644 (file)
@@ -122,3 +122,4 @@ obj-y                               += ieee802154/
 obj-y                          += clk/
 
 obj-$(CONFIG_HWSPINLOCK)       += hwspinlock/
+obj-$(CONFIG_NFC)              += nfc/
index 83e9adf46441b549c619bc6c13da391e00781cf2..ae0a02e1b808dafdf2e13edfac026f339fb68a43 100644 (file)
@@ -27,6 +27,12 @@ config BCMA_HOST_PCI
        bool "Support for BCMA on PCI-host bus"
        depends on BCMA_HOST_PCI_POSSIBLE
 
+config BCMA_DRIVER_PCI_HOSTMODE
+       bool "Driver for PCI core working in hostmode"
+       depends on BCMA && MIPS
+       help
+         PCI core hostmode operation (external PCI bus).
+
 config BCMA_DEBUG
        bool "BCMA debugging"
        depends on BCMA
index cde0182bd1dcdb4b1d680b8b3fac1e60840c4956..a2161cceafb9b3810278ee35e1a8749070c58649 100644 (file)
@@ -1,6 +1,7 @@
 bcma-y                                 += main.o scan.o core.o sprom.o
 bcma-y                                 += driver_chipcommon.o driver_chipcommon_pmu.o
 bcma-y                                 += driver_pci.o
+bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE)        += driver_pci_host.o
 bcma-$(CONFIG_BCMA_HOST_PCI)           += host_pci.o
 obj-$(CONFIG_BCMA)                     += bcma.o
 
index 4228736de0e8adb13a85d18a35219468fd0aec34..e02ff21835c90b960722ab6419fed6039f47c828 100644 (file)
@@ -28,4 +28,8 @@ extern int __init bcma_host_pci_init(void);
 extern void __exit bcma_host_pci_exit(void);
 #endif /* CONFIG_BCMA_HOST_PCI */
 
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
+#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+
 #endif
index b0c19ede0d2e170fcd441a11a1d04f9cae075de7..dc6f34ac96a08aaa8e42b80eff23a18a77ff5cc2 100644 (file)
@@ -157,11 +157,47 @@ static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
  * Init.
  **************************************************/
 
-void bcma_core_pci_init(struct bcma_drv_pci *pc)
+static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
 {
        bcma_pcicore_serdes_workaround(pc);
 }
 
+static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
+{
+       struct bcma_bus *bus = pc->core->bus;
+       u16 chipid_top;
+
+       chipid_top = (bus->chipinfo.id & 0xFF00);
+       if (chipid_top != 0x4700 &&
+           chipid_top != 0x5300)
+               return false;
+
+       if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI)
+               return false;
+
+#if 0
+       /* TODO: on BCMA we use address from EROM instead of magic formula */
+       u32 tmp;
+       return !mips_busprobe32(tmp, (bus->mmio +
+               (pc->core->core_index * BCMA_CORE_SIZE)));
+#endif
+
+       return true;
+}
+
+void bcma_core_pci_init(struct bcma_drv_pci *pc)
+{
+       if (bcma_core_pci_is_in_hostmode(pc)) {
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+               bcma_core_pci_hostmode_init(pc);
+#else
+               pr_err("Driver compiled without support for hostmode PCI\n");
+#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+       } else {
+               bcma_core_pci_clientmode_init(pc);
+       }
+}
+
 int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
                          bool enable)
 {
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
new file mode 100644 (file)
index 0000000..eb332b7
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Broadcom specific AMBA
+ * PCI Core in hostmode
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/bcma/bcma.h>
+
+void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
+{
+       pr_err("No support for PCI core in hostmode yet\n");
+}
index 4da01a9f5680feef4726f9a9fbff56914b396045..f9a4655ea0b8b1d8381c40f6eff95d9fc52303ac 100644 (file)
@@ -67,6 +67,8 @@
 
 #define PAYLOAD_MAX    (CARL9170_MAX_CMD_LEN / 4 - 1)
 
+static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 3, 2, 1, 0 };
+
 enum carl9170_rf_init_mode {
        CARL9170_RFI_NONE,
        CARL9170_RFI_WARM,
@@ -440,7 +442,6 @@ struct ar9170 {
 enum carl9170_ps_off_override_reasons {
        PS_OFF_VIF      = BIT(0),
        PS_OFF_BCN      = BIT(1),
-       PS_OFF_5GHZ     = BIT(2),
 };
 
 struct carl9170_ba_stats {
index 221957c5d37370830eef6c74ae3aacddcbb62f89..39ddea5794f703c45233600113ed94e3c3546a72 100644 (file)
@@ -237,7 +237,7 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
                ar->disable_offload = true;
        }
 
-       if (SUPP(CARL9170FW_PSM))
+       if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM))
                ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS;
 
        if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) {
index 30449d21b7624e9a356005830cea2cc80a60e396..0a6dec529b593ecb3b63d383893a927117b9d4d0 100644 (file)
@@ -4,7 +4,7 @@
  * Firmware command interface definitions
  *
  * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.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
@@ -54,6 +54,7 @@ enum carl9170_cmd_oids {
        CARL9170_CMD_BCN_CTRL           = 0x05,
        CARL9170_CMD_READ_TSF           = 0x06,
        CARL9170_CMD_RX_FILTER          = 0x07,
+       CARL9170_CMD_WOL                = 0x08,
 
        /* CAM */
        CARL9170_CMD_EKEY               = 0x10,
@@ -180,6 +181,21 @@ struct carl9170_bcn_ctrl_cmd {
 #define CARL9170_BCN_CTRL_DRAIN        0
 #define CARL9170_BCN_CTRL_CAB_TRIGGER  1
 
+struct carl9170_wol_cmd {
+       __le32          flags;
+       u8              mac[6];
+       u8              bssid[6];
+       __le32          null_interval;
+       __le32          free_for_use2;
+       __le32          mask;
+       u8              pattern[32];
+} __packed;
+
+#define CARL9170_WOL_CMD_SIZE          60
+
+#define CARL9170_WOL_DISCONNECT                1
+#define CARL9170_WOL_MAGIC_PKT         2
+
 struct carl9170_cmd_head {
        union {
                struct {
@@ -203,6 +219,7 @@ struct carl9170_cmd {
                struct carl9170_write_reg       wreg;
                struct carl9170_rf_init         rf_init;
                struct carl9170_psm             psm;
+               struct carl9170_wol_cmd         wol;
                struct carl9170_bcn_ctrl_cmd    bcn_ctrl;
                struct carl9170_rx_filter_cmd   rx_filter;
                u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
index 921066822dd5a77529b9834fd958b1d7c7da3dbc..7ba62bb7705462130e226115ad4edb697d3667e6 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Firmware descriptor format
  *
- * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.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
@@ -72,6 +72,9 @@ enum carl9170fw_feature_list {
        /* Wake up on WLAN */
        CARL9170FW_WOL,
 
+       /* Firmware supports PSM in the 5GHZ Band */
+       CARL9170FW_FIXED_5GHZ_PSM,
+
        /* KEEP LAST */
        __CARL9170FW_FEATURE_NUM
 };
@@ -82,6 +85,7 @@ enum carl9170fw_feature_list {
 #define DBG_MAGIC      "DBG\0"
 #define CHK_MAGIC      "CHK\0"
 #define TXSQ_MAGIC     "TXSQ"
+#define WOL_MAGIC      "WOL\0"
 #define LAST_MAGIC     "LAST"
 
 #define CARL9170FW_SET_DAY(d) (((d) - 1) % 31)
@@ -104,7 +108,7 @@ struct carl9170fw_desc_head {
        (sizeof(struct carl9170fw_desc_head))
 
 #define CARL9170FW_OTUS_DESC_MIN_VER           6
-#define CARL9170FW_OTUS_DESC_CUR_VER           6
+#define CARL9170FW_OTUS_DESC_CUR_VER           7
 struct carl9170fw_otus_desc {
        struct carl9170fw_desc_head head;
        __le32 feature_set;
@@ -186,6 +190,16 @@ struct carl9170fw_txsq_desc {
 #define CARL9170FW_TXSQ_DESC_SIZE                      \
        (sizeof(struct carl9170fw_txsq_desc))
 
+#define CARL9170FW_WOL_DESC_MIN_VER                    1
+#define CARL9170FW_WOL_DESC_CUR_VER                    1
+struct carl9170fw_wol_desc {
+       struct carl9170fw_desc_head head;
+
+       __le32 supported_triggers;      /* CARL9170_WOL_ */
+} __packed;
+#define CARL9170FW_WOL_DESC_SIZE                       \
+       (sizeof(struct carl9170fw_wol_desc))
+
 #define CARL9170FW_LAST_DESC_MIN_VER                   1
 #define CARL9170FW_LAST_DESC_CUR_VER                   2
 struct carl9170fw_last_desc {
index 4e30762dd903de04a704789ac823e53f28311f1d..261f8935107006c9f5915aeb6b398690ce51ebae 100644 (file)
@@ -4,7 +4,7 @@
  * Register map, hardware-specific definitions
  *
  * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.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
 
 #define AR9170_MAC_REG_DMA_WLAN_STATUS         (AR9170_MAC_REG_BASE + 0xd38)
 #define        AR9170_MAC_REG_DMA_STATUS               (AR9170_MAC_REG_BASE + 0xd3c)
-
+#define AR9170_MAC_REG_DMA_TXQ_LAST_ADDR       (AR9170_MAC_REG_BASE + 0xd40)
+#define        AR9170_MAC_REG_DMA_TXQ0_LAST_ADDR       (AR9170_MAC_REG_BASE + 0xd40)
+#define        AR9170_MAC_REG_DMA_TXQ1_LAST_ADDR       (AR9170_MAC_REG_BASE + 0xd44)
+#define        AR9170_MAC_REG_DMA_TXQ2_LAST_ADDR       (AR9170_MAC_REG_BASE + 0xd48)
+#define        AR9170_MAC_REG_DMA_TXQ3_LAST_ADDR       (AR9170_MAC_REG_BASE + 0xd4c)
+#define        AR9170_MAC_REG_DMA_TXQ4_LAST_ADDR       (AR9170_MAC_REG_BASE + 0xd50)
+#define        AR9170_MAC_REG_DMA_TXQ0Q1_LEN           (AR9170_MAC_REG_BASE + 0xd54)
+#define        AR9170_MAC_REG_DMA_TXQ2Q3_LEN           (AR9170_MAC_REG_BASE + 0xd58)
+#define        AR9170_MAC_REG_DMA_TXQ4_LEN             (AR9170_MAC_REG_BASE + 0xd5c)
+
+#define AR9170_MAC_REG_DMA_TXQX_LAST_ADDR      (AR9170_MAC_REG_BASE + 0xd74)
+#define AR9170_MAC_REG_DMA_TXQX_FAIL_ADDR      (AR9170_MAC_REG_BASE + 0xd78)
 #define        AR9170_MAC_REG_TXRX_MPI                 (AR9170_MAC_REG_BASE + 0xd7c)
 #define                AR9170_MAC_TXRX_MPI_TX_MPI_MASK         0x0000000f
 #define                AR9170_MAC_TXRX_MPI_TX_TO_MASK          0x0000fff0
index 54d093c2ab44f956b5988d73ae819ed9b398a58b..d2b9f1256bc86900e70a3c598a5c4ac765e02fbc 100644 (file)
@@ -345,11 +345,11 @@ static int carl9170_op_start(struct ieee80211_hw *hw)
        carl9170_zap_queues(ar);
 
        /* reset QoS defaults */
-       CARL9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023,  0); /* BEST EFFORT */
-       CARL9170_FILL_QUEUE(ar->edcf[1], 2, 7,    15, 94); /* VIDEO */
-       CARL9170_FILL_QUEUE(ar->edcf[2], 2, 3,     7, 47); /* VOICE */
-       CARL9170_FILL_QUEUE(ar->edcf[3], 7, 15, 1023,  0); /* BACKGROUND */
-       CARL9170_FILL_QUEUE(ar->edcf[4], 2, 3,     7,  0); /* SPECIAL */
+       CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_VO], 2, 3,     7, 47);
+       CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_VI], 2, 7,    15, 94);
+       CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_BE], 3, 15, 1023,  0);
+       CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_BK], 7, 15, 1023,  0);
+       CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_SPECIAL], 2, 3, 7, 0);
 
        ar->current_factor = ar->current_density = -1;
        /* "The first key is unique." */
@@ -1577,6 +1577,7 @@ void *carl9170_alloc(size_t priv_size)
                     IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                     IEEE80211_HW_SUPPORTS_PS |
                     IEEE80211_HW_PS_NULLFUNC_STACK |
+                    IEEE80211_HW_NEED_DTIM_PERIOD |
                     IEEE80211_HW_SIGNAL_DBM;
 
        if (!modparam_noht) {
index b6ae0e179c8d6cec059016d3f7650bca0f5a89c1..da1ab962ee4839cd52bb06c81e8c68a465c4016c 100644 (file)
@@ -1783,12 +1783,6 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
                }
        }
 
-       /* FIXME: PSM does not work in 5GHz Band */
-       if (channel->band == IEEE80211_BAND_5GHZ)
-               ar->ps.off_override |= PS_OFF_5GHZ;
-       else
-               ar->ps.off_override &= ~PS_OFF_5GHZ;
-
        ar->channel = channel;
        ar->ht_settings = new_ht;
        return 0;
index 15095c035169c71c2f0b0576329d50e45be3e004..64703778cfeaac239ac91fe80e0b22cb94f67dab 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __CARL9170_SHARED_VERSION_H
 #define __CARL9170_SHARED_VERSION_H
 #define CARL9170FW_VERSION_YEAR 11
-#define CARL9170FW_VERSION_MONTH 1
-#define CARL9170FW_VERSION_DAY 22
-#define CARL9170FW_VERSION_GIT "1.9.2"
+#define CARL9170FW_VERSION_MONTH 6
+#define CARL9170FW_VERSION_DAY 30
+#define CARL9170FW_VERSION_GIT "1.9.4"
 #endif /* __CARL9170_SHARED_VERSION_H */
index 9e1324b67e08e19cc0bea538b74ed9ab0b5fb924..ea17995b32f4ef60ac082e49e0f64188c8ad2652 100644 (file)
@@ -4,7 +4,7 @@
  * RX/TX meta descriptor format
  *
  * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.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
@@ -278,7 +278,7 @@ struct ar9170_tx_frame {
 struct carl9170_tx_superframe {
        struct carl9170_tx_superdesc s;
        struct ar9170_tx_frame f;
-} __packed;
+} __packed __aligned(4);
 
 #endif /* __CARL9170FW__ */
 
@@ -328,7 +328,7 @@ struct _carl9170_tx_superframe {
        struct _carl9170_tx_superdesc s;
        struct _ar9170_tx_hwdesc f;
        u8 frame_data[0];
-} __packed;
+} __packed __aligned(4);
 
 #define        CARL9170_TX_SUPERDESC_LEN               24
 #define        AR9170_TX_HWDESC_LEN                    8
@@ -404,16 +404,6 @@ static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t)
               (t->DAidx & 0xc0) >> 6;
 }
 
-enum ar9170_txq {
-       AR9170_TXQ_BE,
-
-       AR9170_TXQ_VI,
-       AR9170_TXQ_VO,
-       AR9170_TXQ_BK,
-
-       __AR9170_NUM_TXQ,
-};
-
 /*
  * This is an workaround for several undocumented bugs.
  * Don't mess with the QoS/AC <-> HW Queue map, if you don't
@@ -431,7 +421,14 @@ enum ar9170_txq {
  * result, this makes the device pretty much useless
  * for any serious 802.11n setup.
  */
-static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 2, 1, 0, 3 };
+enum ar9170_txq {
+       AR9170_TXQ_BK = 0,      /* TXQ0 */
+       AR9170_TXQ_BE,          /* TXQ1 */
+       AR9170_TXQ_VI,          /* TXQ2 */
+       AR9170_TXQ_VO,          /* TXQ3 */
+
+       __AR9170_NUM_TXQ,
+};
 
 #define        AR9170_TXQ_DEPTH                        32
 
index d02cf8300e3eee0407c0dcf4dec5b2e18de55d86..7a09a467339c825fd078218e10e8f0b2723b7d0c 100644 (file)
@@ -1600,6 +1600,7 @@ void b43_dma_rx(struct b43_dmaring *ring)
                dma_rx(ring, &slot);
                update_max_used_slots(ring, ++used_slots);
        }
+       wmb();
        ops->set_current_rxslot(ring, slot);
        ring->current_slot = slot;
 }
index 44307753587da5dd5e00507ada40669ffa6b73be..3774dd034746286815af04f0322e7d4e778d6f32 100644 (file)
@@ -287,7 +287,7 @@ static const char *command_types[] = {
        "unused",               /* HOST_INTERRUPT_COALESCING */
        "undefined",
        "CARD_DISABLE_PHY_OFF",
-       "MSDU_TX_RATES" "undefined",
+       "MSDU_TX_RATES",
        "undefined",
        "SET_STATION_STAT_BITS",
        "CLEAR_STATIONS_STAT_BITS",
index 9a56ce5467156d649c6cbbfb2dea6db722691f8a..19150398a24807a245d8b2816a6c9280b33611bd 100644 (file)
@@ -14,6 +14,7 @@ iwlagn-objs             += iwl-6000.o
 iwlagn-objs             += iwl-1000.o
 iwlagn-objs             += iwl-2000.o
 iwlagn-objs             += iwl-pci.o
+iwlagn-objs             += iwl-trans.o
 
 iwlagn-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
 iwlagn-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
index e57fad9f1f093499711710af3e7e8d1c35a95592..cf1449df4f0b406a3afff7b566a1f545cea1ce5f 100644 (file)
@@ -138,7 +138,6 @@ static int iwl1000_hw_set_hw_params(struct iwl_priv *priv)
 
        priv->hw_params.ht40_channel =  BIT(IEEE80211_BAND_2GHZ) |
                                        BIT(IEEE80211_BAND_5GHZ);
-       priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
 
        priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
        if (priv->cfg->rx_with_siso_diversity)
@@ -197,7 +196,6 @@ static struct iwl_lib_ops iwl1000_lib = {
 
 static const struct iwl_ops iwl1000_ops = {
        .lib = &iwl1000_lib,
-       .hcmd = &iwlagn_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
index 64ed1f247df03fff3b4ec7517d952ffe94bdcd1c..a401113c065a752094769caf2a810143369f6d25 100644 (file)
 #define IWL2030_UCODE_API_MAX 5
 #define IWL2000_UCODE_API_MAX 5
 #define IWL105_UCODE_API_MAX 5
+#define IWL135_UCODE_API_MAX 5
 
 /* Lowest firmware API version supported */
 #define IWL2030_UCODE_API_MIN 5
 #define IWL2000_UCODE_API_MIN 5
 #define IWL105_UCODE_API_MIN 5
+#define IWL135_UCODE_API_MIN 5
 
 #define IWL2030_FW_PRE "iwlwifi-2030-"
 #define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode"
@@ -65,6 +67,9 @@
 #define IWL105_FW_PRE "iwlwifi-105-"
 #define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode"
 
+#define IWL135_FW_PRE "iwlwifi-135-"
+#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE #api ".ucode"
+
 static void iwl2000_set_ct_threshold(struct iwl_priv *priv)
 {
        /* want Celsius */
@@ -131,7 +136,6 @@ static int iwl2000_hw_set_hw_params(struct iwl_priv *priv)
 
        priv->hw_params.ht40_channel =  BIT(IEEE80211_BAND_2GHZ) |
                                        BIT(IEEE80211_BAND_5GHZ);
-       priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
 
        priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
        if (priv->cfg->rx_with_siso_diversity)
@@ -193,25 +197,21 @@ static struct iwl_lib_ops iwl2000_lib = {
 
 static const struct iwl_ops iwl2000_ops = {
        .lib = &iwl2000_lib,
-       .hcmd = &iwlagn_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
 static const struct iwl_ops iwl2030_ops = {
        .lib = &iwl2000_lib,
-       .hcmd = &iwlagn_bt_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
 static const struct iwl_ops iwl105_ops = {
        .lib = &iwl2000_lib,
-       .hcmd = &iwlagn_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
 static const struct iwl_ops iwl135_ops = {
        .lib = &iwl2000_lib,
-       .hcmd = &iwlagn_bt_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
@@ -344,9 +344,9 @@ struct iwl_cfg iwl105_bgn_cfg = {
 };
 
 #define IWL_DEVICE_135                                         \
-       .fw_name_pre = IWL105_FW_PRE,                           \
-       .ucode_api_max = IWL105_UCODE_API_MAX,                  \
-       .ucode_api_min = IWL105_UCODE_API_MIN,                  \
+       .fw_name_pre = IWL135_FW_PRE,                           \
+       .ucode_api_max = IWL135_UCODE_API_MAX,                  \
+       .ucode_api_min = IWL135_UCODE_API_MIN,                  \
        .eeprom_ver = EEPROM_2000_EEPROM_VERSION,               \
        .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION,       \
        .ops = &iwl135_ops,                                     \
@@ -359,12 +359,12 @@ struct iwl_cfg iwl105_bgn_cfg = {
        .rx_with_siso_diversity = true                          \
 
 struct iwl_cfg iwl135_bg_cfg = {
-       .name = "105 Series 1x1 BG/BT",
+       .name = "135 Series 1x1 BG/BT",
        IWL_DEVICE_135,
 };
 
 struct iwl_cfg iwl135_bgn_cfg = {
-       .name = "105 Series 1x1 BGN/BT",
+       .name = "135 Series 1x1 BGN/BT",
        IWL_DEVICE_135,
        .ht_params = &iwl2000_ht_params,
 };
@@ -372,3 +372,4 @@ struct iwl_cfg iwl135_bgn_cfg = {
 MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_MAX));
+MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_MAX));
index 269dfdb9fe1a39f1d7d4a984ebe6f8c5ce8f59e8..c55cec853f50c182a8df879697db7d44b1b25619 100644 (file)
@@ -169,7 +169,6 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
 
        priv->hw_params.ht40_channel =  BIT(IEEE80211_BAND_2GHZ) |
                                        BIT(IEEE80211_BAND_5GHZ);
-       priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
 
        priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
        priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
@@ -214,7 +213,6 @@ static int iwl5150_hw_set_hw_params(struct iwl_priv *priv)
 
        priv->hw_params.ht40_channel =  BIT(IEEE80211_BAND_2GHZ) |
                                        BIT(IEEE80211_BAND_5GHZ);
-       priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
 
        priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
        priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
@@ -379,13 +377,11 @@ static struct iwl_lib_ops iwl5150_lib = {
 
 static const struct iwl_ops iwl5000_ops = {
        .lib = &iwl5000_lib,
-       .hcmd = &iwlagn_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
 static const struct iwl_ops iwl5150_ops = {
        .lib = &iwl5150_lib,
-       .hcmd = &iwlagn_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
index f1c1db76b9dac7ab2ab76fca79a638212b6b1312..965d010794b4b49f496446fd36f0f5ee442a6962 100644 (file)
@@ -157,7 +157,6 @@ static int iwl6000_hw_set_hw_params(struct iwl_priv *priv)
 
        priv->hw_params.ht40_channel =  BIT(IEEE80211_BAND_2GHZ) |
                                        BIT(IEEE80211_BAND_5GHZ);
-       priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
 
        priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
        if (priv->cfg->rx_with_siso_diversity)
@@ -328,27 +327,23 @@ static struct iwl_nic_ops iwl6150_nic_ops = {
 
 static const struct iwl_ops iwl6000_ops = {
        .lib = &iwl6000_lib,
-       .hcmd = &iwlagn_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
 static const struct iwl_ops iwl6050_ops = {
        .lib = &iwl6000_lib,
-       .hcmd = &iwlagn_hcmd,
        .utils = &iwlagn_hcmd_utils,
        .nic = &iwl6050_nic_ops,
 };
 
 static const struct iwl_ops iwl6150_ops = {
        .lib = &iwl6000_lib,
-       .hcmd = &iwlagn_hcmd,
        .utils = &iwlagn_hcmd_utils,
        .nic = &iwl6150_nic_ops,
 };
 
 static const struct iwl_ops iwl6030_ops = {
        .lib = &iwl6030_lib,
-       .hcmd = &iwlagn_bt_hcmd,
        .utils = &iwlagn_hcmd_utils,
 };
 
index ba7ed9157c9239d80c90b177982ed610e8dc0450..ce7d4b56d9b2acc8fd3d0f614fd01d276318b24b 100644 (file)
@@ -205,7 +205,7 @@ static int iwlagn_calc_rssi(struct iwl_priv *priv,
        return max_rssi - agc - IWLAGN_RSSI_OFFSET;
 }
 
-static int iwlagn_set_pan_params(struct iwl_priv *priv)
+int iwlagn_set_pan_params(struct iwl_priv *priv)
 {
        struct iwl_wipan_params_cmd cmd;
        struct iwl_rxon_context *ctx_bss, *ctx_pan;
@@ -297,20 +297,6 @@ static int iwlagn_set_pan_params(struct iwl_priv *priv)
        return ret;
 }
 
-struct iwl_hcmd_ops iwlagn_hcmd = {
-       .set_rxon_chain = iwlagn_set_rxon_chain,
-       .set_tx_ant = iwlagn_send_tx_ant_config,
-       .send_bt_config = iwl_send_bt_config,
-       .set_pan_params = iwlagn_set_pan_params,
-};
-
-struct iwl_hcmd_ops iwlagn_bt_hcmd = {
-       .set_rxon_chain = iwlagn_set_rxon_chain,
-       .set_tx_ant = iwlagn_send_tx_ant_config,
-       .send_bt_config = iwlagn_send_advance_bt_config,
-       .set_pan_params = iwlagn_set_pan_params,
-};
-
 struct iwl_hcmd_utils_ops iwlagn_hcmd_utils = {
        .build_addsta_hcmd = iwlagn_build_addsta_hcmd,
        .gain_computation = iwlagn_gain_computation,
index efdab6506ae773de33b608ebab40b70ebf098131..90d366e15d2fad1ee89b7d15916cd64072abdbc9 100644 (file)
@@ -628,38 +628,6 @@ struct iwl_mod_params iwlagn_mod_params = {
        /* the rest are 0 by default */
 };
 
-void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
-{
-       unsigned long flags;
-       int i;
-       spin_lock_irqsave(&rxq->lock, flags);
-       INIT_LIST_HEAD(&rxq->rx_free);
-       INIT_LIST_HEAD(&rxq->rx_used);
-       /* Fill the rx_used queue with _all_ of the Rx buffers */
-       for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
-               /* In the reset function, these buffers may have been allocated
-                * to an SKB, so we need to unmap and free potential storage */
-               if (rxq->pool[i].page != NULL) {
-                       dma_unmap_page(priv->bus.dev, rxq->pool[i].page_dma,
-                               PAGE_SIZE << priv->hw_params.rx_page_order,
-                               DMA_FROM_DEVICE);
-                       __iwl_free_pages(priv, rxq->pool[i].page);
-                       rxq->pool[i].page = NULL;
-               }
-               list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
-       }
-
-       for (i = 0; i < RX_QUEUE_SIZE; i++)
-               rxq->queue[i] = NULL;
-
-       /* Set us so that we have processed and used all buffers, but have
-        * not restocked the Rx queue with fresh buffers */
-       rxq->read = rxq->write = 0;
-       rxq->write_actual = 0;
-       rxq->free_count = 0;
-       spin_unlock_irqrestore(&rxq->lock, flags);
-}
-
 int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
 {
        u32 rb_size;
@@ -731,7 +699,6 @@ int iwlagn_hw_nic_init(struct iwl_priv *priv)
 {
        unsigned long flags;
        struct iwl_rx_queue *rxq = &priv->rxq;
-       int ret;
 
        /* nic_init */
        spin_lock_irqsave(&priv->lock, flags);
@@ -747,14 +714,7 @@ int iwlagn_hw_nic_init(struct iwl_priv *priv)
        priv->cfg->ops->lib->apm_ops.config(priv);
 
        /* Allocate the RX queue, or reset if it is already allocated */
-       if (!rxq->bd) {
-               ret = iwl_rx_queue_alloc(priv);
-               if (ret) {
-                       IWL_ERR(priv, "Unable to initialize Rx queue\n");
-                       return -ENOMEM;
-               }
-       } else
-               iwlagn_rx_queue_reset(priv, rxq);
+       priv->trans.ops->rx_init(priv);
 
        iwlagn_rx_replenish(priv);
 
@@ -768,12 +728,8 @@ int iwlagn_hw_nic_init(struct iwl_priv *priv)
        spin_unlock_irqrestore(&priv->lock, flags);
 
        /* Allocate or reset and init all Tx and Command queues */
-       if (!priv->txq) {
-               ret = iwlagn_txq_ctx_alloc(priv);
-               if (ret)
-                       return ret;
-       } else
-               iwlagn_txq_ctx_reset(priv);
+       if (priv->trans.ops->tx_init(priv))
+               return -ENOMEM;
 
        if (priv->cfg->base_params->shadow_reg_enable) {
                /* enable shadow regs in HW */
@@ -949,33 +905,6 @@ void iwlagn_rx_replenish_now(struct iwl_priv *priv)
        iwlagn_rx_queue_restock(priv);
 }
 
-/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
- * If an SKB has been detached, the POOL needs to have its SKB set to NULL
- * This free routine walks the list of POOL entries and if SKB is set to
- * non NULL it is unmapped and freed
- */
-void iwlagn_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
-{
-       int i;
-       for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
-               if (rxq->pool[i].page != NULL) {
-                       dma_unmap_page(priv->bus.dev, rxq->pool[i].page_dma,
-                               PAGE_SIZE << priv->hw_params.rx_page_order,
-                               DMA_FROM_DEVICE);
-                       __iwl_free_pages(priv, rxq->pool[i].page);
-                       rxq->pool[i].page = NULL;
-               }
-       }
-
-       dma_free_coherent(priv->bus.dev, 4 * RX_QUEUE_SIZE,
-                         rxq->bd, rxq->bd_dma);
-       dma_free_coherent(priv->bus.dev,
-                         sizeof(struct iwl_rb_status),
-                         rxq->rb_stts, rxq->rb_stts_dma);
-       rxq->bd = NULL;
-       rxq->rb_stts  = NULL;
-}
-
 int iwlagn_rxq_stop(struct iwl_priv *priv)
 {
 
@@ -1437,17 +1366,14 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        /* set scan bit here for PAN params */
        set_bit(STATUS_SCAN_HW, &priv->status);
 
-       if (priv->cfg->ops->hcmd->set_pan_params) {
-               ret = priv->cfg->ops->hcmd->set_pan_params(priv);
-               if (ret)
-                       return ret;
-       }
+       ret = iwlagn_set_pan_params(priv);
+       if (ret)
+               return ret;
 
        ret = iwl_send_cmd_sync(priv, &cmd);
        if (ret) {
                clear_bit(STATUS_SCAN_HW, &priv->status);
-               if (priv->cfg->ops->hcmd->set_pan_params)
-                       priv->cfg->ops->hcmd->set_pan_params(priv);
+               iwlagn_set_pan_params(priv);
        }
 
        return ret;
index 8fa43d4278114a1bad7ef73ea64020b2695cb8e8..c6bb73a66d9f74a6e6f07fc9ea25bed7112c4f08 100644 (file)
@@ -436,11 +436,9 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
        if (ret)
                return ret;
 
-       if (priv->cfg->ops->hcmd->set_pan_params) {
-               ret = priv->cfg->ops->hcmd->set_pan_params(priv);
-               if (ret)
-                       return ret;
-       }
+       ret = iwlagn_set_pan_params(priv);
+       if (ret)
+               return ret;
 
        if (new_assoc)
                return iwlagn_rxon_connect(priv, ctx);
@@ -483,9 +481,8 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
                 * set up the SM PS mode to OFF if an HT channel is
                 * configured.
                 */
-               if (priv->cfg->ops->hcmd->set_rxon_chain)
-                       for_each_context(priv, ctx)
-                               priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+               for_each_context(priv, ctx)
+                       iwlagn_set_rxon_chain(priv, ctx);
        }
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
@@ -741,8 +738,7 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
                iwl_set_rxon_ht(priv, &priv->current_ht_config);
        }
 
-       if (priv->cfg->ops->hcmd->set_rxon_chain)
-               priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+       iwlagn_set_rxon_chain(priv, ctx);
 
        if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ))
                ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
@@ -821,6 +817,5 @@ void iwlagn_post_scan(struct iwl_priv *priv)
                if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
                        iwlagn_commit_rxon(priv, ctx);
 
-       if (priv->cfg->ops->hcmd->set_pan_params)
-               priv->cfg->ops->hcmd->set_pan_params(priv);
+       iwlagn_set_pan_params(priv);
 }
index d0ac090399e96e005ace1f855f2bcb1daa6e23ff..c05a8d9fbd2e86f9d6264d7967cde21452ebe51c 100644 (file)
@@ -877,96 +877,6 @@ void iwlagn_hw_txq_ctx_free(struct iwl_priv *priv)
        iwl_free_txq_mem(priv);
 }
 
-/**
- * iwlagn_txq_ctx_alloc - allocate TX queue context
- * Allocate all Tx DMA structures and initialize them
- *
- * @param priv
- * @return error code
- */
-int iwlagn_txq_ctx_alloc(struct iwl_priv *priv)
-{
-       int ret;
-       int txq_id, slots_num;
-       unsigned long flags;
-
-       /* Free all tx/cmd queues and keep-warm buffer */
-       iwlagn_hw_txq_ctx_free(priv);
-
-       ret = iwlagn_alloc_dma_ptr(priv, &priv->scd_bc_tbls,
-                               priv->hw_params.scd_bc_tbls_size);
-       if (ret) {
-               IWL_ERR(priv, "Scheduler BC Table allocation failed\n");
-               goto error_bc_tbls;
-       }
-       /* Alloc keep-warm buffer */
-       ret = iwlagn_alloc_dma_ptr(priv, &priv->kw, IWL_KW_SIZE);
-       if (ret) {
-               IWL_ERR(priv, "Keep Warm allocation failed\n");
-               goto error_kw;
-       }
-
-       /* allocate tx queue structure */
-       ret = iwl_alloc_txq_mem(priv);
-       if (ret)
-               goto error;
-
-       spin_lock_irqsave(&priv->lock, flags);
-
-       /* Turn off all Tx DMA fifos */
-       iwlagn_txq_set_sched(priv, 0);
-
-       /* Tell NIC where to find the "keep warm" buffer */
-       iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4);
-
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       /* Alloc and init all Tx queues, including the command queue (#4/#9) */
-       for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
-               slots_num = (txq_id == priv->cmd_queue) ?
-                                       TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
-               ret = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
-                                      txq_id);
-               if (ret) {
-                       IWL_ERR(priv, "Tx %d queue init failed\n", txq_id);
-                       goto error;
-               }
-       }
-
-       return ret;
-
- error:
-       iwlagn_hw_txq_ctx_free(priv);
-       iwlagn_free_dma_ptr(priv, &priv->kw);
- error_kw:
-       iwlagn_free_dma_ptr(priv, &priv->scd_bc_tbls);
- error_bc_tbls:
-       return ret;
-}
-
-void iwlagn_txq_ctx_reset(struct iwl_priv *priv)
-{
-       int txq_id, slots_num;
-       unsigned long flags;
-
-       spin_lock_irqsave(&priv->lock, flags);
-
-       /* Turn off all Tx DMA fifos */
-       iwlagn_txq_set_sched(priv, 0);
-
-       /* Tell NIC where to find the "keep warm" buffer */
-       iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4);
-
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       /* Alloc and init all Tx queues, including the command queue (#4) */
-       for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
-               slots_num = txq_id == priv->cmd_queue ?
-                           TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
-               iwl_tx_queue_reset(priv, &priv->txq[txq_id], slots_num, txq_id);
-       }
-}
-
 /**
  * iwlagn_txq_ctx_stop - Stop all Tx DMA channels
  */
index de8277e322534a5631652675195adf99b8d18b6e..2043c8b3139b2ff8c96e4e318bb9b6526c06f84b 100644 (file)
@@ -386,11 +386,13 @@ static int iwlagn_alive_notify(struct iwl_priv *priv)
        spin_lock_irqsave(&priv->lock, flags);
 
        priv->scd_base_addr = iwl_read_prph(priv, IWLAGN_SCD_SRAM_BASE_ADDR);
-       a = priv->scd_base_addr + IWLAGN_SCD_CONTEXT_DATA_OFFSET;
-       for (; a < priv->scd_base_addr + IWLAGN_SCD_TX_STTS_BITMAP_OFFSET;
+       a = priv->scd_base_addr + IWLAGN_SCD_CONTEXT_MEM_LOWER_BOUND;
+       /* reset conext data memory */
+       for (; a < priv->scd_base_addr + IWLAGN_SCD_CONTEXT_MEM_UPPER_BOUND;
                a += 4)
                iwl_write_targ_mem(priv, a, 0);
-       for (; a < priv->scd_base_addr + IWLAGN_SCD_TRANSLATE_TBL_OFFSET;
+       /* reset tx status memory */
+       for (; a < priv->scd_base_addr + IWLAGN_SCD_TX_STTS_MEM_UPPER_BOUND;
                a += 4)
                iwl_write_targ_mem(priv, a, 0);
        for (; a < priv->scd_base_addr +
index e2f6b2ab0d45778f5656de9f15278d855ecdaa7b..7e6c463abbdff3bdbf658a894045def6c2166a61 100644 (file)
@@ -56,7 +56,7 @@
 #include "iwl-agn-calib.h"
 #include "iwl-agn.h"
 #include "iwl-pci.h"
-
+#include "iwl-trans.h"
 
 /******************************************************************************
  *
@@ -90,12 +90,10 @@ void iwl_update_chain_flags(struct iwl_priv *priv)
 {
        struct iwl_rxon_context *ctx;
 
-       if (priv->cfg->ops->hcmd->set_rxon_chain) {
-               for_each_context(priv, ctx) {
-                       priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
-                       if (ctx->active.rx_chain != ctx->staging.rx_chain)
-                               iwlagn_commit_rxon(priv, ctx);
-               }
+       for_each_context(priv, ctx) {
+               iwlagn_set_rxon_chain(priv, ctx);
+               if (ctx->active.rx_chain != ctx->staging.rx_chain)
+                       iwlagn_commit_rxon(priv, ctx);
        }
 }
 
@@ -260,7 +258,7 @@ static void iwl_bg_bt_runtime_config(struct work_struct *work)
        /* dont send host command if rf-kill is on */
        if (!iwl_is_ready_rf(priv))
                return;
-       priv->cfg->ops->hcmd->send_bt_config(priv);
+       iwlagn_send_advance_bt_config(priv);
 }
 
 static void iwl_bg_bt_full_concurrency(struct work_struct *work)
@@ -287,12 +285,11 @@ static void iwl_bg_bt_full_concurrency(struct work_struct *work)
         * to avoid 3-wire collisions
         */
        for_each_context(priv, ctx) {
-               if (priv->cfg->ops->hcmd->set_rxon_chain)
-                       priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+               iwlagn_set_rxon_chain(priv, ctx);
                iwlagn_commit_rxon(priv, ctx);
        }
 
-       priv->cfg->ops->hcmd->send_bt_config(priv);
+       iwlagn_send_advance_bt_config(priv);
 out:
        mutex_unlock(&priv->mutex);
 }
@@ -2017,7 +2014,7 @@ int iwl_alive_start(struct iwl_priv *priv)
                priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
                priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
                priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
-               priv->cfg->ops->hcmd->send_bt_config(priv);
+               iwlagn_send_advance_bt_config(priv);
                priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
                iwlagn_send_prio_tbl(priv);
 
@@ -2030,7 +2027,13 @@ int iwl_alive_start(struct iwl_priv *priv)
                                         BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
                if (ret)
                        return ret;
+       } else {
+               /*
+                * default is 2-wire BT coexexistence support
+                */
+               iwl_send_bt_config(priv);
        }
+
        if (priv->hw_params.calib_rt_cfg)
                iwlagn_send_calib_cfg_rt(priv, priv->hw_params.calib_rt_cfg);
 
@@ -2039,8 +2042,7 @@ int iwl_alive_start(struct iwl_priv *priv)
        priv->active_rate = IWL_RATES_MASK;
 
        /* Configure Tx antenna selection based on H/W config */
-       if (priv->cfg->ops->hcmd->set_tx_ant)
-               priv->cfg->ops->hcmd->set_tx_ant(priv, priv->cfg->valid_tx_ant);
+       iwlagn_send_tx_ant_config(priv, priv->cfg->valid_tx_ant);
 
        if (iwl_is_associated_ctx(ctx)) {
                struct iwl_rxon_cmd *active_rxon =
@@ -2054,16 +2056,7 @@ int iwl_alive_start(struct iwl_priv *priv)
                for_each_context(priv, tmp)
                        iwl_connection_init_rx_config(priv, tmp);
 
-               if (priv->cfg->ops->hcmd->set_rxon_chain)
-                       priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
-       }
-
-       if (!priv->cfg->bt_params || (priv->cfg->bt_params &&
-           !priv->cfg->bt_params->advanced_bt_coexist)) {
-               /*
-                * default is 2-wire BT coexexistence support
-                */
-               priv->cfg->ops->hcmd->send_bt_config(priv);
+               iwlagn_set_rxon_chain(priv, ctx);
        }
 
        iwl_reset_run_time_calib(priv);
@@ -3288,9 +3281,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
        priv->rx_statistics_jiffies = jiffies;
 
        /* Choose which receivers/antennas to use */
-       if (priv->cfg->ops->hcmd->set_rxon_chain)
-               priv->cfg->ops->hcmd->set_rxon_chain(priv,
-                                       &priv->contexts[IWL_RXON_CTX_BSS]);
+       iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]);
 
        iwl_init_scan_params(priv);
 
@@ -3517,6 +3508,8 @@ int iwl_probe(void *bus_specific, struct iwl_bus_ops *bus_ops,
        priv->bus.ops->set_drv_data(&priv->bus, priv);
        priv->bus.dev = priv->bus.ops->get_dev(&priv->bus);
 
+       iwl_trans_register(&priv->trans);
+
        /* At this point both hw and priv are allocated. */
 
        SET_IEEE80211_DEV(hw, priv->bus.dev);
@@ -3716,8 +3709,7 @@ void __devexit iwl_remove(struct iwl_priv * priv)
 
        iwl_dealloc_ucode(priv);
 
-       if (priv->rxq.bd)
-               iwlagn_rx_queue_free(priv, &priv->rxq);
+       priv->trans.ops->rx_free(priv);
        iwlagn_hw_txq_ctx_free(priv);
 
        iwl_eeprom_free(priv);
@@ -3820,6 +3812,10 @@ MODULE_PARM_DESC(plcp_check, "Check plcp health (default: 1 [enabled])");
 module_param_named(ack_check, iwlagn_mod_params.ack_check, bool, S_IRUGO);
 MODULE_PARM_DESC(ack_check, "Check ack health (default: 0 [disabled])");
 
+module_param_named(wd_disable, iwlagn_mod_params.wd_disable, bool, S_IRUGO);
+MODULE_PARM_DESC(wd_disable,
+               "Disable stuck queue watchdog timer (default: 0 [enabled])");
+
 /*
  * set bt_coex_active to true, uCode will do kill/defer
  * every time the priority line is asserted (BT is sending signals on the
index dcdf2259520fc423f90302e1b12c822d9cd85037..4351151e2a913fdc17abc3afbd8727299f84dc5d 100644 (file)
@@ -182,7 +182,6 @@ void iwlagn_temperature(struct iwl_priv *priv);
 u16 iwlagn_eeprom_calib_version(struct iwl_priv *priv);
 const u8 *iwlagn_eeprom_query_addr(const struct iwl_priv *priv,
                                   size_t offset);
-void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
 int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
 int iwlagn_hw_nic_init(struct iwl_priv *priv);
 int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv);
@@ -194,7 +193,6 @@ void iwlagn_rx_queue_restock(struct iwl_priv *priv);
 void iwlagn_rx_allocate(struct iwl_priv *priv, gfp_t priority);
 void iwlagn_rx_replenish(struct iwl_priv *priv);
 void iwlagn_rx_replenish_now(struct iwl_priv *priv);
-void iwlagn_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
 int iwlagn_rxq_stop(struct iwl_priv *priv);
 int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band);
 void iwl_setup_rx_handlers(struct iwl_priv *priv);
@@ -220,8 +218,6 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
                                struct iwl_rx_mem_buffer *rxb);
 int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index);
 void iwlagn_hw_txq_ctx_free(struct iwl_priv *priv);
-int iwlagn_txq_ctx_alloc(struct iwl_priv *priv);
-void iwlagn_txq_ctx_reset(struct iwl_priv *priv);
 void iwlagn_txq_ctx_stop(struct iwl_priv *priv);
 
 static inline u32 iwl_tx_status_to_mac80211(u32 status)
@@ -260,6 +256,7 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv,
 /* hcmd */
 int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant);
 int iwlagn_send_beacon_cmd(struct iwl_priv *priv);
+int iwlagn_set_pan_params(struct iwl_priv *priv);
 
 /* bt coex */
 void iwlagn_send_advance_bt_config(struct iwl_priv *priv);
index 7f16d120305742094232112073ddbc48c011b8fa..f91e306c2498965652c155cb5b03d534aa06785f 100644 (file)
@@ -585,8 +585,7 @@ static void _iwl_set_rxon_ht(struct iwl_priv *priv,
                rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
        }
 
-       if (priv->cfg->ops->hcmd->set_rxon_chain)
-               priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+       iwlagn_set_rxon_chain(priv, ctx);
 
        IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X "
                        "extension channel offset 0x%x\n",
@@ -1216,8 +1215,7 @@ static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
 {
        iwl_connection_init_rx_config(priv, ctx);
 
-       if (priv->cfg->ops->hcmd->set_rxon_chain)
-               priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
+       iwlagn_set_rxon_chain(priv, ctx);
 
        return iwlagn_commit_rxon(priv, ctx);
 }
@@ -1372,20 +1370,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
 
 }
 
-int iwl_alloc_txq_mem(struct iwl_priv *priv)
-{
-       if (!priv->txq)
-               priv->txq = kzalloc(
-                       sizeof(struct iwl_tx_queue) *
-                               priv->cfg->base_params->num_of_queues,
-                       GFP_KERNEL);
-       if (!priv->txq) {
-               IWL_ERR(priv, "Not enough memory for txq\n");
-               return -ENOMEM;
-       }
-       return 0;
-}
-
 void iwl_free_txq_mem(struct iwl_priv *priv)
 {
        kfree(priv->txq);
@@ -1853,7 +1837,7 @@ void iwl_setup_watchdog(struct iwl_priv *priv)
 {
        unsigned int timeout = priv->cfg->base_params->wd_timeout;
 
-       if (timeout)
+       if (timeout && !iwlagn_mod_params.wd_disable)
                mod_timer(&priv->watchdog,
                          jiffies + msecs_to_jiffies(IWL_WD_TICK(timeout)));
        else
index f881678be762c843986f938d02e97f111b778059..6c21de9f5b60dd4edaac5bf5fc81b7abfd6039a1 100644 (file)
@@ -80,14 +80,6 @@ struct iwl_cmd;
 
 #define IWL_CMD(x) case x: return #x
 
-struct iwl_hcmd_ops {
-       void (*set_rxon_chain)(struct iwl_priv *priv,
-                              struct iwl_rxon_context *ctx);
-       int (*set_tx_ant)(struct iwl_priv *priv, u8 valid_tx_ant);
-       void (*send_bt_config)(struct iwl_priv *priv);
-       int (*set_pan_params)(struct iwl_priv *priv);
-};
-
 struct iwl_hcmd_utils_ops {
        u16 (*build_addsta_hcmd)(const struct iwl_addsta_cmd *cmd, u8 *data);
        void (*gain_computation)(struct iwl_priv *priv,
@@ -146,7 +138,6 @@ struct iwl_nic_ops {
 
 struct iwl_ops {
        const struct iwl_lib_ops *lib;
-       const struct iwl_hcmd_ops *hcmd;
        const struct iwl_hcmd_utils_ops *utils;
        const struct iwl_nic_ops *nic;
 };
@@ -160,6 +151,7 @@ struct iwl_mod_params {
        int restart_fw;         /* def: 1 = restart firmware */
        bool plcp_check;        /* def: true = enable plcp health check */
        bool ack_check;         /* def: false = disable ack health check */
+       bool wd_disable;        /* def: false = enable stuck queue check */
        bool bt_coex_active;    /* def: true = enable bt coex */
        int led_mode;           /* def: 0 = system default */
        bool no_sleep_autoadjust; /* def: true = disable autoadjust */
@@ -336,7 +328,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
 int iwl_mac_change_interface(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             enum nl80211_iftype newtype, bool newp2p);
-int iwl_alloc_txq_mem(struct iwl_priv *priv);
 void iwl_free_txq_mem(struct iwl_priv *priv);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -382,7 +373,6 @@ static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx,
 ******************************************************/
 void iwl_cmd_queue_free(struct iwl_priv *priv);
 void iwl_cmd_queue_unmap(struct iwl_priv *priv);
-int iwl_rx_queue_alloc(struct iwl_priv *priv);
 void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
                                  struct iwl_rx_queue *q);
 int iwl_rx_queue_space(const struct iwl_rx_queue *q);
@@ -396,11 +386,9 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success);
 * TX
 ******************************************************/
 void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq);
-int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
-                     int slots_num, u32 txq_id);
-void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq,
-                       int slots_num, u32 txq_id);
 void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id);
+int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
+                         int count, int slots_num, u32 id);
 void iwl_tx_queue_unmap(struct iwl_priv *priv, int txq_id);
 void iwl_setup_watchdog(struct iwl_priv *priv);
 /*****************************************************
index f1b1128ee1c4dc6de3cd1eaff16084c291bbf68d..c6560e97a62b5705015ee7ba573a4c87a607162c 100644 (file)
@@ -666,7 +666,6 @@ struct iwl_hw_params {
        u16 max_rxq_size;
        u16 max_rxq_log;
        u32 rx_page_order;
-       u32 rx_wrt_ptr_reg;
        u8  max_stations;
        u8  ht40_channel;
        u8  max_beacon_itrvl;   /* in 1024 ms */
@@ -1228,6 +1227,25 @@ struct iwl_bus {
        unsigned int irq;
 };
 
+struct iwl_trans;
+
+/**
+ * struct iwl_trans_ops - transport specific operations
+
+ * @rx_init: inits the rx memory, allocate it if needed
+ * @rx_free: frees the rx memory
+ * @tx_init:inits the tx memory, allocate if needed
+ */
+struct iwl_trans_ops {
+       int (*rx_init)(struct iwl_priv *priv);
+       void (*rx_free)(struct iwl_priv *priv);
+       int (*tx_init)(struct iwl_priv *priv);
+};
+
+struct iwl_trans {
+       const struct iwl_trans_ops *ops;
+};
+
 struct iwl_priv {
 
        /* ieee device used by generic ieee processing code */
@@ -1296,13 +1314,13 @@ struct iwl_priv {
        struct mutex mutex;
 
        struct iwl_bus bus;     /* bus specific data */
+       struct iwl_trans trans;
 
        /* microcode/device supports multiple contexts */
        u8 valid_contexts;
 
        /* command queue number */
        u8 cmd_queue;
-       u8 last_sync_cmd_id;
 
        /* max number of station keys */
        u8 sta_key_max_num;
index e3e5fb614178070b086c73bd3a9656ef0d977cce..107b38e2ee93cff3f8647333d1a2983253abdf22 100644 (file)
@@ -171,6 +171,8 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
        int cmd_idx;
        int ret;
 
+       lockdep_assert_held(&priv->mutex);
+
        if (WARN_ON(cmd->flags & CMD_ASYNC))
                return -EINVAL;
 
@@ -181,16 +183,7 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
        IWL_DEBUG_INFO(priv, "Attempting to send sync command %s\n",
                        get_cmd_string(cmd->id));
 
-       if (test_and_set_bit(STATUS_HCMD_ACTIVE, &priv->status)) {
-               IWL_ERR(priv, "STATUS_HCMD_ACTIVE already set while sending %s"
-                             ". Previous SYNC cmdn is %s\n",
-                       get_cmd_string(cmd->id),
-                       get_cmd_string(priv->last_sync_cmd_id));
-               WARN_ON(1);
-       } else {
-               priv->last_sync_cmd_id = cmd->id;
-       }
-
+       set_bit(STATUS_HCMD_ACTIVE, &priv->status);
        IWL_DEBUG_INFO(priv, "Setting HCMD_ACTIVE for command %s\n",
                        get_cmd_string(cmd->id));
 
index 3b5844b60e7c11286cbe62472b424a909861720f..74911348a2ee31169ca722d6420c2f38aed832d8 100644 (file)
@@ -67,6 +67,7 @@
 #include "iwl-agn.h"
 #include "iwl-core.h"
 #include "iwl-io.h"
+#include "iwl-trans.h"
 
 /* PCI registers */
 #define PCI_CFG_RETRY_TIMEOUT  0x041
@@ -93,7 +94,7 @@ static u16 iwl_pciexp_link_ctrl(struct iwl_bus *bus)
        u16 pci_lnk_ctl;
        struct pci_dev *pci_dev = IWL_BUS_GET_PCI_DEV(bus);
 
-       pos = pci_find_capability(pci_dev, PCI_CAP_ID_EXP);
+       pos = pci_pcie_cap(pci_dev);
        pci_read_config_word(pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
        return pci_lnk_ctl;
 }
index f00d188b2cfc7d7e008ea1c2f8296620d10e6e5d..1cc0ed1f488cac1e615a8c99c1e57afab9730736 100644 (file)
  * the scheduler (especially for queue #4/#9, the command queue, otherwise
  * the driver can't issue commands!):
  */
+#define SCD_MEM_LOWER_BOUND            (0x0000)
 
 /**
  * Max Tx window size is the max number of contiguous TFDs that the scheduler
 #define IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS      (16)
 #define IWLAGN_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK      (0x007F0000)
 
-#define IWLAGN_SCD_CONTEXT_DATA_OFFSET         (0x600)
-#define IWLAGN_SCD_TX_STTS_BITMAP_OFFSET               (0x7B1)
-#define IWLAGN_SCD_TRANSLATE_TBL_OFFSET                (0x7E0)
+/* Context Data */
+#define IWLAGN_SCD_CONTEXT_MEM_LOWER_BOUND     (SCD_MEM_LOWER_BOUND + 0x600)
+#define IWLAGN_SCD_CONTEXT_MEM_UPPER_BOUND     (SCD_MEM_LOWER_BOUND + 0x6A0)
+
+/* Tx status */
+#define IWLAGN_SCD_TX_STTS_MEM_LOWER_BOUND     (SCD_MEM_LOWER_BOUND + 0x6A0)
+#define IWLAGN_SCD_TX_STTS_MEM_UPPER_BOUND     (SCD_MEM_LOWER_BOUND + 0x7E0)
+
+/* Translation Data */
+#define IWLAGN_SCD_TRANS_TBL_MEM_LOWER_BOUND   (SCD_MEM_LOWER_BOUND + 0x7E0)
+#define IWLAGN_SCD_TRANS_TBL_MEM_UPPER_BOUND   (SCD_MEM_LOWER_BOUND + 0x808)
 
 #define IWLAGN_SCD_CONTEXT_QUEUE_OFFSET(x)\
-       (IWLAGN_SCD_CONTEXT_DATA_OFFSET + ((x) * 8))
+       (IWLAGN_SCD_CONTEXT_MEM_LOWER_BOUND + ((x) * 8))
 
 #define IWLAGN_SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \
-       ((IWLAGN_SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffc)
+       ((IWLAGN_SCD_TRANS_TBL_MEM_LOWER_BOUND + ((x) * 2)) & 0xfffc)
 
 #define IWLAGN_SCD_QUEUECHAIN_SEL_ALL(priv)    \
        (((1<<(priv)->hw_params.max_txq_num) - 1) &\
index 3efa7066e9879c36a2a9cef50dcdd46040f65df5..87148bb3f62817d653a278a9efef8cc3f21a04eb 100644 (file)
@@ -134,7 +134,6 @@ int iwl_rx_queue_space(const struct iwl_rx_queue *q)
 void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q)
 {
        unsigned long flags;
-       u32 rx_wrt_ptr_reg = priv->hw_params.rx_wrt_ptr_reg;
        u32 reg;
 
        spin_lock_irqsave(&q->lock, flags);
@@ -146,7 +145,7 @@ void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q
                /* shadow register enabled */
                /* Device expects a multiple of 8 */
                q->write_actual = (q->write & ~0x7);
-               iwl_write32(priv, rx_wrt_ptr_reg, q->write_actual);
+               iwl_write32(priv, FH_RSCSR_CHNL0_WPTR, q->write_actual);
        } else {
                /* If power-saving is in use, make sure device is awake */
                if (test_bit(STATUS_POWER_PMI, &priv->status)) {
@@ -162,14 +161,14 @@ void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q
                        }
 
                        q->write_actual = (q->write & ~0x7);
-                       iwl_write_direct32(priv, rx_wrt_ptr_reg,
+                       iwl_write_direct32(priv, FH_RSCSR_CHNL0_WPTR,
                                        q->write_actual);
 
                /* Else device is assumed to be awake */
                } else {
                        /* Device expects a multiple of 8 */
                        q->write_actual = (q->write & ~0x7);
-                       iwl_write_direct32(priv, rx_wrt_ptr_reg,
+                       iwl_write_direct32(priv, FH_RSCSR_CHNL0_WPTR,
                                q->write_actual);
                }
        }
@@ -179,46 +178,6 @@ void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q
        spin_unlock_irqrestore(&q->lock, flags);
 }
 
-int iwl_rx_queue_alloc(struct iwl_priv *priv)
-{
-       struct iwl_rx_queue *rxq = &priv->rxq;
-       struct device *dev = priv->bus.dev;
-       int i;
-
-       spin_lock_init(&rxq->lock);
-       INIT_LIST_HEAD(&rxq->rx_free);
-       INIT_LIST_HEAD(&rxq->rx_used);
-
-       /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */
-       rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma,
-                                    GFP_KERNEL);
-       if (!rxq->bd)
-               goto err_bd;
-
-       rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct iwl_rb_status),
-                                         &rxq->rb_stts_dma, GFP_KERNEL);
-       if (!rxq->rb_stts)
-               goto err_rb;
-
-       /* Fill the rx_used queue with _all_ of the Rx buffers */
-       for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
-               list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
-
-       /* Set us so that we have processed and used all buffers, but have
-        * not restocked the Rx queue with fresh buffers */
-       rxq->read = rxq->write = 0;
-       rxq->write_actual = 0;
-       rxq->free_count = 0;
-       rxq->need_update = 0;
-       return 0;
-
-err_rb:
-       dma_free_coherent(dev, 4 * RX_QUEUE_SIZE, rxq->bd,
-                         rxq->bd_dma);
-err_bd:
-       return -ENOMEM;
-}
-
 /******************************************************************************
  *
  * Generic RX handler implementations
index 160911a3716a6b5982a5341496a89680b6e2816c..d825188e521551b5979dd953f82e5d62c0bee193 100644 (file)
 #include <linux/types.h>
 
 
-/* Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and
+/*
+ * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and
  * from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX).
- * The command ID is carried with IWL_TM_ATTR_COMMAND. There are three types of
- * of command from user space and two types of command from kernel space.
- * See below.
+ * The command ID is carried with IWL_TM_ATTR_COMMAND.
+ *
+ * @IWL_TM_CMD_APP2DEV_UCODE:
+ *     commands from user application to the uCode,
+ *     the actual uCode host command ID is carried with
+ *     IWL_TM_ATTR_UCODE_CMD_ID
+ *
+ * @IWL_TM_CMD_APP2DEV_REG_READ32:
+ * @IWL_TM_CMD_APP2DEV_REG_WRITE32:
+ * @IWL_TM_CMD_APP2DEV_REG_WRITE8:
+ *     commands from user applicaiton to access register
+ *
+ * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name
+ * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image
+ * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration
+ * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image
+ * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data
+ * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS
+ *     commands fom user space for pure driver level operations
+ *
+ * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
+ * @IWL_TM_CMD_APP2DEV_END_TRACE:
+ * @IWL_TM_CMD_APP2DEV_READ_TRACE:
+ *     commands fom user space for uCode trace operations
+ *
+ * @IWL_TM_CMD_DEV2APP_SYNC_RSP:
+ *     commands from kernel space to carry the synchronous response
+ *     to user application
+ * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT:
+ *     commands from kernel space to multicast the spontaneous messages
+ *     to user application
+ * @IWL_TM_CMD_DEV2APP_EEPROM_RSP:
+ *     commands from kernel space to carry the eeprom response
+ *     to user application
  */
 enum iwl_tm_cmd_t {
-       /* commands from user application to the uCode,
-        * the actual uCode host command ID is carried with
-        * IWL_TM_ATTR_UCODE_CMD_ID */
-       IWL_TM_CMD_APP2DEV_UCODE = 1,
-
-       /* commands from user applicaiton to access register */
-       IWL_TM_CMD_APP2DEV_REG_READ32,
-       IWL_TM_CMD_APP2DEV_REG_WRITE32,
-       IWL_TM_CMD_APP2DEV_REG_WRITE8,
-
-       /* commands fom user space for pure driver level operations */
-       IWL_TM_CMD_APP2DEV_GET_DEVICENAME,
-       IWL_TM_CMD_APP2DEV_LOAD_INIT_FW,
-       IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB,
-       IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW,
-       IWL_TM_CMD_APP2DEV_GET_EEPROM,
-       IWL_TM_CMD_APP2DEV_FIXRATE_REQ,
-       /* if there is other new command for the driver layer operation,
-        * append them here */
-
-       /* commands fom user space for uCode trace operations */
-       IWL_TM_CMD_APP2DEV_BEGIN_TRACE,
-       IWL_TM_CMD_APP2DEV_END_TRACE,
-       IWL_TM_CMD_APP2DEV_READ_TRACE,
-
-       /* commands from kernel space to carry the synchronous response
-        * to user application */
-       IWL_TM_CMD_DEV2APP_SYNC_RSP,
-
-       /* commands from kernel space to multicast the spontaneous messages
-        * to user application */
-       IWL_TM_CMD_DEV2APP_UCODE_RX_PKT,
-
-       /* commands from kernel space to carry the eeprom response
-        * to user application */
-       IWL_TM_CMD_DEV2APP_EEPROM_RSP,
-
-       IWL_TM_CMD_MAX,
+       IWL_TM_CMD_APP2DEV_UCODE                = 1,
+       IWL_TM_CMD_APP2DEV_REG_READ32           = 2,
+       IWL_TM_CMD_APP2DEV_REG_WRITE32          = 3,
+       IWL_TM_CMD_APP2DEV_REG_WRITE8           = 4,
+       IWL_TM_CMD_APP2DEV_GET_DEVICENAME       = 5,
+       IWL_TM_CMD_APP2DEV_LOAD_INIT_FW         = 6,
+       IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB       = 7,
+       IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW      = 8,
+       IWL_TM_CMD_APP2DEV_GET_EEPROM           = 9,
+       IWL_TM_CMD_APP2DEV_FIXRATE_REQ          = 10,
+       IWL_TM_CMD_APP2DEV_BEGIN_TRACE          = 11,
+       IWL_TM_CMD_APP2DEV_END_TRACE            = 12,
+       IWL_TM_CMD_APP2DEV_READ_TRACE           = 13,
+       IWL_TM_CMD_DEV2APP_SYNC_RSP             = 14,
+       IWL_TM_CMD_DEV2APP_UCODE_RX_PKT         = 15,
+       IWL_TM_CMD_DEV2APP_EEPROM_RSP           = 16,
+       IWL_TM_CMD_MAX                          = 17,
 };
 
+/*
+ * Atrribute filed in testmode command
+ * See enum iwl_tm_cmd_t.
+ *
+ * @IWL_TM_ATTR_NOT_APPLICABLE:
+ *     The attribute is not applicable or invalid
+ * @IWL_TM_ATTR_COMMAND:
+ *     From user space to kernel space:
+ *     the command either destines to ucode, driver, or register;
+ *     From kernel space to user space:
+ *     the command either carries synchronous response,
+ *     or the spontaneous message multicast from the device;
+ *
+ * @IWL_TM_ATTR_UCODE_CMD_ID:
+ * @IWL_TM_ATTR_UCODE_CMD_DATA:
+ *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE,
+ *     The mandatory fields are :
+ *     IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID;
+ *     IWL_TM_ATTR_COMMAND_FLAG for the flags of the commands;
+ *     The optional fields are:
+ *     IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload
+ *     to the ucode
+ *
+ * @IWL_TM_ATTR_REG_OFFSET:
+ * @IWL_TM_ATTR_REG_VALUE8:
+ * @IWL_TM_ATTR_REG_VALUE32:
+ *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX,
+ *     The mandatory fields are:
+ *     IWL_TM_ATTR_REG_OFFSET for the offset of the target register;
+ *     IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value
+ *
+ * @IWL_TM_ATTR_SYNC_RSP:
+ *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP,
+ *     The mandatory fields are:
+ *     IWL_TM_ATTR_SYNC_RSP for the data content responding to the user
+ *     application command
+ *
+ * @IWL_TM_ATTR_UCODE_RX_PKT:
+ *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT,
+ *     The mandatory fields are:
+ *     IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user
+ *     application
+ *
+ * @IWL_TM_ATTR_EEPROM:
+ *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM,
+ *     The mandatory fields are:
+ *     IWL_TM_ATTR_EEPROM for the data content responging to the user
+ *     application
+ *
+ * @IWL_TM_ATTR_TRACE_ADDR:
+ * @IWL_TM_ATTR_TRACE_SIZE:
+ * @IWL_TM_ATTR_TRACE_DUMP:
+ *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE,
+ *     The mandatory fields are:
+ *     IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address
+ *     IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size
+ *     IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump
+ *
+ * @IWL_TM_ATTR_FIXRATE:
+ *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ,
+ *     The mandatory fields are:
+ *     IWL_TM_ATTR_FIXRATE for the fixed rate
+ *
+ */
 enum iwl_tm_attr_t {
-       IWL_TM_ATTR_NOT_APPLICABLE = 0,
-
-       /* From user space to kernel space:
-        * the command either destines to ucode, driver, or register;
-        * See enum iwl_tm_cmd_t.
-        *
-        * From kernel space to user space:
-        * the command either carries synchronous response,
-        * or the spontaneous message multicast from the device;
-        * See enum iwl_tm_cmd_t. */
-       IWL_TM_ATTR_COMMAND,
-
-       /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE,
-        * The mandatory fields are :
-        * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID;
-        * IWL_TM_ATTR_COMMAND_FLAG for the flags of the commands;
-        * The optional fields are:
-        * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload
-        * to the ucode */
-       IWL_TM_ATTR_UCODE_CMD_ID,
-       IWL_TM_ATTR_UCODE_CMD_DATA,
-
-       /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX,
-        * The mandatory fields are:
-        * IWL_TM_ATTR_REG_OFFSET for the offset of the target register;
-        * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value */
-       IWL_TM_ATTR_REG_OFFSET,
-       IWL_TM_ATTR_REG_VALUE8,
-       IWL_TM_ATTR_REG_VALUE32,
-
-       /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP,
-        * The mandatory fields are:
-        * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user
-        * application command */
-       IWL_TM_ATTR_SYNC_RSP,
-       /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT,
-        * The mandatory fields are:
-        * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user
-        * application */
-       IWL_TM_ATTR_UCODE_RX_PKT,
-
-       /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM,
-        * The mandatory fields are:
-        * IWL_TM_ATTR_EEPROM for the data content responging to the user
-        * application */
-       IWL_TM_ATTR_EEPROM,
-
-       /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE,
-        * The mandatory fields are:
-        * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address
-        */
-       IWL_TM_ATTR_TRACE_ADDR,
-       IWL_TM_ATTR_TRACE_SIZE,
-       IWL_TM_ATTR_TRACE_DUMP,
-
-       /* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ,
-        * The mandatory fields are:
-        * IWL_TM_ATTR_FIXRATE for the fixed rate
-        */
-       IWL_TM_ATTR_FIXRATE,
-
-       IWL_TM_ATTR_MAX,
+       IWL_TM_ATTR_NOT_APPLICABLE              = 0,
+       IWL_TM_ATTR_COMMAND                     = 1,
+       IWL_TM_ATTR_UCODE_CMD_ID                = 2,
+       IWL_TM_ATTR_UCODE_CMD_DATA              = 3,
+       IWL_TM_ATTR_REG_OFFSET                  = 4,
+       IWL_TM_ATTR_REG_VALUE8                  = 5,
+       IWL_TM_ATTR_REG_VALUE32                 = 6,
+       IWL_TM_ATTR_SYNC_RSP                    = 7,
+       IWL_TM_ATTR_UCODE_RX_PKT                = 8,
+       IWL_TM_ATTR_EEPROM                      = 9,
+       IWL_TM_ATTR_TRACE_ADDR                  = 10,
+       IWL_TM_ATTR_TRACE_SIZE                  = 11,
+       IWL_TM_ATTR_TRACE_DUMP                  = 12,
+       IWL_TM_ATTR_FIXRATE                     = 13,
+       IWL_TM_ATTR_MAX                         = 14,
 };
 
 /* uCode trace buffer */
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c
new file mode 100644 (file)
index 0000000..7b7b97d
--- /dev/null
@@ -0,0 +1,423 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "iwl-dev.h"
+#include "iwl-trans.h"
+#include "iwl-core.h"
+#include "iwl-helpers.h"
+/*TODO remove uneeded includes when the transport layer tx_free will be here */
+#include "iwl-agn.h"
+
+static int iwl_trans_rx_alloc(struct iwl_priv *priv)
+{
+       struct iwl_rx_queue *rxq = &priv->rxq;
+       struct device *dev = priv->bus.dev;
+
+       memset(&priv->rxq, 0, sizeof(priv->rxq));
+
+       spin_lock_init(&rxq->lock);
+       INIT_LIST_HEAD(&rxq->rx_free);
+       INIT_LIST_HEAD(&rxq->rx_used);
+
+       if (WARN_ON(rxq->bd || rxq->rb_stts))
+               return -EINVAL;
+
+       /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */
+       rxq->bd = dma_alloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
+                                    &rxq->bd_dma, GFP_KERNEL);
+       if (!rxq->bd)
+               goto err_bd;
+       memset(rxq->bd, 0, sizeof(__le32) * RX_QUEUE_SIZE);
+
+       /*Allocate the driver's pointer to receive buffer status */
+       rxq->rb_stts = dma_alloc_coherent(dev, sizeof(*rxq->rb_stts),
+                                         &rxq->rb_stts_dma, GFP_KERNEL);
+       if (!rxq->rb_stts)
+               goto err_rb_stts;
+       memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
+
+       return 0;
+
+err_rb_stts:
+       dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
+                       rxq->bd, rxq->bd_dma);
+       memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma));
+       rxq->bd = NULL;
+err_bd:
+       return -ENOMEM;
+}
+
+static void iwl_trans_rxq_free_rx_bufs(struct iwl_priv *priv)
+{
+       struct iwl_rx_queue *rxq = &priv->rxq;
+       int i;
+
+       /* Fill the rx_used queue with _all_ of the Rx buffers */
+       for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
+               /* In the reset function, these buffers may have been allocated
+                * to an SKB, so we need to unmap and free potential storage */
+               if (rxq->pool[i].page != NULL) {
+                       dma_unmap_page(priv->bus.dev, rxq->pool[i].page_dma,
+                               PAGE_SIZE << priv->hw_params.rx_page_order,
+                               DMA_FROM_DEVICE);
+                       __iwl_free_pages(priv, rxq->pool[i].page);
+                       rxq->pool[i].page = NULL;
+               }
+               list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+       }
+}
+
+static int iwl_trans_rx_init(struct iwl_priv *priv)
+{
+       struct iwl_rx_queue *rxq = &priv->rxq;
+       int i, err;
+       unsigned long flags;
+
+       if (!rxq->bd) {
+               err = iwl_trans_rx_alloc(priv);
+               if (err)
+                       return err;
+       }
+
+       spin_lock_irqsave(&rxq->lock, flags);
+       INIT_LIST_HEAD(&rxq->rx_free);
+       INIT_LIST_HEAD(&rxq->rx_used);
+
+       iwl_trans_rxq_free_rx_bufs(priv);
+
+       for (i = 0; i < RX_QUEUE_SIZE; i++)
+               rxq->queue[i] = NULL;
+
+       /* Set us so that we have processed and used all buffers, but have
+        * not restocked the Rx queue with fresh buffers */
+       rxq->read = rxq->write = 0;
+       rxq->write_actual = 0;
+       rxq->free_count = 0;
+       spin_unlock_irqrestore(&rxq->lock, flags);
+
+       return 0;
+}
+
+static void iwl_trans_rx_free(struct iwl_priv *priv)
+{
+       struct iwl_rx_queue *rxq = &priv->rxq;
+       unsigned long flags;
+
+       /*if rxq->bd is NULL, it means that nothing has been allocated,
+        * exit now */
+       if (!rxq->bd) {
+               IWL_DEBUG_INFO(priv, "Free NULL rx context\n");
+               return;
+       }
+
+       spin_lock_irqsave(&rxq->lock, flags);
+       iwl_trans_rxq_free_rx_bufs(priv);
+       spin_unlock_irqrestore(&rxq->lock, flags);
+
+       dma_free_coherent(priv->bus.dev, sizeof(__le32) * RX_QUEUE_SIZE,
+                         rxq->bd, rxq->bd_dma);
+       memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma));
+       rxq->bd = NULL;
+
+       if (rxq->rb_stts)
+               dma_free_coherent(priv->bus.dev,
+                                 sizeof(struct iwl_rb_status),
+                                 rxq->rb_stts, rxq->rb_stts_dma);
+       else
+               IWL_DEBUG_INFO(priv, "Free rxq->rb_stts which is NULL\n");
+       memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma));
+       rxq->rb_stts = NULL;
+}
+
+/* TODO:remove this code duplication */
+static inline int iwlagn_alloc_dma_ptr(struct iwl_priv *priv,
+                                   struct iwl_dma_ptr *ptr, size_t size)
+{
+       if (WARN_ON(ptr->addr))
+               return -EINVAL;
+
+       ptr->addr = dma_alloc_coherent(priv->bus.dev, size,
+                                      &ptr->dma, GFP_KERNEL);
+       if (!ptr->addr)
+               return -ENOMEM;
+       ptr->size = size;
+       return 0;
+}
+
+static int iwl_trans_txq_alloc(struct iwl_priv *priv, struct iwl_tx_queue *txq,
+                     int slots_num, u32 txq_id)
+{
+       size_t tfd_sz = priv->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX;
+       int i;
+
+       if (WARN_ON(txq->meta || txq->cmd || txq->txb || txq->tfds))
+               return -EINVAL;
+
+       txq->meta = kzalloc(sizeof(txq->meta[0]) * slots_num,
+                           GFP_KERNEL);
+       txq->cmd = kzalloc(sizeof(txq->cmd[0]) * slots_num,
+                          GFP_KERNEL);
+
+       if (!txq->meta || !txq->cmd)
+               goto error;
+
+       for (i = 0; i < slots_num; i++) {
+               txq->cmd[i] = kmalloc(sizeof(struct iwl_device_cmd),
+                                       GFP_KERNEL);
+               if (!txq->cmd[i])
+                       goto error;
+       }
+
+       /* Alloc driver data array and TFD circular buffer */
+       /* Driver private data, only for Tx (not command) queues,
+        * not shared with device. */
+       if (txq_id != priv->cmd_queue) {
+               txq->txb = kzalloc(sizeof(txq->txb[0]) *
+                                  TFD_QUEUE_SIZE_MAX, GFP_KERNEL);
+               if (!txq->txb) {
+                       IWL_ERR(priv, "kmalloc for auxiliary BD "
+                                 "structures failed\n");
+                       goto error;
+               }
+       } else {
+               txq->txb = NULL;
+       }
+
+       /* Circular buffer of transmit frame descriptors (TFDs),
+        * shared with device */
+       txq->tfds = dma_alloc_coherent(priv->bus.dev, tfd_sz, &txq->q.dma_addr,
+                                      GFP_KERNEL);
+       if (!txq->tfds) {
+               IWL_ERR(priv, "dma_alloc_coherent(%zd) failed\n", tfd_sz);
+               goto error;
+       }
+       txq->q.id = txq_id;
+
+       return 0;
+error:
+       kfree(txq->txb);
+       txq->txb = NULL;
+       /* since txq->cmd has been zeroed,
+        * all non allocated cmd[i] will be NULL */
+       if (txq->cmd)
+               for (i = 0; i < slots_num; i++)
+                       kfree(txq->cmd[i]);
+       kfree(txq->meta);
+       kfree(txq->cmd);
+       txq->meta = NULL;
+       txq->cmd = NULL;
+
+       return -ENOMEM;
+
+}
+
+static int iwl_trans_txq_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
+                     int slots_num, u32 txq_id)
+{
+       int ret;
+
+       txq->need_update = 0;
+       memset(txq->meta, 0, sizeof(txq->meta[0]) * slots_num);
+
+       /*
+        * For the default queues 0-3, set up the swq_id
+        * already -- all others need to get one later
+        * (if they need one at all).
+        */
+       if (txq_id < 4)
+               iwl_set_swq_id(txq, txq_id, txq_id);
+
+       /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
+        * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
+       BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
+
+       /* Initialize queue's high/low-water marks, and head/tail indexes */
+       ret = iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num,
+                       txq_id);
+       if (ret)
+               return ret;
+
+       /*
+        * Tell nic where to find circular buffer of Tx Frame Descriptors for
+        * given Tx queue, and enable the DMA channel used for that queue.
+        * Circular buffer (TFD queue in DRAM) physical base address */
+       iwl_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id),
+                            txq->q.dma_addr >> 8);
+
+       return 0;
+}
+
+/**
+ * iwl_trans_tx_alloc - allocate TX context
+ * Allocate all Tx DMA structures and initialize them
+ *
+ * @param priv
+ * @return error code
+ */
+static int iwl_trans_tx_alloc(struct iwl_priv *priv)
+{
+       int ret;
+       int txq_id, slots_num;
+
+       /*It is not allowed to alloc twice, so warn when this happens.
+        * We cannot rely on the previous allocation, so free and fail */
+       if (WARN_ON(priv->txq)) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       ret = iwlagn_alloc_dma_ptr(priv, &priv->scd_bc_tbls,
+                               priv->hw_params.scd_bc_tbls_size);
+       if (ret) {
+               IWL_ERR(priv, "Scheduler BC Table allocation failed\n");
+               goto error;
+       }
+
+       /* Alloc keep-warm buffer */
+       ret = iwlagn_alloc_dma_ptr(priv, &priv->kw, IWL_KW_SIZE);
+       if (ret) {
+               IWL_ERR(priv, "Keep Warm allocation failed\n");
+               goto error;
+       }
+
+       priv->txq = kzalloc(sizeof(struct iwl_tx_queue) *
+                       priv->cfg->base_params->num_of_queues, GFP_KERNEL);
+       if (!priv->txq) {
+               IWL_ERR(priv, "Not enough memory for txq\n");
+               ret = ENOMEM;
+               goto error;
+       }
+
+       /* Alloc and init all Tx queues, including the command queue (#4/#9) */
+       for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
+               slots_num = (txq_id == priv->cmd_queue) ?
+                                       TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
+               ret = iwl_trans_txq_alloc(priv, &priv->txq[txq_id], slots_num,
+                                      txq_id);
+               if (ret) {
+                       IWL_ERR(priv, "Tx %d queue alloc failed\n", txq_id);
+                       goto error;
+               }
+       }
+
+       return 0;
+
+error:
+       iwlagn_hw_txq_ctx_free(priv);
+
+       return ret;
+}
+static int iwl_trans_tx_init(struct iwl_priv *priv)
+{
+       int ret;
+       int txq_id, slots_num;
+       unsigned long flags;
+       bool alloc = false;
+
+       if (!priv->txq) {
+               ret = iwl_trans_tx_alloc(priv);
+               if (ret)
+                       goto error;
+               alloc = true;
+       }
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       /* Turn off all Tx DMA fifos */
+       iwl_write_prph(priv, IWLAGN_SCD_TXFACT, 0);
+
+       /* Tell NIC where to find the "keep warm" buffer */
+       iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* Alloc and init all Tx queues, including the command queue (#4/#9) */
+       for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
+               slots_num = (txq_id == priv->cmd_queue) ?
+                                       TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
+               ret = iwl_trans_txq_init(priv, &priv->txq[txq_id], slots_num,
+                                      txq_id);
+               if (ret) {
+                       IWL_ERR(priv, "Tx %d queue init failed\n", txq_id);
+                       goto error;
+               }
+       }
+
+       return 0;
+error:
+       /*Upon error, free only if we allocated something */
+       if (alloc)
+               iwlagn_hw_txq_ctx_free(priv);
+       return ret;
+}
+
+static const struct iwl_trans_ops trans_ops = {
+       .rx_init = iwl_trans_rx_init,
+       .rx_free = iwl_trans_rx_free,
+
+       .tx_init = iwl_trans_tx_init,
+};
+
+void iwl_trans_register(struct iwl_trans *trans)
+{
+       trans->ops = &trans_ops;
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
new file mode 100644 (file)
index 0000000..bec494c
--- /dev/null
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+void iwl_trans_register(struct iwl_trans *trans);
index e72d2279fc5d11b7e41c754fc25b4840bc11b2b9..db5abaa2ff7e1c6d916982054cc0c2a17dc0079c 100644 (file)
@@ -220,24 +220,6 @@ int iwlagn_txq_attach_buf_to_tfd(struct iwl_priv *priv,
        return 0;
 }
 
-/*
- * Tell nic where to find circular buffer of Tx Frame Descriptors for
- * given Tx queue, and enable the DMA channel used for that queue.
- *
- * supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA
- * channels supported in hardware.
- */
-static int iwlagn_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq)
-{
-       int txq_id = txq->q.id;
-
-       /* Circular buffer (TFD queue in DRAM) physical base address */
-       iwl_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id),
-                            txq->q.dma_addr >> 8);
-
-       return 0;
-}
-
 /**
  * iwl_tx_queue_unmap -  Unmap any remaining DMA mappings and free skb's
  */
@@ -392,11 +374,10 @@ int iwl_queue_space(const struct iwl_queue *q)
        return s;
 }
 
-
 /**
  * iwl_queue_init - Initialize queue's high/low-water and read/write indexes
  */
-static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
+int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
                          int count, int slots_num, u32 id)
 {
        q->n_bd = count;
@@ -426,124 +407,6 @@ static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
        return 0;
 }
 
-/**
- * iwl_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue
- */
-static int iwl_tx_queue_alloc(struct iwl_priv *priv,
-                             struct iwl_tx_queue *txq, u32 id)
-{
-       struct device *dev = priv->bus.dev;
-       size_t tfd_sz = priv->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX;
-
-       /* Driver private data, only for Tx (not command) queues,
-        * not shared with device. */
-       if (id != priv->cmd_queue) {
-               txq->txb = kzalloc(sizeof(txq->txb[0]) *
-                                  TFD_QUEUE_SIZE_MAX, GFP_KERNEL);
-               if (!txq->txb) {
-                       IWL_ERR(priv, "kmalloc for auxiliary BD "
-                                 "structures failed\n");
-                       goto error;
-               }
-       } else {
-               txq->txb = NULL;
-       }
-
-       /* Circular buffer of transmit frame descriptors (TFDs),
-        * shared with device */
-       txq->tfds = dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr,
-                                      GFP_KERNEL);
-       if (!txq->tfds) {
-               IWL_ERR(priv, "dma_alloc_coherent(%zd) failed\n", tfd_sz);
-               goto error;
-       }
-       txq->q.id = id;
-
-       return 0;
-
- error:
-       kfree(txq->txb);
-       txq->txb = NULL;
-
-       return -ENOMEM;
-}
-
-/**
- * iwl_tx_queue_init - Allocate and initialize one tx/cmd queue
- */
-int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
-                     int slots_num, u32 txq_id)
-{
-       int i, len;
-       int ret;
-
-       txq->meta = kzalloc(sizeof(struct iwl_cmd_meta) * slots_num,
-                           GFP_KERNEL);
-       txq->cmd = kzalloc(sizeof(struct iwl_device_cmd *) * slots_num,
-                          GFP_KERNEL);
-
-       if (!txq->meta || !txq->cmd)
-               goto out_free_arrays;
-
-       len = sizeof(struct iwl_device_cmd);
-       for (i = 0; i < slots_num; i++) {
-               txq->cmd[i] = kmalloc(len, GFP_KERNEL);
-               if (!txq->cmd[i])
-                       goto err;
-       }
-
-       /* Alloc driver data array and TFD circular buffer */
-       ret = iwl_tx_queue_alloc(priv, txq, txq_id);
-       if (ret)
-               goto err;
-
-       txq->need_update = 0;
-
-       /*
-        * For the default queues 0-3, set up the swq_id
-        * already -- all others need to get one later
-        * (if they need one at all).
-        */
-       if (txq_id < 4)
-               iwl_set_swq_id(txq, txq_id, txq_id);
-
-       /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
-        * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
-       BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
-
-       /* Initialize queue's high/low-water marks, and head/tail indexes */
-       ret = iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id);
-       if (ret)
-               return ret;
-
-       /* Tell device where to find queue */
-       iwlagn_tx_queue_init(priv, txq);
-
-       return 0;
-err:
-       for (i = 0; i < slots_num; i++)
-               kfree(txq->cmd[i]);
-out_free_arrays:
-       kfree(txq->meta);
-       kfree(txq->cmd);
-
-       return -ENOMEM;
-}
-
-void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq,
-                       int slots_num, u32 txq_id)
-{
-       memset(txq->meta, 0, sizeof(struct iwl_cmd_meta) * slots_num);
-
-       txq->need_update = 0;
-
-       /* Initialize queue's high/low-water marks, and head/tail indexes */
-       iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id);
-
-       /* Tell device where to find queue */
-       iwlagn_tx_queue_init(priv, txq);
-}
-
 /*************** HOST COMMAND QUEUE FUNCTIONS   *****/
 
 /**
index c925376fcaaeee8d2fad78e975f80dafae331608..524f78f4ee69b681169f2b8313ed1c8a3ac5b294 100644 (file)
 
 #define SDIO_MP_AGGR_DEF_PKT_LIMIT     8
 
-#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE        (4096)     /* 4K */
+#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE        (8192)     /* 8K */
 
 /* Multi port RX aggregation buffer size */
-#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE        (4096)     /* 4K */
+#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE        (16384)    /* 16K */
 
 /* Misc. Config Register : Auto Re-enable interrupts */
 #define AUTO_RE_ENABLE_INT              BIT(4)
index 937f9e8bf05f51ba38fda4d7e5517f292251960f..76bcc3547976703842747ae4369d47bf734da3f1 100644 (file)
@@ -1723,6 +1723,7 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
        .set_antenna            = rt2x00mac_set_antenna,
        .get_antenna            = rt2x00mac_get_antenna,
        .get_ringparam          = rt2x00mac_get_ringparam,
+       .tx_frames_pending      = rt2x00mac_tx_frames_pending,
 };
 
 static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
index d27d7b8ba3b648f88331ff6e4be7b0c8eeadf2dd..c288d951c03433f15196d7dd708f78d8c973d351 100644 (file)
@@ -2016,6 +2016,7 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
        .set_antenna            = rt2x00mac_set_antenna,
        .get_antenna            = rt2x00mac_get_antenna,
        .get_ringparam          = rt2x00mac_get_ringparam,
+       .tx_frames_pending      = rt2x00mac_tx_frames_pending,
 };
 
 static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
index 15237c275486467b7971a8495480c01863dbcf9e..53c5f878f61d56b134956d489ad43d49911d48aa 100644 (file)
@@ -1827,6 +1827,7 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = {
        .set_antenna            = rt2x00mac_set_antenna,
        .get_antenna            = rt2x00mac_get_antenna,
        .get_ringparam          = rt2x00mac_get_ringparam,
+       .tx_frames_pending      = rt2x00mac_tx_frames_pending,
 };
 
 static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
index 9ccc53733bae556a9ea4e7e4a47a64cf16ec6c10..ebc17ad61dec4612d0bbe872588c4f0e751d4fb4 100644 (file)
@@ -1031,6 +1031,7 @@ static const struct ieee80211_ops rt2800pci_mac80211_ops = {
        .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 rt2800pci_rt2800_ops = {
@@ -1160,6 +1161,7 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
 #endif
 #ifdef CONFIG_RT2800PCI_RT53XX
        { PCI_DEVICE(0x1814, 0x5390) },
+       { PCI_DEVICE(0x1814, 0x539f) },
 #endif
        { 0, }
 };
index 6e9229830a2956d99527883523d363a89a551aa5..59e77797c0fa3eaf825aeda1eaf911adf19e4491 100644 (file)
@@ -757,6 +757,7 @@ static const struct ieee80211_ops rt2800usb_mac80211_ops = {
        .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 rt2800usb_rt2800_ops = {
@@ -1020,6 +1021,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x0df6, 0x0048) },
        { USB_DEVICE(0x0df6, 0x0051) },
        { USB_DEVICE(0x0df6, 0x005f) },
+       { USB_DEVICE(0x0df6, 0x0060) },
        /* SMC */
        { USB_DEVICE(0x083a, 0x6618) },
        { USB_DEVICE(0x083a, 0x7511) },
@@ -1076,6 +1078,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x148f, 0x3572) },
        /* Sitecom */
        { USB_DEVICE(0x0df6, 0x0041) },
+       { USB_DEVICE(0x0df6, 0x0062) },
        /* Toshiba */
        { USB_DEVICE(0x0930, 0x0a07) },
        /* Zinwell */
@@ -1174,8 +1177,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x0df6, 0x004a) },
        { USB_DEVICE(0x0df6, 0x004d) },
        { USB_DEVICE(0x0df6, 0x0053) },
-       { USB_DEVICE(0x0df6, 0x0060) },
-       { USB_DEVICE(0x0df6, 0x0062) },
        /* SMC */
        { USB_DEVICE(0x083a, 0xa512) },
        { USB_DEVICE(0x083a, 0xc522) },
index 4efaf886fb8971cf0d49c6e0610935ff69355f88..f82bfeb79ebb67dc73c8d85cb434ab0b0cdacb7e 100644 (file)
@@ -1277,6 +1277,7 @@ int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
 int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
 void rt2x00mac_get_ringparam(struct ieee80211_hw *hw,
                             u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
+bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw);
 
 /*
  * Driver allocation handlers.
index 1bb9d46077ffe88c9c9e8bd56f5b5cca0ebc299b..1ca4c7ffc1898c9ce97d87506b0b8e78464e78ad 100644 (file)
@@ -45,11 +45,11 @@ enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key)
        }
 }
 
-void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+                                      struct sk_buff *skb,
                                       struct txentry_desc *txdesc)
 {
-       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
+       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)
index 322cc4f3de5d6a6c961e1ed4ff779636cb6b2fcd..15cdc7e57fc4711cbdc7bca15ae24820afff53d8 100644 (file)
@@ -336,7 +336,8 @@ static inline void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev,
  */
 #ifdef CONFIG_RT2X00_LIB_CRYPTO
 enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key);
-void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+                                      struct sk_buff *skb,
                                       struct txentry_desc *txdesc);
 unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev,
                                      struct sk_buff *skb);
index 93bec140e598612145c50b44adc86be70b42bca2..8efab3983528a0e54d0f70bdeb96e2729c414c08 100644 (file)
@@ -818,3 +818,17 @@ void rt2x00mac_get_ringparam(struct ieee80211_hw *hw,
        *rx_max = rt2x00dev->rx->limit;
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_get_ringparam);
+
+bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw)
+{
+       struct rt2x00_dev *rt2x00dev = hw->priv;
+       struct data_queue *queue;
+
+       tx_queue_for_each(rt2x00dev, queue) {
+               if (!rt2x00queue_empty(queue))
+                       return true;
+       }
+
+       return false;
+}
+EXPORT_SYMBOL_GPL(rt2x00mac_tx_frames_pending);
index c7fc9def6bcf7f6933d7b767f352cc922a2c9070..29edb9fbe6f172298285f6269db5b51d1c541576 100644 (file)
@@ -200,11 +200,12 @@ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
        skb_pull(skb, l2pad);
 }
 
-static void rt2x00queue_create_tx_descriptor_seq(struct queue_entry *entry,
+static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
+                                                struct sk_buff *skb,
                                                 struct txentry_desc *txdesc)
 {
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data;
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif);
 
        if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
@@ -212,7 +213,7 @@ static void rt2x00queue_create_tx_descriptor_seq(struct queue_entry *entry,
 
        __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
 
-       if (!test_bit(REQUIRE_SW_SEQNO, &entry->queue->rt2x00dev->cap_flags))
+       if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags))
                return;
 
        /*
@@ -237,12 +238,12 @@ static void rt2x00queue_create_tx_descriptor_seq(struct queue_entry *entry,
 
 }
 
-static void rt2x00queue_create_tx_descriptor_plcp(struct queue_entry *entry,
+static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev,
+                                                 struct sk_buff *skb,
                                                  struct txentry_desc *txdesc,
                                                  const struct rt2x00_rate *hwrate)
 {
-       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0];
        unsigned int data_length;
        unsigned int duration;
@@ -259,8 +260,8 @@ static void rt2x00queue_create_tx_descriptor_plcp(struct queue_entry *entry,
                txdesc->u.plcp.ifs = IFS_SIFS;
 
        /* Data length + CRC + Crypto overhead (IV/EIV/ICV/MIC) */
-       data_length = entry->skb->len + 4;
-       data_length += rt2x00crypto_tx_overhead(rt2x00dev, entry->skb);
+       data_length = skb->len + 4;
+       data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb);
 
        /*
         * PLCP setup
@@ -301,13 +302,14 @@ static void rt2x00queue_create_tx_descriptor_plcp(struct queue_entry *entry,
        }
 }
 
-static void rt2x00queue_create_tx_descriptor_ht(struct queue_entry *entry,
+static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev,
+                                               struct sk_buff *skb,
                                                struct txentry_desc *txdesc,
                                                const struct rt2x00_rate *hwrate)
 {
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0];
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
        if (tx_info->control.sta)
                txdesc->u.ht.mpdu_density =
@@ -380,12 +382,12 @@ static void rt2x00queue_create_tx_descriptor_ht(struct queue_entry *entry,
                txdesc->u.ht.txop = TXOP_HTTXOP;
 }
 
-static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
+static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+                                            struct sk_buff *skb,
                                             struct txentry_desc *txdesc)
 {
-       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data;
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0];
        struct ieee80211_rate *rate;
        const struct rt2x00_rate *hwrate = NULL;
@@ -395,8 +397,8 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
        /*
         * Header and frame information.
         */
-       txdesc->length = entry->skb->len;
-       txdesc->header_length = ieee80211_get_hdrlen_from_skb(entry->skb);
+       txdesc->length = skb->len;
+       txdesc->header_length = ieee80211_get_hdrlen_from_skb(skb);
 
        /*
         * Check whether this frame is to be acked.
@@ -471,13 +473,15 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
        /*
         * Apply TX descriptor handling by components
         */
-       rt2x00crypto_create_tx_descriptor(entry, txdesc);
-       rt2x00queue_create_tx_descriptor_seq(entry, txdesc);
+       rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc);
+       rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc);
 
        if (test_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags))
-               rt2x00queue_create_tx_descriptor_ht(entry, txdesc, hwrate);
+               rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc,
+                                                   hwrate);
        else
-               rt2x00queue_create_tx_descriptor_plcp(entry, txdesc, hwrate);
+               rt2x00queue_create_tx_descriptor_plcp(rt2x00dev, skb, txdesc,
+                                                     hwrate);
 }
 
 static int rt2x00queue_write_tx_data(struct queue_entry *entry,
@@ -555,33 +559,18 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
                               bool local)
 {
        struct ieee80211_tx_info *tx_info;
-       struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX);
+       struct queue_entry *entry;
        struct txentry_desc txdesc;
        struct skb_frame_desc *skbdesc;
        u8 rate_idx, rate_flags;
-
-       if (unlikely(rt2x00queue_full(queue))) {
-               ERROR(queue->rt2x00dev,
-                     "Dropping frame due to full tx queue %d.\n", queue->qid);
-               return -ENOBUFS;
-       }
-
-       if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA,
-                                     &entry->flags))) {
-               ERROR(queue->rt2x00dev,
-                     "Arrived at non-free entry in the non-full queue %d.\n"
-                     "Please file bug report to %s.\n",
-                     queue->qid, DRV_PROJECT);
-               return -EINVAL;
-       }
+       int ret = 0;
 
        /*
         * Copy all TX descriptor information into txdesc,
         * after that we are free to use the skb->cb array
         * for our information.
         */
-       entry->skb = skb;
-       rt2x00queue_create_tx_descriptor(entry, &txdesc);
+       rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc);
 
        /*
         * All information is retrieved from the skb->cb array,
@@ -593,7 +582,6 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
        rate_flags = tx_info->control.rates[0].flags;
        skbdesc = get_skb_frame_desc(skb);
        memset(skbdesc, 0, sizeof(*skbdesc));
-       skbdesc->entry = entry;
        skbdesc->tx_rate_idx = rate_idx;
        skbdesc->tx_rate_flags = rate_flags;
 
@@ -622,9 +610,33 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
         * for PCI devices.
         */
        if (test_bit(REQUIRE_L2PAD, &queue->rt2x00dev->cap_flags))
-               rt2x00queue_insert_l2pad(entry->skb, txdesc.header_length);
+               rt2x00queue_insert_l2pad(skb, txdesc.header_length);
        else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags))
-               rt2x00queue_align_frame(entry->skb);
+               rt2x00queue_align_frame(skb);
+
+       spin_lock(&queue->tx_lock);
+
+       if (unlikely(rt2x00queue_full(queue))) {
+               ERROR(queue->rt2x00dev,
+                     "Dropping frame due to full tx queue %d.\n", queue->qid);
+               ret = -ENOBUFS;
+               goto out;
+       }
+
+       entry = rt2x00queue_get_entry(queue, Q_INDEX);
+
+       if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA,
+                                     &entry->flags))) {
+               ERROR(queue->rt2x00dev,
+                     "Arrived at non-free entry in the non-full queue %d.\n"
+                     "Please file bug report to %s.\n",
+                     queue->qid, DRV_PROJECT);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       skbdesc->entry = entry;
+       entry->skb = skb;
 
        /*
         * It could be possible that the queue was corrupted and this
@@ -634,7 +646,8 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
        if (unlikely(rt2x00queue_write_tx_data(entry, &txdesc))) {
                clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
                entry->skb = NULL;
-               return -EIO;
+               ret = -EIO;
+               goto out;
        }
 
        set_bit(ENTRY_DATA_PENDING, &entry->flags);
@@ -643,7 +656,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
        rt2x00queue_write_tx_descriptor(entry, &txdesc);
        rt2x00queue_kick_tx_queue(queue, &txdesc);
 
-       return 0;
+out:
+       spin_unlock(&queue->tx_lock);
+       return ret;
 }
 
 int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
@@ -697,7 +712,7 @@ int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
         * after that we are free to use the skb->cb array
         * for our information.
         */
-       rt2x00queue_create_tx_descriptor(intf->beacon, &txdesc);
+       rt2x00queue_create_tx_descriptor(rt2x00dev, intf->beacon->skb, &txdesc);
 
        /*
         * Fill in skb descriptor
@@ -1184,6 +1199,7 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
                             struct data_queue *queue, enum data_queue_qid qid)
 {
        mutex_init(&queue->status_lock);
+       spin_lock_init(&queue->tx_lock);
        spin_lock_init(&queue->index_lock);
 
        queue->rt2x00dev = rt2x00dev;
index 590047499e3c905dca376dd36317bc1a151a6fb1..f2100f4ddcffd1c0fa35259a407237cc9eb38a7d 100644 (file)
@@ -432,6 +432,7 @@ enum data_queue_flags {
  * @flags: Entry flags, see &enum queue_entry_flags.
  * @status_lock: The mutex for protecting the start/stop/flush
  *     handling on this queue.
+ * @tx_lock: Spinlock to serialize tx operations on this queue.
  * @index_lock: Spinlock to protect index handling. Whenever @index, @index_done or
  *     @index_crypt needs to be changed this lock should be grabbed to prevent
  *     index corruption due to concurrency.
@@ -458,6 +459,7 @@ struct data_queue {
        unsigned long flags;
 
        struct mutex status_lock;
+       spinlock_t tx_lock;
        spinlock_t index_lock;
 
        unsigned int count;
index 9d35ec16a3a5ff9c8c226837ce69da8c29fc35df..53110b83bf6e47a2ff172b03c90db0cab81f5c4c 100644 (file)
@@ -2982,6 +2982,7 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {
        .set_antenna            = rt2x00mac_set_antenna,
        .get_antenna            = rt2x00mac_get_antenna,
        .get_ringparam          = rt2x00mac_get_ringparam,
+       .tx_frames_pending      = rt2x00mac_tx_frames_pending,
 };
 
 static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
index ad20953cbf05860c9d26f97604b1bae91cc9fafd..6a93939f44e887984d3f9877a8753485b3d82980 100644 (file)
@@ -2314,6 +2314,7 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = {
        .set_antenna            = rt2x00mac_set_antenna,
        .get_antenna            = rt2x00mac_get_antenna,
        .get_ringparam          = rt2x00mac_get_ringparam,
+       .tx_frames_pending      = rt2x00mac_tx_frames_pending,
 };
 
 static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
index 532c7d38dae242bfbcb365249e9ef932a06ea9fd..5efd578334897bbf451cc0ba33b17e060e50c089 100644 (file)
@@ -788,15 +788,11 @@ static irqreturn_t _rtl_pci_interrupt(int irq, void *dev_id)
 {
        struct ieee80211_hw *hw = dev_id;
        struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
        unsigned long flags;
        u32 inta = 0;
        u32 intb = 0;
 
-       if (rtlpci->irq_enabled == 0)
-               return IRQ_HANDLED;
-
        spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
 
        /*read ISR: 4/8bytes */
index a50e5513256e1d726c2c2a2cdea175fca014bd15..c53c620467479d70251d04ea98676d9329e2e34a 100644 (file)
@@ -158,7 +158,6 @@ struct rtl_pci {
        bool first_init;
        bool being_init_adapter;
        bool init_ready;
-       bool irq_enabled;
 
        /*Tx */
        struct rtl8192_tx_ring tx_ring[RTL_PCI_MAX_TX_QUEUE_COUNT];
index bc6ae9dcf940d94c123a62613b2b40efdaeb1e16..9e2a9e34a69996ca08ce0a72921495b7151504ad 100644 (file)
@@ -1183,7 +1183,6 @@ void rtl92ce_enable_interrupt(struct ieee80211_hw *hw)
 
        rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
        rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
-       rtlpci->irq_enabled = true;
 }
 
 void rtl92ce_disable_interrupt(struct ieee80211_hw *hw)
@@ -1193,7 +1192,6 @@ void rtl92ce_disable_interrupt(struct ieee80211_hw *hw)
 
        rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
        rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
-       rtlpci->irq_enabled = false;
        synchronize_irq(rtlpci->pdev->irq);
 }
 
index 4e057df6f488309e2e03329c0dc86fff169edb56..a90c09b42390064e2088bc27e69d9d280d977da3 100644 (file)
@@ -380,13 +380,11 @@ void rtl92c_enable_interrupt(struct ieee80211_hw *hw)
                                0xFFFFFFFF);
                rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] &
                                0xFFFFFFFF);
-               rtlpci->irq_enabled = true;
        } else {
                rtl_write_dword(rtlpriv, REG_HIMR, rtlusb->irq_mask[0] &
                                0xFFFFFFFF);
                rtl_write_dword(rtlpriv, REG_HIMRE, rtlusb->irq_mask[1] &
                                0xFFFFFFFF);
-               rtlusb->irq_enabled = true;
        }
 }
 
@@ -398,16 +396,9 @@ void rtl92c_init_interrupt(struct ieee80211_hw *hw)
 void rtl92c_disable_interrupt(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-       struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));
 
        rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
        rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
-       if (IS_HARDWARE_TYPE_8192CE(rtlhal))
-               rtlpci->irq_enabled = false;
-       else if (IS_HARDWARE_TYPE_8192CU(rtlhal))
-               rtlusb->irq_enabled = false;
 }
 
 void rtl92c_set_qos(struct ieee80211_hw *hw, int aci)
index e833bbf92c55fe8e3be62ab0018babfcd1292190..5a65bea4cb8f9ecb4a67e9e52c13cb6350a46b2f 100644 (file)
@@ -449,7 +449,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
        case HW_VAR_CORRECT_TSF: {
                u8 btype_ibss = ((u8 *) (val))[0];
 
-               if (btype_ibss == true)
+               if (btype_ibss)
                        _rtl92de_stop_tx_beacon(hw);
                _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(3));
                rtl_write_dword(rtlpriv, REG_TSFTR,
@@ -457,7 +457,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                rtl_write_dword(rtlpriv, REG_TSFTR + 4,
                                (u32) ((mac->tsf >> 32) & 0xffffffff));
                _rtl92de_set_bcn_ctrl_reg(hw, BIT(3), 0);
-               if (btype_ibss == true)
+               if (btype_ibss)
                        _rtl92de_resume_tx_beacon(hw);
 
                break;
@@ -932,8 +932,8 @@ int rtl92de_hw_init(struct ieee80211_hw *hw)
                RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
                         ("Failed to download FW. Init HW "
                         "without FW..\n"));
-               err = 1;
                rtlhal->fw_ready = false;
+               return 1;
        } else {
                rtlhal->fw_ready = true;
        }
@@ -1044,6 +1044,11 @@ int rtl92de_hw_init(struct ieee80211_hw *hw)
                                if (((tmp_rega & BIT(11)) == BIT(11)))
                                        break;
                        }
+                       /* check that loop was successful. If not, exit now */
+                       if (i == 10000) {
+                               rtlpci->init_ready = false;
+                               return 1;
+                       }
                }
        }
        rtlpci->init_ready = true;
@@ -1142,7 +1147,7 @@ void rtl92de_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
 
        if (rtlpriv->psc.rfpwr_state != ERFON)
                return;
-       if (check_bssid == true) {
+       if (check_bssid) {
                reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
                rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
                _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(4));
@@ -1221,7 +1226,6 @@ void rtl92de_enable_interrupt(struct ieee80211_hw *hw)
 
        rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
        rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
-       rtlpci->irq_enabled = true;
 }
 
 void rtl92de_disable_interrupt(struct ieee80211_hw *hw)
@@ -1231,7 +1235,6 @@ void rtl92de_disable_interrupt(struct ieee80211_hw *hw)
 
        rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
        rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
-       rtlpci->irq_enabled = false;
        synchronize_irq(rtlpci->pdev->irq);
 }
 
@@ -1787,7 +1790,7 @@ static void _rtl92de_read_adapter_info(struct ieee80211_hw *hw)
                RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("Autoload OK\n"));
                rtlefuse->autoload_failflag = false;
        }
-       if (rtlefuse->autoload_failflag == true) {
+       if (rtlefuse->autoload_failflag) {
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         ("RTL819X Not boot from eeprom, check it !!"));
                return;
@@ -2149,7 +2152,7 @@ bool rtl92de_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
                          REG_MAC_PINMUX_CFG) & ~(BIT(3)));
        u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL);
        e_rfpowerstate_toset = (u1tmp & BIT(3)) ? ERFON : ERFOFF;
-       if ((ppsc->hwradiooff == true) && (e_rfpowerstate_toset == ERFON)) {
+       if (ppsc->hwradiooff && (e_rfpowerstate_toset == ERFON)) {
                RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
                         ("GPIOChangeRF  - HW Radio ON, RF ON\n"));
                e_rfpowerstate_toset = ERFON;
index 719972c16fcc64f326fecd0b12d88ae062c478e2..f1552f4df65805aea48848d402c6d71e2188395a 100644 (file)
@@ -93,7 +93,7 @@ void rtl92de_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
                break;
        case LED_PIN_LED0:
                ledcfg &= 0xf0;
-               if (pcipriv->ledctl.led_opendrain == true)
+               if (pcipriv->ledctl.led_opendrain)
                        rtl_write_byte(rtlpriv, REG_LEDCFG2,
                                       (ledcfg | BIT(1) | BIT(5) | BIT(6)));
                else
index 97fb6ca39d73ea3cb245312a88092e1bb318eb6d..3ac7af1c5509c3eec85943a5f570c24e542e473d 100644 (file)
@@ -932,7 +932,7 @@ bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
                                          enum rf_content content,
                                          enum radio_path rfpath)
 {
-       int i, j;
+       int i;
        u32 *radioa_array_table;
        u32 *radiob_array_table;
        u16 radioa_arraylen, radiob_arraylen;
@@ -974,13 +974,10 @@ bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
                                mdelay(50);
                        } else if (radioa_array_table[i] == 0xfd) {
                                /* delay_ms(5); */
-                               for (j = 0; j < 100; j++)
-                                       udelay(MAX_STALL_TIME);
+                               mdelay(5);
                        } else if (radioa_array_table[i] == 0xfc) {
                                /* delay_ms(1); */
-                               for (j = 0; j < 20; j++)
-                                       udelay(MAX_STALL_TIME);
-
+                               mdelay(1);
                        } else if (radioa_array_table[i] == 0xfb) {
                                udelay(50);
                        } else if (radioa_array_table[i] == 0xfa) {
@@ -1004,12 +1001,10 @@ bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
                                mdelay(50);
                        } else if (radiob_array_table[i] == 0xfd) {
                                /* delay_ms(5); */
-                               for (j = 0; j < 100; j++)
-                                       udelay(MAX_STALL_TIME);
+                               mdelay(5);
                        } else if (radiob_array_table[i] == 0xfc) {
                                /* delay_ms(1); */
-                               for (j = 0; j < 20; j++)
-                                       udelay(MAX_STALL_TIME);
+                               mdelay(1);
                        } else if (radiob_array_table[i] == 0xfb) {
                                udelay(50);
                        } else if (radiob_array_table[i] == 0xfa) {
@@ -1276,7 +1271,7 @@ static void rtl92d_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
-       u8 i, value8;
+       u8 value8;
 
        RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("==>\n"));
        rtlhal->bandset = band;
@@ -1321,8 +1316,7 @@ static void rtl92d_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band)
                rtl_write_byte(rtlpriv, (rtlhal->interfaceindex ==
                        0 ? REG_MAC0 : REG_MAC1), value8);
        }
-       for (i = 0; i < 20; i++)
-               udelay(MAX_STALL_TIME);
+       mdelay(1);
        RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, ("<==Switch Band OK.\n"));
 }
 
@@ -1684,7 +1678,7 @@ static u8 _rtl92d_phy_patha_iqk(struct ieee80211_hw *hw, bool configpathb)
        RTPRINT(rtlpriv, FINIT, INIT_IQK,
                ("Delay %d ms for One shot, path A LOK & IQK.\n",
                IQK_DELAY_TIME));
-       udelay(IQK_DELAY_TIME * 1000);
+       mdelay(IQK_DELAY_TIME);
        /* Check failed */
        regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
        RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeac = 0x%x\n", regeac));
@@ -1755,7 +1749,7 @@ static u8 _rtl92d_phy_patha_iqk_5g_normal(struct ieee80211_hw *hw,
                RTPRINT(rtlpriv, FINIT, INIT_IQK,
                        ("Delay %d ms for One shot, path A LOK & IQK.\n",
                        IQK_DELAY_TIME));
-               udelay(IQK_DELAY_TIME * 1000 * 10);
+               mdelay(IQK_DELAY_TIME * 10);
                /* Check failed */
                regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
                RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeac = 0x%x\n", regeac));
@@ -1808,7 +1802,7 @@ static u8 _rtl92d_phy_pathb_iqk(struct ieee80211_hw *hw)
        RTPRINT(rtlpriv, FINIT, INIT_IQK,
                ("Delay %d ms for One shot, path B LOK & IQK.\n",
                IQK_DELAY_TIME));
-       udelay(IQK_DELAY_TIME * 1000);
+       mdelay(IQK_DELAY_TIME);
        /* Check failed */
        regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
        RTPRINT(rtlpriv, FINIT, INIT_IQK, ("0xeac = 0x%x\n", regeac));
@@ -1875,7 +1869,7 @@ static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw)
                /* delay x ms */
                RTPRINT(rtlpriv, FINIT, INIT_IQK,
                        ("Delay %d ms for One shot, path B LOK & IQK.\n", 10));
-               udelay(IQK_DELAY_TIME * 1000 * 10);
+               mdelay(IQK_DELAY_TIME * 10);
 
                /* Check failed */
                regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
@@ -2206,7 +2200,7 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw,
         * PHY_REG.txt , and radio_a, radio_b.txt */
 
        RTPRINT(rtlpriv, FINIT, INIT_IQK, ("IQK for 5G NORMAL:Start!!!\n"));
-       udelay(IQK_DELAY_TIME * 1000 * 20);
+       mdelay(IQK_DELAY_TIME * 20);
        if (t == 0) {
                bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD);
                RTPRINT(rtlpriv, FINIT, INIT_IQK, ("==>0x%08x\n", bbvalue));
index c326372220f3c986fe9bc3146d730aaa9918e0b3..db27cebaac2c80052a0479992c88ebfa25017896 100644 (file)
@@ -87,7 +87,7 @@ void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
 
        if (rtlefuse->eeprom_regulatory != 0)
                turbo_scanoff = true;
-       if (mac->act_scanning == true) {
+       if (mac->act_scanning) {
                tx_agc[RF90_PATH_A] = 0x3f3f3f3f;
                tx_agc[RF90_PATH_B] = 0x3f3f3f3f;
                if (turbo_scanoff) {
@@ -416,9 +416,9 @@ bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
        u8 u1btmp;
-       u8 direct = bmac0 == true ? BIT(3) | BIT(2) : BIT(3);
-       u8 mac_reg = bmac0 == true ? REG_MAC1 : REG_MAC0;
-       u8 mac_on_bit = bmac0 == true ? MAC1_ON : MAC0_ON;
+       u8 direct = bmac0 ? BIT(3) | BIT(2) : BIT(3);
+       u8 mac_reg = bmac0 ? REG_MAC1 : REG_MAC0;
+       u8 mac_on_bit = bmac0 ? MAC1_ON : MAC0_ON;
        bool bresult = true; /* true: need to enable BB/RF power */
 
        rtlhal->during_mac0init_radiob = false;
@@ -447,9 +447,9 @@ void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, bool bmac0)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
        u8 u1btmp;
-       u8 direct = bmac0 == true ? BIT(3) | BIT(2) : BIT(3);
-       u8 mac_reg = bmac0 == true ? REG_MAC1 : REG_MAC0;
-       u8 mac_on_bit = bmac0 == true ? MAC1_ON : MAC0_ON;
+       u8 direct = bmac0 ? BIT(3) | BIT(2) : BIT(3);
+       u8 mac_reg = bmac0 ? REG_MAC1 : REG_MAC0;
+       u8 mac_on_bit = bmac0 ? MAC1_ON : MAC0_ON;
 
        rtlhal->during_mac0init_radiob = false;
        rtlhal->during_mac1init_radioa = false;
@@ -573,7 +573,7 @@ bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw)
                udelay(1);
                switch (rfpath) {
                case RF90_PATH_A:
-                       if (true_bpath == true)
+                       if (true_bpath)
                                rtstatus = rtl92d_phy_config_rf_with_headerfile(
                                                hw, radiob_txt,
                                                (enum radio_path)rfpath);
index bf1462f69b52ca30afef5b3b2f9ceb718268b0c0..dc86fcb0b3a3fb90895629405e7e9d89454c1038 100644 (file)
@@ -614,7 +614,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
                                                    (u8)
                                                    GET_RX_DESC_RXMCS(pdesc));
        rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
-       if (phystatus == true) {
+       if (phystatus) {
                p_drvinfo = (struct rx_fwinfo_92d *)(skb->data +
                                                     stats->rx_bufshift);
                _rtl92de_translate_rx_signal_stuff(hw,
@@ -876,7 +876,7 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw,
 
 void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
 {
-       if (istx == true) {
+       if (istx) {
                switch (desc_name) {
                case HW_DESC_OWN:
                        wmb();
@@ -917,7 +917,7 @@ u32 rtl92de_get_desc(u8 *p_desc, bool istx, u8 desc_name)
 {
        u32 ret = 0;
 
-       if (istx == true) {
+       if (istx) {
                switch (desc_name) {
                case HW_DESC_OWN:
                        ret = GET_TX_DESC_OWN(p_desc);
index 13da7b3c02027fa06f44d923ddd45e79edca55d6..b1d0213dc60e6f3063cb38af148ea991a6ba6704 100644 (file)
@@ -1214,8 +1214,6 @@ void rtl92se_enable_interrupt(struct ieee80211_hw *hw)
        rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]);
        /* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */
        rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F);
-
-       rtlpci->irq_enabled = true;
 }
 
 void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
@@ -1226,7 +1224,6 @@ void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
        rtl_write_dword(rtlpriv, INTA_MASK, 0);
        rtl_write_dword(rtlpriv, INTA_MASK + 4, 0);
 
-       rtlpci->irq_enabled = false;
        synchronize_irq(rtlpci->pdev->irq);
 }
 
index 35ce7b0f4a609ad5eff9151bf37b4bf0979b5374..07bcb1548d8b2e51ec758372d7ec0a5b52fcbc2d 100644 (file)
@@ -11,7 +11,6 @@ config WL12XX
        depends on WL12XX_MENU && GENERIC_HARDIRQS
        depends on INET
        select FW_LOADER
-       select CRC7
        ---help---
          This module adds support for wireless adapters based on TI wl1271 and
          TI wl1273 chipsets. This module does *not* include support for wl1251.
@@ -33,6 +32,7 @@ config WL12XX_HT
 config WL12XX_SPI
        tristate "TI wl12xx SPI support"
        depends on WL12XX && SPI_MASTER
+       select CRC7
        ---help---
          This module adds support for the SPI interface of adapters using
          TI wl12xx chipsets.  Select this if your platform is using
index c6ee530e5bf72518b37b738f9c400558291b713a..87caa94fd815e23e5ec648525022b23375a1ee7c 100644 (file)
@@ -25,7 +25,6 @@
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
 
@@ -1068,6 +1067,7 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
        mem_conf->tx_free_req = mem->min_req_tx_blocks;
        mem_conf->rx_free_req = mem->min_req_rx_blocks;
        mem_conf->tx_min = mem->tx_min;
+       mem_conf->fwlog_blocks = wl->conf.fwlog.mem_blocks;
 
        ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
                                   sizeof(*mem_conf));
@@ -1577,6 +1577,53 @@ out:
        return ret;
 }
 
+int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable)
+{
+       struct wl1271_acx_ps_rx_streaming *rx_streaming;
+       u32 conf_queues, enable_queues;
+       int i, ret = 0;
+
+       wl1271_debug(DEBUG_ACX, "acx ps rx streaming");
+
+       rx_streaming = kzalloc(sizeof(*rx_streaming), GFP_KERNEL);
+       if (!rx_streaming) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       conf_queues = wl->conf.rx_streaming.queues;
+       if (enable)
+               enable_queues = conf_queues;
+       else
+               enable_queues = 0;
+
+       for (i = 0; i < 8; i++) {
+               /*
+                * Skip non-changed queues, to avoid redundant acxs.
+                * this check assumes conf.rx_streaming.queues can't
+                * be changed while rx_streaming is enabled.
+                */
+               if (!(conf_queues & BIT(i)))
+                       continue;
+
+               rx_streaming->tid = i;
+               rx_streaming->enable = enable_queues & BIT(i);
+               rx_streaming->period = wl->conf.rx_streaming.interval;
+               rx_streaming->timeout = wl->conf.rx_streaming.interval;
+
+               ret = wl1271_cmd_configure(wl, ACX_PS_RX_STREAMING,
+                                          rx_streaming,
+                                          sizeof(*rx_streaming));
+               if (ret < 0) {
+                       wl1271_warning("acx ps rx streaming failed: %d", ret);
+                       goto out;
+               }
+       }
+out:
+       kfree(rx_streaming);
+       return ret;
+}
+
 int wl1271_acx_max_tx_retry(struct wl1271 *wl)
 {
        struct wl1271_acx_max_tx_retry *acx = NULL;
index 9a895e3cc613884e03361c1489af411efee96e08..d303265f163a2d8292c89518da2d108cb65958b6 100644 (file)
@@ -828,6 +828,8 @@ struct wl1271_acx_sta_config_memory {
        u8 tx_free_req;
        u8 rx_free_req;
        u8 tx_min;
+       u8 fwlog_blocks;
+       u8 padding[3];
 } __packed;
 
 struct wl1271_acx_mem_map {
@@ -1153,6 +1155,19 @@ struct wl1271_acx_fw_tsf_information {
        u8 padding[3];
 } __packed;
 
+struct wl1271_acx_ps_rx_streaming {
+       struct acx_header header;
+
+       u8 tid;
+       u8 enable;
+
+       /* interval between triggers (10-100 msec) */
+       u8 period;
+
+       /* timeout before first trigger (0-200 msec) */
+       u8 timeout;
+} __packed;
+
 struct wl1271_acx_max_tx_retry {
        struct acx_header header;
 
@@ -1384,6 +1399,7 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl,
 int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
                                       bool enable);
 int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
+int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable);
 int wl1271_acx_max_tx_retry(struct wl1271 *wl);
 int wl1271_acx_config_ps(struct wl1271 *wl);
 int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
index 7ccec07a600cb59b97847ef79d7043b74bd965bc..101f7e0f6329fa8c1ff29722d906317dfb1c04eb 100644 (file)
@@ -102,6 +102,33 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
        wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
 }
 
+static unsigned int wl12xx_get_fw_ver_quirks(struct wl1271 *wl)
+{
+       unsigned int quirks = 0;
+       unsigned int *fw_ver = wl->chip.fw_ver;
+
+       /* Only for wl127x */
+       if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) &&
+           /* Check STA version */
+           (((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
+             (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) ||
+            /* Check AP version */
+            ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) &&
+             (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN))))
+               quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS;
+
+       /* Only new station firmwares support routing fw logs to the host */
+       if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
+           (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN))
+               quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;
+
+       /* This feature is not yet supported for AP mode */
+       if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP)
+               quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;
+
+       return quirks;
+}
+
 static void wl1271_parse_fw_ver(struct wl1271 *wl)
 {
        int ret;
@@ -116,6 +143,9 @@ static void wl1271_parse_fw_ver(struct wl1271 *wl)
                memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
                return;
        }
+
+       /* Check if any quirks are needed with older fw versions */
+       wl->quirks |= wl12xx_get_fw_ver_quirks(wl);
 }
 
 static void wl1271_boot_fw_version(struct wl1271 *wl)
@@ -749,6 +779,9 @@ int wl1271_load_firmware(struct wl1271 *wl)
                clk |= (wl->ref_clock << 1) << 4;
        }
 
+       if (wl->quirks & WL12XX_QUIRK_LPD_MODE)
+               clk |= SCRATCH_ENABLE_LPD;
+
        wl1271_write32(wl, DRPW_SCRATCH_START, clk);
 
        wl1271_set_partition(wl, &part_table[PART_WORK]);
index 5d0ad2d93cb339274664fb7a4575b372e60e0fde..68972cbc68b4757fd3a38905dd22eb3507b0e638 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/etherdevice.h>
 #include <linux/ieee80211.h>
@@ -106,7 +105,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
 fail:
        WARN_ON(1);
-       ieee80211_queue_work(wl->hw, &wl->recovery_work);
+       wl12xx_queue_recovery_work(wl);
        return ret;
 }
 
@@ -135,6 +134,11 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
        /* Override the REF CLK from the NVS with the one from platform data */
        gen_parms->general_params.ref_clock = wl->ref_clock;
 
+       /* LPD mode enable (bits 6-7) in WL1271 AP mode only */
+       if (wl->quirks & WL12XX_QUIRK_LPD_MODE)
+               gen_parms->general_params.general_settings |=
+                       GENERAL_SETTINGS_DRPW_LPD;
+
        ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
        if (ret < 0) {
                wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
@@ -352,7 +356,7 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
 
        ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask);
        if (ret != 0) {
-               ieee80211_queue_work(wl->hw, &wl->recovery_work);
+               wl12xx_queue_recovery_work(wl);
                return ret;
        }
 
@@ -1223,3 +1227,87 @@ out_free:
 out:
        return ret;
 }
+
+int wl12xx_cmd_config_fwlog(struct wl1271 *wl)
+{
+       struct wl12xx_cmd_config_fwlog *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd config firmware logger");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->logger_mode = wl->conf.fwlog.mode;
+       cmd->log_severity = wl->conf.fwlog.severity;
+       cmd->timestamp = wl->conf.fwlog.timestamp;
+       cmd->output = wl->conf.fwlog.output;
+       cmd->threshold = wl->conf.fwlog.threshold;
+
+       ret = wl1271_cmd_send(wl, CMD_CONFIG_FWLOGGER, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send config firmware logger command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+
+out:
+       return ret;
+}
+
+int wl12xx_cmd_start_fwlog(struct wl1271 *wl)
+{
+       struct wl12xx_cmd_start_fwlog *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd start firmware logger");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = wl1271_cmd_send(wl, CMD_START_FWLOGGER, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send start firmware logger command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+
+out:
+       return ret;
+}
+
+int wl12xx_cmd_stop_fwlog(struct wl1271 *wl)
+{
+       struct wl12xx_cmd_stop_fwlog *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd stop firmware logger");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_FWLOGGER, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send stop firmware logger command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+
+out:
+       return ret;
+}
index 5cac95d9480c33b92f313b9b83be0e9ec0e172aa..1f7037292c15785b50e033f75d2ede5cad77adb4 100644 (file)
@@ -70,6 +70,9 @@ int wl1271_cmd_start_bss(struct wl1271 *wl);
 int wl1271_cmd_stop_bss(struct wl1271 *wl);
 int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid);
 int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid);
+int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
+int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
+int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
 
 enum wl1271_commands {
        CMD_INTERROGATE     = 1,    /*use this to read information elements*/
@@ -107,6 +110,9 @@ enum wl1271_commands {
        CMD_START_PERIODIC_SCAN      = 50,
        CMD_STOP_PERIODIC_SCAN       = 51,
        CMD_SET_STA_STATE            = 52,
+       CMD_CONFIG_FWLOGGER          = 53,
+       CMD_START_FWLOGGER           = 54,
+       CMD_STOP_FWLOGGER            = 55,
 
        /* AP mode commands */
        CMD_BSS_START                = 60,
@@ -575,4 +581,60 @@ struct wl1271_cmd_remove_sta {
        u8 padding1;
 } __packed;
 
+/*
+ * Continuous mode - packets are transferred to the host periodically
+ * via the data path.
+ * On demand - Log messages are stored in a cyclic buffer in the
+ * firmware, and only transferred to the host when explicitly requested
+ */
+enum wl12xx_fwlogger_log_mode {
+       WL12XX_FWLOG_CONTINUOUS,
+       WL12XX_FWLOG_ON_DEMAND
+};
+
+/* Include/exclude timestamps from the log messages */
+enum wl12xx_fwlogger_timestamp {
+       WL12XX_FWLOG_TIMESTAMP_DISABLED,
+       WL12XX_FWLOG_TIMESTAMP_ENABLED
+};
+
+/*
+ * Logs can be routed to the debug pinouts (where available), to the host bus
+ * (SDIO/SPI), or dropped
+ */
+enum wl12xx_fwlogger_output {
+       WL12XX_FWLOG_OUTPUT_NONE,
+       WL12XX_FWLOG_OUTPUT_DBG_PINS,
+       WL12XX_FWLOG_OUTPUT_HOST,
+};
+
+struct wl12xx_cmd_config_fwlog {
+       struct wl1271_cmd_header header;
+
+       /* See enum wl12xx_fwlogger_log_mode */
+       u8 logger_mode;
+
+       /* Minimum log level threshold */
+       u8 log_severity;
+
+       /* Include/exclude timestamps from the log messages */
+       u8 timestamp;
+
+       /* See enum wl1271_fwlogger_output */
+       u8 output;
+
+       /* Regulates the frequency of log messages */
+       u8 threshold;
+
+       u8 padding[3];
+} __packed;
+
+struct wl12xx_cmd_start_fwlog {
+       struct wl1271_cmd_header header;
+} __packed;
+
+struct wl12xx_cmd_stop_fwlog {
+       struct wl1271_cmd_header header;
+} __packed;
+
 #endif /* __WL1271_CMD_H__ */
index c83fefb6662f3a2a433294c01e210098241e3c00..b5a7b30afda39af2189c3d45f1fca55c73e86a77 100644 (file)
@@ -1248,6 +1248,59 @@ struct conf_fm_coex {
        u8 swallow_clk_diff;
 };
 
+struct conf_rx_streaming_settings {
+       /*
+        * RX Streaming duration (in msec) from last tx/rx
+        *
+        * Range: u32
+        */
+       u32 duration;
+
+       /*
+        * Bitmap of tids to be polled during RX streaming.
+        * (Note: it doesn't look like it really matters)
+        *
+        * Range: 0x1-0xff
+        */
+       u8 queues;
+
+       /*
+        * RX Streaming interval.
+        * (Note:this value is also used as the rx streaming timeout)
+        * Range: 0 (disabled), 10 - 100
+        */
+       u8 interval;
+
+       /*
+        * enable rx streaming also when there is no coex activity
+        */
+       u8 always;
+};
+
+struct conf_fwlog {
+       /* Continuous or on-demand */
+       u8 mode;
+
+       /*
+        * Number of memory blocks dedicated for the FW logger
+        *
+        * Range: 1-3, or 0 to disable the FW logger
+        */
+       u8 mem_blocks;
+
+       /* Minimum log level threshold */
+       u8 severity;
+
+       /* Include/exclude timestamps from the log messages */
+       u8 timestamp;
+
+       /* See enum wl1271_fwlogger_output */
+       u8 output;
+
+       /* Regulates the frequency of log messages */
+       u8 threshold;
+};
+
 struct conf_drv_settings {
        struct conf_sg_settings sg;
        struct conf_rx_settings rx;
@@ -1263,6 +1316,8 @@ struct conf_drv_settings {
        struct conf_memory_settings mem_wl127x;
        struct conf_memory_settings mem_wl128x;
        struct conf_fm_coex fm_coex;
+       struct conf_rx_streaming_settings rx_streaming;
+       struct conf_fwlog fwlog;
        u8 hci_io_ds;
 };
 
index f1f8df9b6cd73905c7df330e379441160da09aa9..da21270183003f7202549310c36705132b13dd8f 100644 (file)
@@ -71,6 +71,14 @@ static const struct file_operations name## _ops = {                  \
        if (!entry || IS_ERR(entry))                                    \
                goto err;                                               \
 
+#define DEBUGFS_ADD_PREFIX(prefix, name, parent)                       \
+       do {                                                            \
+               entry = debugfs_create_file(#name, 0400, parent,        \
+                                   wl, &prefix## _## name## _ops);     \
+               if (!entry || IS_ERR(entry))                            \
+                       goto err;                                       \
+       } while (0);
+
 #define DEBUGFS_FWSTATS_FILE(sub, name, fmt)                           \
 static ssize_t sub## _ ##name## _read(struct file *file,               \
                                      char __user *userbuf,             \
@@ -298,7 +306,7 @@ static ssize_t start_recovery_write(struct file *file,
        struct wl1271 *wl = file->private_data;
 
        mutex_lock(&wl->mutex);
-       ieee80211_queue_work(wl->hw, &wl->recovery_work);
+       wl12xx_queue_recovery_work(wl);
        mutex_unlock(&wl->mutex);
 
        return count;
@@ -527,11 +535,129 @@ static const struct file_operations beacon_interval_ops = {
        .llseek = default_llseek,
 };
 
+static ssize_t rx_streaming_interval_write(struct file *file,
+                          const char __user *user_buf,
+                          size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       char buf[10];
+       size_t len;
+       unsigned long value;
+       int ret;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+       buf[len] = '\0';
+
+       ret = kstrtoul(buf, 0, &value);
+       if (ret < 0) {
+               wl1271_warning("illegal value in rx_streaming_interval!");
+               return -EINVAL;
+       }
+
+       /* valid values: 0, 10-100 */
+       if (value && (value < 10 || value > 100)) {
+               wl1271_warning("value is not in range!");
+               return -ERANGE;
+       }
+
+       mutex_lock(&wl->mutex);
+
+       wl->conf.rx_streaming.interval = value;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl1271_recalc_rx_streaming(wl);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+       return count;
+}
+
+static ssize_t rx_streaming_interval_read(struct file *file,
+                           char __user *userbuf,
+                           size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       return wl1271_format_buffer(userbuf, count, ppos,
+                                   "%d\n", wl->conf.rx_streaming.interval);
+}
+
+static const struct file_operations rx_streaming_interval_ops = {
+       .read = rx_streaming_interval_read,
+       .write = rx_streaming_interval_write,
+       .open = wl1271_open_file_generic,
+       .llseek = default_llseek,
+};
+
+static ssize_t rx_streaming_always_write(struct file *file,
+                          const char __user *user_buf,
+                          size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       char buf[10];
+       size_t len;
+       unsigned long value;
+       int ret;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+       buf[len] = '\0';
+
+       ret = kstrtoul(buf, 0, &value);
+       if (ret < 0) {
+               wl1271_warning("illegal value in rx_streaming_write!");
+               return -EINVAL;
+       }
+
+       /* valid values: 0, 10-100 */
+       if (!(value == 0 || value == 1)) {
+               wl1271_warning("value is not in valid!");
+               return -EINVAL;
+       }
+
+       mutex_lock(&wl->mutex);
+
+       wl->conf.rx_streaming.always = value;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl1271_recalc_rx_streaming(wl);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+       return count;
+}
+
+static ssize_t rx_streaming_always_read(struct file *file,
+                           char __user *userbuf,
+                           size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       return wl1271_format_buffer(userbuf, count, ppos,
+                                   "%d\n", wl->conf.rx_streaming.always);
+}
+
+static const struct file_operations rx_streaming_always_ops = {
+       .read = rx_streaming_always_read,
+       .write = rx_streaming_always_write,
+       .open = wl1271_open_file_generic,
+       .llseek = default_llseek,
+};
+
 static int wl1271_debugfs_add_files(struct wl1271 *wl,
                                     struct dentry *rootdir)
 {
        int ret = 0;
-       struct dentry *entry, *stats;
+       struct dentry *entry, *stats, *streaming;
 
        stats = debugfs_create_dir("fw-statistics", rootdir);
        if (!stats || IS_ERR(stats)) {
@@ -640,6 +766,14 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
        DEBUGFS_ADD(dtim_interval, rootdir);
        DEBUGFS_ADD(beacon_interval, rootdir);
 
+       streaming = debugfs_create_dir("rx_streaming", rootdir);
+       if (!streaming || IS_ERR(streaming))
+               goto err;
+
+       DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming);
+       DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming);
+
+
        return 0;
 
 err:
index 94bbd00ec31b4ff5ab6d01e250e3c32dec88668f..a16dee58a6647678dbf6a1bbbee4a0dc30c927b1 100644 (file)
@@ -133,10 +133,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
                if (ret < 0)
                        break;
 
-               /* enable beacon early termination */
-               ret = wl1271_acx_bet_enable(wl, true);
-               if (ret < 0)
-                       break;
+               /*
+                * BET has only a minor effect in 5GHz and masks
+                * channel switch IEs, so we only enable BET on 2.4GHz
+               */
+               if (wl->band == IEEE80211_BAND_2GHZ)
+                       /* enable beacon early termination */
+                       ret = wl1271_acx_bet_enable(wl, true);
 
                if (wl->ps_compl) {
                        complete(wl->ps_compl);
@@ -183,6 +186,21 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, u8 ba_allowed)
        ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap, wl->bssid);
 }
 
+static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
+                                              u8 enable)
+{
+       if (enable) {
+               /* disable dynamic PS when requested by the firmware */
+               ieee80211_disable_dyn_ps(wl->vif);
+               set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
+       } else {
+               ieee80211_enable_dyn_ps(wl->vif);
+               clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
+               wl1271_recalc_rx_streaming(wl);
+       }
+
+}
+
 static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
 {
        wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
@@ -226,14 +244,10 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
                }
        }
 
-       /* disable dynamic PS when requested by the firmware */
        if (vector & SOFT_GEMINI_SENSE_EVENT_ID &&
-           wl->bss_type == BSS_TYPE_STA_BSS) {
-               if (mbox->soft_gemini_sense_info)
-                       ieee80211_disable_dyn_ps(wl->vif);
-               else
-                       ieee80211_enable_dyn_ps(wl->vif);
-       }
+           wl->bss_type == BSS_TYPE_STA_BSS)
+               wl12xx_event_soft_gemini_sense(wl,
+                                              mbox->soft_gemini_sense_info);
 
        /*
         * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon
index 1420c842b8f1585ac0146071fa88cb63d263cf09..4cf9ecc56212322b54ae70d414bcebbc93b96715 100644 (file)
@@ -24,6 +24,9 @@
 #ifndef __INI_H__
 #define __INI_H__
 
+#define GENERAL_SETTINGS_DRPW_LPD 0xc0
+#define SCRATCH_ENABLE_LPD        BIT(25)
+
 #define WL1271_INI_MAX_SMART_REFLEX_PARAM 16
 
 struct wl1271_ini_general_params {
index f5c2c9e6f84b10a1b382e4c232f439a752c43af7..cf40ac93cead2a004dbf307d306425546d9e5b1a 100644 (file)
@@ -321,6 +321,20 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
        return 0;
 }
 
+static int wl12xx_init_fwlog(struct wl1271 *wl)
+{
+       int ret;
+
+       if (wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED)
+               return 0;
+
+       ret = wl12xx_cmd_config_fwlog(wl);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
 static int wl1271_sta_hw_init(struct wl1271 *wl)
 {
        int ret;
@@ -382,6 +396,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
+       /* Configure the FW logger */
+       ret = wl12xx_init_fwlog(wl);
+       if (ret < 0)
+               return ret;
+
        return 0;
 }
 
index da5c1ad942a45d81c08de332f1c0a7871e9c546b..c2da66f45046774e183160bb45f6ef6bc773a5d3 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/crc7.h>
 #include <linux/spi/spi.h>
 
 #include "wl12xx.h"
@@ -128,12 +127,14 @@ EXPORT_SYMBOL_GPL(wl1271_set_partition);
 
 void wl1271_io_reset(struct wl1271 *wl)
 {
-       wl->if_ops->reset(wl);
+       if (wl->if_ops->reset)
+               wl->if_ops->reset(wl);
 }
 
 void wl1271_io_init(struct wl1271 *wl)
 {
-       wl->if_ops->init(wl);
+       if (wl->if_ops->init)
+               wl->if_ops->init(wl);
 }
 
 void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val)
index 20b00319e444af4ce3903b23318e06a576140def..a2fe4f506adaf88335b47c5806507ca3a7310b91 100644 (file)
@@ -129,6 +129,20 @@ static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf,
        wl1271_raw_write(wl, physical, buf, len, fixed);
 }
 
+static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr,
+                                     void *buf, size_t len, bool fixed)
+{
+       int physical;
+       int addr;
+
+       /* Addresses are stored internally as addresses to 32 bytes blocks */
+       addr = hwaddr << 5;
+
+       physical = wl1271_translate_addr(wl, addr);
+
+       wl1271_raw_read(wl, physical, buf, len, fixed);
+}
+
 static inline u32 wl1271_read32(struct wl1271 *wl, int addr)
 {
        return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr));
index f37f0b873c73341c4ca1c03d702a7a774a0d0f12..a3734bdf5119b0d2e8fb09d63021e8416a572826 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/wl12xx.h>
+#include <linux/sched.h>
 
 #include "wl12xx.h"
 #include "wl12xx_80211.h"
@@ -362,9 +363,25 @@ static struct conf_drv_settings default_conf = {
                .fm_disturbed_band_margin     = 0xff,       /* default */
                .swallow_clk_diff             = 0xff,       /* default */
        },
+       .rx_streaming = {
+               .duration                      = 150,
+               .queues                        = 0x1,
+               .interval                      = 20,
+               .always                        = 0,
+       },
+       .fwlog = {
+               .mode                         = WL12XX_FWLOG_ON_DEMAND,
+               .mem_blocks                   = 2,
+               .severity                     = 0,
+               .timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
+               .output                       = WL12XX_FWLOG_OUTPUT_HOST,
+               .threshold                    = 0,
+       },
        .hci_io_ds = HCI_IO_DS_6MA,
 };
 
+static char *fwlog_param;
+
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
                                         bool reset_tx_queues);
 static void wl1271_free_ap_keys(struct wl1271 *wl);
@@ -388,6 +405,22 @@ static struct platform_device wl1271_device = {
 static DEFINE_MUTEX(wl_list_mutex);
 static LIST_HEAD(wl_list);
 
+static int wl1271_check_operstate(struct wl1271 *wl, unsigned char operstate)
+{
+       int ret;
+       if (operstate != IF_OPER_UP)
+               return 0;
+
+       if (test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags))
+               return 0;
+
+       ret = wl1271_cmd_set_sta_state(wl);
+       if (ret < 0)
+               return ret;
+
+       wl1271_info("Association completed.");
+       return 0;
+}
 static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
                             void *arg)
 {
@@ -437,11 +470,7 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
        if (ret < 0)
                goto out;
 
-       if ((dev->operstate == IF_OPER_UP) &&
-           !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
-               wl1271_cmd_set_sta_state(wl);
-               wl1271_info("Association completed.");
-       }
+       wl1271_check_operstate(wl, dev->operstate);
 
        wl1271_ps_elp_sleep(wl);
 
@@ -473,6 +502,117 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
        return 0;
 }
 
+static int wl1271_set_rx_streaming(struct wl1271 *wl, bool enable)
+{
+       int ret = 0;
+
+       /* we should hold wl->mutex */
+       ret = wl1271_acx_ps_rx_streaming(wl, enable);
+       if (ret < 0)
+               goto out;
+
+       if (enable)
+               set_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags);
+       else
+               clear_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags);
+out:
+       return ret;
+}
+
+/*
+ * this function is being called when the rx_streaming interval
+ * has beed changed or rx_streaming should be disabled
+ */
+int wl1271_recalc_rx_streaming(struct wl1271 *wl)
+{
+       int ret = 0;
+       int period = wl->conf.rx_streaming.interval;
+
+       /* don't reconfigure if rx_streaming is disabled */
+       if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
+               goto out;
+
+       /* reconfigure/disable according to new streaming_period */
+       if (period &&
+           test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) &&
+           (wl->conf.rx_streaming.always ||
+            test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
+               ret = wl1271_set_rx_streaming(wl, true);
+       else {
+               ret = wl1271_set_rx_streaming(wl, false);
+               /* don't cancel_work_sync since we might deadlock */
+               del_timer_sync(&wl->rx_streaming_timer);
+       }
+out:
+       return ret;
+}
+
+static void wl1271_rx_streaming_enable_work(struct work_struct *work)
+{
+       int ret;
+       struct wl1271 *wl =
+               container_of(work, struct wl1271, rx_streaming_enable_work);
+
+       mutex_lock(&wl->mutex);
+
+       if (test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags) ||
+           !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) ||
+           (!wl->conf.rx_streaming.always &&
+            !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
+               goto out;
+
+       if (!wl->conf.rx_streaming.interval)
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wl1271_set_rx_streaming(wl, true);
+       if (ret < 0)
+               goto out_sleep;
+
+       /* stop it after some time of inactivity */
+       mod_timer(&wl->rx_streaming_timer,
+                 jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
+
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static void wl1271_rx_streaming_disable_work(struct work_struct *work)
+{
+       int ret;
+       struct wl1271 *wl =
+               container_of(work, struct wl1271, rx_streaming_disable_work);
+
+       mutex_lock(&wl->mutex);
+
+       if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wl1271_set_rx_streaming(wl, false);
+       if (ret)
+               goto out_sleep;
+
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static void wl1271_rx_streaming_timer(unsigned long data)
+{
+       struct wl1271 *wl = (struct wl1271 *)data;
+       ieee80211_queue_work(wl->hw, &wl->rx_streaming_disable_work);
+}
+
 static void wl1271_conf_init(struct wl1271 *wl)
 {
 
@@ -488,8 +628,24 @@ static void wl1271_conf_init(struct wl1271 *wl)
 
        /* apply driver default configuration */
        memcpy(&wl->conf, &default_conf, sizeof(default_conf));
-}
 
+       /* Adjust settings according to optional module parameters */
+       if (fwlog_param) {
+               if (!strcmp(fwlog_param, "continuous")) {
+                       wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
+               } else if (!strcmp(fwlog_param, "ondemand")) {
+                       wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
+               } else if (!strcmp(fwlog_param, "dbgpins")) {
+                       wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
+                       wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
+               } else if (!strcmp(fwlog_param, "disable")) {
+                       wl->conf.fwlog.mem_blocks = 0;
+                       wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE;
+               } else {
+                       wl1271_error("Unknown fwlog parameter %s", fwlog_param);
+               }
+       }
+}
 
 static int wl1271_plt_init(struct wl1271 *wl)
 {
@@ -741,7 +897,7 @@ static void wl1271_flush_deferred_work(struct wl1271 *wl)
 
        /* Return sent skbs to the network stack */
        while ((skb = skb_dequeue(&wl->deferred_tx_queue)))
-               ieee80211_tx_status(wl->hw, skb);
+               ieee80211_tx_status_ni(wl->hw, skb);
 }
 
 static void wl1271_netstack_work(struct work_struct *work)
@@ -808,7 +964,7 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
                if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
                        wl1271_error("watchdog interrupt received! "
                                     "starting recovery.");
-                       ieee80211_queue_work(wl->hw, &wl->recovery_work);
+                       wl12xx_queue_recovery_work(wl);
 
                        /* restarting the chip. ignore any other interrupt. */
                        goto out;
@@ -970,6 +1126,89 @@ out:
        return ret;
 }
 
+void wl12xx_queue_recovery_work(struct wl1271 *wl)
+{
+       if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+               ieee80211_queue_work(wl->hw, &wl->recovery_work);
+}
+
+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;
+       }
+
+       /* Make sure we have enough room */
+       len = min(len, (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);
+       wl->fwlog_size += len;
+
+       return len;
+}
+
+static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
+{
+       u32 addr;
+       u32 first_addr;
+       u8 *block;
+
+       if ((wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
+           (wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) ||
+           (wl->conf.fwlog.mem_blocks == 0))
+               return;
+
+       wl1271_info("Reading FW panic log");
+
+       block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
+       if (!block)
+               return;
+
+       /*
+        * Make sure the chip is awake and the logger isn't active.
+        * This might fail if the firmware hanged.
+        */
+       if (!wl1271_ps_elp_wakeup(wl))
+               wl12xx_cmd_stop_fwlog(wl);
+
+       /* Read the first memory block address */
+       wl1271_fw_status(wl, wl->fw_status);
+       first_addr = __le32_to_cpu(wl->fw_status->sta.log_start_addr);
+       if (!first_addr)
+               goto out;
+
+       /* Traverse the memory blocks linked list */
+       addr = first_addr;
+       do {
+               memset(block, 0, WL12XX_HW_BLOCK_SIZE);
+               wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
+                                  false);
+
+               /*
+                * Memory blocks are linked to one another. The first 4 bytes
+                * of each memory block hold the hardware address of the next
+                * one. The last memory block points to the first one.
+                */
+               addr = __le32_to_cpup((__le32 *)block);
+               if (!wl12xx_copy_fwlog(wl, block + sizeof(addr),
+                                      WL12XX_HW_BLOCK_SIZE - sizeof(addr)))
+                       break;
+       } while (addr && (addr != first_addr));
+
+       wake_up_interruptible(&wl->fwlog_waitq);
+
+out:
+       kfree(block);
+}
+
 static void wl1271_recovery_work(struct work_struct *work)
 {
        struct wl1271 *wl =
@@ -980,6 +1219,11 @@ static void wl1271_recovery_work(struct work_struct *work)
        if (wl->state != WL1271_STATE_ON)
                goto out;
 
+       /* Avoid a recursive recovery */
+       set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+
+       wl12xx_read_fwlog_panic(wl);
+
        wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
                    wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
 
@@ -996,6 +1240,9 @@ static void wl1271_recovery_work(struct work_struct *work)
 
        /* reboot the chipset */
        __wl1271_op_remove_interface(wl, false);
+
+       clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+
        ieee80211_restart_hw(wl->hw);
 
        /*
@@ -1074,9 +1321,13 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
                             wl->chip.id);
 
-               /* end-of-transaction flag should be set in wl127x AP mode */
+               /*
+                * 'end-of-transaction flag' and 'LPD mode flag'
+                * should be set in wl127x AP mode only
+                */
                if (wl->bss_type == BSS_TYPE_AP_BSS)
-                       wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
+                       wl->quirks |= (WL12XX_QUIRK_END_OF_TRANSACTION |
+                                      WL12XX_QUIRK_LPD_MODE);
 
                ret = wl1271_setup(wl);
                if (ret < 0)
@@ -1089,6 +1340,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
                ret = wl1271_setup(wl);
                if (ret < 0)
                        goto out;
+
                if (wl1271_set_block_size(wl))
                        wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT;
                break;
@@ -1117,24 +1369,6 @@ out:
        return ret;
 }
 
-static unsigned int wl1271_get_fw_ver_quirks(struct wl1271 *wl)
-{
-       unsigned int quirks = 0;
-       unsigned int *fw_ver = wl->chip.fw_ver;
-
-       /* Only for wl127x */
-       if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) &&
-           /* Check STA version */
-           (((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
-             (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) ||
-            /* Check AP version */
-            ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) &&
-             (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN))))
-               quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS;
-
-       return quirks;
-}
-
 int wl1271_plt_start(struct wl1271 *wl)
 {
        int retries = WL1271_BOOT_RETRIES;
@@ -1171,8 +1405,6 @@ int wl1271_plt_start(struct wl1271 *wl)
                wl1271_notice("firmware booted in PLT mode (%s)",
                              wl->chip.fw_ver_str);
 
-               /* Check if any quirks are needed with older fw versions */
-               wl->quirks |= wl1271_get_fw_ver_quirks(wl);
                goto out;
 
 irq_disable:
@@ -1352,13 +1584,10 @@ static struct notifier_block wl1271_dev_notifier = {
 };
 
 #ifdef CONFIG_PM
-static int wl1271_configure_suspend(struct wl1271 *wl)
+static int wl1271_configure_suspend_sta(struct wl1271 *wl)
 {
        int ret;
 
-       if (wl->bss_type != BSS_TYPE_STA_BSS)
-               return 0;
-
        mutex_lock(&wl->mutex);
 
        ret = wl1271_ps_elp_wakeup(wl);
@@ -1403,11 +1632,41 @@ out:
 
 }
 
+static int wl1271_configure_suspend_ap(struct wl1271 *wl)
+{
+       int ret;
+
+       mutex_lock(&wl->mutex);
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_unlock;
+
+       ret = wl1271_acx_set_ap_beacon_filter(wl, true);
+
+       wl1271_ps_elp_sleep(wl);
+out_unlock:
+       mutex_unlock(&wl->mutex);
+       return ret;
+
+}
+
+static int wl1271_configure_suspend(struct wl1271 *wl)
+{
+       if (wl->bss_type == BSS_TYPE_STA_BSS)
+               return wl1271_configure_suspend_sta(wl);
+       if (wl->bss_type == BSS_TYPE_AP_BSS)
+               return wl1271_configure_suspend_ap(wl);
+       return 0;
+}
+
 static void wl1271_configure_resume(struct wl1271 *wl)
 {
        int ret;
+       bool is_sta = wl->bss_type == BSS_TYPE_STA_BSS;
+       bool is_ap = wl->bss_type == BSS_TYPE_AP_BSS;
 
-       if (wl->bss_type != BSS_TYPE_STA_BSS)
+       if (!is_sta && !is_ap)
                return;
 
        mutex_lock(&wl->mutex);
@@ -1415,10 +1674,14 @@ static void wl1271_configure_resume(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
-       /* exit psm if it wasn't configured */
-       if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
-               wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
-                                  wl->basic_rate, true);
+       if (is_sta) {
+               /* exit psm if it wasn't configured */
+               if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
+                       wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
+                                          wl->basic_rate, true);
+       } else if (is_ap) {
+               wl1271_acx_set_ap_beacon_filter(wl, false);
+       }
 
        wl1271_ps_elp_sleep(wl);
 out:
@@ -1429,69 +1692,69 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
                            struct cfg80211_wowlan *wow)
 {
        struct wl1271 *wl = hw->priv;
+       int ret;
+
        wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
-       wl->wow_enabled = !!wow;
-       if (wl->wow_enabled) {
-               int ret;
-               ret = wl1271_configure_suspend(wl);
-               if (ret < 0) {
-                       wl1271_warning("couldn't prepare device to suspend");
-                       return ret;
-               }
-               /* flush any remaining work */
-               wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
-               flush_delayed_work(&wl->scan_complete_work);
+       WARN_ON(!wow || !wow->any);
 
-               /*
-                * disable and re-enable interrupts in order to flush
-                * the threaded_irq
-                */
-               wl1271_disable_interrupts(wl);
+       wl->wow_enabled = true;
+       ret = wl1271_configure_suspend(wl);
+       if (ret < 0) {
+               wl1271_warning("couldn't prepare device to suspend");
+               return ret;
+       }
+       /* flush any remaining work */
+       wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
+       flush_delayed_work(&wl->scan_complete_work);
 
-               /*
-                * set suspended flag to avoid triggering a new threaded_irq
-                * work. no need for spinlock as interrupts are disabled.
-                */
-               set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
+       /*
+        * disable and re-enable interrupts in order to flush
+        * the threaded_irq
+        */
+       wl1271_disable_interrupts(wl);
+
+       /*
+        * set suspended flag to avoid triggering a new threaded_irq
+        * work. no need for spinlock as interrupts are disabled.
+        */
+       set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
+
+       wl1271_enable_interrupts(wl);
+       flush_work(&wl->tx_work);
+       flush_delayed_work(&wl->pspoll_work);
+       flush_delayed_work(&wl->elp_work);
 
-               wl1271_enable_interrupts(wl);
-               flush_work(&wl->tx_work);
-               flush_delayed_work(&wl->pspoll_work);
-               flush_delayed_work(&wl->elp_work);
-       }
        return 0;
 }
 
 static int wl1271_op_resume(struct ieee80211_hw *hw)
 {
        struct wl1271 *wl = hw->priv;
+       unsigned long flags;
+       bool run_irq_work = false;
+
        wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
                     wl->wow_enabled);
+       WARN_ON(!wl->wow_enabled);
 
        /*
         * re-enable irq_work enqueuing, and call irq_work directly if
         * there is a pending work.
         */
-       if (wl->wow_enabled) {
-               struct wl1271 *wl = hw->priv;
-               unsigned long flags;
-               bool run_irq_work = false;
-
-               spin_lock_irqsave(&wl->wl_lock, flags);
-               clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
-               if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
-                       run_irq_work = true;
-               spin_unlock_irqrestore(&wl->wl_lock, flags);
-
-               if (run_irq_work) {
-                       wl1271_debug(DEBUG_MAC80211,
-                                    "run postponed irq_work directly");
-                       wl1271_irq(0, wl);
-                       wl1271_enable_interrupts(wl);
-               }
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
+       if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
+               run_irq_work = true;
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
 
-               wl1271_configure_resume(wl);
+       if (run_irq_work) {
+               wl1271_debug(DEBUG_MAC80211,
+                            "run postponed irq_work directly");
+               wl1271_irq(0, wl);
+               wl1271_enable_interrupts(wl);
        }
+       wl1271_configure_resume(wl);
+       wl->wow_enabled = false;
 
        return 0;
 }
@@ -1629,9 +1892,6 @@ power_off:
        strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
                sizeof(wiphy->fw_version));
 
-       /* Check if any quirks are needed with older fw versions */
-       wl->quirks |= wl1271_get_fw_ver_quirks(wl);
-
        /*
         * Now we know if 11a is supported (info from the NVS), so disable
         * 11a channels if not supported
@@ -1694,6 +1954,9 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
        cancel_delayed_work_sync(&wl->scan_complete_work);
        cancel_work_sync(&wl->netstack_work);
        cancel_work_sync(&wl->tx_work);
+       del_timer_sync(&wl->rx_streaming_timer);
+       cancel_work_sync(&wl->rx_streaming_enable_work);
+       cancel_work_sync(&wl->rx_streaming_disable_work);
        cancel_delayed_work_sync(&wl->pspoll_work);
        cancel_delayed_work_sync(&wl->elp_work);
 
@@ -2780,24 +3043,6 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
                }
        }
 
-       if (changed & BSS_CHANGED_IBSS) {
-               wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
-                            bss_conf->ibss_joined);
-
-               if (bss_conf->ibss_joined) {
-                       u32 rates = bss_conf->basic_rates;
-                       wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
-                                                                        rates);
-                       wl->basic_rate = wl1271_tx_min_rate_get(wl);
-
-                       /* by default, use 11b rates */
-                       wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
-                       ret = wl1271_acx_sta_rate_policies(wl);
-                       if (ret < 0)
-                               goto out;
-               }
-       }
-
        ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
        if (ret < 0)
                goto out;
@@ -3023,6 +3268,24 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                }
        }
 
+       if (changed & BSS_CHANGED_IBSS) {
+               wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
+                            bss_conf->ibss_joined);
+
+               if (bss_conf->ibss_joined) {
+                       u32 rates = bss_conf->basic_rates;
+                       wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
+                                                                        rates);
+                       wl->basic_rate = wl1271_tx_min_rate_get(wl);
+
+                       /* by default, use 11b rates */
+                       wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
+                       ret = wl1271_acx_sta_rate_policies(wl);
+                       if (ret < 0)
+                               goto out;
+               }
+       }
+
        ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
        if (ret < 0)
                goto out;
@@ -3061,6 +3324,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        wl1271_warning("cmd join failed %d", ret);
                        goto out;
                }
+               wl1271_check_operstate(wl, ieee80211_get_operstate(vif));
        }
 
 out:
@@ -3784,6 +4048,69 @@ static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
 static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
                   wl1271_sysfs_show_hw_pg_ver, NULL);
 
+static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
+                                      struct bin_attribute *bin_attr,
+                                      char *buffer, loff_t pos, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+       int ret;
+
+       ret = mutex_lock_interruptible(&wl->mutex);
+       if (ret < 0)
+               return -ERESTARTSYS;
+
+       /* Let only one thread read the log at a time, blocking others */
+       while (wl->fwlog_size == 0) {
+               DEFINE_WAIT(wait);
+
+               prepare_to_wait_exclusive(&wl->fwlog_waitq,
+                                         &wait,
+                                         TASK_INTERRUPTIBLE);
+
+               if (wl->fwlog_size != 0) {
+                       finish_wait(&wl->fwlog_waitq, &wait);
+                       break;
+               }
+
+               mutex_unlock(&wl->mutex);
+
+               schedule();
+               finish_wait(&wl->fwlog_waitq, &wait);
+
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+
+               ret = mutex_lock_interruptible(&wl->mutex);
+               if (ret < 0)
+                       return -ERESTARTSYS;
+       }
+
+       /* Check if the fwlog is still valid */
+       if (wl->fwlog_size < 0) {
+               mutex_unlock(&wl->mutex);
+               return 0;
+       }
+
+       /* Seeking is not supported - old logs are not kept. Disregard pos. */
+       len = min(count, (size_t)wl->fwlog_size);
+       wl->fwlog_size -= len;
+       memcpy(buffer, wl->fwlog, len);
+
+       /* Make room for new messages */
+       memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
+
+       mutex_unlock(&wl->mutex);
+
+       return len;
+}
+
+static struct bin_attribute fwlog_attr = {
+       .attr = {.name = "fwlog", .mode = S_IRUSR},
+       .read = wl1271_sysfs_read_fwlog,
+};
+
 int wl1271_register_hw(struct wl1271 *wl)
 {
        int ret;
@@ -3964,6 +4291,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        INIT_WORK(&wl->tx_work, wl1271_tx_work);
        INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
        INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
+       INIT_WORK(&wl->rx_streaming_enable_work,
+                 wl1271_rx_streaming_enable_work);
+       INIT_WORK(&wl->rx_streaming_disable_work,
+                 wl1271_rx_streaming_disable_work);
+
+       wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
+       if (!wl->freezable_wq) {
+               ret = -ENOMEM;
+               goto err_hw;
+       }
+
        wl->channel = WL1271_DEFAULT_CHANNEL;
        wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
        wl->default_key = 0;
@@ -3989,6 +4327,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        wl->quirks = 0;
        wl->platform_quirks = 0;
        wl->sched_scanning = false;
+       setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer,
+                   (unsigned long) wl);
+       wl->fwlog_size = 0;
+       init_waitqueue_head(&wl->fwlog_waitq);
 
        memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
        for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
@@ -4006,7 +4348,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
        if (!wl->aggr_buf) {
                ret = -ENOMEM;
-               goto err_hw;
+               goto err_wq;
        }
 
        wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
@@ -4015,11 +4357,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
                goto err_aggr;
        }
 
+       /* Allocate one page for the FW log */
+       wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
+       if (!wl->fwlog) {
+               ret = -ENOMEM;
+               goto err_dummy_packet;
+       }
+
        /* Register platform device */
        ret = platform_device_register(wl->plat_dev);
        if (ret) {
                wl1271_error("couldn't register platform device");
-               goto err_dummy_packet;
+               goto err_fwlog;
        }
        dev_set_drvdata(&wl->plat_dev->dev, wl);
 
@@ -4037,20 +4386,36 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
                goto err_bt_coex_state;
        }
 
+       /* Create sysfs file for the FW log */
+       ret = device_create_bin_file(&wl->plat_dev->dev, &fwlog_attr);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file fwlog");
+               goto err_hw_pg_ver;
+       }
+
        return hw;
 
+err_hw_pg_ver:
+       device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
+
 err_bt_coex_state:
        device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
 
 err_platform:
        platform_device_unregister(wl->plat_dev);
 
+err_fwlog:
+       free_page((unsigned long)wl->fwlog);
+
 err_dummy_packet:
        dev_kfree_skb(wl->dummy_packet);
 
 err_aggr:
        free_pages((unsigned long)wl->aggr_buf, order);
 
+err_wq:
+       destroy_workqueue(wl->freezable_wq);
+
 err_hw:
        wl1271_debugfs_exit(wl);
        kfree(plat_dev);
@@ -4066,7 +4431,15 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
 
 int wl1271_free_hw(struct wl1271 *wl)
 {
+       /* Unblock any fwlog readers */
+       mutex_lock(&wl->mutex);
+       wl->fwlog_size = -1;
+       wake_up_interruptible_all(&wl->fwlog_waitq);
+       mutex_unlock(&wl->mutex);
+
+       device_remove_bin_file(&wl->plat_dev->dev, &fwlog_attr);
        platform_device_unregister(wl->plat_dev);
+       free_page((unsigned long)wl->fwlog);
        dev_kfree_skb(wl->dummy_packet);
        free_pages((unsigned long)wl->aggr_buf,
                        get_order(WL1271_AGGR_BUFFER_SIZE));
@@ -4081,6 +4454,7 @@ int wl1271_free_hw(struct wl1271 *wl)
 
        kfree(wl->fw_status);
        kfree(wl->tx_res_if);
+       destroy_workqueue(wl->freezable_wq);
 
        ieee80211_free_hw(wl->hw);
 
@@ -4093,6 +4467,10 @@ EXPORT_SYMBOL_GPL(wl12xx_debug_level);
 module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
 
+module_param_named(fwlog, fwlog_param, charp, 0);
+MODULE_PARM_DESC(keymap,
+                "FW logger options: continuous, ondemand, dbgpins or disable");
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
index b59b67711a177ea6ca1160d5789a9efc62144ebe..3e68a664c9de220a58d7b42af96783c0021dacf9 100644 (file)
@@ -118,7 +118,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
                        &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
                if (ret == 0) {
                        wl1271_error("ELP wakeup timeout!");
-                       ieee80211_queue_work(wl->hw, &wl->recovery_work);
+                       wl12xx_queue_recovery_work(wl);
                        ret = -ETIMEDOUT;
                        goto err;
                } else if (ret < 0) {
@@ -169,9 +169,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
                wl1271_debug(DEBUG_PSM, "leaving psm");
 
                /* disable beacon early termination */
-               ret = wl1271_acx_bet_enable(wl, false);
-               if (ret < 0)
-                       return ret;
+               if (wl->band == IEEE80211_BAND_2GHZ) {
+                       ret = wl1271_acx_bet_enable(wl, false);
+                       if (ret < 0)
+                               return ret;
+               }
 
                /* disable beacon filtering */
                ret = wl1271_acx_beacon_filter_opt(wl, false);
@@ -202,7 +204,7 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
                        info = IEEE80211_SKB_CB(skb);
                        info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
                        info->status.rates[0].idx = -1;
-                       ieee80211_tx_status(wl->hw, skb);
+                       ieee80211_tx_status_ni(wl->hw, skb);
                        filtered++;
                }
        }
index 70091035e0199fc15fc18e1377e7b3e3eaf4d63f..0450fb49dbb198c117f521f4e379b645cb85b029 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include <linux/gfp.h>
+#include <linux/sched.h>
 
 #include "wl12xx.h"
 #include "acx.h"
@@ -95,6 +96,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
        struct ieee80211_hdr *hdr;
        u8 *buf;
        u8 beacon = 0;
+       u8 is_data = 0;
 
        /*
         * In PLT mode we seem to get frames and mac80211 warns about them,
@@ -106,6 +108,13 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
        /* the data read starts with the descriptor */
        desc = (struct wl1271_rx_descriptor *) data;
 
+       if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) {
+               size_t len = length - sizeof(*desc);
+               wl12xx_copy_fwlog(wl, data + sizeof(*desc), len);
+               wake_up_interruptible(&wl->fwlog_waitq);
+               return 0;
+       }
+
        switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
        /* discard corrupted packets */
        case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
@@ -137,6 +146,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
        hdr = (struct ieee80211_hdr *)skb->data;
        if (ieee80211_is_beacon(hdr->frame_control))
                beacon = 1;
+       if (ieee80211_is_data_present(hdr->frame_control))
+               is_data = 1;
 
        wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
 
@@ -147,9 +158,9 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
        skb_trim(skb, skb->len - desc->pad_len);
 
        skb_queue_tail(&wl->deferred_rx_queue, skb);
-       ieee80211_queue_work(wl->hw, &wl->netstack_work);
+       queue_work(wl->freezable_wq, &wl->netstack_work);
 
-       return 0;
+       return is_data;
 }
 
 void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
@@ -162,6 +173,8 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
        u32 mem_block;
        u32 pkt_length;
        u32 pkt_offset;
+       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+       bool had_data = false;
 
        while (drv_rx_counter != fw_rx_counter) {
                buf_size = 0;
@@ -214,9 +227,11 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
                         * conditions, in that case the received frame will just
                         * be dropped.
                         */
-                       wl1271_rx_handle_data(wl,
-                                             wl->aggr_buf + pkt_offset,
-                                             pkt_length);
+                       if (wl1271_rx_handle_data(wl,
+                                                 wl->aggr_buf + pkt_offset,
+                                                 pkt_length) == 1)
+                               had_data = true;
+
                        wl->rx_counter++;
                        drv_rx_counter++;
                        drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
@@ -230,6 +245,20 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
         */
        if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION)
                wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
+
+       if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
+           (wl->conf.rx_streaming.always ||
+            test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
+               u32 timeout = wl->conf.rx_streaming.duration;
+
+               /* restart rx streaming */
+               if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
+                       ieee80211_queue_work(wl->hw,
+                                            &wl->rx_streaming_enable_work);
+
+               mod_timer(&wl->rx_streaming_timer,
+                         jiffies + msecs_to_jiffies(timeout));
+       }
 }
 
 void wl1271_set_default_filters(struct wl1271 *wl)
index 75fabf83649137f959201aa0117be827c5fa54bd..c88e3fa1d6039c24aec78336117a00b811f1255c 100644 (file)
 #define RX_BUF_SIZE_MASK      0xFFF00
 #define RX_BUF_SIZE_SHIFT_DIV 6
 
+enum {
+       WL12XX_RX_CLASS_UNKNOWN,
+       WL12XX_RX_CLASS_MANAGEMENT,
+       WL12XX_RX_CLASS_DATA,
+       WL12XX_RX_CLASS_QOS_DATA,
+       WL12XX_RX_CLASS_BCN_PRBRSP,
+       WL12XX_RX_CLASS_EAPOL,
+       WL12XX_RX_CLASS_BA_EVENT,
+       WL12XX_RX_CLASS_AMSDU,
+       WL12XX_RX_CLASS_LOGGER,
+};
+
 struct wl1271_rx_descriptor {
        __le16 length;
        u8  status;
index 56f76abc754d170b8982961df3013a64ebd24ba0..5e5c66dd06d592e29ec2d16386a8d402533d789d 100644 (file)
@@ -62,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
 
        if (wl->scan.failed) {
                wl1271_info("Scan completed due to error.");
-               ieee80211_queue_work(wl->hw, &wl->recovery_work);
+               wl12xx_queue_recovery_work(wl);
        }
 
 out:
@@ -326,7 +326,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
                                    struct cfg80211_sched_scan_request *req,
                                    struct conn_scan_ch_params *channels,
                                    u32 band, bool radar, bool passive,
-                                   int start)
+                                   int start, int max_channels)
 {
        struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
        int i, j;
@@ -334,7 +334,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
        bool force_passive = !req->n_ssids;
 
        for (i = 0, j = start;
-            i < req->n_channels && j < MAX_CHANNELS_ALL_BANDS;
+            i < req->n_channels && j < max_channels;
             i++) {
                flags = req->channels[i]->flags;
 
@@ -380,46 +380,42 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
        return j - start;
 }
 
-static int
+static bool
 wl1271_scan_sched_scan_channels(struct wl1271 *wl,
                                struct cfg80211_sched_scan_request *req,
                                struct wl1271_cmd_sched_scan_config *cfg)
 {
-       int idx = 0;
-
        cfg->passive[0] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels,
+               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
                                                    IEEE80211_BAND_2GHZ,
-                                                   false, true, idx);
-       idx += cfg->passive[0];
-
+                                                   false, true, 0,
+                                                   MAX_CHANNELS_2GHZ);
        cfg->active[0] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels,
+               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
                                                    IEEE80211_BAND_2GHZ,
-                                                   false, false, idx);
-       /*
-        * 5GHz channels always start at position 14, not immediately
-        * after the last 2.4GHz channel
-        */
-       idx = 14;
-
+                                                   false, false,
+                                                   cfg->passive[0],
+                                                   MAX_CHANNELS_2GHZ);
        cfg->passive[1] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels,
+               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
                                                    IEEE80211_BAND_5GHZ,
-                                                   false, true, idx);
-       idx += cfg->passive[1];
-
+                                                   false, true, 0,
+                                                   MAX_CHANNELS_5GHZ);
        cfg->dfs =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels,
+               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
                                                    IEEE80211_BAND_5GHZ,
-                                                   true, true, idx);
-       idx += cfg->dfs;
-
+                                                   true, true,
+                                                   cfg->passive[1],
+                                                   MAX_CHANNELS_5GHZ);
        cfg->active[1] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels,
+               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
                                                    IEEE80211_BAND_5GHZ,
-                                                   false, false, idx);
-       idx += cfg->active[1];
+                                                   false, false,
+                                                   cfg->passive[1] + cfg->dfs,
+                                                   MAX_CHANNELS_5GHZ);
+       /* 802.11j channels are not supported yet */
+       cfg->passive[2] = 0;
+       cfg->active[2] = 0;
 
        wl1271_debug(DEBUG_SCAN, "    2.4GHz: active %d passive %d",
                     cfg->active[0], cfg->passive[0]);
@@ -427,7 +423,9 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
                     cfg->active[1], cfg->passive[1]);
        wl1271_debug(DEBUG_SCAN, "    DFS: %d", cfg->dfs);
 
-       return idx;
+       return  cfg->passive[0] || cfg->active[0] ||
+               cfg->passive[1] || cfg->active[1] || cfg->dfs ||
+               cfg->passive[2] || cfg->active[2];
 }
 
 int wl1271_scan_sched_scan_config(struct wl1271 *wl,
@@ -436,7 +434,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
 {
        struct wl1271_cmd_sched_scan_config *cfg = NULL;
        struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
-       int i, total_channels, ret;
+       int i, ret;
        bool force_passive = !req->n_ssids;
 
        wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
@@ -471,8 +469,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                cfg->ssid_len = 0;
        }
 
-       total_channels = wl1271_scan_sched_scan_channels(wl, req, cfg);
-       if (total_channels == 0) {
+       if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
                wl1271_error("scan channel list is empty");
                ret = -EINVAL;
                goto out;
index a0b6c5d67b0745218bb99396115c4386c0048b4e..ca81de20ebef838018511195624b0b61eb23912c 100644 (file)
@@ -112,18 +112,13 @@ struct wl1271_cmd_trigger_scan_to {
        __le32 timeout;
 } __packed;
 
-#define MAX_CHANNELS_ALL_BANDS 41
+#define MAX_CHANNELS_2GHZ      14
+#define MAX_CHANNELS_5GHZ      23
+#define MAX_CHANNELS_4GHZ      4
+
 #define SCAN_MAX_CYCLE_INTERVALS 16
 #define SCAN_MAX_BANDS 3
 
-enum {
-       SCAN_CHANNEL_TYPE_2GHZ_PASSIVE,
-       SCAN_CHANNEL_TYPE_2GHZ_ACTIVE,
-       SCAN_CHANNEL_TYPE_5GHZ_PASSIVE,
-       SCAN_CHANNEL_TYPE_5GHZ_ACTIVE,
-       SCAN_CHANNEL_TYPE_5GHZ_DFS,
-};
-
 enum {
        SCAN_SSID_FILTER_ANY      = 0,
        SCAN_SSID_FILTER_SPECIFIC = 1,
@@ -182,7 +177,9 @@ struct wl1271_cmd_sched_scan_config {
 
        u8 padding[3];
 
-       struct conn_scan_ch_params channels[MAX_CHANNELS_ALL_BANDS];
+       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+       struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
+       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
 } __packed;
 
 
index 536e5065454b084cb5042f71f24e707bd8951ccf..4dc4573b686175eacbf585bf12b54d95855ab6af 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <linux/irq.h>
 #include <linux/module.h>
-#include <linux/crc7.h>
 #include <linux/vmalloc.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/sdio_ids.h>
@@ -45,7 +44,7 @@
 #define SDIO_DEVICE_ID_TI_WL1271       0x4076
 #endif
 
-static const struct sdio_device_id wl1271_devices[] = {
+static const struct sdio_device_id wl1271_devices[] __devinitconst = {
        { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
        {}
 };
@@ -107,14 +106,6 @@ static void wl1271_sdio_enable_interrupts(struct wl1271 *wl)
        enable_irq(wl->irq);
 }
 
-static void wl1271_sdio_reset(struct wl1271 *wl)
-{
-}
-
-static void wl1271_sdio_init(struct wl1271 *wl)
-{
-}
-
 static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
                                 size_t len, bool fixed)
 {
@@ -170,10 +161,12 @@ static int wl1271_sdio_power_on(struct wl1271 *wl)
        struct sdio_func *func = wl_to_func(wl);
        int ret;
 
-       /* Make sure the card will not be powered off by runtime PM */
-       ret = pm_runtime_get_sync(&func->dev);
-       if (ret < 0)
-               goto out;
+       /* If enabled, tell runtime PM not to power off the card */
+       if (pm_runtime_enabled(&func->dev)) {
+               ret = pm_runtime_get_sync(&func->dev);
+               if (ret)
+                       goto out;
+       }
 
        /* Runtime PM might be disabled, so power up the card manually */
        ret = mmc_power_restore_host(func->card->host);
@@ -200,8 +193,11 @@ static int wl1271_sdio_power_off(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
-       /* Let runtime PM know the card is powered off */
-       return pm_runtime_put_sync(&func->dev);
+       /* If enabled, let runtime PM know the card is powered off */
+       if (pm_runtime_enabled(&func->dev))
+               ret = pm_runtime_put_sync(&func->dev);
+
+       return ret;
 }
 
 static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
@@ -215,8 +211,6 @@ static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
 static struct wl1271_if_operations sdio_ops = {
        .read           = wl1271_sdio_raw_read,
        .write          = wl1271_sdio_raw_write,
-       .reset          = wl1271_sdio_reset,
-       .init           = wl1271_sdio_init,
        .power          = wl1271_sdio_set_power,
        .dev            = wl1271_sdio_wl_to_dev,
        .enable_irq     = wl1271_sdio_enable_interrupts,
@@ -278,17 +272,19 @@ static int __devinit wl1271_probe(struct sdio_func *func,
                goto out_free;
        }
 
-       enable_irq_wake(wl->irq);
-       device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1);
-
-       disable_irq(wl->irq);
+       ret = enable_irq_wake(wl->irq);
+       if (!ret) {
+               wl->irq_wake_enabled = true;
+               device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1);
 
-       /* if sdio can keep power while host is suspended, enable wow */
-       mmcflags = sdio_get_host_pm_caps(func);
-       wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags);
+               /* if sdio can keep power while host is suspended, enable wow */
+               mmcflags = sdio_get_host_pm_caps(func);
+               wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags);
 
-       if (mmcflags & MMC_PM_KEEP_POWER)
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
+               if (mmcflags & MMC_PM_KEEP_POWER)
+                       hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
+       }
+       disable_irq(wl->irq);
 
        ret = wl1271_init_ieee80211(wl);
        if (ret)
@@ -303,8 +299,6 @@ static int __devinit wl1271_probe(struct sdio_func *func,
        /* Tell PM core that we don't need the card to be powered now */
        pm_runtime_put_noidle(&func->dev);
 
-       wl1271_notice("initialized");
-
        return 0;
 
  out_irq:
@@ -324,8 +318,10 @@ static void __devexit wl1271_remove(struct sdio_func *func)
        pm_runtime_get_noresume(&func->dev);
 
        wl1271_unregister_hw(wl);
-       device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0);
-       disable_irq_wake(wl->irq);
+       if (wl->irq_wake_enabled) {
+               device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0);
+               disable_irq_wake(wl->irq);
+       }
        free_irq(wl->irq, wl);
        wl1271_free_hw(wl);
 }
@@ -402,23 +398,12 @@ static struct sdio_driver wl1271_sdio_driver = {
 
 static int __init wl1271_init(void)
 {
-       int ret;
-
-       ret = sdio_register_driver(&wl1271_sdio_driver);
-       if (ret < 0) {
-               wl1271_error("failed to register sdio driver: %d", ret);
-               goto out;
-       }
-
-out:
-       return ret;
+       return sdio_register_driver(&wl1271_sdio_driver);
 }
 
 static void __exit wl1271_exit(void)
 {
        sdio_unregister_driver(&wl1271_sdio_driver);
-
-       wl1271_notice("unloaded");
 }
 
 module_init(wl1271_init);
index beebf64c535927ca43173a9cb66507ad6e2ad003..e0b3736d7e1921886d7b3e1882efb9eed11beaaa 100644 (file)
@@ -436,8 +436,6 @@ static int __devinit wl1271_probe(struct spi_device *spi)
        if (ret)
                goto out_irq;
 
-       wl1271_notice("initialized");
-
        return 0;
 
  out_irq:
@@ -474,23 +472,12 @@ static struct spi_driver wl1271_spi_driver = {
 
 static int __init wl1271_init(void)
 {
-       int ret;
-
-       ret = spi_register_driver(&wl1271_spi_driver);
-       if (ret < 0) {
-               wl1271_error("failed to register spi driver: %d", ret);
-               goto out;
-       }
-
-out:
-       return ret;
+       return spi_register_driver(&wl1271_spi_driver);
 }
 
 static void __exit wl1271_exit(void)
 {
        spi_unregister_driver(&wl1271_spi_driver);
-
-       wl1271_notice("unloaded");
 }
 
 module_init(wl1271_init);
index da351d7cd1f29ff3772f53ac27c2faad561e560d..5d5e1ef87206a4e1722510084ba4cab2783cf0bd 100644 (file)
@@ -260,7 +260,7 @@ static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[])
 {
        wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover");
 
-       ieee80211_queue_work(wl->hw, &wl->recovery_work);
+       wl12xx_queue_recovery_work(wl);
 
        return 0;
 }
index ca3ab1c1aceffcd497cc6aca83bad587c553b4b0..200590c0d9e3484ef2192d6e89a8c7ba4e0cdb3e 100644 (file)
@@ -562,17 +562,29 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
+static bool wl1271_tx_is_data_present(struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);
+
+       return ieee80211_is_data_present(hdr->frame_control);
+}
+
 void wl1271_tx_work_locked(struct wl1271 *wl)
 {
        struct sk_buff *skb;
        u32 buf_offset = 0;
        bool sent_packets = false;
+       bool had_data = false;
+       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
        int ret;
 
        if (unlikely(wl->state == WL1271_STATE_OFF))
                return;
 
        while ((skb = wl1271_skb_dequeue(wl))) {
+               if (wl1271_tx_is_data_present(skb))
+                       had_data = true;
+
                ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
                if (ret == -EAGAIN) {
                        /*
@@ -619,6 +631,19 @@ out_ack:
 
                wl1271_handle_tx_low_watermark(wl);
        }
+       if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
+           (wl->conf.rx_streaming.always ||
+            test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
+               u32 timeout = wl->conf.rx_streaming.duration;
+
+               /* enable rx streaming */
+               if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
+                       ieee80211_queue_work(wl->hw,
+                                            &wl->rx_streaming_enable_work);
+
+               mod_timer(&wl->rx_streaming_timer,
+                         jiffies + msecs_to_jiffies(timeout));
+       }
 }
 
 void wl1271_tx_work(struct work_struct *work)
@@ -702,7 +727,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
 
        /* return the packet to the stack */
        skb_queue_tail(&wl->deferred_tx_queue, skb);
-       ieee80211_queue_work(wl->hw, &wl->netstack_work);
+       queue_work(wl->freezable_wq, &wl->netstack_work);
        wl1271_free_tx_id(wl, result->id);
 }
 
@@ -757,7 +782,7 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
                        info = IEEE80211_SKB_CB(skb);
                        info->status.rates[0].idx = -1;
                        info->status.rates[0].count = 0;
-                       ieee80211_tx_status(wl->hw, skb);
+                       ieee80211_tx_status_ni(wl->hw, skb);
                        total++;
                }
        }
@@ -795,7 +820,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
                                        info = IEEE80211_SKB_CB(skb);
                                        info->status.rates[0].idx = -1;
                                        info->status.rates[0].count = 0;
-                                       ieee80211_tx_status(wl->hw, skb);
+                                       ieee80211_tx_status_ni(wl->hw, skb);
                                }
                        }
                }
@@ -838,7 +863,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
                        info->status.rates[0].idx = -1;
                        info->status.rates[0].count = 0;
 
-                       ieee80211_tx_status(wl->hw, skb);
+                       ieee80211_tx_status_ni(wl->hw, skb);
                }
        }
 }
index 3bc794a1ee75b71cb3730fbe621523577ed9a113..d7db6e77047a684538c8ba2ee99f578ef29b2e99 100644 (file)
@@ -226,6 +226,8 @@ enum {
 #define FW_VER_MINOR_1_SPARE_STA_MIN 58
 #define FW_VER_MINOR_1_SPARE_AP_MIN  47
 
+#define FW_VER_MINOR_FWLOG_STA_MIN 70
+
 struct wl1271_chip {
        u32 id;
        char fw_ver_str[ETHTOOL_BUSINFO_LEN];
@@ -284,8 +286,7 @@ struct wl1271_fw_sta_status {
        u8  tx_total;
        u8  reserved1;
        __le16 reserved2;
-       /* Total structure size is 68 bytes */
-       u32 padding;
+       __le32 log_start_addr;
 } __packed;
 
 struct wl1271_fw_full_status {
@@ -359,6 +360,9 @@ enum wl12xx_flags {
        WL1271_FLAG_DUMMY_PACKET_PENDING,
        WL1271_FLAG_SUSPENDED,
        WL1271_FLAG_PENDING_WORK,
+       WL1271_FLAG_SOFT_GEMINI,
+       WL1271_FLAG_RX_STREAMING_STARTED,
+       WL1271_FLAG_RECOVERY_IN_PROGRESS,
 };
 
 struct wl1271_link {
@@ -443,6 +447,7 @@ struct wl1271 {
        struct sk_buff_head deferred_tx_queue;
 
        struct work_struct tx_work;
+       struct workqueue_struct *freezable_wq;
 
        /* Pending TX frames */
        unsigned long tx_frames_map[BITS_TO_LONGS(ACX_TX_DESCRIPTORS)];
@@ -468,6 +473,15 @@ struct wl1271 {
        /* Network stack work  */
        struct work_struct netstack_work;
 
+       /* FW log buffer */
+       u8 *fwlog;
+
+       /* Number of valid bytes in the FW log buffer */
+       ssize_t fwlog_size;
+
+       /* Sysfs FW log entry readers wait queue */
+       wait_queue_head_t fwlog_waitq;
+
        /* Hardware recovery work */
        struct work_struct recovery_work;
 
@@ -508,6 +522,11 @@ struct wl1271 {
        /* Default key (for WEP) */
        u32 default_key;
 
+       /* Rx Streaming */
+       struct work_struct rx_streaming_enable_work;
+       struct work_struct rx_streaming_disable_work;
+       struct timer_list rx_streaming_timer;
+
        unsigned int filters;
        unsigned int rx_config;
        unsigned int rx_filter;
@@ -573,6 +592,7 @@ struct wl1271 {
         * (currently, only "ANY" trigger is supported)
         */
        bool wow_enabled;
+       bool irq_wake_enabled;
 
        /*
         * AP-mode - links indexed by HLID. The global and broadcast links
@@ -602,6 +622,9 @@ struct wl1271_station {
 
 int wl1271_plt_start(struct wl1271 *wl);
 int wl1271_plt_stop(struct wl1271 *wl);
+int wl1271_recalc_rx_streaming(struct wl1271 *wl);
+void wl12xx_queue_recovery_work(struct wl1271 *wl);
+size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
 
 #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
 
@@ -637,4 +660,15 @@ int wl1271_plt_stop(struct wl1271 *wl);
 /* WL128X requires aggregated packets to be aligned to the SDIO block size */
 #define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT       BIT(2)
 
+/*
+ * WL127X AP mode requires Low Power DRPw (LPD) enable to reduce power
+ * consumption
+ */
+#define WL12XX_QUIRK_LPD_MODE                   BIT(3)
+
+/* Older firmwares did not implement the FW logger over bus feature */
+#define WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED     BIT(4)
+
+#define WL12XX_HW_BLOCK_SIZE   256
+
 #endif
index ea1580085347fda5a378dfc3e2291c918ba86a66..2acff4307ca401c70b40909826e9d5e1cccb6c3f 100644 (file)
@@ -2,17 +2,8 @@
 # Near Field Communication (NFC) devices
 #
 
-menuconfig NFC_DEVICES
-       bool "Near Field Communication (NFC) devices"
-       default n
-       ---help---
-         You'll have to say Y if your computer contains an NFC device that
-         you want to use under Linux.
-
-         You can say N here if you don't have any Near Field Communication
-         devices connected to your computer.
-
-if NFC_DEVICES
+menu "Near Field Communication (NFC) devices"
+       depends on NFC
 
 config PN544_NFC
        tristate "PN544 NFC driver"
@@ -26,5 +17,14 @@ config PN544_NFC
          To compile this driver as a module, choose m here. The module will
          be called pn544.
 
+config NFC_PN533
+       tristate "NXP PN533 USB driver"
+       depends on USB
+       help
+         NXP PN533 USB driver.
+         This driver provides support for NFC NXP PN533 devices.
+
+         Say Y here to compile support for PN533 devices into the
+         kernel or say M to compile it as module (pn533).
 
-endif # NFC_DEVICES
+endmenu
index a4efb164ec49cef00fba7ee8214f5f524cea9489..8ef446d2c1bd9f57206c8c48cba7a0487eb6d98a 100644 (file)
@@ -3,3 +3,6 @@
 #
 
 obj-$(CONFIG_PN544_NFC)                += pn544.o
+obj-$(CONFIG_NFC_PN533)                += pn533.o
+
+ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
new file mode 100644 (file)
index 0000000..0372315
--- /dev/null
@@ -0,0 +1,1632 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/nfc.h>
+#include <linux/netdevice.h>
+#include <net/nfc.h>
+
+#define VERSION "0.1"
+
+#define PN533_VENDOR_ID 0x4CC
+#define PN533_PRODUCT_ID 0x2533
+
+#define SCM_VENDOR_ID 0x4E6
+#define SCL3711_PRODUCT_ID 0x5591
+
+static const struct usb_device_id pn533_table[] = {
+       { USB_DEVICE(PN533_VENDOR_ID, PN533_PRODUCT_ID) },
+       { USB_DEVICE(SCM_VENDOR_ID, SCL3711_PRODUCT_ID) },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, pn533_table);
+
+/* frame definitions */
+#define PN533_FRAME_TAIL_SIZE 2
+#define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \
+                               PN533_FRAME_TAIL_SIZE)
+#define PN533_FRAME_ACK_SIZE (sizeof(struct pn533_frame) + 1)
+#define PN533_FRAME_CHECKSUM(f) (f->data[f->datalen])
+#define PN533_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
+
+/* start of frame */
+#define PN533_SOF 0x00FF
+
+/* frame identifier: in/out/error */
+#define PN533_FRAME_IDENTIFIER(f) (f->data[0])
+#define PN533_DIR_OUT 0xD4
+#define PN533_DIR_IN 0xD5
+
+/* PN533 Commands */
+#define PN533_FRAME_CMD(f) (f->data[1])
+#define PN533_FRAME_CMD_PARAMS_PTR(f) (&f->data[2])
+#define PN533_FRAME_CMD_PARAMS_LEN(f) (f->datalen - 2)
+
+#define PN533_CMD_GET_FIRMWARE_VERSION 0x02
+#define PN533_CMD_RF_CONFIGURATION 0x32
+#define PN533_CMD_IN_DATA_EXCHANGE 0x40
+#define PN533_CMD_IN_LIST_PASSIVE_TARGET 0x4A
+#define PN533_CMD_IN_ATR 0x50
+#define PN533_CMD_IN_RELEASE 0x52
+
+#define PN533_CMD_RESPONSE(cmd) (cmd + 1)
+
+/* PN533 Return codes */
+#define PN533_CMD_RET_MASK 0x3F
+#define PN533_CMD_MI_MASK 0x40
+#define PN533_CMD_RET_SUCCESS 0x00
+
+struct pn533;
+
+typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg,
+                                       u8 *params, int params_len);
+
+/* structs for pn533 commands */
+
+/* PN533_CMD_GET_FIRMWARE_VERSION */
+struct pn533_fw_version {
+       u8 ic;
+       u8 ver;
+       u8 rev;
+       u8 support;
+};
+
+/* PN533_CMD_RF_CONFIGURATION */
+#define PN533_CFGITEM_MAX_RETRIES 0x05
+
+#define PN533_CONFIG_MAX_RETRIES_NO_RETRY 0x00
+#define PN533_CONFIG_MAX_RETRIES_ENDLESS 0xFF
+
+struct pn533_config_max_retries {
+       u8 mx_rty_atr;
+       u8 mx_rty_psl;
+       u8 mx_rty_passive_act;
+} __packed;
+
+/* PN533_CMD_IN_LIST_PASSIVE_TARGET */
+
+/* felica commands opcode */
+#define PN533_FELICA_OPC_SENSF_REQ 0
+#define PN533_FELICA_OPC_SENSF_RES 1
+/* felica SENSF_REQ parameters */
+#define PN533_FELICA_SENSF_SC_ALL 0xFFFF
+#define PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE 0
+#define PN533_FELICA_SENSF_RC_SYSTEM_CODE 1
+#define PN533_FELICA_SENSF_RC_ADVANCED_PROTOCOL 2
+
+/* type B initiator_data values */
+#define PN533_TYPE_B_AFI_ALL_FAMILIES 0
+#define PN533_TYPE_B_POLL_METHOD_TIMESLOT 0
+#define PN533_TYPE_B_POLL_METHOD_PROBABILISTIC 1
+
+union pn533_cmd_poll_initdata {
+       struct {
+               u8 afi;
+               u8 polling_method;
+       } __packed type_b;
+       struct {
+               u8 opcode;
+               __be16 sc;
+               u8 rc;
+               u8 tsn;
+       } __packed felica;
+};
+
+/* Poll modulations */
+enum {
+       PN533_POLL_MOD_106KBPS_A,
+       PN533_POLL_MOD_212KBPS_FELICA,
+       PN533_POLL_MOD_424KBPS_FELICA,
+       PN533_POLL_MOD_106KBPS_JEWEL,
+       PN533_POLL_MOD_847KBPS_B,
+
+       __PN533_POLL_MOD_AFTER_LAST,
+};
+#define PN533_POLL_MOD_MAX (__PN533_POLL_MOD_AFTER_LAST - 1)
+
+struct pn533_poll_modulations {
+       struct {
+               u8 maxtg;
+               u8 brty;
+               union pn533_cmd_poll_initdata initiator_data;
+       } __packed data;
+       u8 len;
+};
+
+const struct pn533_poll_modulations poll_mod[] = {
+       [PN533_POLL_MOD_106KBPS_A] = {
+               .data = {
+                       .maxtg = 1,
+                       .brty = 0,
+               },
+               .len = 2,
+       },
+       [PN533_POLL_MOD_212KBPS_FELICA] = {
+               .data = {
+                       .maxtg = 1,
+                       .brty = 1,
+                       .initiator_data.felica = {
+                               .opcode = PN533_FELICA_OPC_SENSF_REQ,
+                               .sc = PN533_FELICA_SENSF_SC_ALL,
+                               .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+                               .tsn = 0,
+                       },
+               },
+               .len = 7,
+       },
+       [PN533_POLL_MOD_424KBPS_FELICA] = {
+               .data = {
+                       .maxtg = 1,
+                       .brty = 2,
+                       .initiator_data.felica = {
+                               .opcode = PN533_FELICA_OPC_SENSF_REQ,
+                               .sc = PN533_FELICA_SENSF_SC_ALL,
+                               .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+                               .tsn = 0,
+                       },
+                },
+               .len = 7,
+       },
+       [PN533_POLL_MOD_106KBPS_JEWEL] = {
+               .data = {
+                       .maxtg = 1,
+                       .brty = 4,
+               },
+               .len = 2,
+       },
+       [PN533_POLL_MOD_847KBPS_B] = {
+               .data = {
+                       .maxtg = 1,
+                       .brty = 8,
+                       .initiator_data.type_b = {
+                               .afi = PN533_TYPE_B_AFI_ALL_FAMILIES,
+                               .polling_method =
+                                       PN533_TYPE_B_POLL_METHOD_TIMESLOT,
+                       },
+               },
+               .len = 3,
+       },
+};
+
+/* PN533_CMD_IN_ATR */
+
+struct pn533_cmd_activate_param {
+       u8 tg;
+       u8 next;
+} __packed;
+
+struct pn533_cmd_activate_response {
+       u8 status;
+       u8 nfcid3t[10];
+       u8 didt;
+       u8 bst;
+       u8 brt;
+       u8 to;
+       u8 ppt;
+       /* optional */
+       u8 gt[];
+} __packed;
+
+
+struct pn533 {
+       struct usb_device *udev;
+       struct usb_interface *interface;
+       struct nfc_dev *nfc_dev;
+
+       struct urb *out_urb;
+       int out_maxlen;
+       struct pn533_frame *out_frame;
+
+       struct urb *in_urb;
+       int in_maxlen;
+       struct pn533_frame *in_frame;
+
+       struct tasklet_struct tasklet;
+       struct pn533_frame *tklt_in_frame;
+       int tklt_in_error;
+
+       pn533_cmd_complete_t cmd_complete;
+       void *cmd_complete_arg;
+       struct semaphore cmd_lock;
+       u8 cmd;
+
+       struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
+       u8 poll_mod_count;
+       u8 poll_mod_curr;
+       u32 poll_protocols;
+
+       u8 tgt_available_prots;
+       u8 tgt_active_prot;
+};
+
+struct pn533_frame {
+       u8 preamble;
+       __be16 start_frame;
+       u8 datalen;
+       u8 datalen_checksum;
+       u8 data[];
+} __packed;
+
+/* The rule: value + checksum = 0 */
+static inline u8 pn533_checksum(u8 value)
+{
+       return ~value + 1;
+}
+
+/* The rule: sum(data elements) + checksum = 0 */
+static u8 pn533_data_checksum(u8 *data, int datalen)
+{
+       u8 sum = 0;
+       int i;
+
+       for (i = 0; i < datalen; i++)
+               sum += data[i];
+
+       return pn533_checksum(sum);
+}
+
+/**
+ * pn533_tx_frame_ack - create a ack frame
+ * @frame:     The frame to be set as ack
+ *
+ * Ack is different type of standard frame. As a standard frame, it has
+ * preamble and start_frame. However the checksum of this frame must fail,
+ * i.e. datalen + datalen_checksum must NOT be zero. When the checksum test
+ * fails and datalen = 0 and datalen_checksum = 0xFF, the frame is a ack.
+ * After datalen_checksum field, the postamble is placed.
+ */
+static void pn533_tx_frame_ack(struct pn533_frame *frame)
+{
+       frame->preamble = 0;
+       frame->start_frame = cpu_to_be16(PN533_SOF);
+       frame->datalen = 0;
+       frame->datalen_checksum = 0xFF;
+       /* data[0] is used as postamble */
+       frame->data[0] = 0;
+}
+
+static void pn533_tx_frame_init(struct pn533_frame *frame, u8 cmd)
+{
+       frame->preamble = 0;
+       frame->start_frame = cpu_to_be16(PN533_SOF);
+       PN533_FRAME_IDENTIFIER(frame) = PN533_DIR_OUT;
+       PN533_FRAME_CMD(frame) = cmd;
+       frame->datalen = 2;
+}
+
+static void pn533_tx_frame_finish(struct pn533_frame *frame)
+{
+       frame->datalen_checksum = pn533_checksum(frame->datalen);
+
+       PN533_FRAME_CHECKSUM(frame) =
+               pn533_data_checksum(frame->data, frame->datalen);
+
+       PN533_FRAME_POSTAMBLE(frame) = 0;
+}
+
+static bool pn533_rx_frame_is_valid(struct pn533_frame *frame)
+{
+       u8 checksum;
+
+       if (frame->start_frame != cpu_to_be16(PN533_SOF))
+               return false;
+
+       checksum = pn533_checksum(frame->datalen);
+       if (checksum != frame->datalen_checksum)
+               return false;
+
+       checksum = pn533_data_checksum(frame->data, frame->datalen);
+       if (checksum != PN533_FRAME_CHECKSUM(frame))
+               return false;
+
+       return true;
+}
+
+static bool pn533_rx_frame_is_ack(struct pn533_frame *frame)
+{
+       if (frame->start_frame != cpu_to_be16(PN533_SOF))
+               return false;
+
+       if (frame->datalen != 0 || frame->datalen_checksum != 0xFF)
+               return false;
+
+       return true;
+}
+
+static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd)
+{
+       return (PN533_FRAME_CMD(frame) == PN533_CMD_RESPONSE(cmd));
+}
+
+static void pn533_tasklet_cmd_complete(unsigned long arg)
+{
+       struct pn533 *dev = (struct pn533 *) arg;
+       struct pn533_frame *in_frame = dev->tklt_in_frame;
+       int rc;
+
+       if (dev->tklt_in_error)
+               rc = dev->cmd_complete(dev, dev->cmd_complete_arg, NULL,
+                                                       dev->tklt_in_error);
+       else
+               rc = dev->cmd_complete(dev, dev->cmd_complete_arg,
+                                       PN533_FRAME_CMD_PARAMS_PTR(in_frame),
+                                       PN533_FRAME_CMD_PARAMS_LEN(in_frame));
+
+       if (rc != -EINPROGRESS)
+               up(&dev->cmd_lock);
+}
+
+static void pn533_recv_response(struct urb *urb)
+{
+       struct pn533 *dev = urb->context;
+       struct pn533_frame *in_frame;
+
+       dev->tklt_in_frame = NULL;
+
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
+                                               " status: %d", urb->status);
+               dev->tklt_in_error = urb->status;
+               goto sched_tasklet;
+       default:
+               nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:"
+                                                       " %d", urb->status);
+               dev->tklt_in_error = urb->status;
+               goto sched_tasklet;
+       }
+
+       in_frame = dev->in_urb->transfer_buffer;
+
+       if (!pn533_rx_frame_is_valid(in_frame)) {
+               nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
+               dev->tklt_in_error = -EIO;
+               goto sched_tasklet;
+       }
+
+       if (!pn533_rx_frame_is_cmd_response(in_frame, dev->cmd)) {
+               nfc_dev_err(&dev->interface->dev, "The received frame is not "
+                                               "response to the last command");
+               dev->tklt_in_error = -EIO;
+               goto sched_tasklet;
+       }
+
+       nfc_dev_dbg(&dev->interface->dev, "Received a valid frame");
+       dev->tklt_in_error = 0;
+       dev->tklt_in_frame = in_frame;
+
+sched_tasklet:
+       tasklet_schedule(&dev->tasklet);
+}
+
+static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags)
+{
+       dev->in_urb->complete = pn533_recv_response;
+
+       return usb_submit_urb(dev->in_urb, flags);
+}
+
+static void pn533_recv_ack(struct urb *urb)
+{
+       struct pn533 *dev = urb->context;
+       struct pn533_frame *in_frame;
+       int rc;
+
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
+                                               " status: %d", urb->status);
+               dev->tklt_in_error = urb->status;
+               goto sched_tasklet;
+       default:
+               nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:"
+                                                       " %d", urb->status);
+               dev->tklt_in_error = urb->status;
+               goto sched_tasklet;
+       }
+
+       in_frame = dev->in_urb->transfer_buffer;
+
+       if (!pn533_rx_frame_is_ack(in_frame)) {
+               nfc_dev_err(&dev->interface->dev, "Received an invalid ack");
+               dev->tklt_in_error = -EIO;
+               goto sched_tasklet;
+       }
+
+       nfc_dev_dbg(&dev->interface->dev, "Received a valid ack");
+
+       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);
+               dev->tklt_in_error = rc;
+               goto sched_tasklet;
+       }
+
+       return;
+
+sched_tasklet:
+       dev->tklt_in_frame = NULL;
+       tasklet_schedule(&dev->tasklet);
+}
+
+static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags)
+{
+       dev->in_urb->complete = pn533_recv_ack;
+
+       return usb_submit_urb(dev->in_urb, flags);
+}
+
+static int pn533_send_ack(struct pn533 *dev, gfp_t flags)
+{
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       pn533_tx_frame_ack(dev->out_frame);
+
+       dev->out_urb->transfer_buffer = dev->out_frame;
+       dev->out_urb->transfer_buffer_length = PN533_FRAME_ACK_SIZE;
+       rc = usb_submit_urb(dev->out_urb, flags);
+
+       return rc;
+}
+
+static int __pn533_send_cmd_frame_async(struct pn533 *dev,
+                                       struct pn533_frame *out_frame,
+                                       struct pn533_frame *in_frame,
+                                       int in_frame_len,
+                                       pn533_cmd_complete_t cmd_complete,
+                                       void *arg, gfp_t flags)
+{
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x",
+                                               PN533_FRAME_CMD(out_frame));
+
+       dev->cmd = PN533_FRAME_CMD(out_frame);
+       dev->cmd_complete = cmd_complete;
+       dev->cmd_complete_arg = arg;
+
+       dev->out_urb->transfer_buffer = out_frame;
+       dev->out_urb->transfer_buffer_length =
+                               PN533_FRAME_SIZE(out_frame);
+
+       dev->in_urb->transfer_buffer = in_frame;
+       dev->in_urb->transfer_buffer_length = in_frame_len;
+
+       rc = usb_submit_urb(dev->out_urb, flags);
+       if (rc)
+               return rc;
+
+       rc = pn533_submit_urb_for_ack(dev, flags);
+       if (rc)
+               goto error;
+
+       return 0;
+
+error:
+       usb_unlink_urb(dev->out_urb);
+       return rc;
+}
+
+static int pn533_send_cmd_frame_async(struct pn533 *dev,
+                                       struct pn533_frame *out_frame,
+                                       struct pn533_frame *in_frame,
+                                       int in_frame_len,
+                                       pn533_cmd_complete_t cmd_complete,
+                                       void *arg, gfp_t flags)
+{
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (down_trylock(&dev->cmd_lock))
+               return -EBUSY;
+
+       rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
+                                       in_frame_len, cmd_complete, arg, flags);
+       if (rc)
+               goto error;
+
+       return 0;
+error:
+       up(&dev->cmd_lock);
+       return rc;
+}
+
+struct pn533_sync_cmd_response {
+       int rc;
+       struct completion done;
+};
+
+static int pn533_sync_cmd_complete(struct pn533 *dev, void *_arg,
+                                       u8 *params, int params_len)
+{
+       struct pn533_sync_cmd_response *arg = _arg;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       arg->rc = 0;
+
+       if (params_len < 0) /* error */
+               arg->rc = params_len;
+
+       complete(&arg->done);
+
+       return 0;
+}
+
+static int pn533_send_cmd_frame_sync(struct pn533 *dev,
+                                               struct pn533_frame *out_frame,
+                                               struct pn533_frame *in_frame,
+                                               int in_frame_len)
+{
+       int rc;
+       struct pn533_sync_cmd_response arg;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       init_completion(&arg.done);
+
+       rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, in_frame_len,
+                               pn533_sync_cmd_complete, &arg, GFP_KERNEL);
+       if (rc)
+               return rc;
+
+       wait_for_completion(&arg.done);
+
+       return arg.rc;
+}
+
+static void pn533_send_complete(struct urb *urb)
+{
+       struct pn533 *dev = urb->context;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
+                                               " status: %d", urb->status);
+               break;
+       default:
+               nfc_dev_dbg(&dev->interface->dev, "Nonzero urb status received:"
+                                                       " %d", urb->status);
+       }
+}
+
+struct pn533_target_type_a {
+       __be16 sens_res;
+       u8 sel_res;
+       u8 nfcid_len;
+       u8 nfcid_data[];
+} __packed;
+
+
+#define PN533_TYPE_A_SENS_RES_NFCID1(x) ((u8)((be16_to_cpu(x) & 0x00C0) >> 6))
+#define PN533_TYPE_A_SENS_RES_SSD(x) ((u8)((be16_to_cpu(x) & 0x001F) >> 0))
+#define PN533_TYPE_A_SENS_RES_PLATCONF(x) ((u8)((be16_to_cpu(x) & 0x0F00) >> 8))
+
+#define PN533_TYPE_A_SENS_RES_SSD_JEWEL 0x00
+#define PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL 0x0C
+
+#define PN533_TYPE_A_SEL_PROT(x) (((x) & 0x60) >> 5)
+#define PN533_TYPE_A_SEL_CASCADE(x) (((x) & 0x04) >> 2)
+
+#define PN533_TYPE_A_SEL_PROT_MIFARE 0
+#define PN533_TYPE_A_SEL_PROT_ISO14443 1
+#define PN533_TYPE_A_SEL_PROT_DEP 2
+#define PN533_TYPE_A_SEL_PROT_ISO14443_DEP 3
+
+static bool pn533_target_type_a_is_valid(struct pn533_target_type_a *type_a,
+                                                       int target_data_len)
+{
+       u8 ssd;
+       u8 platconf;
+
+       if (target_data_len < sizeof(struct pn533_target_type_a))
+               return false;
+
+       /* The lenght check of nfcid[] and ats[] are not being performed because
+          the values are not being used */
+
+       /* Requirement 4.6.3.3 from NFC Forum Digital Spec */
+       ssd = PN533_TYPE_A_SENS_RES_SSD(type_a->sens_res);
+       platconf = PN533_TYPE_A_SENS_RES_PLATCONF(type_a->sens_res);
+
+       if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+                       platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+                       (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+                       platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+               return false;
+
+       /* Requirements 4.8.2.1, 4.8.2.3, 4.8.2.5 and 4.8.2.7 from NFC Forum */
+       if (PN533_TYPE_A_SEL_CASCADE(type_a->sel_res) != 0)
+               return false;
+
+       return true;
+}
+
+static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
+                                                       int tgt_data_len)
+{
+       struct pn533_target_type_a *tgt_type_a;
+
+       tgt_type_a = (struct pn533_target_type_a *) tgt_data;
+
+       if (!pn533_target_type_a_is_valid(tgt_type_a, tgt_data_len))
+               return -EPROTO;
+
+       switch (PN533_TYPE_A_SEL_PROT(tgt_type_a->sel_res)) {
+       case PN533_TYPE_A_SEL_PROT_MIFARE:
+               nfc_tgt->supported_protocols = NFC_PROTO_MIFARE_MASK;
+               break;
+       case PN533_TYPE_A_SEL_PROT_ISO14443:
+               nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK;
+               break;
+       case PN533_TYPE_A_SEL_PROT_DEP:
+               nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+               break;
+       case PN533_TYPE_A_SEL_PROT_ISO14443_DEP:
+               nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK |
+                                                       NFC_PROTO_NFC_DEP_MASK;
+               break;
+       }
+
+       nfc_tgt->sens_res = be16_to_cpu(tgt_type_a->sens_res);
+       nfc_tgt->sel_res = tgt_type_a->sel_res;
+
+       return 0;
+}
+
+struct pn533_target_felica {
+       u8 pol_res;
+       u8 opcode;
+       u8 nfcid2[8];
+       u8 pad[8];
+       /* optional */
+       u8 syst_code[];
+} __packed;
+
+#define PN533_FELICA_SENSF_NFCID2_DEP_B1 0x01
+#define PN533_FELICA_SENSF_NFCID2_DEP_B2 0xFE
+
+static bool pn533_target_felica_is_valid(struct pn533_target_felica *felica,
+                                                       int target_data_len)
+{
+       if (target_data_len < sizeof(struct pn533_target_felica))
+               return false;
+
+       if (felica->opcode != PN533_FELICA_OPC_SENSF_RES)
+               return false;
+
+       return true;
+}
+
+static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
+                                                       int tgt_data_len)
+{
+       struct pn533_target_felica *tgt_felica;
+
+       tgt_felica = (struct pn533_target_felica *) tgt_data;
+
+       if (!pn533_target_felica_is_valid(tgt_felica, tgt_data_len))
+               return -EPROTO;
+
+       if (tgt_felica->nfcid2[0] == PN533_FELICA_SENSF_NFCID2_DEP_B1 &&
+                                       tgt_felica->nfcid2[1] ==
+                                       PN533_FELICA_SENSF_NFCID2_DEP_B2)
+               nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+       else
+               nfc_tgt->supported_protocols = NFC_PROTO_FELICA_MASK;
+
+       return 0;
+}
+
+struct pn533_target_jewel {
+       __be16 sens_res;
+       u8 jewelid[4];
+} __packed;
+
+static bool pn533_target_jewel_is_valid(struct pn533_target_jewel *jewel,
+                                                       int target_data_len)
+{
+       u8 ssd;
+       u8 platconf;
+
+       if (target_data_len < sizeof(struct pn533_target_jewel))
+               return false;
+
+       /* Requirement 4.6.3.3 from NFC Forum Digital Spec */
+       ssd = PN533_TYPE_A_SENS_RES_SSD(jewel->sens_res);
+       platconf = PN533_TYPE_A_SENS_RES_PLATCONF(jewel->sens_res);
+
+       if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+                       platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+                       (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+                       platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+               return false;
+
+       return true;
+}
+
+static int pn533_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data,
+                                                       int tgt_data_len)
+{
+       struct pn533_target_jewel *tgt_jewel;
+
+       tgt_jewel = (struct pn533_target_jewel *) tgt_data;
+
+       if (!pn533_target_jewel_is_valid(tgt_jewel, tgt_data_len))
+               return -EPROTO;
+
+       nfc_tgt->supported_protocols = NFC_PROTO_JEWEL_MASK;
+       nfc_tgt->sens_res = be16_to_cpu(tgt_jewel->sens_res);
+
+       return 0;
+}
+
+struct pn533_type_b_prot_info {
+       u8 bitrate;
+       u8 fsci_type;
+       u8 fwi_adc_fo;
+} __packed;
+
+#define PN533_TYPE_B_PROT_FCSI(x) (((x) & 0xF0) >> 4)
+#define PN533_TYPE_B_PROT_TYPE(x) (((x) & 0x0F) >> 0)
+#define PN533_TYPE_B_PROT_TYPE_RFU_MASK 0x8
+
+struct pn533_type_b_sens_res {
+       u8 opcode;
+       u8 nfcid[4];
+       u8 appdata[4];
+       struct pn533_type_b_prot_info prot_info;
+} __packed;
+
+#define PN533_TYPE_B_OPC_SENSB_RES 0x50
+
+struct pn533_target_type_b {
+       struct pn533_type_b_sens_res sensb_res;
+       u8 attrib_res_len;
+       u8 attrib_res[];
+} __packed;
+
+static bool pn533_target_type_b_is_valid(struct pn533_target_type_b *type_b,
+                                                       int target_data_len)
+{
+       if (target_data_len < sizeof(struct pn533_target_type_b))
+               return false;
+
+       if (type_b->sensb_res.opcode != PN533_TYPE_B_OPC_SENSB_RES)
+               return false;
+
+       if (PN533_TYPE_B_PROT_TYPE(type_b->sensb_res.prot_info.fsci_type) &
+                                               PN533_TYPE_B_PROT_TYPE_RFU_MASK)
+               return false;
+
+       return true;
+}
+
+static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data,
+                                                       int tgt_data_len)
+{
+       struct pn533_target_type_b *tgt_type_b;
+
+       tgt_type_b = (struct pn533_target_type_b *) tgt_data;
+
+       if (!pn533_target_type_b_is_valid(tgt_type_b, tgt_data_len))
+               return -EPROTO;
+
+       nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK;
+
+       return 0;
+}
+
+struct pn533_poll_response {
+       u8 nbtg;
+       u8 tg;
+       u8 target_data[];
+} __packed;
+
+static int pn533_target_found(struct pn533 *dev,
+                       struct pn533_poll_response *resp, int resp_len)
+{
+       int target_data_len;
+       struct nfc_target nfc_tgt;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s - modulation=%d", __func__,
+                                                       dev->poll_mod_curr);
+
+       if (resp->tg != 1)
+               return -EPROTO;
+
+       target_data_len = resp_len - sizeof(struct pn533_poll_response);
+
+       switch (dev->poll_mod_curr) {
+       case PN533_POLL_MOD_106KBPS_A:
+               rc = pn533_target_found_type_a(&nfc_tgt, resp->target_data,
+                                                       target_data_len);
+               break;
+       case PN533_POLL_MOD_212KBPS_FELICA:
+       case PN533_POLL_MOD_424KBPS_FELICA:
+               rc = pn533_target_found_felica(&nfc_tgt, resp->target_data,
+                                                       target_data_len);
+               break;
+       case PN533_POLL_MOD_106KBPS_JEWEL:
+               rc = pn533_target_found_jewel(&nfc_tgt, resp->target_data,
+                                                       target_data_len);
+               break;
+       case PN533_POLL_MOD_847KBPS_B:
+               rc = pn533_target_found_type_b(&nfc_tgt, resp->target_data,
+                                                       target_data_len);
+               break;
+       default:
+               nfc_dev_err(&dev->interface->dev, "Unknown current poll"
+                                                               " modulation");
+               return -EPROTO;
+       }
+
+       if (rc)
+               return rc;
+
+       if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) {
+               nfc_dev_dbg(&dev->interface->dev, "The target found does not"
+                                               " have the desired protocol");
+               return -EAGAIN;
+       }
+
+       nfc_dev_dbg(&dev->interface->dev, "Target found - supported protocols: "
+                                       "0x%x", nfc_tgt.supported_protocols);
+
+       dev->tgt_available_prots = nfc_tgt.supported_protocols;
+
+       nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1);
+
+       return 0;
+}
+
+static void pn533_poll_reset_mod_list(struct pn533 *dev)
+{
+       dev->poll_mod_count = 0;
+}
+
+static void pn533_poll_add_mod(struct pn533 *dev, u8 mod_index)
+{
+       dev->poll_mod_active[dev->poll_mod_count] =
+               (struct pn533_poll_modulations *) &poll_mod[mod_index];
+       dev->poll_mod_count++;
+}
+
+static void pn533_poll_create_mod_list(struct pn533 *dev, u32 protocols)
+{
+       pn533_poll_reset_mod_list(dev);
+
+       if (protocols & NFC_PROTO_MIFARE_MASK
+                                       || protocols & NFC_PROTO_ISO14443_MASK
+                                       || protocols & NFC_PROTO_NFC_DEP_MASK)
+               pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A);
+
+       if (protocols & NFC_PROTO_FELICA_MASK
+                                       || protocols & NFC_PROTO_NFC_DEP_MASK) {
+               pn533_poll_add_mod(dev, PN533_POLL_MOD_212KBPS_FELICA);
+               pn533_poll_add_mod(dev, PN533_POLL_MOD_424KBPS_FELICA);
+       }
+
+       if (protocols & NFC_PROTO_JEWEL_MASK)
+               pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_JEWEL);
+
+       if (protocols & NFC_PROTO_ISO14443_MASK)
+               pn533_poll_add_mod(dev, PN533_POLL_MOD_847KBPS_B);
+}
+
+static void pn533_start_poll_frame(struct pn533_frame *frame,
+                                       struct pn533_poll_modulations *mod)
+{
+
+       pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET);
+
+       memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len);
+       frame->datalen += mod->len;
+
+       pn533_tx_frame_finish(frame);
+}
+
+static int pn533_start_poll_complete(struct pn533 *dev, void *arg,
+                                               u8 *params, int params_len)
+{
+       struct pn533_poll_response *resp;
+       struct pn533_poll_modulations *next_mod;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (params_len == -ENOENT) {
+               nfc_dev_dbg(&dev->interface->dev, "Polling operation has been"
+                                                               " stopped");
+               goto stop_poll;
+       }
+
+       if (params_len < 0) {
+               nfc_dev_err(&dev->interface->dev, "Error %d when running poll",
+                                                               params_len);
+               goto stop_poll;
+       }
+
+       resp = (struct pn533_poll_response *) params;
+       if (resp->nbtg) {
+               rc = pn533_target_found(dev, resp, params_len);
+
+               /* We must stop the poll after a valid target found */
+               if (rc == 0)
+                       goto stop_poll;
+
+               if (rc != -EAGAIN)
+                       nfc_dev_err(&dev->interface->dev, "The target found is"
+                                       " not valid - continuing to poll");
+       }
+
+       dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count;
+
+       next_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+       nfc_dev_dbg(&dev->interface->dev, "Polling next modulation (0x%x)",
+                                                       dev->poll_mod_curr);
+
+       pn533_start_poll_frame(dev->out_frame, next_mod);
+
+       /* Don't need to down the semaphore again */
+       rc = __pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
+                               dev->in_maxlen, pn533_start_poll_complete,
+                               NULL, GFP_ATOMIC);
+
+       if (rc == -EPERM) {
+               nfc_dev_dbg(&dev->interface->dev, "Cannot poll next modulation"
+                                       " because poll has been stopped");
+               goto stop_poll;
+       }
+
+       if (rc) {
+               nfc_dev_err(&dev->interface->dev, "Error %d when trying to poll"
+                                                       " next modulation", rc);
+               goto stop_poll;
+       }
+
+       /* Inform caller function to do not up the semaphore */
+       return -EINPROGRESS;
+
+stop_poll:
+       pn533_poll_reset_mod_list(dev);
+       dev->poll_protocols = 0;
+       return 0;
+}
+
+static int pn533_start_poll(struct nfc_dev *nfc_dev, u32 protocols)
+{
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       struct pn533_poll_modulations *start_mod;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s - protocols=0x%x", __func__,
+                                                               protocols);
+
+       if (dev->poll_mod_count) {
+               nfc_dev_err(&dev->interface->dev, "Polling operation already"
+                                                               " active");
+               return -EBUSY;
+       }
+
+       if (dev->tgt_active_prot) {
+               nfc_dev_err(&dev->interface->dev, "Cannot poll with a target"
+                                                       " already activated");
+               return -EBUSY;
+       }
+
+       pn533_poll_create_mod_list(dev, protocols);
+
+       if (!dev->poll_mod_count) {
+               nfc_dev_err(&dev->interface->dev, "No valid protocols"
+                                                               " specified");
+               rc = -EINVAL;
+               goto error;
+       }
+
+       nfc_dev_dbg(&dev->interface->dev, "It will poll %d modulations types",
+                                                       dev->poll_mod_count);
+
+       dev->poll_mod_curr = 0;
+       start_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+       pn533_start_poll_frame(dev->out_frame, start_mod);
+
+       rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
+                               dev->in_maxlen, pn533_start_poll_complete,
+                               NULL, GFP_KERNEL);
+
+       if (rc) {
+               nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
+                                                       " start poll", rc);
+               goto error;
+       }
+
+       dev->poll_protocols = protocols;
+
+       return 0;
+
+error:
+       pn533_poll_reset_mod_list(dev);
+       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__);
+
+       if (!dev->poll_mod_count) {
+               nfc_dev_dbg(&dev->interface->dev, "Polling operation was not"
+                                                               " running");
+               return;
+       }
+
+       /* An ack will cancel the last issued command (poll) */
+       pn533_send_ack(dev, GFP_KERNEL);
+
+       /* prevent pn533_start_poll_complete to issue a new poll meanwhile */
+       usb_kill_urb(dev->in_urb);
+}
+
+static int pn533_activate_target_nfcdep(struct pn533 *dev)
+{
+       struct pn533_cmd_activate_param param;
+       struct pn533_cmd_activate_response *resp;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_ATR);
+
+       param.tg = 1;
+       param.next = 0;
+       memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), &param,
+                               sizeof(struct pn533_cmd_activate_param));
+       dev->out_frame->datalen += sizeof(struct pn533_cmd_activate_param);
+
+       pn533_tx_frame_finish(dev->out_frame);
+
+       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
+                                                               dev->in_maxlen);
+       if (rc)
+               return rc;
+
+       resp = (struct pn533_cmd_activate_response *)
+                               PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame);
+       rc = resp->status & PN533_CMD_RET_MASK;
+       if (rc != PN533_CMD_RET_SUCCESS)
+               return -EIO;
+
+       return 0;
+}
+
+static int pn533_activate_target(struct nfc_dev *nfc_dev, u32 target_idx,
+                                                               u32 protocol)
+{
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s - protocol=%u", __func__,
+                                                               protocol);
+
+       if (dev->poll_mod_count) {
+               nfc_dev_err(&dev->interface->dev, "Cannot activate while"
+                                                               " polling");
+               return -EBUSY;
+       }
+
+       if (dev->tgt_active_prot) {
+               nfc_dev_err(&dev->interface->dev, "There is already an active"
+                                                               " target");
+               return -EBUSY;
+       }
+
+       if (!dev->tgt_available_prots) {
+               nfc_dev_err(&dev->interface->dev, "There is no available target"
+                                                               " to activate");
+               return -EINVAL;
+       }
+
+       if (!(dev->tgt_available_prots & (1 << protocol))) {
+               nfc_dev_err(&dev->interface->dev, "The target does not support"
+                                       " the requested protocol %u", protocol);
+               return -EINVAL;
+       }
+
+       if (protocol == NFC_PROTO_NFC_DEP) {
+               rc = pn533_activate_target_nfcdep(dev);
+               if (rc) {
+                       nfc_dev_err(&dev->interface->dev, "Error %d when"
+                                               " activating target with"
+                                               " NFC_DEP protocol", rc);
+                       return rc;
+               }
+       }
+
+       dev->tgt_active_prot = protocol;
+       dev->tgt_available_prots = 0;
+
+       return 0;
+}
+
+static void pn533_deactivate_target(struct nfc_dev *nfc_dev, u32 target_idx)
+{
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       u8 tg;
+       u8 status;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (!dev->tgt_active_prot) {
+               nfc_dev_err(&dev->interface->dev, "There is no active target");
+               return;
+       }
+
+       dev->tgt_active_prot = 0;
+
+       pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_RELEASE);
+
+       tg = 1;
+       memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), &tg, sizeof(u8));
+       dev->out_frame->datalen += sizeof(u8);
+
+       pn533_tx_frame_finish(dev->out_frame);
+
+       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
+                                                               dev->in_maxlen);
+       if (rc) {
+               nfc_dev_err(&dev->interface->dev, "Error when sending release"
+                                               " command to the controller");
+               return;
+       }
+
+       status = PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame)[0];
+       rc = status & PN533_CMD_RET_MASK;
+       if (rc != PN533_CMD_RET_SUCCESS)
+               nfc_dev_err(&dev->interface->dev, "Error 0x%x when releasing"
+                                                       " the target", rc);
+
+       return;
+}
+
+#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3)
+#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+
+static int pn533_data_exchange_tx_frame(struct pn533 *dev, struct sk_buff *skb)
+{
+       int payload_len = skb->len;
+       struct pn533_frame *out_frame;
+       struct sk_buff *discarded;
+       u8 tg;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s - Sending %d bytes", __func__,
+                                                               payload_len);
+
+       if (payload_len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+               /* TODO: Implement support to multi-part data exchange */
+               nfc_dev_err(&dev->interface->dev, "Data length greater than the"
+                                               " max allowed: %d",
+                                               PN533_CMD_DATAEXCH_DATA_MAXLEN);
+               return -ENOSYS;
+       }
+
+       /* Reserving header space */
+       if (skb_cow_head(skb, PN533_CMD_DATAEXCH_HEAD_LEN)) {
+               nfc_dev_err(&dev->interface->dev, "Error to add header data");
+               return -ENOMEM;
+       }
+
+       /* Reserving tail space, see pn533_tx_frame_finish */
+       if (skb_cow_data(skb, PN533_FRAME_TAIL_SIZE, &discarded) < 0) {
+               nfc_dev_err(&dev->interface->dev, "Error to add tail data");
+               return -ENOMEM;
+       }
+
+       skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN);
+       out_frame = (struct pn533_frame *) skb->data;
+
+       pn533_tx_frame_init(out_frame, PN533_CMD_IN_DATA_EXCHANGE);
+
+       tg = 1;
+       memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame), &tg, sizeof(u8));
+       out_frame->datalen += sizeof(u8);
+
+       /* The data is already in the out_frame, just update the datalen */
+       out_frame->datalen += payload_len;
+
+       pn533_tx_frame_finish(out_frame);
+       skb_put(skb, PN533_FRAME_TAIL_SIZE);
+
+       return 0;
+}
+
+struct pn533_data_exchange_arg {
+       struct sk_buff *skb_resp;
+       struct sk_buff *skb_out;
+       data_exchange_cb_t cb;
+       void *cb_context;
+};
+
+static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
+                                               u8 *params, int params_len)
+{
+       struct pn533_data_exchange_arg *arg = _arg;
+       struct sk_buff *skb_resp = arg->skb_resp;
+       struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
+       int err = 0;
+       u8 status;
+       u8 cmd_ret;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       dev_kfree_skb_irq(arg->skb_out);
+
+       if (params_len < 0) { /* error */
+               err = params_len;
+               goto error;
+       }
+
+       skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
+
+       status = params[0];
+
+       cmd_ret = status & PN533_CMD_RET_MASK;
+       if (cmd_ret != PN533_CMD_RET_SUCCESS) {
+               nfc_dev_err(&dev->interface->dev, "PN533 reported error %d when"
+                                               " exchanging data", cmd_ret);
+               err = -EIO;
+               goto error;
+       }
+
+       if (status & PN533_CMD_MI_MASK) {
+               /* TODO: Implement support to multi-part data exchange */
+               nfc_dev_err(&dev->interface->dev, "Multi-part message not yet"
+                                                               " supported");
+               /* Prevent the other messages from controller */
+               pn533_send_ack(dev, GFP_ATOMIC);
+               err = -ENOSYS;
+               goto error;
+       }
+
+       skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
+       skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
+
+       arg->cb(arg->cb_context, skb_resp, 0);
+       kfree(arg);
+       return 0;
+
+error:
+       dev_kfree_skb_irq(skb_resp);
+       arg->cb(arg->cb_context, NULL, err);
+       kfree(arg);
+       return 0;
+}
+
+int pn533_data_exchange(struct nfc_dev *nfc_dev, u32 target_idx,
+                                               struct sk_buff *skb,
+                                               data_exchange_cb_t cb,
+                                               void *cb_context)
+{
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       struct pn533_frame *out_frame, *in_frame;
+       struct pn533_data_exchange_arg *arg;
+       struct sk_buff *skb_resp;
+       int skb_resp_len;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (!dev->tgt_active_prot) {
+               nfc_dev_err(&dev->interface->dev, "Cannot exchange data if"
+                                               " there is no active target");
+               rc = -EINVAL;
+               goto error;
+       }
+
+       rc = pn533_data_exchange_tx_frame(dev, skb);
+       if (rc)
+               goto error;
+
+       skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
+                       PN533_CMD_DATAEXCH_DATA_MAXLEN +
+                       PN533_FRAME_TAIL_SIZE;
+
+       skb_resp = nfc_alloc_skb(skb_resp_len, GFP_KERNEL);
+       if (!skb_resp) {
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       in_frame = (struct pn533_frame *) skb_resp->data;
+       out_frame = (struct pn533_frame *) skb->data;
+
+       arg = kmalloc(sizeof(struct pn533_data_exchange_arg), GFP_KERNEL);
+       if (!arg) {
+               rc = -ENOMEM;
+               goto free_skb_resp;
+       }
+
+       arg->skb_resp = skb_resp;
+       arg->skb_out = skb;
+       arg->cb = cb;
+       arg->cb_context = cb_context;
+
+       rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, skb_resp_len,
+                                       pn533_data_exchange_complete, arg,
+                                       GFP_KERNEL);
+       if (rc) {
+               nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
+                                               " perform data_exchange", rc);
+               goto free_arg;
+       }
+
+       return 0;
+
+free_arg:
+       kfree(arg);
+free_skb_resp:
+       kfree_skb(skb_resp);
+error:
+       kfree_skb(skb);
+       return rc;
+}
+
+static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
+                                                               u8 cfgdata_len)
+{
+       int rc;
+       u8 *params;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       pn533_tx_frame_init(dev->out_frame, PN533_CMD_RF_CONFIGURATION);
+
+       params = PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame);
+       params[0] = cfgitem;
+       memcpy(&params[1], cfgdata, cfgdata_len);
+       dev->out_frame->datalen += (1 + cfgdata_len);
+
+       pn533_tx_frame_finish(dev->out_frame);
+
+       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
+                                                               dev->in_maxlen);
+
+       return rc;
+}
+
+struct nfc_ops pn533_nfc_ops = {
+       .start_poll = pn533_start_poll,
+       .stop_poll = pn533_stop_poll,
+       .activate_target = pn533_activate_target,
+       .deactivate_target = pn533_deactivate_target,
+       .data_exchange = pn533_data_exchange,
+};
+
+static int pn533_probe(struct usb_interface *interface,
+                       const struct usb_device_id *id)
+{
+       struct pn533_fw_version *fw_ver;
+       struct pn533 *dev;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       struct pn533_config_max_retries max_retries;
+       int in_endpoint = 0;
+       int out_endpoint = 0;
+       int rc = -ENOMEM;
+       int i;
+       u32 protocols;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dev->udev = usb_get_dev(interface_to_usbdev(interface));
+       dev->interface = interface;
+       sema_init(&dev->cmd_lock, 1);
+
+       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)) {
+                       dev->in_maxlen = le16_to_cpu(endpoint->wMaxPacketSize);
+                       in_endpoint = endpoint->bEndpointAddress;
+               }
+
+               if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) {
+                       dev->out_maxlen =
+                               le16_to_cpu(endpoint->wMaxPacketSize);
+                       out_endpoint = endpoint->bEndpointAddress;
+               }
+       }
+
+       if (!in_endpoint || !out_endpoint) {
+               nfc_dev_err(&interface->dev, "Could not find bulk-in or"
+                                                       " bulk-out endpoint");
+               rc = -ENODEV;
+               goto error;
+       }
+
+       dev->in_frame = kmalloc(dev->in_maxlen, GFP_KERNEL);
+       dev->in_urb = usb_alloc_urb(0, GFP_KERNEL);
+       dev->out_frame = kmalloc(dev->out_maxlen, GFP_KERNEL);
+       dev->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+       if (!dev->in_frame || !dev->out_frame ||
+               !dev->in_urb || !dev->out_urb)
+               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,
+                       pn533_send_complete, dev);
+
+       tasklet_init(&dev->tasklet, pn533_tasklet_cmd_complete, (ulong)dev);
+
+       usb_set_intfdata(interface, dev);
+
+       pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION);
+       pn533_tx_frame_finish(dev->out_frame);
+
+       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
+                                                               dev->in_maxlen);
+       if (rc)
+               goto kill_tasklet;
+
+       fw_ver = (struct pn533_fw_version *)
+                               PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame);
+       nfc_dev_info(&dev->interface->dev, "NXP PN533 firmware ver %d.%d now"
+                                       " attached", fw_ver->ver, fw_ver->rev);
+
+       protocols = NFC_PROTO_JEWEL_MASK
+                       | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
+                       | NFC_PROTO_ISO14443_MASK
+                       | NFC_PROTO_NFC_DEP_MASK;
+
+       dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols);
+       if (!dev->nfc_dev)
+               goto kill_tasklet;
+
+       nfc_set_parent_dev(dev->nfc_dev, &interface->dev);
+       nfc_set_drvdata(dev->nfc_dev, dev);
+
+       rc = nfc_register_device(dev->nfc_dev);
+       if (rc)
+               goto free_nfc_dev;
+
+       max_retries.mx_rty_atr = PN533_CONFIG_MAX_RETRIES_ENDLESS;
+       max_retries.mx_rty_psl = 2;
+       max_retries.mx_rty_passive_act = PN533_CONFIG_MAX_RETRIES_NO_RETRY;
+
+       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");
+               goto free_nfc_dev;
+       }
+
+       return 0;
+
+free_nfc_dev:
+       nfc_free_device(dev->nfc_dev);
+kill_tasklet:
+       tasklet_kill(&dev->tasklet);
+error:
+       kfree(dev->in_frame);
+       usb_free_urb(dev->in_urb);
+       kfree(dev->out_frame);
+       usb_free_urb(dev->out_urb);
+       kfree(dev);
+       return rc;
+}
+
+static void pn533_disconnect(struct usb_interface *interface)
+{
+       struct pn533 *dev;
+
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       nfc_unregister_device(dev->nfc_dev);
+       nfc_free_device(dev->nfc_dev);
+
+       usb_kill_urb(dev->in_urb);
+       usb_kill_urb(dev->out_urb);
+
+       tasklet_kill(&dev->tasklet);
+
+       kfree(dev->in_frame);
+       usb_free_urb(dev->in_urb);
+       kfree(dev->out_frame);
+       usb_free_urb(dev->out_urb);
+       kfree(dev);
+
+       nfc_dev_info(&dev->interface->dev, "NXP PN533 NFC device disconnected");
+}
+
+static struct usb_driver pn533_driver = {
+       .name =         "pn533",
+       .probe =        pn533_probe,
+       .disconnect =   pn533_disconnect,
+       .id_table =     pn533_table,
+};
+
+static int __init pn533_init(void)
+{
+       int rc;
+
+       rc = usb_register(&pn533_driver);
+       if (rc)
+               err("usb_register failed. Error number %d", rc);
+
+       return rc;
+}
+
+static void __exit pn533_exit(void)
+{
+       usb_deregister(&pn533_driver);
+}
+
+module_init(pn533_init);
+module_exit(pn533_exit);
+
+MODULE_AUTHOR("Lauro Ramos Venancio <lauro.venancio@openbossa.org>,"
+                       " Aloisio Almeida Jr <aloisio.almeida@openbossa.org>");
+MODULE_DESCRIPTION("PN533 usb driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
index 7ad48585c5e6c0cc0ad9c0593ae3ad24394b0314..a00b35f0308478d5c8cb36b4f6022d70bba69d95 100644 (file)
@@ -734,12 +734,9 @@ out_free:
 static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
                                  struct ssb_boardinfo *bi)
 {
-       pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID,
-                            &bi->vendor);
-       pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID,
-                            &bi->type);
-       pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
-                            &bi->rev);
+       bi->vendor = bus->host_pci->subsystem_vendor;
+       bi->type = bus->host_pci->subsystem_device;
+       bi->rev = bus->host_pci->revision;
 }
 
 int ssb_pci_get_invariants(struct ssb_bus *bus,
diff --git a/include/linux/nfc.h b/include/linux/nfc.h
new file mode 100644 (file)
index 0000000..330a4c5
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_NFC_H
+#define __LINUX_NFC_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+#define NFC_GENL_NAME "nfc"
+#define NFC_GENL_VERSION 1
+
+#define NFC_GENL_MCAST_EVENT_NAME "events"
+
+/**
+ * enum nfc_commands - supported nfc commands
+ *
+ * @NFC_CMD_UNSPEC: unspecified command
+ *
+ * @NFC_CMD_GET_DEVICE: request information about a device (requires
+ *     %NFC_ATTR_DEVICE_INDEX) or dump request to get a list of all nfc devices
+ * @NFC_CMD_START_POLL: start polling for targets using the given protocols
+ *     (requires %NFC_ATTR_DEVICE_INDEX and %NFC_ATTR_PROTOCOLS)
+ * @NFC_CMD_STOP_POLL: stop polling for targets (requires
+ *     %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_CMD_GET_TARGET: dump all targets found by the previous poll (requires
+ *     %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_TARGETS_FOUND: event emitted when a new target is found
+ *     (it sends %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_DEVICE_ADDED: event emitted when a new device is registred
+ *     (it sends %NFC_ATTR_DEVICE_NAME, %NFC_ATTR_DEVICE_INDEX and
+ *     %NFC_ATTR_PROTOCOLS)
+ * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed
+ *     (it sends %NFC_ATTR_DEVICE_INDEX)
+ */
+enum nfc_commands {
+       NFC_CMD_UNSPEC,
+       NFC_CMD_GET_DEVICE,
+       NFC_CMD_START_POLL,
+       NFC_CMD_STOP_POLL,
+       NFC_CMD_GET_TARGET,
+       NFC_EVENT_TARGETS_FOUND,
+       NFC_EVENT_DEVICE_ADDED,
+       NFC_EVENT_DEVICE_REMOVED,
+/* private: internal use only */
+       __NFC_CMD_AFTER_LAST
+};
+#define NFC_CMD_MAX (__NFC_CMD_AFTER_LAST - 1)
+
+/**
+ * enum nfc_attrs - supported nfc attributes
+ *
+ * @NFC_ATTR_UNSPEC: unspecified attribute
+ *
+ * @NFC_ATTR_DEVICE_INDEX: index of nfc device
+ * @NFC_ATTR_DEVICE_NAME: device name, max 8 chars
+ * @NFC_ATTR_PROTOCOLS: nfc protocols - bitwise or-ed combination from
+ *     NFC_PROTO_*_MASK constants
+ * @NFC_ATTR_TARGET_INDEX: index of the nfc target
+ * @NFC_ATTR_TARGET_SENS_RES: NFC-A targets extra information such as NFCID
+ * @NFC_ATTR_TARGET_SEL_RES: NFC-A targets extra information (useful if the
+ *     target is not NFC-Forum compliant)
+ */
+enum nfc_attrs {
+       NFC_ATTR_UNSPEC,
+       NFC_ATTR_DEVICE_INDEX,
+       NFC_ATTR_DEVICE_NAME,
+       NFC_ATTR_PROTOCOLS,
+       NFC_ATTR_TARGET_INDEX,
+       NFC_ATTR_TARGET_SENS_RES,
+       NFC_ATTR_TARGET_SEL_RES,
+/* private: internal use only */
+       __NFC_ATTR_AFTER_LAST
+};
+#define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1)
+
+#define NFC_DEVICE_NAME_MAXSIZE 8
+
+/* NFC protocols */
+#define NFC_PROTO_JEWEL                1
+#define NFC_PROTO_MIFARE       2
+#define NFC_PROTO_FELICA       3
+#define NFC_PROTO_ISO14443     4
+#define NFC_PROTO_NFC_DEP      5
+
+#define NFC_PROTO_MAX          6
+
+/* NFC protocols masks used in bitsets */
+#define NFC_PROTO_JEWEL_MASK   (1 << NFC_PROTO_JEWEL)
+#define NFC_PROTO_MIFARE_MASK  (1 << NFC_PROTO_MIFARE)
+#define NFC_PROTO_FELICA_MASK  (1 << NFC_PROTO_FELICA)
+#define NFC_PROTO_ISO14443_MASK        (1 << NFC_PROTO_ISO14443)
+#define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP)
+
+struct sockaddr_nfc {
+       sa_family_t sa_family;
+       __u32 dev_idx;
+       __u32 target_idx;
+       __u32 nfc_protocol;
+};
+
+/* NFC socket protocols */
+#define NFC_SOCKPROTO_RAW      0
+#define NFC_SOCKPROTO_MAX      1
+
+#endif /*__LINUX_NFC_H */
index c7ccaae15af60074d4e2da9d8f645b91608e3265..3ec2f949bf7ac72f527e00e4e8e5cafd4c669da3 100644 (file)
  *     more background information, see
  *     http://wireless.kernel.org/en/users/Documentation/WoWLAN.
  *
+ * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
+ *     the necessary information for supporting GTK rekey offload. This
+ *     feature is typically used during WoWLAN. The configuration data
+ *     is contained in %NL80211_ATTR_REKEY_DATA (which is nested and
+ *     contains the data in sub-attributes). After rekeying happened,
+ *     this command may also be sent by the driver as an MLME event to
+ *     inform userspace of the new replay counter.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -605,6 +613,8 @@ enum nl80211_commands {
        NL80211_CMD_SCHED_SCAN_RESULTS,
        NL80211_CMD_SCHED_SCAN_STOPPED,
 
+       NL80211_CMD_SET_REKEY_OFFLOAD,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -996,6 +1006,9 @@ enum nl80211_commands {
  *     are managed in software: interfaces of these types aren't subject to
  *     any restrictions in their number or combinations.
  *
+ * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ *     necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1194,6 +1207,8 @@ enum nl80211_attrs {
        NL80211_ATTR_INTERFACE_COMBINATIONS,
        NL80211_ATTR_SOFTWARE_IFTYPES,
 
+       NL80211_ATTR_REKEY_DATA,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2361,4 +2376,28 @@ enum nl80211_plink_state {
        MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
 };
 
+#define NL80211_KCK_LEN                        16
+#define NL80211_KEK_LEN                        16
+#define NL80211_REPLAY_CTR_LEN         8
+
+/**
+ * enum nl80211_rekey_data - attributes for GTK rekey offload
+ * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes
+ * @NL80211_REKEY_DATA_KEK: key encryption key (binary)
+ * @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
+ * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
+ * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
+ */
+enum nl80211_rekey_data {
+       __NL80211_REKEY_DATA_INVALID,
+       NL80211_REKEY_DATA_KEK,
+       NL80211_REKEY_DATA_KCK,
+       NL80211_REKEY_DATA_REPLAY_CTR,
+
+       /* keep last */
+       NUM_NL80211_REKEY_DATA,
+       MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
+};
+
 #endif /* __LINUX_NL80211_H */
index 4ef98e422fde2c13169b851670dbc9d511e25088..e17f82266639e8b2a54763ebc089e482fb50a2ca 100644 (file)
@@ -192,7 +192,8 @@ struct ucred {
 #define AF_IEEE802154  36      /* IEEE802154 sockets           */
 #define AF_CAIF                37      /* CAIF sockets                 */
 #define AF_ALG         38      /* Algorithm sockets            */
-#define AF_MAX         39      /* For now.. */
+#define AF_NFC         39      /* NFC sockets                  */
+#define AF_MAX         40      /* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC      AF_UNSPEC
@@ -234,6 +235,7 @@ struct ucred {
 #define PF_IEEE802154  AF_IEEE802154
 #define PF_CAIF                AF_CAIF
 #define PF_ALG         AF_ALG
+#define PF_NFC         AF_NFC
 #define PF_MAX         AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
index 252e448217874ec0e8b47ce4ec2af2d844c45354..b0928c10111b547e5e323bf1b2f2a5952a430c94 100644 (file)
@@ -99,7 +99,7 @@ struct ssb_sprom {
 struct ssb_boardinfo {
        u16 vendor;
        u16 type;
-       u16 rev;
+       u rev;
 };
 
 
index 7202bce7bfebcb13d5080f66aca858c4c2d3a556..4bf101bada4eaf08ece4826f82470502c825370c 100644 (file)
@@ -1153,6 +1153,18 @@ struct cfg80211_wowlan {
        int n_patterns;
 };
 
+/**
+ * struct cfg80211_gtk_rekey_data - rekey data
+ * @kek: key encryption key
+ * @kck: key confirmation key
+ * @replay_ctr: replay counter
+ */
+struct cfg80211_gtk_rekey_data {
+       u8 kek[NL80211_KEK_LEN];
+       u8 kck[NL80211_KCK_LEN];
+       u8 replay_ctr[NL80211_REPLAY_CTR_LEN];
+};
+
 /**
  * struct cfg80211_ops - backend description for wireless configuration
  *
@@ -1197,6 +1209,8 @@ struct cfg80211_wowlan {
  *
  * @set_default_mgmt_key: set the default management frame key on an interface
  *
+ * @set_rekey_data: give the data necessary for GTK rekeying to the driver
+ *
  * @add_beacon: Add a beacon with given parameters, @head, @interval
  *     and @dtim_period will be valid, @tail is optional.
  * @set_beacon: Change the beacon parameters for an access point mode
@@ -1499,6 +1513,9 @@ struct cfg80211_ops {
                                struct net_device *dev,
                                struct cfg80211_sched_scan_request *request);
        int     (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev);
+
+       int     (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev,
+                                 struct cfg80211_gtk_rekey_data *data);
 };
 
 /*
@@ -3033,6 +3050,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
 void cfg80211_cqm_pktloss_notify(struct net_device *dev,
                                 const u8 *peer, u32 num_packets, gfp_t gfp);
 
+/**
+ * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
+ * @dev: network device
+ * @bssid: BSSID of AP (to avoid races)
+ * @replay_ctr: new replay counter
+ */
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+                              const u8 *replay_ctr, gfp_t gfp);
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
index c9def42c12867c73804e7fff82807eb2269de729..2474019f47d3723a7b39455928f85c6533b3cae4 100644 (file)
@@ -1628,6 +1628,10 @@ enum ieee80211_ampdu_mlme_action {
  *     ask the device to suspend. This is only invoked when WoWLAN is
  *     configured, otherwise the device is deconfigured completely and
  *     reconfigured at resume time.
+ *     The driver may also impose special conditions under which it
+ *     wants to use the "normal" suspend (deconfigure), say if it only
+ *     supports WoWLAN when the device is associated. In this case, it
+ *     must return 1 from this function.
  *
  * @resume: If WoWLAN was configured, this indicates that mac80211 is
  *     now resuming its operation, after this the device must be fully
@@ -1696,6 +1700,12 @@ enum ieee80211_ampdu_mlme_action {
  *     which set IEEE80211_KEY_FLAG_TKIP_REQ_RX_P1_KEY.
  *     The callback must be atomic.
  *
+ * @set_rekey_data: If the device supports GTK rekeying, for example while the
+ *     host is suspended, it can assign this callback to retrieve the data
+ *     necessary to do GTK rekeying, this is the KEK, KCK and replay counter.
+ *     After rekeying was done it should (for example during resume) notify
+ *     userspace of the new replay counter using ieee80211_gtk_rekey_notify().
+ *
  * @hw_scan: Ask the hardware to service the scan request, no need to start
  *     the scan state machine in stack. The scan must honour the channel
  *     configuration done by the regulatory agent in the wiphy's
@@ -1908,6 +1918,9 @@ struct ieee80211_ops {
                                struct ieee80211_key_conf *conf,
                                struct ieee80211_sta *sta,
                                u32 iv32, u16 *phase1key);
+       void (*set_rekey_data)(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct cfg80211_gtk_rekey_data *data);
        int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       struct cfg80211_scan_request *req);
        void (*cancel_hw_scan)(struct ieee80211_hw *hw,
@@ -2581,6 +2594,17 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf,
                                struct sk_buff *skb,
                                enum ieee80211_tkip_key_type type, u8 *key);
+
+/**
+ * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying
+ * @vif: virtual interface the rekeying was done on
+ * @bssid: The BSSID of the AP, for checking association
+ * @replay_ctr: the new replay counter after GTK rekeying
+ * @gfp: allocation flags
+ */
+void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
+                               const u8 *replay_ctr, gfp_t gfp);
+
 /**
  * ieee80211_wake_queue - wake specific queue
  * @hw: pointer as obtained from ieee80211_alloc_hw().
@@ -2845,6 +2869,29 @@ struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
 void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
                               struct ieee80211_sta *pubsta, bool block);
 
+/**
+ * ieee80211_iter_keys - iterate keys programmed into the device
+ * @hw: pointer obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface to iterate, may be %NULL for all
+ * @iter: iterator function that will be called for each key
+ * @iter_data: custom data to pass to the iterator function
+ *
+ * This function can be used to iterate all the keys known to
+ * mac80211, even those that weren't previously programmed into
+ * the device. This is intended for use in WoWLAN if the device
+ * needs reprogramming of the keys during suspend. Note that due
+ * to locking reasons, it is also only safe to call this at few
+ * spots since it must hold the RTNL and be able to sleep.
+ */
+void ieee80211_iter_keys(struct ieee80211_hw *hw,
+                        struct ieee80211_vif *vif,
+                        void (*iter)(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_sta *sta,
+                                     struct ieee80211_key_conf *key,
+                                     void *data),
+                        void *iter_data);
+
 /**
  * ieee80211_ap_probereq_get - retrieve a Probe Request template
  * @hw: pointer obtained from ieee80211_alloc_hw().
diff --git a/include/net/nfc.h b/include/net/nfc.h
new file mode 100644 (file)
index 0000000..cc01303
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * 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.
+ */
+
+#ifndef __NET_NFC_H
+#define __NET_NFC_H
+
+#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)
+
+struct nfc_dev;
+
+/**
+ * data_exchange_cb_t - Definition of nfc_data_exchange callback
+ *
+ * @context: nfc_data_exchange cb_context parameter
+ * @skb: response data
+ * @err: If an error has occurred during data exchange, it is the
+ *     error number. Zero means no error.
+ *
+ * When a rx or tx package is lost or corrupted or the target gets out
+ * of the operating field, err is -EIO.
+ */
+typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb,
+                                                               int err);
+
+struct nfc_ops {
+       int (*start_poll)(struct nfc_dev *dev, u32 protocols);
+       void (*stop_poll)(struct nfc_dev *dev);
+       int (*activate_target)(struct nfc_dev *dev, u32 target_idx,
+                                                       u32 protocol);
+       void (*deactivate_target)(struct nfc_dev *dev, u32 target_idx);
+       int (*data_exchange)(struct nfc_dev *dev, u32 target_idx,
+                               struct sk_buff *skb, data_exchange_cb_t cb,
+                                                       void *cb_context);
+};
+
+struct nfc_target {
+       u32 idx;
+       u32 supported_protocols;
+       u16 sens_res;
+       u8 sel_res;
+};
+
+struct nfc_genl_data {
+       u32 poll_req_pid;
+       struct mutex genl_data_mutex;
+};
+
+struct nfc_dev {
+       unsigned idx;
+       unsigned target_idx;
+       struct nfc_target *targets;
+       int n_targets;
+       int targets_generation;
+       spinlock_t targets_lock;
+       struct device dev;
+       bool polling;
+       struct nfc_genl_data genl_data;
+       u32 supported_protocols;
+
+       struct nfc_ops *ops;
+};
+#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
+
+extern struct class nfc_class;
+
+struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
+                                       u32 supported_protocols);
+
+/**
+ * nfc_free_device - free nfc device
+ *
+ * @dev: The nfc device to free
+ */
+static inline void nfc_free_device(struct nfc_dev *dev)
+{
+       put_device(&dev->dev);
+}
+
+int nfc_register_device(struct nfc_dev *dev);
+
+void nfc_unregister_device(struct nfc_dev *dev);
+
+/**
+ * nfc_set_parent_dev - set the parent device
+ *
+ * @nfc_dev: The nfc device whose parent is being set
+ * @dev: The parent device
+ */
+static inline void nfc_set_parent_dev(struct nfc_dev *nfc_dev,
+                                       struct device *dev)
+{
+       nfc_dev->dev.parent = dev;
+}
+
+/**
+ * nfc_set_drvdata - set driver specifc data
+ *
+ * @dev: The nfc device
+ * @data: Pointer to driver specifc data
+ */
+static inline void nfc_set_drvdata(struct nfc_dev *dev, void *data)
+{
+       dev_set_drvdata(&dev->dev, data);
+}
+
+/**
+ * nfc_get_drvdata - get driver specifc data
+ *
+ * @dev: The nfc device
+ */
+static inline void *nfc_get_drvdata(struct nfc_dev *dev)
+{
+       return dev_get_drvdata(&dev->dev);
+}
+
+/**
+ * nfc_device_name - get the nfc device name
+ *
+ * @dev: The nfc device whose name to return
+ */
+static inline const char *nfc_device_name(struct nfc_dev *dev)
+{
+       return dev_name(&dev->dev);
+}
+
+struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp);
+
+int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
+                                                       int ntargets);
+
+#endif /* __NET_NFC_H */
index 878151c772c9ba9fbe8f89636790a52c22b5c889..a07314844238f4c19fbc4d82d99c547ed9f1f907 100644 (file)
@@ -322,6 +322,7 @@ source "net/rfkill/Kconfig"
 source "net/9p/Kconfig"
 source "net/caif/Kconfig"
 source "net/ceph/Kconfig"
+source "net/nfc/Kconfig"
 
 
 endif   # if NET
index a51d9465e6284e47753394b45330eba20abfcaac..acdde4950de428ea0312c789c0f673a31d4c4bd8 100644 (file)
@@ -68,3 +68,4 @@ obj-$(CONFIG_WIMAX)           += wimax/
 obj-$(CONFIG_DNS_RESOLVER)     += dns_resolver/
 obj-$(CONFIG_CEPH_LIB)         += ceph/
 obj-$(CONFIG_BATMAN_ADV)       += batman-adv/
+obj-$(CONFIG_NFC)              += nfc/
index 76c40314675038f35669ca3642fcf73fbce89d67..bc745d00ea4dd31177445dcc4282afd669a33cb6 100644 (file)
@@ -160,7 +160,7 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
   "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN"     , "sk_lock-AF_PHONET"   ,
   "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG"      ,
-  "sk_lock-AF_MAX"
+  "sk_lock-AF_NFC"   , "sk_lock-AF_MAX"
 };
 static const char *const af_family_slock_key_strings[AF_MAX+1] = {
   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
@@ -176,7 +176,7 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
   "slock-AF_RXRPC" , "slock-AF_ISDN"     , "slock-AF_PHONET"   ,
   "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG"      ,
-  "slock-AF_MAX"
+  "slock-AF_NFC"   , "slock-AF_MAX"
 };
 static const char *const af_family_clock_key_strings[AF_MAX+1] = {
   "clock-AF_UNSPEC", "clock-AF_UNIX"     , "clock-AF_INET"     ,
@@ -192,7 +192,7 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
   "clock-AF_TIPC"  , "clock-AF_BLUETOOTH", "clock-AF_IUCV"     ,
   "clock-AF_RXRPC" , "clock-AF_ISDN"     , "clock-AF_PHONET"   ,
   "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG"      ,
-  "clock-AF_MAX"
+  "clock-AF_NFC"   , "clock-AF_MAX"
 };
 
 /*
index 9fe22cc393c8b1e7e222b63bff22fb1fb9934c3b..295ab747663f45955ccfe0a79d2541a741984d72 100644 (file)
@@ -2101,6 +2101,21 @@ static void ieee80211_get_ringparam(struct wiphy *wiphy,
        drv_get_ringparam(local, tx, tx_max, rx, rx_max);
 }
 
+static int ieee80211_set_rekey_data(struct wiphy *wiphy,
+                                   struct net_device *dev,
+                                   struct cfg80211_gtk_rekey_data *data)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (!local->ops->set_rekey_data)
+               return -EOPNOTSUPP;
+
+       drv_set_rekey_data(local, sdata, data);
+
+       return 0;
+}
+
 struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -2163,4 +2178,5 @@ struct cfg80211_ops mac80211_config_ops = {
        .get_antenna = ieee80211_get_antenna,
        .set_ringparam = ieee80211_set_ringparam,
        .get_ringparam = ieee80211_get_ringparam,
+       .set_rekey_data = ieee80211_set_rekey_data,
 };
index 0e7e4268ddf657e0739373f8541772c1b32d2ffe..edd2dd79c9be36020870bcee014d09f9ee241e25 100644 (file)
@@ -647,4 +647,14 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
        return ret;
 }
 
+static inline void drv_set_rekey_data(struct ieee80211_local *local,
+                                     struct ieee80211_sub_if_data *sdata,
+                                     struct cfg80211_gtk_rekey_data *data)
+{
+       trace_drv_set_rekey_data(local, sdata, data);
+       if (local->ops->set_rekey_data)
+               local->ops->set_rekey_data(&local->hw, &sdata->vif, data);
+       trace_drv_return_void(local);
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index 3cb6795e926db0cb0e834ce4df5087d598a4a571..31a9dfa81f6529292a6a8cde4337d91ed26d6fa9 100644 (file)
@@ -1024,6 +1024,34 @@ TRACE_EVENT(drv_set_bitrate_mask,
        )
 );
 
+TRACE_EVENT(drv_set_rekey_data,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct cfg80211_gtk_rekey_data *data),
+
+       TP_ARGS(local, sdata, data),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __array(u8, kek, NL80211_KEK_LEN)
+               __array(u8, kck, NL80211_KCK_LEN)
+               __array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               memcpy(__entry->kek, data->kek, NL80211_KEK_LEN);
+               memcpy(__entry->kck, data->kck, NL80211_KCK_LEN);
+               memcpy(__entry->replay_ctr, data->replay_ctr,
+                      NL80211_REPLAY_CTR_LEN);
+       ),
+
+       TP_printk(LOCAL_PR_FMT VIF_PR_FMT,
+                 LOCAL_PR_ARG, VIF_PR_ARG)
+);
+
 /*
  * Tracing for API calls that drivers call.
  */
@@ -1293,6 +1321,27 @@ DEFINE_EVENT(local_only_evt, api_remain_on_channel_expired,
        TP_ARGS(local)
 );
 
+TRACE_EVENT(api_gtk_rekey_notify,
+       TP_PROTO(struct ieee80211_sub_if_data *sdata,
+                const u8 *bssid, const u8 *replay_ctr),
+
+       TP_ARGS(sdata, bssid, replay_ctr),
+
+       TP_STRUCT__entry(
+               VIF_ENTRY
+               __array(u8, bssid, ETH_ALEN)
+               __array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN)
+       ),
+
+       TP_fast_assign(
+               VIF_ASSIGN;
+               memcpy(__entry->bssid, bssid, ETH_ALEN);
+               memcpy(__entry->replay_ctr, replay_ctr, NL80211_REPLAY_CTR_LEN);
+       ),
+
+       TP_printk(VIF_PR_FMT, VIF_PR_ARG)
+);
+
 /*
  * Tracing for internal functions
  * (which may also be called in response to driver calls)
index 25c15cc63319a13f304ec809f3fea03d86320e65..4f2e424e8b1b85b75ce34d1d7319824cebf4ee32 100644 (file)
@@ -544,6 +544,9 @@ struct ieee80211_sub_if_data {
        /* keys */
        struct list_head key_list;
 
+       /* count for keys needing tailroom space allocation */
+       int crypto_tx_tailroom_needed_cnt;
+
        struct net_device *dev;
        struct ieee80211_local *local;
 
index f825e2f0a57e034a839f37bfafc19e12206367b9..1208a7878bfd1cc25a252bdbb77eaf4ebab02163 100644 (file)
@@ -61,6 +61,36 @@ static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key)
        return NULL;
 }
 
+static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata)
+{
+       /*
+        * When this count is zero, SKB resizing for allocating tailroom
+        * for IV or MMIC is skipped. But, this check has created two race
+        * cases in xmit path while transiting from zero count to one:
+        *
+        * 1. SKB resize was skipped because no key was added but just before
+        * the xmit key is added and SW encryption kicks off.
+        *
+        * 2. SKB resize was skipped because all the keys were hw planted but
+        * just before xmit one of the key is deleted and SW encryption kicks
+        * off.
+        *
+        * In both the above case SW encryption will find not enough space for
+        * tailroom and exits with WARN_ON. (See WARN_ONs at wpa.c)
+        *
+        * Solution has been explained at
+        * http://mid.gmane.org/1308590980.4322.19.camel@jlt3.sipsolutions.net
+        */
+
+       if (!sdata->crypto_tx_tailroom_needed_cnt++) {
+               /*
+                * Flush all XMIT packets currently using HW encryption or no
+                * encryption at all if the count transition is from 0 -> 1.
+                */
+               synchronize_net();
+       }
+}
+
 static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 {
        struct ieee80211_sub_if_data *sdata;
@@ -101,6 +131,11 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 
        if (!ret) {
                key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+               if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+                     (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+                       sdata->crypto_tx_tailroom_needed_cnt--;
+
                return 0;
        }
 
@@ -142,6 +177,10 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
        sta = get_sta_for_key(key);
        sdata = key->sdata;
 
+       if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+             (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+               increment_tailroom_need_count(sdata);
+
        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                sdata = container_of(sdata->bss,
                                     struct ieee80211_sub_if_data,
@@ -394,8 +433,10 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
                ieee80211_aes_key_free(key->u.ccmp.tfm);
        if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
                ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
-       if (key->local)
+       if (key->local) {
                ieee80211_debugfs_key_remove(key);
+               key->sdata->crypto_tx_tailroom_needed_cnt--;
+       }
 
        kfree(key);
 }
@@ -452,6 +493,8 @@ int ieee80211_key_link(struct ieee80211_key *key,
        else
                old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
 
+       increment_tailroom_need_count(sdata);
+
        __ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
        __ieee80211_key_destroy(old_key);
 
@@ -498,12 +541,49 @@ void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
 
        mutex_lock(&sdata->local->key_mtx);
 
-       list_for_each_entry(key, &sdata->key_list, list)
+       sdata->crypto_tx_tailroom_needed_cnt = 0;
+
+       list_for_each_entry(key, &sdata->key_list, list) {
+               increment_tailroom_need_count(sdata);
                ieee80211_key_enable_hw_accel(key);
+       }
 
        mutex_unlock(&sdata->local->key_mtx);
 }
 
+void ieee80211_iter_keys(struct ieee80211_hw *hw,
+                        struct ieee80211_vif *vif,
+                        void (*iter)(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_sta *sta,
+                                     struct ieee80211_key_conf *key,
+                                     void *data),
+                        void *iter_data)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_key *key;
+       struct ieee80211_sub_if_data *sdata;
+
+       ASSERT_RTNL();
+
+       mutex_lock(&local->key_mtx);
+       if (vif) {
+               sdata = vif_to_sdata(vif);
+               list_for_each_entry(key, &sdata->key_list, list)
+                       iter(hw, &sdata->vif,
+                            key->sta ? &key->sta->sta : NULL,
+                            &key->conf, iter_data);
+       } else {
+               list_for_each_entry(sdata, &local->interfaces, list)
+                       list_for_each_entry(key, &sdata->key_list, list)
+                               iter(hw, &sdata->vif,
+                                    key->sta ? &key->sta->sta : NULL,
+                                    &key->conf, iter_data);
+       }
+       mutex_unlock(&local->key_mtx);
+}
+EXPORT_SYMBOL(ieee80211_iter_keys);
+
 void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_key *key;
@@ -533,3 +613,15 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
 
        mutex_unlock(&sdata->local->key_mtx);
 }
+
+
+void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
+                               const u8 *replay_ctr, gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       trace_api_gtk_rekey_notify(sdata, bssid, replay_ctr);
+
+       cfg80211_gtk_rekey_notify(sdata->dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_notify);
index 0d2faacc3e870c0feee42ad2d854cb1110f32ecc..068ee65182547e0f825a404768a2c72b8c8b7a7d 100644 (file)
@@ -647,12 +647,12 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
                mpath = node->mpath;
                if (mpath->sdata == sdata &&
                    memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
-                       spin_lock_bh(&mpath->state_lock);
+                       spin_lock(&mpath->state_lock);
                        mpath->flags |= MESH_PATH_RESOLVING;
                        hlist_del_rcu(&node->list);
                        call_rcu(&node->rcu, mesh_path_node_reclaim);
                        atomic_dec(&tbl->entries);
-                       spin_unlock_bh(&mpath->state_lock);
+                       spin_unlock(&mpath->state_lock);
                        goto enddel;
                }
        }
index b87420088c33cb4504e6c77199c49a49c2363fd6..182cda66ebefd954ebfb861b6eb40474a32b4617 100644 (file)
@@ -749,7 +749,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                container_of(work, struct ieee80211_local,
                             dynamic_ps_enable_work);
        struct ieee80211_sub_if_data *sdata = local->ps_sdata;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_if_managed *ifmgd;
        unsigned long flags;
        int q;
 
@@ -757,6 +757,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
        if (!sdata)
                return;
 
+       ifmgd = &sdata->u.mgd;
+
        if (local->hw.conf.flags & IEEE80211_CONF_PS)
                return;
 
index 67839eb90cc178dca9de0be7ee21fe7a4d26e180..f87e993e713bf28ec05b4c6690711ddc6197dde1 100644 (file)
@@ -72,15 +72,19 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        local->wowlan = wowlan && local->open_count;
        if (local->wowlan) {
                int err = drv_suspend(local, wowlan);
-               if (err) {
+               if (err < 0) {
                        local->quiescing = false;
                        return err;
+               } else if (err > 0) {
+                       WARN_ON(err != 1);
+                       local->wowlan = false;
+               } else {
+                       list_for_each_entry(sdata, &local->interfaces, list) {
+                               cancel_work_sync(&sdata->work);
+                               ieee80211_quiesce(sdata);
+                       }
+                       goto suspend;
                }
-               list_for_each_entry(sdata, &local->interfaces, list) {
-                       cancel_work_sync(&sdata->work);
-                       ieee80211_quiesce(sdata);
-               }
-               goto suspend;
        }
 
        /* disable keys */
index 3104c844b544c5465ef5ba1e7efcff05ca920501..e8d0d2d226650165b95eef4f8076d1476e65adf1 100644 (file)
@@ -1474,18 +1474,14 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
 
 /* device xmit handlers */
 
-static int ieee80211_skb_resize(struct ieee80211_local *local,
+static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb,
                                int head_need, bool may_encrypt)
 {
+       struct ieee80211_local *local = sdata->local;
        int tail_need = 0;
 
-       /*
-        * This could be optimised, devices that do full hardware
-        * crypto (including TKIP MMIC) need no tailroom... But we
-        * have no drivers for such devices currently.
-        */
-       if (may_encrypt) {
+       if (may_encrypt && sdata->crypto_tx_tailroom_needed_cnt) {
                tail_need = IEEE80211_ENCRYPT_TAILROOM;
                tail_need -= skb_tailroom(skb);
                tail_need = max_t(int, tail_need, 0);
@@ -1578,7 +1574,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
        headroom -= skb_headroom(skb);
        headroom = max_t(int, 0, headroom);
 
-       if (ieee80211_skb_resize(local, skb, headroom, may_encrypt)) {
+       if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) {
                dev_kfree_skb(skb);
                rcu_read_unlock();
                return;
@@ -1945,7 +1941,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                head_need += IEEE80211_ENCRYPT_HEADROOM;
                head_need += local->tx_headroom;
                head_need = max_t(int, 0, head_need);
-               if (ieee80211_skb_resize(local, skb, head_need, true))
+               if (ieee80211_skb_resize(sdata, skb, head_need, true))
                        goto fail;
        }
 
diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig
new file mode 100644 (file)
index 0000000..33e095b
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# NFC sybsystem configuration
+#
+
+menuconfig NFC
+       depends on NET && EXPERIMENTAL
+       tristate "NFC subsystem support (EXPERIMENTAL)"
+       default n
+       help
+         Say Y here if you want to build support for NFC (Near field
+         communication) devices.
+
+         To compile this support as a module, choose M here: the module will
+         be called nfc.
+
+source "drivers/nfc/Kconfig"
diff --git a/net/nfc/Makefile b/net/nfc/Makefile
new file mode 100644 (file)
index 0000000..16250c3
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux NFC subsystem.
+#
+
+obj-$(CONFIG_NFC) += nfc.o
+
+nfc-objs := core.o netlink.o af_nfc.o rawsock.o
diff --git a/net/nfc/af_nfc.c b/net/nfc/af_nfc.c
new file mode 100644 (file)
index 0000000..e982cef
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *
+ * 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.
+ */
+
+#include <linux/nfc.h>
+
+#include "nfc.h"
+
+static DEFINE_RWLOCK(proto_tab_lock);
+static const struct nfc_protocol *proto_tab[NFC_SOCKPROTO_MAX];
+
+static int nfc_sock_create(struct net *net, struct socket *sock, int proto,
+                                                               int kern)
+{
+       int rc = -EPROTONOSUPPORT;
+
+       if (net != &init_net)
+               return -EAFNOSUPPORT;
+
+       if (proto < 0 || proto >= NFC_SOCKPROTO_MAX)
+               return -EINVAL;
+
+       read_lock(&proto_tab_lock);
+       if (proto_tab[proto] && try_module_get(proto_tab[proto]->owner)) {
+               rc = proto_tab[proto]->create(net, sock, proto_tab[proto]);
+               module_put(proto_tab[proto]->owner);
+       }
+       read_unlock(&proto_tab_lock);
+
+       return rc;
+}
+
+static struct net_proto_family nfc_sock_family_ops = {
+       .owner  = THIS_MODULE,
+       .family = PF_NFC,
+       .create = nfc_sock_create,
+};
+
+int nfc_proto_register(const struct nfc_protocol *nfc_proto)
+{
+       int rc;
+
+       if (nfc_proto->id < 0 || nfc_proto->id >= NFC_SOCKPROTO_MAX)
+               return -EINVAL;
+
+       rc = proto_register(nfc_proto->proto, 0);
+       if (rc)
+               return rc;
+
+       write_lock(&proto_tab_lock);
+       if (proto_tab[nfc_proto->id])
+               rc = -EBUSY;
+       else
+               proto_tab[nfc_proto->id] = nfc_proto;
+       write_unlock(&proto_tab_lock);
+
+       return rc;
+}
+EXPORT_SYMBOL(nfc_proto_register);
+
+void nfc_proto_unregister(const struct nfc_protocol *nfc_proto)
+{
+       write_lock(&proto_tab_lock);
+       proto_tab[nfc_proto->id] = NULL;
+       write_unlock(&proto_tab_lock);
+
+       proto_unregister(nfc_proto->proto);
+}
+EXPORT_SYMBOL(nfc_proto_unregister);
+
+int __init af_nfc_init(void)
+{
+       return sock_register(&nfc_sock_family_ops);
+}
+
+void af_nfc_exit(void)
+{
+       sock_unregister(PF_NFC);
+}
diff --git a/net/nfc/core.c b/net/nfc/core.c
new file mode 100644 (file)
index 0000000..b6fd4e1
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "nfc.h"
+
+#define VERSION "0.1"
+
+int nfc_devlist_generation;
+DEFINE_MUTEX(nfc_devlist_mutex);
+
+int nfc_printk(const char *level, const char *format, ...)
+{
+       struct va_format vaf;
+       va_list args;
+       int r;
+
+       va_start(args, format);
+
+       vaf.fmt = format;
+       vaf.va = &args;
+
+       r = printk("%sNFC: %pV\n", level, &vaf);
+
+       va_end(args);
+
+       return r;
+}
+EXPORT_SYMBOL(nfc_printk);
+
+/**
+ * nfc_start_poll - start polling for nfc targets
+ *
+ * @dev: The nfc device that must start polling
+ * @protocols: bitset of nfc protocols that must be used for polling
+ *
+ * The device remains polling for targets until a target is found or
+ * the nfc_stop_poll function is called.
+ */
+int nfc_start_poll(struct nfc_dev *dev, u32 protocols)
+{
+       int rc;
+
+       nfc_dbg("dev_name=%s protocols=0x%x", dev_name(&dev->dev), protocols);
+
+       if (!protocols)
+               return -EINVAL;
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (dev->polling) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       rc = dev->ops->start_poll(dev, protocols);
+       if (!rc)
+               dev->polling = true;
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+/**
+ * nfc_stop_poll - stop polling for nfc targets
+ *
+ * @dev: The nfc device that must stop polling
+ */
+int nfc_stop_poll(struct nfc_dev *dev)
+{
+       int rc = 0;
+
+       nfc_dbg("dev_name=%s", dev_name(&dev->dev));
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->polling) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       dev->ops->stop_poll(dev);
+       dev->polling = false;
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+/**
+ * nfc_activate_target - prepare the target for data exchange
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target that must be activated
+ * @protocol: nfc protocol that will be used for data exchange
+ */
+int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
+{
+       int rc;
+
+       nfc_dbg("dev_name=%s target_idx=%u protocol=%u", dev_name(&dev->dev),
+                                                       target_idx, protocol);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       rc = dev->ops->activate_target(dev, target_idx, protocol);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+/**
+ * nfc_deactivate_target - deactivate a nfc target
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target that must be deactivated
+ */
+int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx)
+{
+       int rc = 0;
+
+       nfc_dbg("dev_name=%s target_idx=%u", dev_name(&dev->dev), target_idx);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       dev->ops->deactivate_target(dev, target_idx);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+/**
+ * nfc_data_exchange - transceive data
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target
+ * @skb: data to be sent
+ * @cb: callback called when the response is received
+ * @cb_context: parameter for the callback function
+ *
+ * The user must wait for the callback before calling this function again.
+ */
+int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx,
+                                       struct sk_buff *skb,
+                                       data_exchange_cb_t cb,
+                                       void *cb_context)
+{
+       int rc;
+
+       nfc_dbg("dev_name=%s target_idx=%u skb->len=%u", dev_name(&dev->dev),
+                                                       target_idx, skb->len);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               kfree_skb(skb);
+               goto error;
+       }
+
+       rc = dev->ops->data_exchange(dev, target_idx, skb, cb, cb_context);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+/**
+ * nfc_alloc_skb - allocate a skb for data exchange responses
+ *
+ * @size: size to allocate
+ * @gfp: gfp flags
+ */
+struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp)
+{
+       struct sk_buff *skb;
+       unsigned int total_size;
+
+       total_size = size + 1;
+       skb = alloc_skb(total_size, gfp);
+
+       if (skb)
+               skb_reserve(skb, 1);
+
+       return skb;
+}
+EXPORT_SYMBOL(nfc_alloc_skb);
+
+/**
+ * nfc_targets_found - inform that targets were found
+ *
+ * @dev: The nfc device that found the targets
+ * @targets: array of nfc targets found
+ * @ntargets: targets array size
+ *
+ * The device driver must call this function when one or many nfc targets
+ * are found. After calling this function, the device driver must stop
+ * polling for targets.
+ */
+int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
+                                                       int n_targets)
+{
+       int i;
+
+       nfc_dbg("dev_name=%s n_targets=%d", dev_name(&dev->dev), n_targets);
+
+       dev->polling = false;
+
+       for (i = 0; i < n_targets; i++)
+               targets[i].idx = dev->target_idx++;
+
+       spin_lock_bh(&dev->targets_lock);
+
+       dev->targets_generation++;
+
+       kfree(dev->targets);
+       dev->targets = kmemdup(targets, n_targets * sizeof(struct nfc_target),
+                                                               GFP_ATOMIC);
+
+       if (!dev->targets) {
+               dev->n_targets = 0;
+               spin_unlock_bh(&dev->targets_lock);
+               return -ENOMEM;
+       }
+
+       dev->n_targets = n_targets;
+       spin_unlock_bh(&dev->targets_lock);
+
+       nfc_genl_targets_found(dev);
+
+       return 0;
+}
+EXPORT_SYMBOL(nfc_targets_found);
+
+static void nfc_release(struct device *d)
+{
+       struct nfc_dev *dev = to_nfc_dev(d);
+
+       nfc_dbg("dev_name=%s", dev_name(&dev->dev));
+
+       nfc_genl_data_exit(&dev->genl_data);
+       kfree(dev->targets);
+       kfree(dev);
+}
+
+struct class nfc_class = {
+       .name = "nfc",
+       .dev_release = nfc_release,
+};
+EXPORT_SYMBOL(nfc_class);
+
+static int match_idx(struct device *d, void *data)
+{
+       struct nfc_dev *dev = to_nfc_dev(d);
+       unsigned *idx = data;
+
+       return dev->idx == *idx;
+}
+
+struct nfc_dev *nfc_get_device(unsigned idx)
+{
+       struct device *d;
+
+       d = class_find_device(&nfc_class, NULL, &idx, match_idx);
+       if (!d)
+               return NULL;
+
+       return to_nfc_dev(d);
+}
+
+/**
+ * nfc_allocate_device - allocate a new nfc device
+ *
+ * @ops: device operations
+ * @supported_protocols: NFC protocols supported by the device
+ */
+struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
+                                       u32 supported_protocols)
+{
+       static atomic_t dev_no = ATOMIC_INIT(0);
+       struct nfc_dev *dev;
+
+       if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
+               !ops->deactivate_target || !ops->data_exchange)
+               return NULL;
+
+       if (!supported_protocols)
+               return NULL;
+
+       dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
+       if (!dev)
+               return NULL;
+
+       dev->dev.class = &nfc_class;
+       dev->idx = atomic_inc_return(&dev_no) - 1;
+       dev_set_name(&dev->dev, "nfc%d", dev->idx);
+       device_initialize(&dev->dev);
+
+       dev->ops = ops;
+       dev->supported_protocols = supported_protocols;
+
+       spin_lock_init(&dev->targets_lock);
+       nfc_genl_data_init(&dev->genl_data);
+
+       /* first generation must not be 0 */
+       dev->targets_generation = 1;
+
+       return dev;
+}
+EXPORT_SYMBOL(nfc_allocate_device);
+
+/**
+ * nfc_register_device - register a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to register
+ */
+int nfc_register_device(struct nfc_dev *dev)
+{
+       int rc;
+
+       nfc_dbg("dev_name=%s", dev_name(&dev->dev));
+
+       mutex_lock(&nfc_devlist_mutex);
+       nfc_devlist_generation++;
+       rc = device_add(&dev->dev);
+       mutex_unlock(&nfc_devlist_mutex);
+
+       if (rc < 0)
+               return rc;
+
+       rc = nfc_genl_device_added(dev);
+       if (rc)
+               nfc_dbg("The userspace won't be notified that the device %s was"
+                                               " added", dev_name(&dev->dev));
+
+
+       return 0;
+}
+EXPORT_SYMBOL(nfc_register_device);
+
+/**
+ * nfc_unregister_device - unregister a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to unregister
+ */
+void nfc_unregister_device(struct nfc_dev *dev)
+{
+       int rc;
+
+       nfc_dbg("dev_name=%s", dev_name(&dev->dev));
+
+       mutex_lock(&nfc_devlist_mutex);
+       nfc_devlist_generation++;
+
+       /* lock to avoid unregistering a device while an operation
+          is in progress */
+       device_lock(&dev->dev);
+       device_del(&dev->dev);
+       device_unlock(&dev->dev);
+
+       mutex_unlock(&nfc_devlist_mutex);
+
+       rc = nfc_genl_device_removed(dev);
+       if (rc)
+               nfc_dbg("The userspace won't be notified that the device %s"
+                                       " was removed", dev_name(&dev->dev));
+
+}
+EXPORT_SYMBOL(nfc_unregister_device);
+
+static int __init nfc_init(void)
+{
+       int rc;
+
+       nfc_info("NFC Core ver %s", VERSION);
+
+       rc = class_register(&nfc_class);
+       if (rc)
+               return rc;
+
+       rc = nfc_genl_init();
+       if (rc)
+               goto err_genl;
+
+       /* the first generation must not be 0 */
+       nfc_devlist_generation = 1;
+
+       rc = rawsock_init();
+       if (rc)
+               goto err_rawsock;
+
+       rc = af_nfc_init();
+       if (rc)
+               goto err_af_nfc;
+
+       return 0;
+
+err_af_nfc:
+       rawsock_exit();
+err_rawsock:
+       nfc_genl_exit();
+err_genl:
+       class_unregister(&nfc_class);
+       return rc;
+}
+
+static void __exit nfc_exit(void)
+{
+       af_nfc_exit();
+       rawsock_exit();
+       nfc_genl_exit();
+       class_unregister(&nfc_class);
+}
+
+subsys_initcall(nfc_init);
+module_exit(nfc_exit);
+
+MODULE_AUTHOR("Lauro Ramos Venancio <lauro.venancio@openbossa.org>");
+MODULE_DESCRIPTION("NFC Core ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
new file mode 100644 (file)
index 0000000..ccdff79
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * 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.
+ */
+
+#include <net/genetlink.h>
+#include <linux/nfc.h>
+#include <linux/slab.h>
+
+#include "nfc.h"
+
+static struct genl_multicast_group nfc_genl_event_mcgrp = {
+       .name = NFC_GENL_MCAST_EVENT_NAME,
+};
+
+struct genl_family nfc_genl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = 0,
+       .name = NFC_GENL_NAME,
+       .version = NFC_GENL_VERSION,
+       .maxattr = NFC_ATTR_MAX,
+};
+
+static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
+       [NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 },
+       [NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING,
+                               .len = NFC_DEVICE_NAME_MAXSIZE },
+       [NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 },
+};
+
+static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
+                                       struct netlink_callback *cb, int flags)
+{
+       void *hdr;
+
+       nfc_dbg("entry");
+
+       hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+                               &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
+
+       NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target->idx);
+       NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS,
+                               target->supported_protocols);
+       NLA_PUT_U16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res);
+       NLA_PUT_U8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res);
+
+       return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx;
+
+       rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize,
+                                               nfc_genl_family.attrbuf,
+                                               nfc_genl_family.maxattr,
+                                               nfc_genl_policy);
+       if (rc < 0)
+               return ERR_PTR(rc);
+
+       if (!nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX])
+               return ERR_PTR(-EINVAL);
+
+       idx = nla_get_u32(nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return ERR_PTR(-ENODEV);
+
+       return dev;
+}
+
+static int nfc_genl_dump_targets(struct sk_buff *skb,
+                               struct netlink_callback *cb)
+{
+       int i = cb->args[0];
+       struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+       int rc;
+
+       nfc_dbg("entry");
+
+       if (!dev) {
+               dev = __get_device_from_cb(cb);
+               if (IS_ERR(dev))
+                       return PTR_ERR(dev);
+
+               cb->args[1] = (long) dev;
+       }
+
+       spin_lock_bh(&dev->targets_lock);
+
+       cb->seq = dev->targets_generation;
+
+       while (i < dev->n_targets) {
+               rc = nfc_genl_send_target(skb, &dev->targets[i], cb,
+                                                               NLM_F_MULTI);
+               if (rc < 0)
+                       break;
+
+               i++;
+       }
+
+       spin_unlock_bh(&dev->targets_lock);
+
+       cb->args[0] = i;
+
+       return skb->len;
+}
+
+static int nfc_genl_dump_targets_done(struct netlink_callback *cb)
+{
+       struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+
+       nfc_dbg("entry");
+
+       if (dev)
+               nfc_put_device(dev);
+
+       return 0;
+}
+
+int nfc_genl_targets_found(struct nfc_dev *dev)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       nfc_dbg("entry");
+
+       dev->genl_data.poll_req_pid = 0;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                               NFC_EVENT_TARGETS_FOUND);
+       if (!hdr)
+               goto free_msg;
+
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+
+       genlmsg_end(msg, hdr);
+
+       return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+int nfc_genl_device_added(struct nfc_dev *dev)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       nfc_dbg("entry");
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                               NFC_EVENT_DEVICE_ADDED);
+       if (!hdr)
+               goto free_msg;
+
+       NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev));
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+       NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols);
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+int nfc_genl_device_removed(struct nfc_dev *dev)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       nfc_dbg("entry");
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                               NFC_EVENT_DEVICE_REMOVED);
+       if (!hdr)
+               goto free_msg;
+
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
+                                               u32 pid, u32 seq,
+                                               struct netlink_callback *cb,
+                                               int flags)
+{
+       void *hdr;
+
+       nfc_dbg("entry");
+
+       hdr = genlmsg_put(msg, pid, seq, &nfc_genl_family, flags,
+                                                       NFC_CMD_GET_DEVICE);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       if (cb)
+               genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
+
+       NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev));
+       NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+       NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols);
+
+       return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int nfc_genl_dump_devices(struct sk_buff *skb,
+                               struct netlink_callback *cb)
+{
+       struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+       struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+       bool first_call = false;
+
+       nfc_dbg("entry");
+
+       if (!iter) {
+               first_call = true;
+               iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
+               if (!iter)
+                       return -ENOMEM;
+               cb->args[0] = (long) iter;
+       }
+
+       mutex_lock(&nfc_devlist_mutex);
+
+       cb->seq = nfc_devlist_generation;
+
+       if (first_call) {
+               nfc_device_iter_init(iter);
+               dev = nfc_device_iter_next(iter);
+       }
+
+       while (dev) {
+               int rc;
+
+               rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).pid,
+                                                       cb->nlh->nlmsg_seq,
+                                                       cb, NLM_F_MULTI);
+               if (rc < 0)
+                       break;
+
+               dev = nfc_device_iter_next(iter);
+       }
+
+       mutex_unlock(&nfc_devlist_mutex);
+
+       cb->args[1] = (long) dev;
+
+       return skb->len;
+}
+
+static int nfc_genl_dump_devices_done(struct netlink_callback *cb)
+{
+       struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+
+       nfc_dbg("entry");
+
+       nfc_device_iter_exit(iter);
+       kfree(iter);
+
+       return 0;
+}
+
+static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info)
+{
+       struct sk_buff *msg;
+       struct nfc_dev *dev;
+       u32 idx;
+       int rc = -ENOBUFS;
+
+       nfc_dbg("entry");
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg) {
+               rc = -ENOMEM;
+               goto out_putdev;
+       }
+
+       rc = nfc_genl_send_device(msg, dev, info->snd_pid, info->snd_seq,
+                                                               NULL, 0);
+       if (rc < 0)
+               goto out_free;
+
+       nfc_put_device(dev);
+
+       return genlmsg_reply(msg, info);
+
+out_free:
+       nlmsg_free(msg);
+out_putdev:
+       nfc_put_device(dev);
+       return rc;
+}
+
+static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx;
+       u32 protocols;
+
+       nfc_dbg("entry");
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+               !info->attrs[NFC_ATTR_PROTOCOLS])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       mutex_lock(&dev->genl_data.genl_data_mutex);
+
+       rc = nfc_start_poll(dev, protocols);
+       if (!rc)
+               dev->genl_data.poll_req_pid = info->snd_pid;
+
+       mutex_unlock(&dev->genl_data.genl_data_mutex);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
+static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx;
+
+       nfc_dbg("entry");
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       mutex_lock(&dev->genl_data.genl_data_mutex);
+
+       if (dev->genl_data.poll_req_pid != info->snd_pid) {
+               rc = -EBUSY;
+               goto out;
+       }
+
+       rc = nfc_stop_poll(dev);
+       dev->genl_data.poll_req_pid = 0;
+
+out:
+       mutex_unlock(&dev->genl_data.genl_data_mutex);
+       nfc_put_device(dev);
+       return rc;
+}
+
+static struct genl_ops nfc_genl_ops[] = {
+       {
+               .cmd = NFC_CMD_GET_DEVICE,
+               .doit = nfc_genl_get_device,
+               .dumpit = nfc_genl_dump_devices,
+               .done = nfc_genl_dump_devices_done,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_START_POLL,
+               .doit = nfc_genl_start_poll,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_STOP_POLL,
+               .doit = nfc_genl_stop_poll,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_GET_TARGET,
+               .dumpit = nfc_genl_dump_targets,
+               .done = nfc_genl_dump_targets_done,
+               .policy = nfc_genl_policy,
+       },
+};
+
+static int nfc_genl_rcv_nl_event(struct notifier_block *this,
+                                               unsigned long event, void *ptr)
+{
+       struct netlink_notify *n = ptr;
+       struct class_dev_iter iter;
+       struct nfc_dev *dev;
+
+       if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
+               goto out;
+
+       nfc_dbg("NETLINK_URELEASE event from id %d", n->pid);
+
+       nfc_device_iter_init(&iter);
+       dev = nfc_device_iter_next(&iter);
+
+       while (dev) {
+               mutex_lock(&dev->genl_data.genl_data_mutex);
+               if (dev->genl_data.poll_req_pid == n->pid) {
+                       nfc_stop_poll(dev);
+                       dev->genl_data.poll_req_pid = 0;
+               }
+               mutex_unlock(&dev->genl_data.genl_data_mutex);
+               dev = nfc_device_iter_next(&iter);
+       }
+
+       nfc_device_iter_exit(&iter);
+
+out:
+       return NOTIFY_DONE;
+}
+
+void nfc_genl_data_init(struct nfc_genl_data *genl_data)
+{
+       genl_data->poll_req_pid = 0;
+       mutex_init(&genl_data->genl_data_mutex);
+}
+
+void nfc_genl_data_exit(struct nfc_genl_data *genl_data)
+{
+       mutex_destroy(&genl_data->genl_data_mutex);
+}
+
+static struct notifier_block nl_notifier = {
+       .notifier_call  = nfc_genl_rcv_nl_event,
+};
+
+/**
+ * nfc_genl_init() - Initialize netlink interface
+ *
+ * This initialization function registers the nfc netlink family.
+ */
+int __init nfc_genl_init(void)
+{
+       int rc;
+
+       rc = genl_register_family_with_ops(&nfc_genl_family, nfc_genl_ops,
+                                       ARRAY_SIZE(nfc_genl_ops));
+       if (rc)
+               return rc;
+
+       rc = genl_register_mc_group(&nfc_genl_family, &nfc_genl_event_mcgrp);
+
+       netlink_register_notifier(&nl_notifier);
+
+       return rc;
+}
+
+/**
+ * nfc_genl_exit() - Deinitialize netlink interface
+ *
+ * This exit function unregisters the nfc netlink family.
+ */
+void nfc_genl_exit(void)
+{
+       netlink_unregister_notifier(&nl_notifier);
+       genl_unregister_family(&nfc_genl_family);
+}
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
new file mode 100644 (file)
index 0000000..aaf9832
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * 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.
+ */
+
+#ifndef __LOCAL_NFC_H
+#define __LOCAL_NFC_H
+
+#include <net/nfc.h>
+#include <net/sock.h>
+
+__attribute__((format (printf, 2, 3)))
+int nfc_printk(const char *level, const char *fmt, ...);
+
+#define nfc_info(fmt, arg...) nfc_printk(KERN_INFO, fmt, ##arg)
+#define nfc_err(fmt, arg...) nfc_printk(KERN_ERR, fmt, ##arg)
+#define nfc_dbg(fmt, arg...) pr_debug(fmt "\n", ##arg)
+
+struct nfc_protocol {
+       int id;
+       struct proto *proto;
+       struct module *owner;
+       int (*create)(struct net *net, struct socket *sock,
+                       const struct nfc_protocol *nfc_proto);
+};
+
+struct nfc_rawsock {
+       struct sock sk;
+       struct nfc_dev *dev;
+       u32 target_idx;
+       struct work_struct tx_work;
+       bool tx_work_scheduled;
+};
+#define nfc_rawsock(sk) ((struct nfc_rawsock *) sk)
+#define to_rawsock_sk(_tx_work) \
+       ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work))
+
+int __init rawsock_init(void);
+void rawsock_exit(void);
+
+int __init af_nfc_init(void);
+void af_nfc_exit(void);
+int nfc_proto_register(const struct nfc_protocol *nfc_proto);
+void nfc_proto_unregister(const struct nfc_protocol *nfc_proto);
+
+extern int nfc_devlist_generation;
+extern struct mutex nfc_devlist_mutex;
+
+int __init nfc_genl_init(void);
+void nfc_genl_exit(void);
+
+void nfc_genl_data_init(struct nfc_genl_data *genl_data);
+void nfc_genl_data_exit(struct nfc_genl_data *genl_data);
+
+int nfc_genl_targets_found(struct nfc_dev *dev);
+
+int nfc_genl_device_added(struct nfc_dev *dev);
+int nfc_genl_device_removed(struct nfc_dev *dev);
+
+struct nfc_dev *nfc_get_device(unsigned idx);
+
+static inline void nfc_put_device(struct nfc_dev *dev)
+{
+       put_device(&dev->dev);
+}
+
+static inline void nfc_device_iter_init(struct class_dev_iter *iter)
+{
+       class_dev_iter_init(iter, &nfc_class, NULL, NULL);
+}
+
+static inline struct nfc_dev *nfc_device_iter_next(struct class_dev_iter *iter)
+{
+       struct device *d = class_dev_iter_next(iter);
+       if (!d)
+               return NULL;
+
+       return to_nfc_dev(d);
+}
+
+static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
+{
+       class_dev_iter_exit(iter);
+}
+
+int nfc_start_poll(struct nfc_dev *dev, u32 protocols);
+
+int nfc_stop_poll(struct nfc_dev *dev);
+
+int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol);
+
+int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx);
+
+int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx,
+                                       struct sk_buff *skb,
+                                       data_exchange_cb_t cb,
+                                       void *cb_context);
+
+#endif /* __LOCAL_NFC_H */
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
new file mode 100644 (file)
index 0000000..52de84a
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *
+ * 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.
+ */
+
+#include <net/tcp_states.h>
+#include <linux/nfc.h>
+
+#include "nfc.h"
+
+static void rawsock_write_queue_purge(struct sock *sk)
+{
+       nfc_dbg("sk=%p", sk);
+
+       spin_lock_bh(&sk->sk_write_queue.lock);
+       __skb_queue_purge(&sk->sk_write_queue);
+       nfc_rawsock(sk)->tx_work_scheduled = false;
+       spin_unlock_bh(&sk->sk_write_queue.lock);
+}
+
+static void rawsock_report_error(struct sock *sk, int err)
+{
+       nfc_dbg("sk=%p err=%d", sk, err);
+
+       sk->sk_shutdown = SHUTDOWN_MASK;
+       sk->sk_err = -err;
+       sk->sk_error_report(sk);
+
+       rawsock_write_queue_purge(sk);
+}
+
+static int rawsock_release(struct socket *sock)
+{
+       struct sock *sk = sock->sk;
+
+       nfc_dbg("sock=%p", sock);
+
+       sock_orphan(sk);
+       sock_put(sk);
+
+       return 0;
+}
+
+static int rawsock_connect(struct socket *sock, struct sockaddr *_addr,
+                                                       int len, int flags)
+{
+       struct sock *sk = sock->sk;
+       struct sockaddr_nfc *addr = (struct sockaddr_nfc *)_addr;
+       struct nfc_dev *dev;
+       int rc = 0;
+
+       nfc_dbg("sock=%p sk=%p flags=%d", sock, sk, flags);
+
+       if (!addr || len < sizeof(struct sockaddr_nfc) ||
+               addr->sa_family != AF_NFC)
+               return -EINVAL;
+
+       nfc_dbg("addr dev_idx=%u target_idx=%u protocol=%u", addr->dev_idx,
+                                       addr->target_idx, addr->nfc_protocol);
+
+       lock_sock(sk);
+
+       if (sock->state == SS_CONNECTED) {
+               rc = -EISCONN;
+               goto error;
+       }
+
+       dev = nfc_get_device(addr->dev_idx);
+       if (!dev) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (addr->target_idx > dev->target_idx - 1 ||
+               addr->target_idx < dev->target_idx - dev->n_targets) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       if (addr->target_idx > dev->target_idx - 1 ||
+               addr->target_idx < dev->target_idx - dev->n_targets) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol);
+       if (rc)
+               goto put_dev;
+
+       nfc_rawsock(sk)->dev = dev;
+       nfc_rawsock(sk)->target_idx = addr->target_idx;
+       sock->state = SS_CONNECTED;
+       sk->sk_state = TCP_ESTABLISHED;
+       sk->sk_state_change(sk);
+
+       release_sock(sk);
+       return 0;
+
+put_dev:
+       nfc_put_device(dev);
+error:
+       release_sock(sk);
+       return rc;
+}
+
+static int rawsock_add_header(struct sk_buff *skb)
+{
+
+       if (skb_cow_head(skb, 1))
+               return -ENOMEM;
+
+       *skb_push(skb, 1) = 0;
+
+       return 0;
+}
+
+static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,
+                                                               int err)
+{
+       struct sock *sk = (struct sock *) context;
+
+       BUG_ON(in_irq());
+
+       nfc_dbg("sk=%p err=%d", sk, err);
+
+       if (err)
+               goto error;
+
+       err = rawsock_add_header(skb);
+       if (err)
+               goto error;
+
+       err = sock_queue_rcv_skb(sk, skb);
+       if (err)
+               goto error;
+
+       spin_lock_bh(&sk->sk_write_queue.lock);
+       if (!skb_queue_empty(&sk->sk_write_queue))
+               schedule_work(&nfc_rawsock(sk)->tx_work);
+       else
+               nfc_rawsock(sk)->tx_work_scheduled = false;
+       spin_unlock_bh(&sk->sk_write_queue.lock);
+
+       sock_put(sk);
+       return;
+
+error:
+       rawsock_report_error(sk, err);
+       sock_put(sk);
+}
+
+static void rawsock_tx_work(struct work_struct *work)
+{
+       struct sock *sk = to_rawsock_sk(work);
+       struct nfc_dev *dev = nfc_rawsock(sk)->dev;
+       u32 target_idx = nfc_rawsock(sk)->target_idx;
+       struct sk_buff *skb;
+       int rc;
+
+       nfc_dbg("sk=%p target_idx=%u", sk, target_idx);
+
+       if (sk->sk_shutdown & SEND_SHUTDOWN) {
+               rawsock_write_queue_purge(sk);
+               return;
+       }
+
+       skb = skb_dequeue(&sk->sk_write_queue);
+
+       sock_hold(sk);
+       rc = nfc_data_exchange(dev, target_idx, skb,
+                               rawsock_data_exchange_complete, sk);
+       if (rc) {
+               rawsock_report_error(sk, rc);
+               sock_put(sk);
+       }
+}
+
+static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock,
+                                       struct msghdr *msg, size_t len)
+{
+       struct sock *sk = sock->sk;
+       struct sk_buff *skb;
+       int rc;
+
+       nfc_dbg("sock=%p sk=%p len=%zu", sock, sk, len);
+
+       if (msg->msg_namelen)
+               return -EOPNOTSUPP;
+
+       if (sock->state != SS_CONNECTED)
+               return -ENOTCONN;
+
+       skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT,
+                                                                       &rc);
+       if (!skb)
+               return rc;
+
+       rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+       if (rc < 0) {
+               kfree_skb(skb);
+               return rc;
+       }
+
+       spin_lock_bh(&sk->sk_write_queue.lock);
+       __skb_queue_tail(&sk->sk_write_queue, skb);
+       if (!nfc_rawsock(sk)->tx_work_scheduled) {
+               schedule_work(&nfc_rawsock(sk)->tx_work);
+               nfc_rawsock(sk)->tx_work_scheduled = true;
+       }
+       spin_unlock_bh(&sk->sk_write_queue.lock);
+
+       return len;
+}
+
+static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
+                               struct msghdr *msg, size_t len, int flags)
+{
+       int noblock = flags & MSG_DONTWAIT;
+       struct sock *sk = sock->sk;
+       struct sk_buff *skb;
+       int copied;
+       int rc;
+
+       nfc_dbg("sock=%p sk=%p len=%zu flags=%d", sock, sk, len, flags);
+
+       skb = skb_recv_datagram(sk, flags, noblock, &rc);
+       if (!skb)
+               return rc;
+
+       msg->msg_namelen = 0;
+
+       copied = skb->len;
+       if (len < copied) {
+               msg->msg_flags |= MSG_TRUNC;
+               copied = len;
+       }
+
+       rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+       skb_free_datagram(sk, skb);
+
+       return rc ? : copied;
+}
+
+
+static const struct proto_ops rawsock_ops = {
+       .family         = PF_NFC,
+       .owner          = THIS_MODULE,
+       .release        = rawsock_release,
+       .bind           = sock_no_bind,
+       .connect        = rawsock_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .getname        = sock_no_getname,
+       .poll           = datagram_poll,
+       .ioctl          = sock_no_ioctl,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = sock_no_setsockopt,
+       .getsockopt     = sock_no_getsockopt,
+       .sendmsg        = rawsock_sendmsg,
+       .recvmsg        = rawsock_recvmsg,
+       .mmap           = sock_no_mmap,
+};
+
+static void rawsock_destruct(struct sock *sk)
+{
+       nfc_dbg("sk=%p", sk);
+
+       if (sk->sk_state == TCP_ESTABLISHED) {
+               nfc_deactivate_target(nfc_rawsock(sk)->dev,
+                                       nfc_rawsock(sk)->target_idx);
+               nfc_put_device(nfc_rawsock(sk)->dev);
+       }
+
+       skb_queue_purge(&sk->sk_receive_queue);
+
+       if (!sock_flag(sk, SOCK_DEAD)) {
+               nfc_err("Freeing alive NFC raw socket %p", sk);
+               return;
+       }
+}
+
+static int rawsock_create(struct net *net, struct socket *sock,
+                               const struct nfc_protocol *nfc_proto)
+{
+       struct sock *sk;
+
+       nfc_dbg("sock=%p", sock);
+
+       if (sock->type != SOCK_SEQPACKET)
+               return -ESOCKTNOSUPPORT;
+
+       sock->ops = &rawsock_ops;
+
+       sk = sk_alloc(net, PF_NFC, GFP_KERNEL, nfc_proto->proto);
+       if (!sk)
+               return -ENOMEM;
+
+       sock_init_data(sock, sk);
+       sk->sk_protocol = nfc_proto->id;
+       sk->sk_destruct = rawsock_destruct;
+       sock->state = SS_UNCONNECTED;
+
+       INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work);
+       nfc_rawsock(sk)->tx_work_scheduled = false;
+
+       return 0;
+}
+
+static struct proto rawsock_proto = {
+       .name     = "NFC_RAW",
+       .owner    = THIS_MODULE,
+       .obj_size = sizeof(struct nfc_rawsock),
+};
+
+static const struct nfc_protocol rawsock_nfc_proto = {
+       .id       = NFC_SOCKPROTO_RAW,
+       .proto    = &rawsock_proto,
+       .owner    = THIS_MODULE,
+       .create   = rawsock_create
+};
+
+int __init rawsock_init(void)
+{
+       int rc;
+
+       rc = nfc_proto_register(&rawsock_nfc_proto);
+
+       return rc;
+}
+
+void rawsock_exit(void)
+{
+       nfc_proto_unregister(&rawsock_nfc_proto);
+}
index 3633ab6af1842a1ba2b656ec126fa1ee9a97de5a..832f6574e4ed7dab26d949154d0aefe04303e98b 100644 (file)
@@ -1084,3 +1084,14 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
        nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);
 }
 EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
+
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+                              const u8 *replay_ctr, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
index 50ff82aa48907dd46f3fe1dfbbd68b67e20dc0cf..491b0ba40c43c5b469e723514c83922c71e08bf7 100644 (file)
@@ -176,6 +176,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
        [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
        [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
+       [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
 };
 
 /* policy for the key attributes */
@@ -206,6 +207,14 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
        [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
 };
 
+/* policy for GTK rekey offload attributes */
+static const struct nla_policy
+nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
+       [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
+       [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
+       [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
+};
+
 /* ifidx get helper */
 static int nl80211_get_ifidx(struct netlink_callback *cb)
 {
@@ -5408,6 +5417,57 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct nlattr *tb[NUM_NL80211_REKEY_DATA];
+       struct cfg80211_gtk_rekey_data rekey_data;
+       int err;
+
+       if (!info->attrs[NL80211_ATTR_REKEY_DATA])
+               return -EINVAL;
+
+       err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
+                       nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
+                       nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
+                       nl80211_rekey_policy);
+       if (err)
+               return err;
+
+       if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
+               return -ERANGE;
+       if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
+               return -ERANGE;
+       if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
+               return -ERANGE;
+
+       memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
+              NL80211_KEK_LEN);
+       memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
+              NL80211_KCK_LEN);
+       memcpy(rekey_data.replay_ctr,
+              nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
+              NL80211_REPLAY_CTR_LEN);
+
+       wdev_lock(wdev);
+       if (!wdev->current_bss) {
+               err = -ENOTCONN;
+               goto out;
+       }
+
+       if (!rdev->ops->set_rekey_data) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
+ out:
+       wdev_unlock(wdev);
+       return err;
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -5939,6 +5999,14 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_WIPHY |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
+               .doit = nl80211_set_rekey_data,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -6883,6 +6951,51 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
+void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+                             struct net_device *netdev, const u8 *bssid,
+                             const u8 *replay_ctr, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       struct nlattr *rekey_attr;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+
+       rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
+       if (!rekey_attr)
+               goto nla_put_failure;
+
+       NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
+               NL80211_REPLAY_CTR_LEN, replay_ctr);
+
+       nla_nest_end(msg, rekey_attr);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
 void
 nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
                                struct net_device *netdev, const u8 *peer,
index 2f1bfb87a651f43f8d698b567832bfb6d468adcc..5d69c56400aedf2bf7e26e0c5294e1c120d3a070 100644 (file)
@@ -109,4 +109,8 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
                                struct net_device *netdev, const u8 *peer,
                                u32 num_packets, gfp_t gfp);
 
+void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+                             struct net_device *netdev, const u8 *bssid,
+                             const u8 *replay_ctr, gfp_t gfp);
+
 #endif /* __NET_WIRELESS_NL80211_H */
index 7a6c67667d708e97400a6974175323f7cfd712e6..5d23503dd5e0ca3a4d8a8d3f0af70b1ca855ca10 100644 (file)
@@ -132,7 +132,6 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
 int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
                               bool driver_initiated)
 {
-       int err;
        struct net_device *dev;
 
        ASSERT_RDEV_LOCK(rdev);
@@ -143,7 +142,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
        dev = rdev->sched_scan_req->dev;
 
        if (!driver_initiated) {
-               err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
+               int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
                if (err)
                        return err;
        }
@@ -153,7 +152,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
        kfree(rdev->sched_scan_req);
        rdev->sched_scan_req = NULL;
 
-       return err;
+       return 0;
 }
 
 static void bss_release(struct kref *ref)