cw1200: move under st vendor directory
authorKalle Valo <kvalo@codeaurora.org>
Tue, 17 Nov 2015 18:09:02 +0000 (20:09 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Wed, 18 Nov 2015 12:28:30 +0000 (14:28 +0200)
Part of reorganising wireless drivers directory and Kconfig.

Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
59 files changed:
MAINTAINERS
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/cw1200/Kconfig [deleted file]
drivers/net/wireless/cw1200/Makefile [deleted file]
drivers/net/wireless/cw1200/bh.c [deleted file]
drivers/net/wireless/cw1200/bh.h [deleted file]
drivers/net/wireless/cw1200/cw1200.h [deleted file]
drivers/net/wireless/cw1200/cw1200_sdio.c [deleted file]
drivers/net/wireless/cw1200/cw1200_spi.c [deleted file]
drivers/net/wireless/cw1200/debug.c [deleted file]
drivers/net/wireless/cw1200/debug.h [deleted file]
drivers/net/wireless/cw1200/fwio.c [deleted file]
drivers/net/wireless/cw1200/fwio.h [deleted file]
drivers/net/wireless/cw1200/hwbus.h [deleted file]
drivers/net/wireless/cw1200/hwio.c [deleted file]
drivers/net/wireless/cw1200/hwio.h [deleted file]
drivers/net/wireless/cw1200/main.c [deleted file]
drivers/net/wireless/cw1200/pm.c [deleted file]
drivers/net/wireless/cw1200/pm.h [deleted file]
drivers/net/wireless/cw1200/queue.c [deleted file]
drivers/net/wireless/cw1200/queue.h [deleted file]
drivers/net/wireless/cw1200/scan.c [deleted file]
drivers/net/wireless/cw1200/scan.h [deleted file]
drivers/net/wireless/cw1200/sta.c [deleted file]
drivers/net/wireless/cw1200/sta.h [deleted file]
drivers/net/wireless/cw1200/txrx.c [deleted file]
drivers/net/wireless/cw1200/txrx.h [deleted file]
drivers/net/wireless/cw1200/wsm.c [deleted file]
drivers/net/wireless/cw1200/wsm.h [deleted file]
drivers/net/wireless/st/Kconfig [new file with mode: 0644]
drivers/net/wireless/st/Makefile [new file with mode: 0644]
drivers/net/wireless/st/cw1200/Kconfig [new file with mode: 0644]
drivers/net/wireless/st/cw1200/Makefile [new file with mode: 0644]
drivers/net/wireless/st/cw1200/bh.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/bh.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/cw1200.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/cw1200_sdio.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/cw1200_spi.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/debug.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/debug.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/fwio.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/fwio.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/hwbus.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/hwio.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/hwio.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/main.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/pm.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/pm.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/queue.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/queue.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/scan.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/scan.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/sta.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/sta.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/txrx.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/txrx.h [new file with mode: 0644]
drivers/net/wireless/st/cw1200/wsm.c [new file with mode: 0644]
drivers/net/wireless/st/cw1200/wsm.h [new file with mode: 0644]

index 748ee82..4cbd464 100644 (file)
@@ -3100,7 +3100,7 @@ F:        sound/pci/cs5535audio/
 CW1200 WLAN driver
 M:     Solomon Peachy <pizza@shaftnet.org>
 S:     Maintained
-F:     drivers/net/wireless/cw1200/
+F:     drivers/net/wireless/st/cw1200/
 
 CX18 VIDEO4LINUX DRIVER
 M:     Andy Walls <awalls@md.metrocast.net>
index c627d9e..c1742f2 100644 (file)
@@ -21,6 +21,7 @@ source "drivers/net/wireless/admtek/Kconfig"
 source "drivers/net/wireless/atmel/Kconfig"
 source "drivers/net/wireless/broadcom/Kconfig"
 source "drivers/net/wireless/cisco/Kconfig"
+source "drivers/net/wireless/st/Kconfig"
 
 config PCMCIA_RAYCS
        tristate "Aviator/Raytheon 2.4GHz wireless support"
@@ -173,7 +174,6 @@ source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zd1211rw/Kconfig"
 source "drivers/net/wireless/mwifiex/Kconfig"
-source "drivers/net/wireless/cw1200/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
 
 endif # WLAN
index 41ebe56..d7211a7 100644 (file)
@@ -6,6 +6,7 @@ obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/
 obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/
 obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/
 obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/
+obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 
 obj-$(CONFIG_IPW2100) += ipw2x00/
 obj-$(CONFIG_IPW2200) += ipw2x00/
@@ -47,5 +48,4 @@ obj-$(CONFIG_WL_TI)   += ti/
 
 obj-$(CONFIG_MWIFIEX)  += mwifiex/
 
-obj-$(CONFIG_CW1200)   += cw1200/
 obj-$(CONFIG_RSI_91X)  += rsi/
diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig
deleted file mode 100644 (file)
index 0880742..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-config CW1200
-       tristate "CW1200 WLAN support"
-       depends on MAC80211 && CFG80211
-       help
-         This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets.
-         This option just enables the driver core, see below for
-         specific bus support.
-
-if CW1200
-
-config CW1200_WLAN_SDIO
-       tristate "Support SDIO platforms"
-       depends on CW1200 && MMC
-       help
-         Enable support for the CW1200 connected via an SDIO bus.
-         By default this driver only supports the Sagrad SG901-1091/1098 EVK
-         and similar designs that utilize a hardware reset circuit. To
-         support different CW1200 SDIO designs you will need to override
-         the default platform data by calling cw1200_sdio_set_platform_data()
-         in your board setup file.
-
-config CW1200_WLAN_SPI
-       tristate "Support SPI platforms"
-       depends on CW1200 && SPI
-       help
-         Enables support for the CW1200 connected via a SPI bus.  You will
-         need to add appropriate platform data glue in your board setup
-         file.
-
-endif
diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile
deleted file mode 100644 (file)
index b086aac..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-cw1200_core-y := \
-               fwio.o \
-               txrx.o \
-               main.o \
-               queue.o \
-               hwio.o \
-               bh.o \
-               wsm.o \
-               sta.o \
-               scan.o \
-               debug.o
-cw1200_core-$(CONFIG_PM)       += pm.o
-
-# CFLAGS_sta.o += -DDEBUG
-
-cw1200_wlan_sdio-y := cw1200_sdio.o
-cw1200_wlan_spi-y := cw1200_spi.o
-
-obj-$(CONFIG_CW1200) += cw1200_core.o
-obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o
-obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o
diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c
deleted file mode 100644 (file)
index 92d299a..0000000
+++ /dev/null
@@ -1,619 +0,0 @@
-/*
- * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * Based on:
- * ST-Ericsson UMAC CW1200 driver, which is
- * Copyright (c) 2010, ST-Ericsson
- * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <net/mac80211.h>
-#include <linux/kthread.h>
-#include <linux/timer.h>
-
-#include "cw1200.h"
-#include "bh.h"
-#include "hwio.h"
-#include "wsm.h"
-#include "hwbus.h"
-#include "debug.h"
-#include "fwio.h"
-
-static int cw1200_bh(void *arg);
-
-#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4)
-/* an SPI message cannot be bigger than (2"12-1)*2 bytes
- * "*2" to cvt to bytes
- */
-#define MAX_SZ_RD_WR_BUFFERS   (DOWNLOAD_BLOCK_SIZE_WR*2)
-#define PIGGYBACK_CTRL_REG     (2)
-#define EFFECTIVE_BUF_SIZE     (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
-
-/* Suspend state privates */
-enum cw1200_bh_pm_state {
-       CW1200_BH_RESUMED = 0,
-       CW1200_BH_SUSPEND,
-       CW1200_BH_SUSPENDED,
-       CW1200_BH_RESUME,
-};
-
-typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
-       u8 *data, size_t size);
-
-static void cw1200_bh_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-       container_of(work, struct cw1200_common, bh_work);
-       cw1200_bh(priv);
-}
-
-int cw1200_register_bh(struct cw1200_common *priv)
-{
-       int err = 0;
-       /* Realtime workqueue */
-       priv->bh_workqueue = alloc_workqueue("cw1200_bh",
-                               WQ_MEM_RECLAIM | WQ_HIGHPRI
-                               | WQ_CPU_INTENSIVE, 1);
-
-       if (!priv->bh_workqueue)
-               return -ENOMEM;
-
-       INIT_WORK(&priv->bh_work, cw1200_bh_work);
-
-       pr_debug("[BH] register.\n");
-
-       atomic_set(&priv->bh_rx, 0);
-       atomic_set(&priv->bh_tx, 0);
-       atomic_set(&priv->bh_term, 0);
-       atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
-       priv->bh_error = 0;
-       priv->hw_bufs_used = 0;
-       priv->buf_id_tx = 0;
-       priv->buf_id_rx = 0;
-       init_waitqueue_head(&priv->bh_wq);
-       init_waitqueue_head(&priv->bh_evt_wq);
-
-       err = !queue_work(priv->bh_workqueue, &priv->bh_work);
-       WARN_ON(err);
-       return err;
-}
-
-void cw1200_unregister_bh(struct cw1200_common *priv)
-{
-       atomic_add(1, &priv->bh_term);
-       wake_up(&priv->bh_wq);
-
-       flush_workqueue(priv->bh_workqueue);
-
-       destroy_workqueue(priv->bh_workqueue);
-       priv->bh_workqueue = NULL;
-
-       pr_debug("[BH] unregistered.\n");
-}
-
-void cw1200_irq_handler(struct cw1200_common *priv)
-{
-       pr_debug("[BH] irq.\n");
-
-       /* Disable Interrupts! */
-       /* NOTE:  hwbus_ops->lock already held */
-       __cw1200_irq_enable(priv, 0);
-
-       if (/* WARN_ON */(priv->bh_error))
-               return;
-
-       if (atomic_add_return(1, &priv->bh_rx) == 1)
-               wake_up(&priv->bh_wq);
-}
-EXPORT_SYMBOL_GPL(cw1200_irq_handler);
-
-void cw1200_bh_wakeup(struct cw1200_common *priv)
-{
-       pr_debug("[BH] wakeup.\n");
-       if (priv->bh_error) {
-               pr_err("[BH] wakeup failed (BH error)\n");
-               return;
-       }
-
-       if (atomic_add_return(1, &priv->bh_tx) == 1)
-               wake_up(&priv->bh_wq);
-}
-
-int cw1200_bh_suspend(struct cw1200_common *priv)
-{
-       pr_debug("[BH] suspend.\n");
-       if (priv->bh_error) {
-               wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n");
-               return -EINVAL;
-       }
-
-       atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
-       wake_up(&priv->bh_wq);
-       return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
-               (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)),
-                1 * HZ) ? 0 : -ETIMEDOUT;
-}
-
-int cw1200_bh_resume(struct cw1200_common *priv)
-{
-       pr_debug("[BH] resume.\n");
-       if (priv->bh_error) {
-               wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n");
-               return -EINVAL;
-       }
-
-       atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
-       wake_up(&priv->bh_wq);
-       return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
-               (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)),
-               1 * HZ) ? 0 : -ETIMEDOUT;
-}
-
-static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
-{
-       ++priv->hw_bufs_used;
-}
-
-int wsm_release_tx_buffer(struct cw1200_common *priv, int count)
-{
-       int ret = 0;
-       int hw_bufs_used = priv->hw_bufs_used;
-
-       priv->hw_bufs_used -= count;
-       if (WARN_ON(priv->hw_bufs_used < 0))
-               ret = -1;
-       else if (hw_bufs_used >= priv->wsm_caps.input_buffers)
-               ret = 1;
-       if (!priv->hw_bufs_used)
-               wake_up(&priv->bh_evt_wq);
-       return ret;
-}
-
-static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv,
-                                         u16 *ctrl_reg)
-{
-       int ret;
-
-       ret = cw1200_reg_read_16(priv,
-                       ST90TDS_CONTROL_REG_ID, ctrl_reg);
-       if (ret) {
-               ret = cw1200_reg_read_16(priv,
-                               ST90TDS_CONTROL_REG_ID, ctrl_reg);
-               if (ret)
-                       pr_err("[BH] Failed to read control register.\n");
-       }
-
-       return ret;
-}
-
-static int cw1200_device_wakeup(struct cw1200_common *priv)
-{
-       u16 ctrl_reg;
-       int ret;
-
-       pr_debug("[BH] Device wakeup.\n");
-
-       /* First, set the dpll register */
-       ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
-                                 cw1200_dpll_from_clk(priv->hw_refclk));
-       if (WARN_ON(ret))
-               return ret;
-
-       /* To force the device to be always-on, the host sets WLAN_UP to 1 */
-       ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
-                       ST90TDS_CONT_WUP_BIT);
-       if (WARN_ON(ret))
-               return ret;
-
-       ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg);
-       if (WARN_ON(ret))
-               return ret;
-
-       /* If the device returns WLAN_RDY as 1, the device is active and will
-        * remain active.
-        */
-       if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
-               pr_debug("[BH] Device awake.\n");
-               return 1;
-       }
-
-       return 0;
-}
-
-/* Must be called from BH thraed. */
-void cw1200_enable_powersave(struct cw1200_common *priv,
-                            bool enable)
-{
-       pr_debug("[BH] Powerave is %s.\n",
-                enable ? "enabled" : "disabled");
-       priv->powersave_enabled = enable;
-}
-
-static int cw1200_bh_rx_helper(struct cw1200_common *priv,
-                              uint16_t *ctrl_reg,
-                              int *tx)
-{
-       size_t read_len = 0;
-       struct sk_buff *skb_rx = NULL;
-       struct wsm_hdr *wsm;
-       size_t wsm_len;
-       u16 wsm_id;
-       u8 wsm_seq;
-       int rx_resync = 1;
-
-       size_t alloc_len;
-       u8 *data;
-
-       read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
-       if (!read_len)
-               return 0; /* No more work */
-
-       if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
-                   (read_len > EFFECTIVE_BUF_SIZE))) {
-               pr_debug("Invalid read len: %zu (%04x)",
-                        read_len, *ctrl_reg);
-               goto err;
-       }
-
-       /* Add SIZE of PIGGYBACK reg (CONTROL Reg)
-        * to the NEXT Message length + 2 Bytes for SKB
-        */
-       read_len = read_len + 2;
-
-       alloc_len = priv->hwbus_ops->align_size(
-               priv->hwbus_priv, read_len);
-
-       /* Check if not exceeding CW1200 capabilities */
-       if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
-               pr_debug("Read aligned len: %zu\n",
-                        alloc_len);
-       }
-
-       skb_rx = dev_alloc_skb(alloc_len);
-       if (WARN_ON(!skb_rx))
-               goto err;
-
-       skb_trim(skb_rx, 0);
-       skb_put(skb_rx, read_len);
-       data = skb_rx->data;
-       if (WARN_ON(!data))
-               goto err;
-
-       if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) {
-               pr_err("rx blew up, len %zu\n", alloc_len);
-               goto err;
-       }
-
-       /* Piggyback */
-       *ctrl_reg = __le16_to_cpu(
-               ((__le16 *)data)[alloc_len / 2 - 1]);
-
-       wsm = (struct wsm_hdr *)data;
-       wsm_len = __le16_to_cpu(wsm->len);
-       if (WARN_ON(wsm_len > read_len))
-               goto err;
-
-       if (priv->wsm_enable_wsm_dumps)
-               print_hex_dump_bytes("<-- ",
-                                    DUMP_PREFIX_NONE,
-                                    data, wsm_len);
-
-       wsm_id  = __le16_to_cpu(wsm->id) & 0xFFF;
-       wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
-
-       skb_trim(skb_rx, wsm_len);
-
-       if (wsm_id == 0x0800) {
-               wsm_handle_exception(priv,
-                                    &data[sizeof(*wsm)],
-                                    wsm_len - sizeof(*wsm));
-               goto err;
-       } else if (!rx_resync) {
-               if (WARN_ON(wsm_seq != priv->wsm_rx_seq))
-                       goto err;
-       }
-       priv->wsm_rx_seq = (wsm_seq + 1) & 7;
-       rx_resync = 0;
-
-       if (wsm_id & 0x0400) {
-               int rc = wsm_release_tx_buffer(priv, 1);
-               if (WARN_ON(rc < 0))
-                       return rc;
-               else if (rc > 0)
-                       *tx = 1;
-       }
-
-       /* cw1200_wsm_rx takes care on SKB livetime */
-       if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx)))
-               goto err;
-
-       if (skb_rx) {
-               dev_kfree_skb(skb_rx);
-               skb_rx = NULL;
-       }
-
-       return 0;
-
-err:
-       if (skb_rx) {
-               dev_kfree_skb(skb_rx);
-               skb_rx = NULL;
-       }
-       return -1;
-}
-
-static int cw1200_bh_tx_helper(struct cw1200_common *priv,
-                              int *pending_tx,
-                              int *tx_burst)
-{
-       size_t tx_len;
-       u8 *data;
-       int ret;
-       struct wsm_hdr *wsm;
-
-       if (priv->device_can_sleep) {
-               ret = cw1200_device_wakeup(priv);
-               if (WARN_ON(ret < 0)) { /* Error in wakeup */
-                       *pending_tx = 1;
-                       return 0;
-               } else if (ret) { /* Woke up */
-                       priv->device_can_sleep = false;
-               } else { /* Did not awake */
-                       *pending_tx = 1;
-                       return 0;
-               }
-       }
-
-       wsm_alloc_tx_buffer(priv);
-       ret = wsm_get_tx(priv, &data, &tx_len, tx_burst);
-       if (ret <= 0) {
-               wsm_release_tx_buffer(priv, 1);
-               if (WARN_ON(ret < 0))
-                       return ret; /* Error */
-               return 0; /* No work */
-       }
-
-       wsm = (struct wsm_hdr *)data;
-       BUG_ON(tx_len < sizeof(*wsm));
-       BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
-
-       atomic_add(1, &priv->bh_tx);
-
-       tx_len = priv->hwbus_ops->align_size(
-               priv->hwbus_priv, tx_len);
-
-       /* Check if not exceeding CW1200 capabilities */
-       if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
-               pr_debug("Write aligned len: %zu\n", tx_len);
-
-       wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
-       wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq));
-
-       if (WARN_ON(cw1200_data_write(priv, data, tx_len))) {
-               pr_err("tx blew up, len %zu\n", tx_len);
-               wsm_release_tx_buffer(priv, 1);
-               return -1; /* Error */
-       }
-
-       if (priv->wsm_enable_wsm_dumps)
-               print_hex_dump_bytes("--> ",
-                                    DUMP_PREFIX_NONE,
-                                    data,
-                                    __le16_to_cpu(wsm->len));
-
-       wsm_txed(priv, data);
-       priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
-
-       if (*tx_burst > 1) {
-               cw1200_debug_tx_burst(priv);
-               return 1; /* Work remains */
-       }
-
-       return 0;
-}
-
-static int cw1200_bh(void *arg)
-{
-       struct cw1200_common *priv = arg;
-       int rx, tx, term, suspend;
-       u16 ctrl_reg = 0;
-       int tx_allowed;
-       int pending_tx = 0;
-       int tx_burst;
-       long status;
-       u32 dummy;
-       int ret;
-
-       for (;;) {
-               if (!priv->hw_bufs_used &&
-                   priv->powersave_enabled &&
-                   !priv->device_can_sleep &&
-                   !atomic_read(&priv->recent_scan)) {
-                       status = 1 * HZ;
-                       pr_debug("[BH] Device wakedown. No data.\n");
-                       cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0);
-                       priv->device_can_sleep = true;
-               } else if (priv->hw_bufs_used) {
-                       /* Interrupt loss detection */
-                       status = 1 * HZ;
-               } else {
-                       status = MAX_SCHEDULE_TIMEOUT;
-               }
-
-               /* Dummy Read for SDIO retry mechanism*/
-               if ((priv->hw_type != -1) &&
-                   (atomic_read(&priv->bh_rx) == 0) &&
-                   (atomic_read(&priv->bh_tx) == 0))
-                       cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID,
-                                       &dummy, sizeof(dummy));
-
-               pr_debug("[BH] waiting ...\n");
-               status = wait_event_interruptible_timeout(priv->bh_wq, ({
-                               rx = atomic_xchg(&priv->bh_rx, 0);
-                               tx = atomic_xchg(&priv->bh_tx, 0);
-                               term = atomic_xchg(&priv->bh_term, 0);
-                               suspend = pending_tx ?
-                                       0 : atomic_read(&priv->bh_suspend);
-                               (rx || tx || term || suspend || priv->bh_error);
-                       }), status);
-
-               pr_debug("[BH] - rx: %d, tx: %d, term: %d, bh_err: %d, suspend: %d, status: %ld\n",
-                        rx, tx, term, suspend, priv->bh_error, status);
-
-               /* Did an error occur? */
-               if ((status < 0 && status != -ERESTARTSYS) ||
-                   term || priv->bh_error) {
-                       break;
-               }
-               if (!status) {  /* wait_event timed out */
-                       unsigned long timestamp = jiffies;
-                       long timeout;
-                       int pending = 0;
-                       int i;
-
-                       /* Check to see if we have any outstanding frames */
-                       if (priv->hw_bufs_used && (!rx || !tx)) {
-                               wiphy_warn(priv->hw->wiphy,
-                                          "Missed interrupt? (%d frames outstanding)\n",
-                                          priv->hw_bufs_used);
-                               rx = 1;
-
-                               /* Get a timestamp of "oldest" frame */
-                               for (i = 0; i < 4; ++i)
-                                       pending += cw1200_queue_get_xmit_timestamp(
-                                               &priv->tx_queue[i],
-                                               &timestamp,
-                                               priv->pending_frame_id);
-
-                               /* Check if frame transmission is timed out.
-                                * Add an extra second with respect to possible
-                                * interrupt loss.
-                                */
-                               timeout = timestamp +
-                                       WSM_CMD_LAST_CHANCE_TIMEOUT +
-                                       1 * HZ  -
-                                       jiffies;
-
-                               /* And terminate BH thread if the frame is "stuck" */
-                               if (pending && timeout < 0) {
-                                       wiphy_warn(priv->hw->wiphy,
-                                                  "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
-                                                  priv->hw_bufs_used, pending,
-                                                  timestamp, jiffies);
-                                       break;
-                               }
-                       } else if (!priv->device_can_sleep &&
-                                  !atomic_read(&priv->recent_scan)) {
-                               pr_debug("[BH] Device wakedown. Timeout.\n");
-                               cw1200_reg_write_16(priv,
-                                                   ST90TDS_CONTROL_REG_ID, 0);
-                               priv->device_can_sleep = true;
-                       }
-                       goto done;
-               } else if (suspend) {
-                       pr_debug("[BH] Device suspend.\n");
-                       if (priv->powersave_enabled) {
-                               pr_debug("[BH] Device wakedown. Suspend.\n");
-                               cw1200_reg_write_16(priv,
-                                                   ST90TDS_CONTROL_REG_ID, 0);
-                               priv->device_can_sleep = true;
-                       }
-
-                       atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
-                       wake_up(&priv->bh_evt_wq);
-                       status = wait_event_interruptible(priv->bh_wq,
-                                                         CW1200_BH_RESUME == atomic_read(&priv->bh_suspend));
-                       if (status < 0) {
-                               wiphy_err(priv->hw->wiphy,
-                                         "Failed to wait for resume: %ld.\n",
-                                         status);
-                               break;
-                       }
-                       pr_debug("[BH] Device resume.\n");
-                       atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
-                       wake_up(&priv->bh_evt_wq);
-                       atomic_add(1, &priv->bh_rx);
-                       goto done;
-               }
-
-       rx:
-               tx += pending_tx;
-               pending_tx = 0;
-
-               if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
-                       break;
-
-               /* Don't bother trying to rx unless we have data to read */
-               if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
-                       ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
-                       if (ret < 0)
-                               break;
-                       /* Double up here if there's more data.. */
-                       if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
-                               ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
-                               if (ret < 0)
-                                       break;
-                       }
-               }
-
-       tx:
-               if (tx) {
-                       tx = 0;
-
-                       BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers);
-                       tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used;
-                       tx_allowed = tx_burst > 0;
-
-                       if (!tx_allowed) {
-                               /* Buffers full.  Ensure we process tx
-                                * after we handle rx..
-                                */
-                               pending_tx = tx;
-                               goto done_rx;
-                       }
-                       ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst);
-                       if (ret < 0)
-                               break;
-                       if (ret > 0) /* More to transmit */
-                               tx = ret;
-
-                       /* Re-read ctrl reg */
-                       if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
-                               break;
-               }
-
-       done_rx:
-               if (priv->bh_error)
-                       break;
-               if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
-                       goto rx;
-               if (tx)
-                       goto tx;
-
-       done:
-               /* Re-enable device interrupts */
-               priv->hwbus_ops->lock(priv->hwbus_priv);
-               __cw1200_irq_enable(priv, 1);
-               priv->hwbus_ops->unlock(priv->hwbus_priv);
-       }
-
-       /* Explicitly disable device interrupts */
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-       __cw1200_irq_enable(priv, 0);
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-
-       if (!term) {
-               pr_err("[BH] Fatal error, exiting.\n");
-               priv->bh_error = 1;
-               /* TODO: schedule_work(recovery) */
-       }
-       return 0;
-}
diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/cw1200/bh.h
deleted file mode 100644 (file)
index af6a485..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef CW1200_BH_H
-#define CW1200_BH_H
-
-/* extern */ struct cw1200_common;
-
-int cw1200_register_bh(struct cw1200_common *priv);
-void cw1200_unregister_bh(struct cw1200_common *priv);
-void cw1200_irq_handler(struct cw1200_common *priv);
-void cw1200_bh_wakeup(struct cw1200_common *priv);
-int cw1200_bh_suspend(struct cw1200_common *priv);
-int cw1200_bh_resume(struct cw1200_common *priv);
-/* Must be called from BH thread. */
-void cw1200_enable_powersave(struct cw1200_common *priv,
-                            bool enable);
-int wsm_release_tx_buffer(struct cw1200_common *priv, int count);
-
-#endif /* CW1200_BH_H */
diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h
deleted file mode 100644 (file)
index 1ad7d36..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Common private data for ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * Based on the mac80211 Prism54 code, which is
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- *
- * Based on the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef CW1200_H
-#define CW1200_H
-
-#include <linux/wait.h>
-#include <linux/mutex.h>
-#include <linux/workqueue.h>
-#include <net/mac80211.h>
-
-#include "queue.h"
-#include "wsm.h"
-#include "scan.h"
-#include "txrx.h"
-#include "pm.h"
-
-/* Forward declarations */
-struct hwbus_ops;
-struct task_struct;
-struct cw1200_debug_priv;
-struct firmware;
-
-#define CW1200_MAX_CTRL_FRAME_LEN      (0x1000)
-
-#define CW1200_MAX_STA_IN_AP_MODE      (5)
-#define CW1200_LINK_ID_AFTER_DTIM      (CW1200_MAX_STA_IN_AP_MODE + 1)
-#define CW1200_LINK_ID_UAPSD           (CW1200_MAX_STA_IN_AP_MODE + 2)
-#define CW1200_LINK_ID_MAX             (CW1200_MAX_STA_IN_AP_MODE + 3)
-#define CW1200_MAX_REQUEUE_ATTEMPTS    (5)
-
-#define CW1200_MAX_TID                 (8)
-
-#define CW1200_BLOCK_ACK_CNT           (30)
-#define CW1200_BLOCK_ACK_THLD          (800)
-#define CW1200_BLOCK_ACK_HIST          (3)
-#define CW1200_BLOCK_ACK_INTERVAL      (1 * HZ / CW1200_BLOCK_ACK_HIST)
-
-#define CW1200_JOIN_TIMEOUT            (1 * HZ)
-#define CW1200_AUTH_TIMEOUT            (5 * HZ)
-
-struct cw1200_ht_info {
-       struct ieee80211_sta_ht_cap     ht_cap;
-       enum nl80211_channel_type       channel_type;
-       u16                             operation_mode;
-};
-
-/* Please keep order */
-enum cw1200_join_status {
-       CW1200_JOIN_STATUS_PASSIVE = 0,
-       CW1200_JOIN_STATUS_MONITOR,
-       CW1200_JOIN_STATUS_JOINING,
-       CW1200_JOIN_STATUS_PRE_STA,
-       CW1200_JOIN_STATUS_STA,
-       CW1200_JOIN_STATUS_IBSS,
-       CW1200_JOIN_STATUS_AP,
-};
-
-enum cw1200_link_status {
-       CW1200_LINK_OFF,
-       CW1200_LINK_RESERVE,
-       CW1200_LINK_SOFT,
-       CW1200_LINK_HARD,
-       CW1200_LINK_RESET,
-       CW1200_LINK_RESET_REMAP,
-};
-
-extern int cw1200_power_mode;
-extern const char * const cw1200_fw_types[];
-
-struct cw1200_link_entry {
-       unsigned long                   timestamp;
-       enum cw1200_link_status         status;
-       enum cw1200_link_status         prev_status;
-       u8                              mac[ETH_ALEN];
-       u8                              buffered[CW1200_MAX_TID];
-       struct sk_buff_head             rx_queue;
-};
-
-struct cw1200_common {
-       /* interfaces to the rest of the stack */
-       struct ieee80211_hw             *hw;
-       struct ieee80211_vif            *vif;
-       struct device                   *pdev;
-
-       /* Statistics */
-       struct ieee80211_low_level_stats stats;
-
-       /* Our macaddr */
-       u8 mac_addr[ETH_ALEN];
-
-       /* Hardware interface */
-       const struct hwbus_ops          *hwbus_ops;
-       struct hwbus_priv               *hwbus_priv;
-
-       /* Hardware information */
-       enum {
-               HIF_9000_SILICON_VERSATILE = 0,
-               HIF_8601_VERSATILE,
-               HIF_8601_SILICON,
-       } hw_type;
-       enum {
-               CW1200_HW_REV_CUT10 = 10,
-               CW1200_HW_REV_CUT11 = 11,
-               CW1200_HW_REV_CUT20 = 20,
-               CW1200_HW_REV_CUT22 = 22,
-               CW1X60_HW_REV       = 40,
-       } hw_revision;
-       int                             hw_refclk;
-       bool                            hw_have_5ghz;
-       const struct firmware           *sdd;
-       char                            *sdd_path;
-
-       struct cw1200_debug_priv        *debug;
-
-       struct workqueue_struct         *workqueue;
-       struct mutex                    conf_mutex;
-
-       struct cw1200_queue             tx_queue[4];
-       struct cw1200_queue_stats       tx_queue_stats;
-       int                             tx_burst_idx;
-
-       /* firmware/hardware info */
-       unsigned int tx_hdr_len;
-
-       /* Radio data */
-       int output_power;
-
-       /* BBP/MAC state */
-       struct ieee80211_rate           *rates;
-       struct ieee80211_rate           *mcs_rates;
-       struct ieee80211_channel        *channel;
-       struct wsm_edca_params          edca;
-       struct wsm_tx_queue_params      tx_queue_params;
-       struct wsm_mib_association_mode association_mode;
-       struct wsm_set_bss_params       bss_params;
-       struct cw1200_ht_info           ht_info;
-       struct wsm_set_pm               powersave_mode;
-       struct wsm_set_pm               firmware_ps_mode;
-       int                             cqm_rssi_thold;
-       unsigned                        cqm_rssi_hyst;
-       bool                            cqm_use_rssi;
-       int                             cqm_beacon_loss_count;
-       int                             channel_switch_in_progress;
-       wait_queue_head_t               channel_switch_done;
-       u8                              long_frame_max_tx_count;
-       u8                              short_frame_max_tx_count;
-       int                             mode;
-       bool                            enable_beacon;
-       int                             beacon_int;
-       bool                            listening;
-       struct wsm_rx_filter            rx_filter;
-       struct wsm_mib_multicast_filter multicast_filter;
-       bool                            has_multicast_subscription;
-       bool                            disable_beacon_filter;
-       struct work_struct              update_filtering_work;
-       struct work_struct              set_beacon_wakeup_period_work;
-
-       u8                              ba_rx_tid_mask;
-       u8                              ba_tx_tid_mask;
-
-       struct cw1200_pm_state          pm_state;
-
-       struct wsm_p2p_ps_modeinfo      p2p_ps_modeinfo;
-       struct wsm_uapsd_info           uapsd_info;
-       bool                            setbssparams_done;
-       bool                            bt_present;
-       u8                              conf_listen_interval;
-       u32                             listen_interval;
-       u32                             erp_info;
-       u32                             rts_threshold;
-
-       /* BH */
-       atomic_t                        bh_rx;
-       atomic_t                        bh_tx;
-       atomic_t                        bh_term;
-       atomic_t                        bh_suspend;
-
-       struct workqueue_struct         *bh_workqueue;
-       struct work_struct              bh_work;
-
-       int                             bh_error;
-       wait_queue_head_t               bh_wq;
-       wait_queue_head_t               bh_evt_wq;
-       u8                              buf_id_tx;
-       u8                              buf_id_rx;
-       u8                              wsm_rx_seq;
-       u8                              wsm_tx_seq;
-       int                             hw_bufs_used;
-       bool                            powersave_enabled;
-       bool                            device_can_sleep;
-
-       /* Scan status */
-       struct cw1200_scan scan;
-       /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid
-        * FW issue with sleeping/waking up.
-        */
-       atomic_t                        recent_scan;
-       struct delayed_work             clear_recent_scan_work;
-
-       /* WSM */
-       struct wsm_startup_ind          wsm_caps;
-       struct mutex                    wsm_cmd_mux;
-       struct wsm_buf                  wsm_cmd_buf;
-       struct wsm_cmd                  wsm_cmd;
-       wait_queue_head_t               wsm_cmd_wq;
-       wait_queue_head_t               wsm_startup_done;
-       int                             firmware_ready;
-       atomic_t                        tx_lock;
-
-       /* WSM debug */
-       int                             wsm_enable_wsm_dumps;
-
-       /* WSM Join */
-       enum cw1200_join_status join_status;
-       u32                     pending_frame_id;
-       bool                    join_pending;
-       struct delayed_work     join_timeout;
-       struct work_struct      unjoin_work;
-       struct work_struct      join_complete_work;
-       int                     join_complete_status;
-       int                     join_dtim_period;
-       bool                    delayed_unjoin;
-
-       /* TX/RX and security */
-       s8                      wep_default_key_id;
-       struct work_struct      wep_key_work;
-       u32                     key_map;
-       struct wsm_add_key      keys[WSM_KEY_MAX_INDEX + 1];
-
-       /* AP powersave */
-       u32                     link_id_map;
-       struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE];
-       struct work_struct      link_id_work;
-       struct delayed_work     link_id_gc_work;
-       u32                     sta_asleep_mask;
-       u32                     pspoll_mask;
-       bool                    aid0_bit_set;
-       spinlock_t              ps_state_lock; /* Protect power save state */
-       bool                    buffered_multicasts;
-       bool                    tx_multicast;
-       struct work_struct      set_tim_work;
-       struct work_struct      set_cts_work;
-       struct work_struct      multicast_start_work;
-       struct work_struct      multicast_stop_work;
-       struct timer_list       mcast_timeout;
-
-       /* WSM events and CQM implementation */
-       spinlock_t              event_queue_lock; /* Protect event queue */
-       struct list_head        event_queue;
-       struct work_struct      event_handler;
-
-       struct delayed_work     bss_loss_work;
-       spinlock_t              bss_loss_lock; /* Protect BSS loss state */
-       int                     bss_loss_state;
-       u32                     bss_loss_confirm_id;
-       int                     delayed_link_loss;
-       struct work_struct      bss_params_work;
-
-       /* TX rate policy cache */
-       struct tx_policy_cache tx_policy_cache;
-       struct work_struct tx_policy_upload_work;
-
-       /* legacy PS mode switch in suspend */
-       int                     ps_mode_switch_in_progress;
-       wait_queue_head_t       ps_mode_switch_done;
-
-       /* Workaround for WFD testcase 6.1.10*/
-       struct work_struct      linkid_reset_work;
-       u8                      action_frame_sa[ETH_ALEN];
-       u8                      action_linkid;
-};
-
-struct cw1200_sta_priv {
-       int link_id;
-};
-
-/* interfaces for the drivers */
-int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
-                     struct hwbus_priv *hwbus,
-                     struct device *pdev,
-                     struct cw1200_common **pself,
-                     int ref_clk, const u8 *macaddr,
-                     const char *sdd_path, bool have_5ghz);
-void cw1200_core_release(struct cw1200_common *self);
-
-#define FWLOAD_BLOCK_SIZE (1024)
-
-static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info)
-{
-       return ht_info->channel_type != NL80211_CHAN_NO_HT;
-}
-
-static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info)
-{
-       return cw1200_is_ht(ht_info) &&
-               (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
-               !(ht_info->operation_mode &
-                 IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
-}
-
-static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info)
-{
-       if (!cw1200_is_ht(ht_info))
-               return 0;
-       return ht_info->ht_cap.ampdu_density;
-}
-
-#endif /* CW1200_H */
diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c
deleted file mode 100644 (file)
index d3acc85..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Mac80211 SDIO driver for ST-Ericsson CW1200 device
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/sdio.h>
-#include <net/mac80211.h>
-
-#include "cw1200.h"
-#include "hwbus.h"
-#include <linux/platform_data/net-cw1200.h>
-#include "hwio.h"
-
-MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
-MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
-MODULE_LICENSE("GPL");
-
-#define SDIO_BLOCK_SIZE (512)
-
-/* Default platform data for Sagrad modules */
-static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = {
-       .ref_clk = 38400,
-       .have_5ghz = false,
-       .sdd_file = "sdd_sagrad_1091_1098.bin",
-};
-
-/* Allow platform data to be overridden */
-static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data;
-
-void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata)
-{
-       global_plat_data = pdata;
-}
-
-struct hwbus_priv {
-       struct sdio_func        *func;
-       struct cw1200_common    *core;
-       const struct cw1200_platform_data_sdio *pdata;
-};
-
-#ifndef SDIO_VENDOR_ID_STE
-#define SDIO_VENDOR_ID_STE             0x0020
-#endif
-
-#ifndef SDIO_DEVICE_ID_STE_CW1200
-#define SDIO_DEVICE_ID_STE_CW1200      0x2280
-#endif
-
-static const struct sdio_device_id cw1200_sdio_ids[] = {
-       { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
-       { /* end: all zeroes */                 },
-};
-
-/* hwbus_ops implemetation */
-
-static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self,
-                                    unsigned int addr,
-                                    void *dst, int count)
-{
-       return sdio_memcpy_fromio(self->func, dst, addr, count);
-}
-
-static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self,
-                                  unsigned int addr,
-                                  const void *src, int count)
-{
-       return sdio_memcpy_toio(self->func, addr, (void *)src, count);
-}
-
-static void cw1200_sdio_lock(struct hwbus_priv *self)
-{
-       sdio_claim_host(self->func);
-}
-
-static void cw1200_sdio_unlock(struct hwbus_priv *self)
-{
-       sdio_release_host(self->func);
-}
-
-static void cw1200_sdio_irq_handler(struct sdio_func *func)
-{
-       struct hwbus_priv *self = sdio_get_drvdata(func);
-
-       /* note:  sdio_host already claimed here. */
-       if (self->core)
-               cw1200_irq_handler(self->core);
-}
-
-static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
-{
-       return IRQ_WAKE_THREAD;
-}
-
-static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
-{
-       struct hwbus_priv *self = dev_id;
-
-       if (self->core) {
-               cw1200_sdio_lock(self);
-               cw1200_irq_handler(self->core);
-               cw1200_sdio_unlock(self);
-               return IRQ_HANDLED;
-       } else {
-               return IRQ_NONE;
-       }
-}
-
-static int cw1200_request_irq(struct hwbus_priv *self)
-{
-       int ret;
-       u8 cccr;
-
-       cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
-       if (WARN_ON(ret))
-               goto err;
-
-       /* Master interrupt enable ... */
-       cccr |= BIT(0);
-
-       /* ... for our function */
-       cccr |= BIT(self->func->num);
-
-       sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
-       if (WARN_ON(ret))
-               goto err;
-
-       ret = enable_irq_wake(self->pdata->irq);
-       if (WARN_ON(ret))
-               goto err;
-
-       /* Request the IRQ */
-       ret =  request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq,
-                                   cw1200_gpio_irq,
-                                   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
-                                   "cw1200_wlan_irq", self);
-       if (WARN_ON(ret))
-               goto err;
-
-       return 0;
-
-err:
-       return ret;
-}
-
-static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self)
-{
-       int ret = 0;
-
-       pr_debug("SW IRQ subscribe\n");
-       sdio_claim_host(self->func);
-       if (self->pdata->irq)
-               ret = cw1200_request_irq(self);
-       else
-               ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
-
-       sdio_release_host(self->func);
-       return ret;
-}
-
-static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self)
-{
-       int ret = 0;
-
-       pr_debug("SW IRQ unsubscribe\n");
-
-       if (self->pdata->irq) {
-               disable_irq_wake(self->pdata->irq);
-               free_irq(self->pdata->irq, self);
-       } else {
-               sdio_claim_host(self->func);
-               ret = sdio_release_irq(self->func);
-               sdio_release_host(self->func);
-       }
-       return ret;
-}
-
-static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
-{
-       if (pdata->reset) {
-               gpio_set_value(pdata->reset, 0);
-               msleep(30); /* Min is 2 * CLK32K cycles */
-               gpio_free(pdata->reset);
-       }
-
-       if (pdata->power_ctrl)
-               pdata->power_ctrl(pdata, false);
-       if (pdata->clk_ctrl)
-               pdata->clk_ctrl(pdata, false);
-
-       return 0;
-}
-
-static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
-{
-       /* Ensure I/Os are pulled low */
-       if (pdata->reset) {
-               gpio_request(pdata->reset, "cw1200_wlan_reset");
-               gpio_direction_output(pdata->reset, 0);
-       }
-       if (pdata->powerup) {
-               gpio_request(pdata->powerup, "cw1200_wlan_powerup");
-               gpio_direction_output(pdata->powerup, 0);
-       }
-       if (pdata->reset || pdata->powerup)
-               msleep(10); /* Settle time? */
-
-       /* Enable 3v3 and 1v8 to hardware */
-       if (pdata->power_ctrl) {
-               if (pdata->power_ctrl(pdata, true)) {
-                       pr_err("power_ctrl() failed!\n");
-                       return -1;
-               }
-       }
-
-       /* Enable CLK32K */
-       if (pdata->clk_ctrl) {
-               if (pdata->clk_ctrl(pdata, true)) {
-                       pr_err("clk_ctrl() failed!\n");
-                       return -1;
-               }
-               msleep(10); /* Delay until clock is stable for 2 cycles */
-       }
-
-       /* Enable POWERUP signal */
-       if (pdata->powerup) {
-               gpio_set_value(pdata->powerup, 1);
-               msleep(250); /* or more..? */
-       }
-       /* Enable RSTn signal */
-       if (pdata->reset) {
-               gpio_set_value(pdata->reset, 1);
-               msleep(50); /* Or more..? */
-       }
-       return 0;
-}
-
-static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size)
-{
-       if (self->pdata->no_nptb)
-               size = round_up(size, SDIO_BLOCK_SIZE);
-       else
-               size = sdio_align_size(self->func, size);
-
-       return size;
-}
-
-static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend)
-{
-       int ret = 0;
-
-       if (self->pdata->irq)
-               ret = irq_set_irq_wake(self->pdata->irq, suspend);
-       return ret;
-}
-
-static struct hwbus_ops cw1200_sdio_hwbus_ops = {
-       .hwbus_memcpy_fromio    = cw1200_sdio_memcpy_fromio,
-       .hwbus_memcpy_toio      = cw1200_sdio_memcpy_toio,
-       .lock                   = cw1200_sdio_lock,
-       .unlock                 = cw1200_sdio_unlock,
-       .align_size             = cw1200_sdio_align_size,
-       .power_mgmt             = cw1200_sdio_pm,
-};
-
-/* Probe Function to be called by SDIO stack when device is discovered */
-static int cw1200_sdio_probe(struct sdio_func *func,
-                            const struct sdio_device_id *id)
-{
-       struct hwbus_priv *self;
-       int status;
-
-       pr_info("cw1200_wlan_sdio: Probe called\n");
-
-       /* We are only able to handle the wlan function */
-       if (func->num != 0x01)
-               return -ENODEV;
-
-       self = kzalloc(sizeof(*self), GFP_KERNEL);
-       if (!self) {
-               pr_err("Can't allocate SDIO hwbus_priv.\n");
-               return -ENOMEM;
-       }
-
-       func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
-
-       self->pdata = global_plat_data; /* FIXME */
-       self->func = func;
-       sdio_set_drvdata(func, self);
-       sdio_claim_host(func);
-       sdio_enable_func(func);
-       sdio_release_host(func);
-
-       status = cw1200_sdio_irq_subscribe(self);
-
-       status = cw1200_core_probe(&cw1200_sdio_hwbus_ops,
-                                  self, &func->dev, &self->core,
-                                  self->pdata->ref_clk,
-                                  self->pdata->macaddr,
-                                  self->pdata->sdd_file,
-                                  self->pdata->have_5ghz);
-       if (status) {
-               cw1200_sdio_irq_unsubscribe(self);
-               sdio_claim_host(func);
-               sdio_disable_func(func);
-               sdio_release_host(func);
-               sdio_set_drvdata(func, NULL);
-               kfree(self);
-       }
-
-       return status;
-}
-
-/* Disconnect Function to be called by SDIO stack when
- * device is disconnected
- */
-static void cw1200_sdio_disconnect(struct sdio_func *func)
-{
-       struct hwbus_priv *self = sdio_get_drvdata(func);
-
-       if (self) {
-               cw1200_sdio_irq_unsubscribe(self);
-               if (self->core) {
-                       cw1200_core_release(self->core);
-                       self->core = NULL;
-               }
-               sdio_claim_host(func);
-               sdio_disable_func(func);
-               sdio_release_host(func);
-               sdio_set_drvdata(func, NULL);
-               kfree(self);
-       }
-}
-
-#ifdef CONFIG_PM
-static int cw1200_sdio_suspend(struct device *dev)
-{
-       int ret;
-       struct sdio_func *func = dev_to_sdio_func(dev);
-       struct hwbus_priv *self = sdio_get_drvdata(func);
-
-       if (!cw1200_can_suspend(self->core))
-               return -EAGAIN;
-
-       /* Notify SDIO that CW1200 will remain powered during suspend */
-       ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
-       if (ret)
-               pr_err("Error setting SDIO pm flags: %i\n", ret);
-
-       return ret;
-}
-
-static int cw1200_sdio_resume(struct device *dev)
-{
-       return 0;
-}
-
-static const struct dev_pm_ops cw1200_pm_ops = {
-       .suspend = cw1200_sdio_suspend,
-       .resume = cw1200_sdio_resume,
-};
-#endif
-
-static struct sdio_driver sdio_driver = {
-       .name           = "cw1200_wlan_sdio",
-       .id_table       = cw1200_sdio_ids,
-       .probe          = cw1200_sdio_probe,
-       .remove         = cw1200_sdio_disconnect,
-#ifdef CONFIG_PM
-       .drv = {
-               .pm = &cw1200_pm_ops,
-       }
-#endif
-};
-
-/* Init Module function -> Called by insmod */
-static int __init cw1200_sdio_init(void)
-{
-       const struct cw1200_platform_data_sdio *pdata;
-       int ret;
-
-       /* FIXME -- this won't support multiple devices */
-       pdata = global_plat_data;
-
-       if (cw1200_sdio_on(pdata)) {
-               ret = -1;
-               goto err;
-       }
-
-       ret = sdio_register_driver(&sdio_driver);
-       if (ret)
-               goto err;
-
-       return 0;
-
-err:
-       cw1200_sdio_off(pdata);
-       return ret;
-}
-
-/* Called at Driver Unloading */
-static void __exit cw1200_sdio_exit(void)
-{
-       const struct cw1200_platform_data_sdio *pdata;
-
-       /* FIXME -- this won't support multiple devices */
-       pdata = global_plat_data;
-       sdio_unregister_driver(&sdio_driver);
-       cw1200_sdio_off(pdata);
-}
-
-
-module_init(cw1200_sdio_init);
-module_exit(cw1200_sdio_exit);
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c
deleted file mode 100644 (file)
index a740083..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * Mac80211 SPI driver for ST-Ericsson CW1200 device
- *
- * Copyright (c) 2011, Sagrad Inc.
- * Author:  Solomon Peachy <speachy@sagrad.com>
- *
- * Based on cw1200_sdio.c
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <net/mac80211.h>
-
-#include <linux/spi/spi.h>
-#include <linux/device.h>
-
-#include "cw1200.h"
-#include "hwbus.h"
-#include <linux/platform_data/net-cw1200.h>
-#include "hwio.h"
-
-MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
-MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("spi:cw1200_wlan_spi");
-
-/* #define SPI_DEBUG */
-
-struct hwbus_priv {
-       struct spi_device       *func;
-       struct cw1200_common    *core;
-       const struct cw1200_platform_data_spi *pdata;
-       spinlock_t              lock; /* Serialize all bus operations */
-       wait_queue_head_t       wq;
-       int claimed;
-};
-
-#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
-#define SET_WRITE 0x7FFF /* usage: and operation */
-#define SET_READ 0x8000  /* usage: or operation */
-
-/* Notes on byte ordering:
-   LE:  B0 B1 B2 B3
-   BE:  B3 B2 B1 B0
-
-   Hardware expects 32-bit data to be written as 16-bit BE words:
-
-   B1 B0 B3 B2
-*/
-
-static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self,
-                                    unsigned int addr,
-                                    void *dst, int count)
-{
-       int ret, i;
-       u16 regaddr;
-       struct spi_message      m;
-
-       struct spi_transfer     t_addr = {
-               .tx_buf         = &regaddr,
-               .len            = sizeof(regaddr),
-       };
-       struct spi_transfer     t_msg = {
-               .rx_buf         = dst,
-               .len            = count,
-       };
-
-       regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
-       regaddr |= SET_READ;
-       regaddr |= (count>>1);
-
-#ifdef SPI_DEBUG
-       pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr);
-#endif
-
-       /* Header is LE16 */
-       regaddr = cpu_to_le16(regaddr);
-
-       /* We have to byteswap if the SPI bus is limited to 8b operation
-          or we are running on a Big Endian system
-       */
-#if defined(__LITTLE_ENDIAN)
-       if (self->func->bits_per_word == 8)
-#endif
-               regaddr = swab16(regaddr);
-
-       spi_message_init(&m);
-       spi_message_add_tail(&t_addr, &m);
-       spi_message_add_tail(&t_msg, &m);
-       ret = spi_sync(self->func, &m);
-
-#ifdef SPI_DEBUG
-       pr_info("READ : ");
-       for (i = 0; i < t_addr.len; i++)
-               printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
-       printk(" : ");
-       for (i = 0; i < t_msg.len; i++)
-               printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
-       printk("\n");
-#endif
-
-       /* We have to byteswap if the SPI bus is limited to 8b operation
-          or we are running on a Big Endian system
-       */
-#if defined(__LITTLE_ENDIAN)
-       if (self->func->bits_per_word == 8)
-#endif
-       {
-               uint16_t *buf = (uint16_t *)dst;
-               for (i = 0; i < ((count + 1) >> 1); i++)
-                       buf[i] = swab16(buf[i]);
-       }
-
-       return ret;
-}
-
-static int cw1200_spi_memcpy_toio(struct hwbus_priv *self,
-                                  unsigned int addr,
-                                  const void *src, int count)
-{
-       int rval, i;
-       u16 regaddr;
-       struct spi_transfer     t_addr = {
-               .tx_buf         = &regaddr,
-               .len            = sizeof(regaddr),
-       };
-       struct spi_transfer     t_msg = {
-               .tx_buf         = src,
-               .len            = count,
-       };
-       struct spi_message      m;
-
-       regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
-       regaddr &= SET_WRITE;
-       regaddr |= (count>>1);
-
-#ifdef SPI_DEBUG
-       pr_info("WRITE: %04d  to  0x%02x (%04x)\n", count, addr, regaddr);
-#endif
-
-       /* Header is LE16 */
-       regaddr = cpu_to_le16(regaddr);
-
-       /* We have to byteswap if the SPI bus is limited to 8b operation
-          or we are running on a Big Endian system
-       */
-#if defined(__LITTLE_ENDIAN)
-       if (self->func->bits_per_word == 8)
-#endif
-       {
-               uint16_t *buf = (uint16_t *)src;
-               regaddr = swab16(regaddr);
-               for (i = 0; i < ((count + 1) >> 1); i++)
-                       buf[i] = swab16(buf[i]);
-       }
-
-#ifdef SPI_DEBUG
-       pr_info("WRITE: ");
-       for (i = 0; i < t_addr.len; i++)
-               printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
-       printk(" : ");
-       for (i = 0; i < t_msg.len; i++)
-               printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
-       printk("\n");
-#endif
-
-       spi_message_init(&m);
-       spi_message_add_tail(&t_addr, &m);
-       spi_message_add_tail(&t_msg, &m);
-       rval = spi_sync(self->func, &m);
-
-#ifdef SPI_DEBUG
-       pr_info("WROTE: %d\n", m.actual_length);
-#endif
-
-#if defined(__LITTLE_ENDIAN)
-       /* We have to byteswap if the SPI bus is limited to 8b operation */
-       if (self->func->bits_per_word == 8)
-#endif
-       {
-               uint16_t *buf = (uint16_t *)src;
-               for (i = 0; i < ((count + 1) >> 1); i++)
-                       buf[i] = swab16(buf[i]);
-       }
-       return rval;
-}
-
-static void cw1200_spi_lock(struct hwbus_priv *self)
-{
-       unsigned long flags;
-
-       DECLARE_WAITQUEUE(wait, current);
-
-       might_sleep();
-
-       add_wait_queue(&self->wq, &wait);
-       spin_lock_irqsave(&self->lock, flags);
-       while (1) {
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               if (!self->claimed)
-                       break;
-               spin_unlock_irqrestore(&self->lock, flags);
-               schedule();
-               spin_lock_irqsave(&self->lock, flags);
-       }
-       set_current_state(TASK_RUNNING);
-       self->claimed = 1;
-       spin_unlock_irqrestore(&self->lock, flags);
-       remove_wait_queue(&self->wq, &wait);
-
-       return;
-}
-
-static void cw1200_spi_unlock(struct hwbus_priv *self)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&self->lock, flags);
-       self->claimed = 0;
-       spin_unlock_irqrestore(&self->lock, flags);
-       wake_up(&self->wq);
-
-       return;
-}
-
-static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
-{
-       struct hwbus_priv *self = dev_id;
-
-       if (self->core) {
-               cw1200_spi_lock(self);
-               cw1200_irq_handler(self->core);
-               cw1200_spi_unlock(self);
-               return IRQ_HANDLED;
-       } else {
-               return IRQ_NONE;
-       }
-}
-
-static int cw1200_spi_irq_subscribe(struct hwbus_priv *self)
-{
-       int ret;
-
-       pr_debug("SW IRQ subscribe\n");
-
-       ret = request_threaded_irq(self->func->irq, NULL,
-                                  cw1200_spi_irq_handler,
-                                  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
-                                  "cw1200_wlan_irq", self);
-       if (WARN_ON(ret < 0))
-               goto exit;
-
-       ret = enable_irq_wake(self->func->irq);
-       if (WARN_ON(ret))
-               goto free_irq;
-
-       return 0;
-
-free_irq:
-       free_irq(self->func->irq, self);
-exit:
-       return ret;
-}
-
-static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
-{
-       int ret = 0;
-
-       pr_debug("SW IRQ unsubscribe\n");
-       disable_irq_wake(self->func->irq);
-       free_irq(self->func->irq, self);
-
-       return ret;
-}
-
-static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
-{
-       if (pdata->reset) {
-               gpio_set_value(pdata->reset, 0);
-               msleep(30); /* Min is 2 * CLK32K cycles */
-               gpio_free(pdata->reset);
-       }
-
-       if (pdata->power_ctrl)
-               pdata->power_ctrl(pdata, false);
-       if (pdata->clk_ctrl)
-               pdata->clk_ctrl(pdata, false);
-
-       return 0;
-}
-
-static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
-{
-       /* Ensure I/Os are pulled low */
-       if (pdata->reset) {
-               gpio_request(pdata->reset, "cw1200_wlan_reset");
-               gpio_direction_output(pdata->reset, 0);
-       }
-       if (pdata->powerup) {
-               gpio_request(pdata->powerup, "cw1200_wlan_powerup");
-               gpio_direction_output(pdata->powerup, 0);
-       }
-       if (pdata->reset || pdata->powerup)
-               msleep(10); /* Settle time? */
-
-       /* Enable 3v3 and 1v8 to hardware */
-       if (pdata->power_ctrl) {
-               if (pdata->power_ctrl(pdata, true)) {
-                       pr_err("power_ctrl() failed!\n");
-                       return -1;
-               }
-       }
-
-       /* Enable CLK32K */
-       if (pdata->clk_ctrl) {
-               if (pdata->clk_ctrl(pdata, true)) {
-                       pr_err("clk_ctrl() failed!\n");
-                       return -1;
-               }
-               msleep(10); /* Delay until clock is stable for 2 cycles */
-       }
-
-       /* Enable POWERUP signal */
-       if (pdata->powerup) {
-               gpio_set_value(pdata->powerup, 1);
-               msleep(250); /* or more..? */
-       }
-       /* Enable RSTn signal */
-       if (pdata->reset) {
-               gpio_set_value(pdata->reset, 1);
-               msleep(50); /* Or more..? */
-       }
-       return 0;
-}
-
-static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size)
-{
-       return size & 1 ? size + 1 : size;
-}
-
-static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend)
-{
-       return irq_set_irq_wake(self->func->irq, suspend);
-}
-
-static struct hwbus_ops cw1200_spi_hwbus_ops = {
-       .hwbus_memcpy_fromio    = cw1200_spi_memcpy_fromio,
-       .hwbus_memcpy_toio      = cw1200_spi_memcpy_toio,
-       .lock                   = cw1200_spi_lock,
-       .unlock                 = cw1200_spi_unlock,
-       .align_size             = cw1200_spi_align_size,
-       .power_mgmt             = cw1200_spi_pm,
-};
-
-/* Probe Function to be called by SPI stack when device is discovered */
-static int cw1200_spi_probe(struct spi_device *func)
-{
-       const struct cw1200_platform_data_spi *plat_data =
-               dev_get_platdata(&func->dev);
-       struct hwbus_priv *self;
-       int status;
-
-       /* Sanity check speed */
-       if (func->max_speed_hz > 52000000)
-               func->max_speed_hz = 52000000;
-       if (func->max_speed_hz < 1000000)
-               func->max_speed_hz = 1000000;
-
-       /* Fix up transfer size */
-       if (plat_data->spi_bits_per_word)
-               func->bits_per_word = plat_data->spi_bits_per_word;
-       if (!func->bits_per_word)
-               func->bits_per_word = 16;
-
-       /* And finally.. */
-       func->mode = SPI_MODE_0;
-
-       pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
-               func->chip_select, func->mode, func->bits_per_word,
-               func->max_speed_hz);
-
-       if (cw1200_spi_on(plat_data)) {
-               pr_err("spi_on() failed!\n");
-               return -1;
-       }
-
-       if (spi_setup(func)) {
-               pr_err("spi_setup() failed!\n");
-               return -1;
-       }
-
-       self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL);
-       if (!self) {
-               pr_err("Can't allocate SPI hwbus_priv.");
-               return -ENOMEM;
-       }
-
-       self->pdata = plat_data;
-       self->func = func;
-       spin_lock_init(&self->lock);
-
-       spi_set_drvdata(func, self);
-
-       init_waitqueue_head(&self->wq);
-
-       status = cw1200_spi_irq_subscribe(self);
-
-       status = cw1200_core_probe(&cw1200_spi_hwbus_ops,
-                                  self, &func->dev, &self->core,
-                                  self->pdata->ref_clk,
-                                  self->pdata->macaddr,
-                                  self->pdata->sdd_file,
-                                  self->pdata->have_5ghz);
-
-       if (status) {
-               cw1200_spi_irq_unsubscribe(self);
-               cw1200_spi_off(plat_data);
-       }
-
-       return status;
-}
-
-/* Disconnect Function to be called by SPI stack when device is disconnected */
-static int cw1200_spi_disconnect(struct spi_device *func)
-{
-       struct hwbus_priv *self = spi_get_drvdata(func);
-
-       if (self) {
-               cw1200_spi_irq_unsubscribe(self);
-               if (self->core) {
-                       cw1200_core_release(self->core);
-                       self->core = NULL;
-               }
-       }
-       cw1200_spi_off(dev_get_platdata(&func->dev));
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int cw1200_spi_suspend(struct device *dev)
-{
-       struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
-
-       if (!cw1200_can_suspend(self->core))
-               return -EAGAIN;
-
-       /* XXX notify host that we have to keep CW1200 powered on? */
-       return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL);
-
-#endif
-
-static struct spi_driver spi_driver = {
-       .probe          = cw1200_spi_probe,
-       .remove         = cw1200_spi_disconnect,
-       .driver = {
-               .name           = "cw1200_wlan_spi",
-#ifdef CONFIG_PM
-               .pm             = &cw1200_pm_ops,
-#endif
-       },
-};
-
-module_spi_driver(spi_driver);
diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c
deleted file mode 100644 (file)
index 34f97c3..0000000
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
- * DebugFS code
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include "cw1200.h"
-#include "debug.h"
-#include "fwio.h"
-
-/* join_status */
-static const char * const cw1200_debug_join_status[] = {
-       "passive",
-       "monitor",
-       "station (joining)",
-       "station (not authenticated yet)",
-       "station",
-       "adhoc",
-       "access point",
-};
-
-/* WSM_JOIN_PREAMBLE_... */
-static const char * const cw1200_debug_preamble[] = {
-       "long",
-       "short",
-       "long on 1 and 2 Mbps",
-};
-
-
-static const char * const cw1200_debug_link_id[] = {
-       "OFF",
-       "REQ",
-       "SOFT",
-       "HARD",
-       "RESET",
-       "RESET_REMAP",
-};
-
-static const char *cw1200_debug_mode(int mode)
-{
-       switch (mode) {
-       case NL80211_IFTYPE_UNSPECIFIED:
-               return "unspecified";
-       case NL80211_IFTYPE_MONITOR:
-               return "monitor";
-       case NL80211_IFTYPE_STATION:
-               return "station";
-       case NL80211_IFTYPE_ADHOC:
-               return "adhoc";
-       case NL80211_IFTYPE_MESH_POINT:
-               return "mesh point";
-       case NL80211_IFTYPE_AP:
-               return "access point";
-       case NL80211_IFTYPE_P2P_CLIENT:
-               return "p2p client";
-       case NL80211_IFTYPE_P2P_GO:
-               return "p2p go";
-       default:
-               return "unsupported";
-       }
-}
-
-static void cw1200_queue_status_show(struct seq_file *seq,
-                                    struct cw1200_queue *q)
-{
-       int i;
-       seq_printf(seq, "Queue       %d:\n", q->queue_id);
-       seq_printf(seq, "  capacity: %zu\n", q->capacity);
-       seq_printf(seq, "  queued:   %zu\n", q->num_queued);
-       seq_printf(seq, "  pending:  %zu\n", q->num_pending);
-       seq_printf(seq, "  sent:     %zu\n", q->num_sent);
-       seq_printf(seq, "  locked:   %s\n", q->tx_locked_cnt ? "yes" : "no");
-       seq_printf(seq, "  overfull: %s\n", q->overfull ? "yes" : "no");
-       seq_puts(seq,   "  link map: 0-> ");
-       for (i = 0; i < q->stats->map_capacity; ++i)
-               seq_printf(seq, "%.2d ", q->link_map_cache[i]);
-       seq_printf(seq, "<-%zu\n", q->stats->map_capacity);
-}
-
-static void cw1200_debug_print_map(struct seq_file *seq,
-                                  struct cw1200_common *priv,
-                                  const char *label,
-                                  u32 map)
-{
-       int i;
-       seq_printf(seq, "%s0-> ", label);
-       for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i)
-               seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
-       seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1);
-}
-
-static int cw1200_status_show(struct seq_file *seq, void *v)
-{
-       int i;
-       struct list_head *item;
-       struct cw1200_common *priv = seq->private;
-       struct cw1200_debug_priv *d = priv->debug;
-
-       seq_puts(seq,   "CW1200 Wireless LAN driver status\n");
-       seq_printf(seq, "Hardware:   %d.%d\n",
-                  priv->wsm_caps.hw_id,
-                  priv->wsm_caps.hw_subid);
-       seq_printf(seq, "Firmware:   %s %d.%d\n",
-                  cw1200_fw_types[priv->wsm_caps.fw_type],
-                  priv->wsm_caps.fw_ver,
-                  priv->wsm_caps.fw_build);
-       seq_printf(seq, "FW API:     %d\n",
-                  priv->wsm_caps.fw_api);
-       seq_printf(seq, "FW caps:    0x%.4X\n",
-                  priv->wsm_caps.fw_cap);
-       seq_printf(seq, "FW label:  '%s'\n",
-                  priv->wsm_caps.fw_label);
-       seq_printf(seq, "Mode:       %s%s\n",
-                  cw1200_debug_mode(priv->mode),
-                  priv->listening ? " (listening)" : "");
-       seq_printf(seq, "Join state: %s\n",
-                  cw1200_debug_join_status[priv->join_status]);
-       if (priv->channel)
-               seq_printf(seq, "Channel:    %d%s\n",
-                          priv->channel->hw_value,
-                          priv->channel_switch_in_progress ?
-                          " (switching)" : "");
-       if (priv->rx_filter.promiscuous)
-               seq_puts(seq,   "Filter:     promisc\n");
-       else if (priv->rx_filter.fcs)
-               seq_puts(seq,   "Filter:     fcs\n");
-       if (priv->rx_filter.bssid)
-               seq_puts(seq,   "Filter:     bssid\n");
-       if (!priv->disable_beacon_filter)
-               seq_puts(seq,   "Filter:     beacons\n");
-
-       if (priv->enable_beacon ||
-           priv->mode == NL80211_IFTYPE_AP ||
-           priv->mode == NL80211_IFTYPE_ADHOC ||
-           priv->mode == NL80211_IFTYPE_MESH_POINT ||
-           priv->mode == NL80211_IFTYPE_P2P_GO)
-               seq_printf(seq, "Beaconing:  %s\n",
-                          priv->enable_beacon ?
-                          "enabled" : "disabled");
-
-       for (i = 0; i < 4; ++i)
-               seq_printf(seq, "EDCA(%d):    %d, %d, %d, %d, %d\n", i,
-                          priv->edca.params[i].cwmin,
-                          priv->edca.params[i].cwmax,
-                          priv->edca.params[i].aifns,
-                          priv->edca.params[i].txop_limit,
-                          priv->edca.params[i].max_rx_lifetime);
-
-       if (priv->join_status == CW1200_JOIN_STATUS_STA) {
-               static const char *pm_mode = "unknown";
-               switch (priv->powersave_mode.mode) {
-               case WSM_PSM_ACTIVE:
-                       pm_mode = "off";
-                       break;
-               case WSM_PSM_PS:
-                       pm_mode = "on";
-                       break;
-               case WSM_PSM_FAST_PS:
-                       pm_mode = "dynamic";
-                       break;
-               }
-               seq_printf(seq, "Preamble:   %s\n",
-                          cw1200_debug_preamble[priv->association_mode.preamble]);
-               seq_printf(seq, "AMPDU spcn: %d\n",
-                          priv->association_mode.mpdu_start_spacing);
-               seq_printf(seq, "Basic rate: 0x%.8X\n",
-                          le32_to_cpu(priv->association_mode.basic_rate_set));
-               seq_printf(seq, "Bss lost:   %d beacons\n",
-                          priv->bss_params.beacon_lost_count);
-               seq_printf(seq, "AID:        %d\n",
-                          priv->bss_params.aid);
-               seq_printf(seq, "Rates:      0x%.8X\n",
-                          priv->bss_params.operational_rate_set);
-               seq_printf(seq, "Powersave:  %s\n", pm_mode);
-       }
-       seq_printf(seq, "HT:         %s\n",
-                  cw1200_is_ht(&priv->ht_info) ? "on" : "off");
-       if (cw1200_is_ht(&priv->ht_info)) {
-               seq_printf(seq, "Greenfield: %s\n",
-                          cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no");
-               seq_printf(seq, "AMPDU dens: %d\n",
-                          cw1200_ht_ampdu_density(&priv->ht_info));
-       }
-       seq_printf(seq, "RSSI thold: %d\n",
-                  priv->cqm_rssi_thold);
-       seq_printf(seq, "RSSI hyst:  %d\n",
-                  priv->cqm_rssi_hyst);
-       seq_printf(seq, "Long retr:  %d\n",
-                  priv->long_frame_max_tx_count);
-       seq_printf(seq, "Short retr: %d\n",
-                  priv->short_frame_max_tx_count);
-       spin_lock_bh(&priv->tx_policy_cache.lock);
-       i = 0;
-       list_for_each(item, &priv->tx_policy_cache.used)
-               ++i;
-       spin_unlock_bh(&priv->tx_policy_cache.lock);
-       seq_printf(seq, "RC in use:  %d\n", i);
-
-       seq_puts(seq, "\n");
-       for (i = 0; i < 4; ++i) {
-               cw1200_queue_status_show(seq, &priv->tx_queue[i]);
-               seq_puts(seq, "\n");
-       }
-
-       cw1200_debug_print_map(seq, priv, "Link map:   ",
-                              priv->link_id_map);
-       cw1200_debug_print_map(seq, priv, "Asleep map: ",
-                              priv->sta_asleep_mask);
-       cw1200_debug_print_map(seq, priv, "PSPOLL map: ",
-                              priv->pspoll_mask);
-
-       seq_puts(seq, "\n");
-
-       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
-               if (priv->link_id_db[i].status) {
-                       seq_printf(seq, "Link %d:     %s, %pM\n",
-                                  i + 1,
-                                  cw1200_debug_link_id[priv->link_id_db[i].status],
-                                  priv->link_id_db[i].mac);
-               }
-       }
-
-       seq_puts(seq, "\n");
-
-       seq_printf(seq, "BH status:  %s\n",
-                  atomic_read(&priv->bh_term) ? "terminated" : "alive");
-       seq_printf(seq, "Pending RX: %d\n",
-                  atomic_read(&priv->bh_rx));
-       seq_printf(seq, "Pending TX: %d\n",
-                  atomic_read(&priv->bh_tx));
-       if (priv->bh_error)
-               seq_printf(seq, "BH errcode: %d\n",
-                          priv->bh_error);
-       seq_printf(seq, "TX bufs:    %d x %d bytes\n",
-                  priv->wsm_caps.input_buffers,
-                  priv->wsm_caps.input_buffer_size);
-       seq_printf(seq, "Used bufs:  %d\n",
-                  priv->hw_bufs_used);
-       seq_printf(seq, "Powermgmt:  %s\n",
-                  priv->powersave_enabled ? "on" : "off");
-       seq_printf(seq, "Device:     %s\n",
-                  priv->device_can_sleep ? "asleep" : "awake");
-
-       spin_lock(&priv->wsm_cmd.lock);
-       seq_printf(seq, "WSM status: %s\n",
-                  priv->wsm_cmd.done ? "idle" : "active");
-       seq_printf(seq, "WSM cmd:    0x%.4X (%td bytes)\n",
-                  priv->wsm_cmd.cmd, priv->wsm_cmd.len);
-       seq_printf(seq, "WSM retval: %d\n",
-                  priv->wsm_cmd.ret);
-       spin_unlock(&priv->wsm_cmd.lock);
-
-       seq_printf(seq, "Datapath:   %s\n",
-                  atomic_read(&priv->tx_lock) ? "locked" : "unlocked");
-       if (atomic_read(&priv->tx_lock))
-               seq_printf(seq, "TXlock cnt: %d\n",
-                          atomic_read(&priv->tx_lock));
-
-       seq_printf(seq, "TXed:       %d\n",
-                  d->tx);
-       seq_printf(seq, "AGG TXed:   %d\n",
-                  d->tx_agg);
-       seq_printf(seq, "MULTI TXed: %d (%d)\n",
-                  d->tx_multi, d->tx_multi_frames);
-       seq_printf(seq, "RXed:       %d\n",
-                  d->rx);
-       seq_printf(seq, "AGG RXed:   %d\n",
-                  d->rx_agg);
-       seq_printf(seq, "TX miss:    %d\n",
-                  d->tx_cache_miss);
-       seq_printf(seq, "TX align:   %d\n",
-                  d->tx_align);
-       seq_printf(seq, "TX burst:   %d\n",
-                  d->tx_burst);
-       seq_printf(seq, "TX TTL:     %d\n",
-                  d->tx_ttl);
-       seq_printf(seq, "Scan:       %s\n",
-                  atomic_read(&priv->scan.in_progress) ? "active" : "idle");
-
-       return 0;
-}
-
-static int cw1200_status_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, &cw1200_status_show,
-               inode->i_private);
-}
-
-static const struct file_operations fops_status = {
-       .open = cw1200_status_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-       .owner = THIS_MODULE,
-};
-
-static int cw1200_counters_show(struct seq_file *seq, void *v)
-{
-       int ret;
-       struct cw1200_common *priv = seq->private;
-       struct wsm_mib_counters_table counters;
-
-       ret = wsm_get_counters_table(priv, &counters);
-       if (ret)
-               return ret;
-
-#define PUT_COUNTER(tab, name) \
-       seq_printf(seq, "%s:" tab "%d\n", #name, \
-               __le32_to_cpu(counters.name))
-
-       PUT_COUNTER("\t\t", plcp_errors);
-       PUT_COUNTER("\t\t", fcs_errors);
-       PUT_COUNTER("\t\t", tx_packets);
-       PUT_COUNTER("\t\t", rx_packets);
-       PUT_COUNTER("\t\t", rx_packet_errors);
-       PUT_COUNTER("\t",   rx_decryption_failures);
-       PUT_COUNTER("\t\t", rx_mic_failures);
-       PUT_COUNTER("\t",   rx_no_key_failures);
-       PUT_COUNTER("\t",   tx_multicast_frames);
-       PUT_COUNTER("\t",   tx_frames_success);
-       PUT_COUNTER("\t",   tx_frame_failures);
-       PUT_COUNTER("\t",   tx_frames_retried);
-       PUT_COUNTER("\t",   tx_frames_multi_retried);
-       PUT_COUNTER("\t",   rx_frame_duplicates);
-       PUT_COUNTER("\t\t", rts_success);
-       PUT_COUNTER("\t\t", rts_failures);
-       PUT_COUNTER("\t\t", ack_failures);
-       PUT_COUNTER("\t",   rx_multicast_frames);
-       PUT_COUNTER("\t",   rx_frames_success);
-       PUT_COUNTER("\t",   rx_cmac_icv_errors);
-       PUT_COUNTER("\t\t", rx_cmac_replays);
-       PUT_COUNTER("\t",   rx_mgmt_ccmp_replays);
-
-#undef PUT_COUNTER
-
-       return 0;
-}
-
-static int cw1200_counters_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, &cw1200_counters_show,
-               inode->i_private);
-}
-
-static const struct file_operations fops_counters = {
-       .open = cw1200_counters_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-       .owner = THIS_MODULE,
-};
-
-static ssize_t cw1200_wsm_dumps(struct file *file,
-       const char __user *user_buf, size_t count, loff_t *ppos)
-{
-       struct cw1200_common *priv = file->private_data;
-       char buf[1];
-
-       if (!count)
-               return -EINVAL;
-       if (copy_from_user(buf, user_buf, 1))
-               return -EFAULT;
-
-       if (buf[0] == '1')
-               priv->wsm_enable_wsm_dumps = 1;
-       else
-               priv->wsm_enable_wsm_dumps = 0;
-
-       return count;
-}
-
-static const struct file_operations fops_wsm_dumps = {
-       .open = simple_open,
-       .write = cw1200_wsm_dumps,
-       .llseek = default_llseek,
-};
-
-int cw1200_debug_init(struct cw1200_common *priv)
-{
-       int ret = -ENOMEM;
-       struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv),
-                       GFP_KERNEL);
-       priv->debug = d;
-       if (!d)
-               return ret;
-
-       d->debugfs_phy = debugfs_create_dir("cw1200",
-                                           priv->hw->wiphy->debugfsdir);
-       if (!d->debugfs_phy)
-               goto err;
-
-       if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
-                                priv, &fops_status))
-               goto err;
-
-       if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
-                                priv, &fops_counters))
-               goto err;
-
-       if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
-                                priv, &fops_wsm_dumps))
-               goto err;
-
-       return 0;
-
-err:
-       priv->debug = NULL;
-       debugfs_remove_recursive(d->debugfs_phy);
-       kfree(d);
-       return ret;
-}
-
-void cw1200_debug_release(struct cw1200_common *priv)
-{
-       struct cw1200_debug_priv *d = priv->debug;
-       if (d) {
-               debugfs_remove_recursive(d->debugfs_phy);
-               priv->debug = NULL;
-               kfree(d);
-       }
-}
diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h
deleted file mode 100644 (file)
index b525aba..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * DebugFS code for ST-Ericsson CW1200 mac80211 driver
- *
- * Copyright (c) 2011, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef CW1200_DEBUG_H_INCLUDED
-#define CW1200_DEBUG_H_INCLUDED
-
-struct cw1200_debug_priv {
-       struct dentry *debugfs_phy;
-       int tx;
-       int tx_agg;
-       int rx;
-       int rx_agg;
-       int tx_multi;
-       int tx_multi_frames;
-       int tx_cache_miss;
-       int tx_align;
-       int tx_ttl;
-       int tx_burst;
-       int ba_cnt;
-       int ba_acc;
-       int ba_cnt_rx;
-       int ba_acc_rx;
-};
-
-int cw1200_debug_init(struct cw1200_common *priv);
-void cw1200_debug_release(struct cw1200_common *priv);
-
-static inline void cw1200_debug_txed(struct cw1200_common *priv)
-{
-       ++priv->debug->tx;
-}
-
-static inline void cw1200_debug_txed_agg(struct cw1200_common *priv)
-{
-       ++priv->debug->tx_agg;
-}
-
-static inline void cw1200_debug_txed_multi(struct cw1200_common *priv,
-                                          int count)
-{
-       ++priv->debug->tx_multi;
-       priv->debug->tx_multi_frames += count;
-}
-
-static inline void cw1200_debug_rxed(struct cw1200_common *priv)
-{
-       ++priv->debug->rx;
-}
-
-static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv)
-{
-       ++priv->debug->rx_agg;
-}
-
-static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv)
-{
-       ++priv->debug->tx_cache_miss;
-}
-
-static inline void cw1200_debug_tx_align(struct cw1200_common *priv)
-{
-       ++priv->debug->tx_align;
-}
-
-static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv)
-{
-       ++priv->debug->tx_ttl;
-}
-
-static inline void cw1200_debug_tx_burst(struct cw1200_common *priv)
-{
-       ++priv->debug->tx_burst;
-}
-
-static inline void cw1200_debug_ba(struct cw1200_common *priv,
-                                  int ba_cnt, int ba_acc,
-                                  int ba_cnt_rx, int ba_acc_rx)
-{
-       priv->debug->ba_cnt = ba_cnt;
-       priv->debug->ba_acc = ba_acc;
-       priv->debug->ba_cnt_rx = ba_cnt_rx;
-       priv->debug->ba_acc_rx = ba_acc_rx;
-}
-
-#endif /* CW1200_DEBUG_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c
deleted file mode 100644 (file)
index 30e7646..0000000
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * Based on:
- * ST-Ericsson UMAC CW1200 driver which is
- * Copyright (c) 2010, ST-Ericsson
- * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/vmalloc.h>
-#include <linux/sched.h>
-#include <linux/firmware.h>
-
-#include "cw1200.h"
-#include "fwio.h"
-#include "hwio.h"
-#include "hwbus.h"
-#include "bh.h"
-
-static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision)
-{
-       int hw_type = -1;
-       u32 silicon_type = (config_reg_val >> 24) & 0x7;
-       u32 silicon_vers = (config_reg_val >> 31) & 0x1;
-
-       switch (silicon_type) {
-       case 0x00:
-               *major_revision = 1;
-               hw_type = HIF_9000_SILICON_VERSATILE;
-               break;
-       case 0x01:
-       case 0x02: /* CW1x00 */
-       case 0x04: /* CW1x60 */
-               *major_revision = silicon_type;
-               if (silicon_vers)
-                       hw_type = HIF_8601_VERSATILE;
-               else
-                       hw_type = HIF_8601_SILICON;
-               break;
-       default:
-               break;
-       }
-
-       return hw_type;
-}
-
-static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
-{
-       int ret, block, num_blocks;
-       unsigned i;
-       u32 val32;
-       u32 put = 0, get = 0;
-       u8 *buf = NULL;
-       const char *fw_path;
-       const struct firmware *firmware = NULL;
-
-       /* Macroses are local. */
-#define APB_WRITE(reg, val) \
-       do { \
-               ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
-               if (ret < 0) \
-                       goto exit; \
-       } while (0)
-#define APB_WRITE2(reg, val) \
-       do { \
-               ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
-               if (ret < 0) \
-                       goto free_buffer; \
-       } while (0)
-#define APB_READ(reg, val) \
-       do { \
-               ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
-               if (ret < 0) \
-                       goto free_buffer; \
-       } while (0)
-#define REG_WRITE(reg, val) \
-       do { \
-               ret = cw1200_reg_write_32(priv, (reg), (val)); \
-               if (ret < 0) \
-                       goto exit; \
-       } while (0)
-#define REG_READ(reg, val) \
-       do { \
-               ret = cw1200_reg_read_32(priv, (reg), &(val)); \
-               if (ret < 0) \
-                       goto exit; \
-       } while (0)
-
-       switch (priv->hw_revision) {
-       case CW1200_HW_REV_CUT10:
-               fw_path = FIRMWARE_CUT10;
-               if (!priv->sdd_path)
-                       priv->sdd_path = SDD_FILE_10;
-               break;
-       case CW1200_HW_REV_CUT11:
-               fw_path = FIRMWARE_CUT11;
-               if (!priv->sdd_path)
-                       priv->sdd_path = SDD_FILE_11;
-               break;
-       case CW1200_HW_REV_CUT20:
-               fw_path = FIRMWARE_CUT20;
-               if (!priv->sdd_path)
-                       priv->sdd_path = SDD_FILE_20;
-               break;
-       case CW1200_HW_REV_CUT22:
-               fw_path = FIRMWARE_CUT22;
-               if (!priv->sdd_path)
-                       priv->sdd_path = SDD_FILE_22;
-               break;
-       case CW1X60_HW_REV:
-               fw_path = FIRMWARE_CW1X60;
-               if (!priv->sdd_path)
-                       priv->sdd_path = SDD_FILE_CW1X60;
-               break;
-       default:
-               pr_err("Invalid silicon revision %d.\n", priv->hw_revision);
-               return -EINVAL;
-       }
-
-       /* Initialize common registers */
-       APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE);
-       APB_WRITE(DOWNLOAD_PUT_REG, 0);
-       APB_WRITE(DOWNLOAD_GET_REG, 0);
-       APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING);
-       APB_WRITE(DOWNLOAD_FLAGS_REG, 0);
-
-       /* Write the NOP Instruction */
-       REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000);
-       REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE);
-
-       /* Release CPU from RESET */
-       REG_READ(ST90TDS_CONFIG_REG_ID, val32);
-       val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT;
-       REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
-
-       /* Enable Clock */
-       val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT;
-       REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
-
-       /* Load a firmware file */
-       ret = request_firmware(&firmware, fw_path, priv->pdev);
-       if (ret) {
-               pr_err("Can't load firmware file %s.\n", fw_path);
-               goto exit;
-       }
-
-       buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
-       if (!buf) {
-               pr_err("Can't allocate firmware load buffer.\n");
-               ret = -ENOMEM;
-               goto firmware_release;
-       }
-
-       /* Check if the bootloader is ready */
-       for (i = 0; i < 100; i += 1 + i / 2) {
-               APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32);
-               if (val32 == DOWNLOAD_I_AM_HERE)
-                       break;
-               mdelay(i);
-       } /* End of for loop */
-
-       if (val32 != DOWNLOAD_I_AM_HERE) {
-               pr_err("Bootloader is not ready.\n");
-               ret = -ETIMEDOUT;
-               goto free_buffer;
-       }
-
-       /* Calculcate number of download blocks */
-       num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1;
-
-       /* Updating the length in Download Ctrl Area */
-       val32 = firmware->size; /* Explicit cast from size_t to u32 */
-       APB_WRITE2(DOWNLOAD_IMAGE_SIZE_REG, val32);
-
-       /* Firmware downloading loop */
-       for (block = 0; block < num_blocks; block++) {
-               size_t tx_size;
-               size_t block_size;
-
-               /* check the download status */
-               APB_READ(DOWNLOAD_STATUS_REG, val32);
-               if (val32 != DOWNLOAD_PENDING) {
-                       pr_err("Bootloader reported error %d.\n", val32);
-                       ret = -EIO;
-                       goto free_buffer;
-               }
-
-               /* loop until put - get <= 24K */
-               for (i = 0; i < 100; i++) {
-                       APB_READ(DOWNLOAD_GET_REG, get);
-                       if ((put - get) <=
-                           (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE))
-                               break;
-                       mdelay(i);
-               }
-
-               if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
-                       pr_err("Timeout waiting for FIFO.\n");
-                       ret = -ETIMEDOUT;
-                       goto free_buffer;
-               }
-
-               /* calculate the block size */
-               tx_size = block_size = min_t(size_t, firmware->size - put,
-                                       DOWNLOAD_BLOCK_SIZE);
-
-               memcpy(buf, &firmware->data[put], block_size);
-               if (block_size < DOWNLOAD_BLOCK_SIZE) {
-                       memset(&buf[block_size], 0,
-                              DOWNLOAD_BLOCK_SIZE - block_size);
-                       tx_size = DOWNLOAD_BLOCK_SIZE;
-               }
-
-               /* send the block to sram */
-               ret = cw1200_apb_write(priv,
-                       CW1200_APB(DOWNLOAD_FIFO_OFFSET +
-                                  (put & (DOWNLOAD_FIFO_SIZE - 1))),
-                       buf, tx_size);
-               if (ret < 0) {
-                       pr_err("Can't write firmware block @ %d!\n",
-                              put & (DOWNLOAD_FIFO_SIZE - 1));
-                       goto free_buffer;
-               }
-
-               /* update the put register */
-               put += block_size;
-               APB_WRITE2(DOWNLOAD_PUT_REG, put);
-       } /* End of firmware download loop */
-
-       /* Wait for the download completion */
-       for (i = 0; i < 300; i += 1 + i / 2) {
-               APB_READ(DOWNLOAD_STATUS_REG, val32);
-               if (val32 != DOWNLOAD_PENDING)
-                       break;
-               mdelay(i);
-       }
-       if (val32 != DOWNLOAD_SUCCESS) {
-               pr_err("Wait for download completion failed: 0x%.8X\n", val32);
-               ret = -ETIMEDOUT;
-               goto free_buffer;
-       } else {
-               pr_info("Firmware download completed.\n");
-               ret = 0;
-       }
-
-free_buffer:
-       kfree(buf);
-firmware_release:
-       release_firmware(firmware);
-exit:
-       return ret;
-
-#undef APB_WRITE
-#undef APB_WRITE2
-#undef APB_READ
-#undef REG_WRITE
-#undef REG_READ
-}
-
-
-static int config_reg_read(struct cw1200_common *priv, u32 *val)
-{
-       switch (priv->hw_type) {
-       case HIF_9000_SILICON_VERSATILE: {
-               u16 val16;
-               int ret = cw1200_reg_read_16(priv,
-                                            ST90TDS_CONFIG_REG_ID,
-                                            &val16);
-               if (ret < 0)
-                       return ret;
-               *val = val16;
-               return 0;
-       }
-       case HIF_8601_VERSATILE:
-       case HIF_8601_SILICON:
-       default:
-               cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val);
-               break;
-       }
-       return 0;
-}
-
-static int config_reg_write(struct cw1200_common *priv, u32 val)
-{
-       switch (priv->hw_type) {
-       case HIF_9000_SILICON_VERSATILE:
-               return cw1200_reg_write_16(priv,
-                                          ST90TDS_CONFIG_REG_ID,
-                                          (u16)val);
-       case HIF_8601_VERSATILE:
-       case HIF_8601_SILICON:
-       default:
-               return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val);
-       }
-       return 0;
-}
-
-int cw1200_load_firmware(struct cw1200_common *priv)
-{
-       int ret;
-       int i;
-       u32 val32;
-       u16 val16;
-       int major_revision = -1;
-
-       /* Read CONFIG Register */
-       ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
-       if (ret < 0) {
-               pr_err("Can't read config register.\n");
-               goto out;
-       }
-
-       if (val32 == 0 || val32 == 0xffffffff) {
-               pr_err("Bad config register value (0x%08x)\n", val32);
-               ret = -EIO;
-               goto out;
-       }
-
-       priv->hw_type = cw1200_get_hw_type(val32, &major_revision);
-       if (priv->hw_type < 0) {
-               pr_err("Can't deduce hardware type.\n");
-               ret = -ENOTSUPP;
-               goto out;
-       }
-
-       /* Set DPLL Reg value, and read back to confirm writes work */
-       ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
-                                 cw1200_dpll_from_clk(priv->hw_refclk));
-       if (ret < 0) {
-               pr_err("Can't write DPLL register.\n");
-               goto out;
-       }
-
-       msleep(20);
-
-       ret = cw1200_reg_read_32(priv,
-               ST90TDS_TSET_GEN_R_W_REG_ID, &val32);
-       if (ret < 0) {
-               pr_err("Can't read DPLL register.\n");
-               goto out;
-       }
-
-       if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) {
-               pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n",
-                      cw1200_dpll_from_clk(priv->hw_refclk), val32);
-               ret = -EIO;
-               goto out;
-       }
-
-       /* Set wakeup bit in device */
-       ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16);
-       if (ret < 0) {
-               pr_err("set_wakeup: can't read control register.\n");
-               goto out;
-       }
-
-       ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
-               val16 | ST90TDS_CONT_WUP_BIT);
-       if (ret < 0) {
-               pr_err("set_wakeup: can't write control register.\n");
-               goto out;
-       }
-
-       /* Wait for wakeup */
-       for (i = 0; i < 300; i += (1 + i / 2)) {
-               ret = cw1200_reg_read_16(priv,
-                       ST90TDS_CONTROL_REG_ID, &val16);
-               if (ret < 0) {
-                       pr_err("wait_for_wakeup: can't read control register.\n");
-                       goto out;
-               }
-
-               if (val16 & ST90TDS_CONT_RDY_BIT)
-                       break;
-
-               msleep(i);
-       }
-
-       if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) {
-               pr_err("wait_for_wakeup: device is not responding.\n");
-               ret = -ETIMEDOUT;
-               goto out;
-       }
-
-       switch (major_revision) {
-       case 1:
-               /* CW1200 Hardware detection logic : Check for CUT1.1 */
-               ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32);
-               if (ret) {
-                       pr_err("HW detection: can't read CUT ID.\n");
-                       goto out;
-               }
-
-               switch (val32) {
-               case CW1200_CUT_11_ID_STR:
-                       pr_info("CW1x00 Cut 1.1 silicon detected.\n");
-                       priv->hw_revision = CW1200_HW_REV_CUT11;
-                       break;
-               default:
-                       pr_info("CW1x00 Cut 1.0 silicon detected.\n");
-                       priv->hw_revision = CW1200_HW_REV_CUT10;
-                       break;
-               }
-
-               /* According to ST-E, CUT<2.0 has busted BA TID0-3.
-                  Just disable it entirely...
-               */
-               priv->ba_rx_tid_mask = 0;
-               priv->ba_tx_tid_mask = 0;
-               break;
-       case 2: {
-               u32 ar1, ar2, ar3;
-               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1);
-               if (ret) {
-                       pr_err("(1) HW detection: can't read CUT ID\n");
-                       goto out;
-               }
-               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2);
-               if (ret) {
-                       pr_err("(2) HW detection: can't read CUT ID.\n");
-                       goto out;
-               }
-
-               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3);
-               if (ret) {
-                       pr_err("(3) HW detection: can't read CUT ID.\n");
-                       goto out;
-               }
-
-               if (ar1 == CW1200_CUT_22_ID_STR1 &&
-                   ar2 == CW1200_CUT_22_ID_STR2 &&
-                   ar3 == CW1200_CUT_22_ID_STR3) {
-                       pr_info("CW1x00 Cut 2.2 silicon detected.\n");
-                       priv->hw_revision = CW1200_HW_REV_CUT22;
-               } else {
-                       pr_info("CW1x00 Cut 2.0 silicon detected.\n");
-                       priv->hw_revision = CW1200_HW_REV_CUT20;
-               }
-               break;
-       }
-       case 4:
-               pr_info("CW1x60 silicon detected.\n");
-               priv->hw_revision = CW1X60_HW_REV;
-               break;
-       default:
-               pr_err("Unsupported silicon major revision %d.\n",
-                      major_revision);
-               ret = -ENOTSUPP;
-               goto out;
-       }
-
-       /* Checking for access mode */
-       ret = config_reg_read(priv, &val32);
-       if (ret < 0) {
-               pr_err("Can't read config register.\n");
-               goto out;
-       }
-
-       if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) {
-               pr_err("Device is already in QUEUE mode!\n");
-                       ret = -EINVAL;
-                       goto out;
-       }
-
-       switch (priv->hw_type)  {
-       case HIF_8601_SILICON:
-               if (priv->hw_revision == CW1X60_HW_REV) {
-                       pr_err("Can't handle CW1160/1260 firmware load yet.\n");
-                       ret = -ENOTSUPP;
-                       goto out;
-               }
-               ret = cw1200_load_firmware_cw1200(priv);
-               break;
-       default:
-               pr_err("Can't perform firmware load for hw type %d.\n",
-                      priv->hw_type);
-               ret = -ENOTSUPP;
-               goto out;
-       }
-       if (ret < 0) {
-               pr_err("Firmware load error.\n");
-               goto out;
-       }
-
-       /* Enable interrupt signalling */
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-       ret = __cw1200_irq_enable(priv, 1);
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-       if (ret < 0)
-               goto unsubscribe;
-
-       /* Configure device for MESSSAGE MODE */
-       ret = config_reg_read(priv, &val32);
-       if (ret < 0) {
-               pr_err("Can't read config register.\n");
-               goto unsubscribe;
-       }
-       ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT);
-       if (ret < 0) {
-               pr_err("Can't write config register.\n");
-               goto unsubscribe;
-       }
-
-       /* Unless we read the CONFIG Register we are
-        * not able to get an interrupt
-        */
-       mdelay(10);
-       config_reg_read(priv, &val32);
-
-out:
-       return ret;
-
-unsubscribe:
-       /* Disable interrupt signalling */
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-       ret = __cw1200_irq_enable(priv, 0);
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-       return ret;
-}
diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/cw1200/fwio.h
deleted file mode 100644 (file)
index ea30993..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Firmware API for mac80211 ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * Based on:
- * ST-Ericsson UMAC CW1200 driver which is
- * Copyright (c) 2010, ST-Ericsson
- * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef FWIO_H_INCLUDED
-#define FWIO_H_INCLUDED
-
-#define BOOTLOADER_CW1X60       "boot_cw1x60.bin"
-#define FIRMWARE_CW1X60                "wsm_cw1x60.bin"
-#define FIRMWARE_CUT22         "wsm_22.bin"
-#define FIRMWARE_CUT20         "wsm_20.bin"
-#define FIRMWARE_CUT11         "wsm_11.bin"
-#define FIRMWARE_CUT10         "wsm_10.bin"
-#define SDD_FILE_CW1X60                "sdd_cw1x60.bin"
-#define SDD_FILE_22            "sdd_22.bin"
-#define SDD_FILE_20            "sdd_20.bin"
-#define SDD_FILE_11            "sdd_11.bin"
-#define SDD_FILE_10            "sdd_10.bin"
-
-int cw1200_load_firmware(struct cw1200_common *priv);
-
-/* SDD definitions */
-#define SDD_PTA_CFG_ELT_ID 0xEB
-#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5
-u32 cw1200_dpll_from_clk(u16 clk);
-
-#endif
diff --git a/drivers/net/wireless/cw1200/hwbus.h b/drivers/net/wireless/cw1200/hwbus.h
deleted file mode 100644 (file)
index 8b2fc83..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Common hwbus abstraction layer interface for cw1200 wireless driver
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef CW1200_HWBUS_H
-#define CW1200_HWBUS_H
-
-struct hwbus_priv;
-
-void cw1200_irq_handler(struct cw1200_common *priv);
-
-/* This MUST be wrapped with hwbus_ops->lock/unlock! */
-int __cw1200_irq_enable(struct cw1200_common *priv, int enable);
-
-struct hwbus_ops {
-       int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr,
-                                       void *dst, int count);
-       int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr,
-                                       const void *src, int count);
-       void (*lock)(struct hwbus_priv *self);
-       void (*unlock)(struct hwbus_priv *self);
-       size_t (*align_size)(struct hwbus_priv *self, size_t size);
-       int (*power_mgmt)(struct hwbus_priv *self, bool suspend);
-};
-
-#endif /* CW1200_HWBUS_H */
diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c
deleted file mode 100644 (file)
index ff230b7..0000000
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Low-level device IO routines for ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * Based on:
- * ST-Ericsson UMAC CW1200 driver, which is
- * Copyright (c) 2010, ST-Ericsson
- * Author: Ajitpal Singh <ajitpal.singh@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/types.h>
-
-#include "cw1200.h"
-#include "hwio.h"
-#include "hwbus.h"
-
- /* Sdio addr is 4*spi_addr */
-#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
-#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
-                               ((((buf_id)    & 0x1F) << 7) \
-                               | (((mpf)        & 1) << 6) \
-                               | (((rfu)        & 1) << 5) \
-                               | (((reg_id_ofs) & 0x1F) << 0))
-#define MAX_RETRY              3
-
-
-static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr,
-                            void *buf, size_t buf_len, int buf_id)
-{
-       u16 addr_sdio;
-       u32 sdio_reg_addr_17bit;
-
-       /* Check if buffer is aligned to 4 byte boundary */
-       if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
-               pr_err("buffer is not aligned.\n");
-               return -EINVAL;
-       }
-
-       /* Convert to SDIO Register Address */
-       addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
-       sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
-
-       return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv,
-                                                 sdio_reg_addr_17bit,
-                                                 buf, buf_len);
-}
-
-static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr,
-                               const void *buf, size_t buf_len, int buf_id)
-{
-       u16 addr_sdio;
-       u32 sdio_reg_addr_17bit;
-
-       /* Convert to SDIO Register Address */
-       addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
-       sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
-
-       return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv,
-                                               sdio_reg_addr_17bit,
-                                               buf, buf_len);
-}
-
-static inline int __cw1200_reg_read_32(struct cw1200_common *priv,
-                                       u16 addr, u32 *val)
-{
-       __le32 tmp;
-       int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0);
-       *val = le32_to_cpu(tmp);
-       return i;
-}
-
-static inline int __cw1200_reg_write_32(struct cw1200_common *priv,
-                                       u16 addr, u32 val)
-{
-       __le32 tmp = cpu_to_le32(val);
-       return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0);
-}
-
-static inline int __cw1200_reg_read_16(struct cw1200_common *priv,
-                                       u16 addr, u16 *val)
-{
-       __le16 tmp;
-       int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0);
-       *val = le16_to_cpu(tmp);
-       return i;
-}
-
-static inline int __cw1200_reg_write_16(struct cw1200_common *priv,
-                                       u16 addr, u16 val)
-{
-       __le16 tmp = cpu_to_le16(val);
-       return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0);
-}
-
-int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf,
-                       size_t buf_len)
-{
-       int ret;
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-       ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-       return ret;
-}
-
-int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
-                       size_t buf_len)
-{
-       int ret;
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-       ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-       return ret;
-}
-
-int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len)
-{
-       int ret, retry = 1;
-       int buf_id_rx = priv->buf_id_rx;
-
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-
-       while (retry <= MAX_RETRY) {
-               ret = __cw1200_reg_read(priv,
-                                       ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
-                                       buf_len, buf_id_rx + 1);
-               if (!ret) {
-                       buf_id_rx = (buf_id_rx + 1) & 3;
-                       priv->buf_id_rx = buf_id_rx;
-                       break;
-               } else {
-                       retry++;
-                       mdelay(1);
-                       pr_err("error :[%d]\n", ret);
-               }
-       }
-
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-       return ret;
-}
-
-int cw1200_data_write(struct cw1200_common *priv, const void *buf,
-                       size_t buf_len)
-{
-       int ret, retry = 1;
-       int buf_id_tx = priv->buf_id_tx;
-
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-
-       while (retry <= MAX_RETRY) {
-               ret = __cw1200_reg_write(priv,
-                                        ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
-                                        buf_len, buf_id_tx);
-               if (!ret) {
-                       buf_id_tx = (buf_id_tx + 1) & 31;
-                       priv->buf_id_tx = buf_id_tx;
-                       break;
-               } else {
-                       retry++;
-                       mdelay(1);
-                       pr_err("error :[%d]\n", ret);
-               }
-       }
-
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-       return ret;
-}
-
-int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
-                        size_t buf_len, u32 prefetch, u16 port_addr)
-{
-       u32 val32 = 0;
-       int i, ret;
-
-       if ((buf_len / 2) >= 0x1000) {
-               pr_err("Can't read more than 0xfff words.\n");
-               return -EINVAL;
-       }
-
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-       /* Write address */
-       ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
-       if (ret < 0) {
-               pr_err("Can't write address register.\n");
-               goto out;
-       }
-
-       /* Read CONFIG Register Value - We will read 32 bits */
-       ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
-       if (ret < 0) {
-               pr_err("Can't read config register.\n");
-               goto out;
-       }
-
-       /* Set PREFETCH bit */
-       ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID,
-                                       val32 | prefetch);
-       if (ret < 0) {
-               pr_err("Can't write prefetch bit.\n");
-               goto out;
-       }
-
-       /* Check for PRE-FETCH bit to be cleared */
-       for (i = 0; i < 20; i++) {
-               ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
-               if (ret < 0) {
-                       pr_err("Can't check prefetch bit.\n");
-                       goto out;
-               }
-               if (!(val32 & prefetch))
-                       break;
-
-               mdelay(i);
-       }
-
-       if (val32 & prefetch) {
-               pr_err("Prefetch bit is not cleared.\n");
-               goto out;
-       }
-
-       /* Read data port */
-       ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0);
-       if (ret < 0) {
-               pr_err("Can't read data port.\n");
-               goto out;
-       }
-
-out:
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-       return ret;
-}
-
-int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
-                       size_t buf_len)
-{
-       int ret;
-
-       if ((buf_len / 2) >= 0x1000) {
-               pr_err("Can't write more than 0xfff words.\n");
-               return -EINVAL;
-       }
-
-       priv->hwbus_ops->lock(priv->hwbus_priv);
-
-       /* Write address */
-       ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
-       if (ret < 0) {
-               pr_err("Can't write address register.\n");
-               goto out;
-       }
-
-       /* Write data port */
-       ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID,
-                                       buf, buf_len, 0);
-       if (ret < 0) {
-               pr_err("Can't write data port.\n");
-               goto out;
-       }
-
-out:
-       priv->hwbus_ops->unlock(priv->hwbus_priv);
-       return ret;
-}
-
-int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
-{
-       u32 val32;
-       u16 val16;
-       int ret;
-
-       if (HIF_8601_SILICON == priv->hw_type) {
-               ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
-               if (ret < 0) {
-                       pr_err("Can't read config register.\n");
-                       return ret;
-               }
-
-               if (enable)
-                       val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE;
-               else
-                       val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE;
-
-               ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32);
-               if (ret < 0) {
-                       pr_err("Can't write config register.\n");
-                       return ret;
-               }
-       } else {
-               ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16);
-               if (ret < 0) {
-                       pr_err("Can't read control register.\n");
-                       return ret;
-               }
-
-               if (enable)
-                       val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE;
-               else
-                       val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE;
-
-               ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16);
-               if (ret < 0) {
-                       pr_err("Can't write control register.\n");
-                       return ret;
-               }
-       }
-       return 0;
-}
diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h
deleted file mode 100644 (file)
index ddf5266..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Low-level API for mac80211 ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * Based on:
- * ST-Ericsson UMAC CW1200 driver which is
- * Copyright (c) 2010, ST-Ericsson
- * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef CW1200_HWIO_H_INCLUDED
-#define CW1200_HWIO_H_INCLUDED
-
-/* extern */ struct cw1200_common;
-
-#define CW1200_CUT_11_ID_STR           (0x302E3830)
-#define CW1200_CUT_22_ID_STR1          (0x302e3132)
-#define CW1200_CUT_22_ID_STR2          (0x32302e30)
-#define CW1200_CUT_22_ID_STR3          (0x3335)
-#define CW1200_CUT_ID_ADDR             (0xFFF17F90)
-#define CW1200_CUT2_ID_ADDR            (0xFFF1FF90)
-
-/* Download control area */
-/* boot loader start address in SRAM */
-#define DOWNLOAD_BOOT_LOADER_OFFSET    (0x00000000)
-/* 32K, 0x4000 to 0xDFFF */
-#define DOWNLOAD_FIFO_OFFSET           (0x00004000)
-/* 32K */
-#define DOWNLOAD_FIFO_SIZE             (0x00008000)
-/* 128 bytes, 0xFF80 to 0xFFFF */
-#define DOWNLOAD_CTRL_OFFSET           (0x0000FF80)
-#define DOWNLOAD_CTRL_DATA_DWORDS      (32-6)
-
-struct download_cntl_t {
-       /* size of whole firmware file (including Cheksum), host init */
-       u32 image_size;
-       /* downloading flags */
-       u32 flags;
-       /* No. of bytes put into the download, init & updated by host */
-       u32 put;
-       /* last traced program counter, last ARM reg_pc */
-       u32 trace_pc;
-       /* No. of bytes read from the download, host init, device updates */
-       u32 get;
-       /* r0, boot losader status, host init to pending, device updates */
-       u32 status;
-       /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
-       u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS];
-};
-
-#define        DOWNLOAD_IMAGE_SIZE_REG         \
-       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size))
-#define        DOWNLOAD_FLAGS_REG              \
-       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags))
-#define DOWNLOAD_PUT_REG               \
-       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put))
-#define DOWNLOAD_TRACE_PC_REG          \
-       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc))
-#define        DOWNLOAD_GET_REG                \
-       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get))
-#define        DOWNLOAD_STATUS_REG             \
-       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status))
-#define DOWNLOAD_DEBUG_DATA_REG                \
-       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data))
-#define DOWNLOAD_DEBUG_DATA_LEN                (108)
-
-#define DOWNLOAD_BLOCK_SIZE            (1024)
-
-/* For boot loader detection */
-#define DOWNLOAD_ARE_YOU_HERE          (0x87654321)
-#define DOWNLOAD_I_AM_HERE             (0x12345678)
-
-/* Download error code */
-#define DOWNLOAD_PENDING               (0xFFFFFFFF)
-#define DOWNLOAD_SUCCESS               (0)
-#define DOWNLOAD_EXCEPTION             (1)
-#define DOWNLOAD_ERR_MEM_1             (2)
-#define DOWNLOAD_ERR_MEM_2             (3)
-#define DOWNLOAD_ERR_SOFTWARE          (4)
-#define DOWNLOAD_ERR_FILE_SIZE         (5)
-#define DOWNLOAD_ERR_CHECKSUM          (6)
-#define DOWNLOAD_ERR_OVERFLOW          (7)
-#define DOWNLOAD_ERR_IMAGE             (8)
-#define DOWNLOAD_ERR_HOST              (9)
-#define DOWNLOAD_ERR_ABORT             (10)
-
-
-#define SYS_BASE_ADDR_SILICON          (0)
-#define PAC_BASE_ADDRESS_SILICON       (SYS_BASE_ADDR_SILICON + 0x09000000)
-#define PAC_SHARED_MEMORY_SILICON      (PAC_BASE_ADDRESS_SILICON)
-
-#define CW1200_APB(addr)               (PAC_SHARED_MEMORY_SILICON + (addr))
-
-/* Device register definitions */
-
-/* WBF - SPI Register Addresses */
-#define ST90TDS_ADDR_ID_BASE           (0x0000)
-/* 16/32 bits */
-#define ST90TDS_CONFIG_REG_ID          (0x0000)
-/* 16/32 bits */
-#define ST90TDS_CONTROL_REG_ID         (0x0001)
-/* 16 bits, Q mode W/R */
-#define ST90TDS_IN_OUT_QUEUE_REG_ID    (0x0002)
-/* 32 bits, AHB bus R/W */
-#define ST90TDS_AHB_DPORT_REG_ID       (0x0003)
-/* 16/32 bits */
-#define ST90TDS_SRAM_BASE_ADDR_REG_ID   (0x0004)
-/* 32 bits, APB bus R/W */
-#define ST90TDS_SRAM_DPORT_REG_ID      (0x0005)
-/* 32 bits, t_settle/general */
-#define ST90TDS_TSET_GEN_R_W_REG_ID    (0x0006)
-/* 16 bits, Q mode read, no length */
-#define ST90TDS_FRAME_OUT_REG_ID       (0x0007)
-#define ST90TDS_ADDR_ID_MAX            (ST90TDS_FRAME_OUT_REG_ID)
-
-/* WBF - Control register bit set */
-/* next o/p length, bit 11 to 0 */
-#define ST90TDS_CONT_NEXT_LEN_MASK     (0x0FFF)
-#define ST90TDS_CONT_WUP_BIT           (BIT(12))
-#define ST90TDS_CONT_RDY_BIT           (BIT(13))
-#define ST90TDS_CONT_IRQ_ENABLE                (BIT(14))
-#define ST90TDS_CONT_RDY_ENABLE                (BIT(15))
-#define ST90TDS_CONT_IRQ_RDY_ENABLE    (BIT(14)|BIT(15))
-
-/* SPI Config register bit set */
-#define ST90TDS_CONFIG_FRAME_BIT       (BIT(2))
-#define ST90TDS_CONFIG_WORD_MODE_BITS  (BIT(3)|BIT(4))
-#define ST90TDS_CONFIG_WORD_MODE_1     (BIT(3))
-#define ST90TDS_CONFIG_WORD_MODE_2     (BIT(4))
-#define ST90TDS_CONFIG_ERROR_0_BIT     (BIT(5))
-#define ST90TDS_CONFIG_ERROR_1_BIT     (BIT(6))
-#define ST90TDS_CONFIG_ERROR_2_BIT     (BIT(7))
-/* TBD: Sure??? */
-#define ST90TDS_CONFIG_CSN_FRAME_BIT   (BIT(7))
-#define ST90TDS_CONFIG_ERROR_3_BIT     (BIT(8))
-#define ST90TDS_CONFIG_ERROR_4_BIT     (BIT(9))
-/* QueueM */
-#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
-/* AHB bus */
-#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11))
-#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
-/* APB bus */
-#define ST90TDS_CONFIG_PRFETCH_BIT     (BIT(13))
-/* cpu reset */
-#define ST90TDS_CONFIG_CPU_RESET_BIT   (BIT(14))
-#define ST90TDS_CONFIG_CLEAR_INT_BIT   (BIT(15))
-
-/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */
-#define ST90TDS_CONF_IRQ_ENABLE                (BIT(16))
-#define ST90TDS_CONF_RDY_ENABLE                (BIT(17))
-#define ST90TDS_CONF_IRQ_RDY_ENABLE    (BIT(16)|BIT(17))
-
-int cw1200_data_read(struct cw1200_common *priv,
-                    void *buf, size_t buf_len);
-int cw1200_data_write(struct cw1200_common *priv,
-                     const void *buf, size_t buf_len);
-
-int cw1200_reg_read(struct cw1200_common *priv, u16 addr,
-                   void *buf, size_t buf_len);
-int cw1200_reg_write(struct cw1200_common *priv, u16 addr,
-                    const void *buf, size_t buf_len);
-
-static inline int cw1200_reg_read_16(struct cw1200_common *priv,
-                                    u16 addr, u16 *val)
-{
-       __le32 tmp;
-       int i;
-       i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
-       *val = le32_to_cpu(tmp) & 0xfffff;
-       return i;
-}
-
-static inline int cw1200_reg_write_16(struct cw1200_common *priv,
-                                     u16 addr, u16 val)
-{
-       __le32 tmp = cpu_to_le32((u32)val);
-       return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp));
-}
-
-static inline int cw1200_reg_read_32(struct cw1200_common *priv,
-                                    u16 addr, u32 *val)
-{
-       __le32 tmp;
-       int i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
-       *val = le32_to_cpu(tmp);
-       return i;
-}
-
-static inline int cw1200_reg_write_32(struct cw1200_common *priv,
-                                     u16 addr, u32 val)
-{
-       __le32 tmp = cpu_to_le32(val);
-       return cw1200_reg_write(priv, addr, &tmp, sizeof(val));
-}
-
-int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
-                        size_t buf_len, u32 prefetch, u16 port_addr);
-int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
-                    size_t buf_len);
-
-static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr,
-                                 void *buf, size_t buf_len)
-{
-       return cw1200_indirect_read(priv, addr, buf, buf_len,
-                                   ST90TDS_CONFIG_PRFETCH_BIT,
-                                   ST90TDS_SRAM_DPORT_REG_ID);
-}
-
-static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr,
-                                 void *buf, size_t buf_len)
-{
-       return cw1200_indirect_read(priv, addr, buf, buf_len,
-                                   ST90TDS_CONFIG_AHB_PRFETCH_BIT,
-                                   ST90TDS_AHB_DPORT_REG_ID);
-}
-
-static inline int cw1200_apb_read_32(struct cw1200_common *priv,
-                                    u32 addr, u32 *val)
-{
-       __le32 tmp;
-       int i = cw1200_apb_read(priv, addr, &tmp, sizeof(tmp));
-       *val = le32_to_cpu(tmp);
-       return i;
-}
-
-static inline int cw1200_apb_write_32(struct cw1200_common *priv,
-                                     u32 addr, u32 val)
-{
-       __le32 tmp = cpu_to_le32(val);
-       return cw1200_apb_write(priv, addr, &tmp, sizeof(val));
-}
-static inline int cw1200_ahb_read_32(struct cw1200_common *priv,
-                                    u32 addr, u32 *val)
-{
-       __le32 tmp;
-       int i = cw1200_ahb_read(priv, addr, &tmp, sizeof(tmp));
-       *val = le32_to_cpu(tmp);
-       return i;
-}
-
-#endif /* CW1200_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c
deleted file mode 100644 (file)
index 0e51e27..0000000
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * Based on:
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
- * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- *
- * Based on:
- * - the islsm (softmac prism54) driver, which is:
- *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- * - stlc45xx driver
- *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-#include <linux/vmalloc.h>
-#include <linux/random.h>
-#include <linux/sched.h>
-#include <net/mac80211.h>
-
-#include "cw1200.h"
-#include "txrx.h"
-#include "hwbus.h"
-#include "fwio.h"
-#include "hwio.h"
-#include "bh.h"
-#include "sta.h"
-#include "scan.h"
-#include "debug.h"
-#include "pm.h"
-
-MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
-MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("cw1200_core");
-
-/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
-static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
-module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
-MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
-
-static char *cw1200_sdd_path;
-module_param(cw1200_sdd_path, charp, 0644);
-MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
-static int cw1200_refclk;
-module_param(cw1200_refclk, int, 0644);
-MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
-
-int cw1200_power_mode = wsm_power_mode_quiescent;
-module_param(cw1200_power_mode, int, 0644);
-MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode.  0 == active, 1 == doze, 2 == quiescent (default)");
-
-#define RATETAB_ENT(_rate, _rateid, _flags)            \
-       {                                               \
-               .bitrate        = (_rate),              \
-               .hw_value       = (_rateid),            \
-               .flags          = (_flags),             \
-       }
-
-static struct ieee80211_rate cw1200_rates[] = {
-       RATETAB_ENT(10,  0,   0),
-       RATETAB_ENT(20,  1,   0),
-       RATETAB_ENT(55,  2,   0),
-       RATETAB_ENT(110, 3,   0),
-       RATETAB_ENT(60,  6,  0),
-       RATETAB_ENT(90,  7,  0),
-       RATETAB_ENT(120, 8,  0),
-       RATETAB_ENT(180, 9,  0),
-       RATETAB_ENT(240, 10, 0),
-       RATETAB_ENT(360, 11, 0),
-       RATETAB_ENT(480, 12, 0),
-       RATETAB_ENT(540, 13, 0),
-};
-
-static struct ieee80211_rate cw1200_mcs_rates[] = {
-       RATETAB_ENT(65,  14, IEEE80211_TX_RC_MCS),
-       RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
-       RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
-       RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
-       RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
-       RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
-       RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
-       RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
-};
-
-#define cw1200_a_rates         (cw1200_rates + 4)
-#define cw1200_a_rates_size    (ARRAY_SIZE(cw1200_rates) - 4)
-#define cw1200_g_rates         (cw1200_rates + 0)
-#define cw1200_g_rates_size    (ARRAY_SIZE(cw1200_rates))
-#define cw1200_n_rates         (cw1200_mcs_rates)
-#define cw1200_n_rates_size    (ARRAY_SIZE(cw1200_mcs_rates))
-
-
-#define CHAN2G(_channel, _freq, _flags) {                      \
-       .band                   = IEEE80211_BAND_2GHZ,          \
-       .center_freq            = (_freq),                      \
-       .hw_value               = (_channel),                   \
-       .flags                  = (_flags),                     \
-       .max_antenna_gain       = 0,                            \
-       .max_power              = 30,                           \
-}
-
-#define CHAN5G(_channel, _flags) {                             \
-       .band                   = IEEE80211_BAND_5GHZ,          \
-       .center_freq    = 5000 + (5 * (_channel)),              \
-       .hw_value               = (_channel),                   \
-       .flags                  = (_flags),                     \
-       .max_antenna_gain       = 0,                            \
-       .max_power              = 30,                           \
-}
-
-static struct ieee80211_channel cw1200_2ghz_chantable[] = {
-       CHAN2G(1, 2412, 0),
-       CHAN2G(2, 2417, 0),
-       CHAN2G(3, 2422, 0),
-       CHAN2G(4, 2427, 0),
-       CHAN2G(5, 2432, 0),
-       CHAN2G(6, 2437, 0),
-       CHAN2G(7, 2442, 0),
-       CHAN2G(8, 2447, 0),
-       CHAN2G(9, 2452, 0),
-       CHAN2G(10, 2457, 0),
-       CHAN2G(11, 2462, 0),
-       CHAN2G(12, 2467, 0),
-       CHAN2G(13, 2472, 0),
-       CHAN2G(14, 2484, 0),
-};
-
-static struct ieee80211_channel cw1200_5ghz_chantable[] = {
-       CHAN5G(34, 0),          CHAN5G(36, 0),
-       CHAN5G(38, 0),          CHAN5G(40, 0),
-       CHAN5G(42, 0),          CHAN5G(44, 0),
-       CHAN5G(46, 0),          CHAN5G(48, 0),
-       CHAN5G(52, 0),          CHAN5G(56, 0),
-       CHAN5G(60, 0),          CHAN5G(64, 0),
-       CHAN5G(100, 0),         CHAN5G(104, 0),
-       CHAN5G(108, 0),         CHAN5G(112, 0),
-       CHAN5G(116, 0),         CHAN5G(120, 0),
-       CHAN5G(124, 0),         CHAN5G(128, 0),
-       CHAN5G(132, 0),         CHAN5G(136, 0),
-       CHAN5G(140, 0),         CHAN5G(149, 0),
-       CHAN5G(153, 0),         CHAN5G(157, 0),
-       CHAN5G(161, 0),         CHAN5G(165, 0),
-       CHAN5G(184, 0),         CHAN5G(188, 0),
-       CHAN5G(192, 0),         CHAN5G(196, 0),
-       CHAN5G(200, 0),         CHAN5G(204, 0),
-       CHAN5G(208, 0),         CHAN5G(212, 0),
-       CHAN5G(216, 0),
-};
-
-static struct ieee80211_supported_band cw1200_band_2ghz = {
-       .channels = cw1200_2ghz_chantable,
-       .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
-       .bitrates = cw1200_g_rates,
-       .n_bitrates = cw1200_g_rates_size,
-       .ht_cap = {
-               .cap = IEEE80211_HT_CAP_GRN_FLD |
-                       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
-                       IEEE80211_HT_CAP_MAX_AMSDU,
-               .ht_supported = 1,
-               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
-               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
-               .mcs = {
-                       .rx_mask[0] = 0xFF,
-                       .rx_highest = __cpu_to_le16(0x41),
-                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
-               },
-       },
-};
-
-static struct ieee80211_supported_band cw1200_band_5ghz = {
-       .channels = cw1200_5ghz_chantable,
-       .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
-       .bitrates = cw1200_a_rates,
-       .n_bitrates = cw1200_a_rates_size,
-       .ht_cap = {
-               .cap = IEEE80211_HT_CAP_GRN_FLD |
-                       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
-                       IEEE80211_HT_CAP_MAX_AMSDU,
-               .ht_supported = 1,
-               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
-               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
-               .mcs = {
-                       .rx_mask[0] = 0xFF,
-                       .rx_highest = __cpu_to_le16(0x41),
-                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
-               },
-       },
-};
-
-static const unsigned long cw1200_ttl[] = {
-       1 * HZ, /* VO */
-       2 * HZ, /* VI */
-       5 * HZ, /* BE */
-       10 * HZ /* BK */
-};
-
-static const struct ieee80211_ops cw1200_ops = {
-       .start                  = cw1200_start,
-       .stop                   = cw1200_stop,
-       .add_interface          = cw1200_add_interface,
-       .remove_interface       = cw1200_remove_interface,
-       .change_interface       = cw1200_change_interface,
-       .tx                     = cw1200_tx,
-       .hw_scan                = cw1200_hw_scan,
-       .set_tim                = cw1200_set_tim,
-       .sta_notify             = cw1200_sta_notify,
-       .sta_add                = cw1200_sta_add,
-       .sta_remove             = cw1200_sta_remove,
-       .set_key                = cw1200_set_key,
-       .set_rts_threshold      = cw1200_set_rts_threshold,
-       .config                 = cw1200_config,
-       .bss_info_changed       = cw1200_bss_info_changed,
-       .prepare_multicast      = cw1200_prepare_multicast,
-       .configure_filter       = cw1200_configure_filter,
-       .conf_tx                = cw1200_conf_tx,
-       .get_stats              = cw1200_get_stats,
-       .ampdu_action           = cw1200_ampdu_action,
-       .flush                  = cw1200_flush,
-#ifdef CONFIG_PM
-       .suspend                = cw1200_wow_suspend,
-       .resume                 = cw1200_wow_resume,
-#endif
-       /* Intentionally not offloaded:                                 */
-       /*.channel_switch       = cw1200_channel_switch,                */
-       /*.remain_on_channel    = cw1200_remain_on_channel,             */
-       /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel,  */
-};
-
-static int cw1200_ba_rx_tids = -1;
-static int cw1200_ba_tx_tids = -1;
-module_param(cw1200_ba_rx_tids, int, 0644);
-module_param(cw1200_ba_tx_tids, int, 0644);
-MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
-MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
-
-#ifdef CONFIG_PM
-static const struct wiphy_wowlan_support cw1200_wowlan_support = {
-       /* Support only for limited wowlan functionalities */
-       .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
-};
-#endif
-
-
-static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
-                                               const bool have_5ghz)
-{
-       int i, band;
-       struct ieee80211_hw *hw;
-       struct cw1200_common *priv;
-
-       hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
-       if (!hw)
-               return NULL;
-
-       priv = hw->priv;
-       priv->hw = hw;
-       priv->hw_type = -1;
-       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
-       priv->rates = cw1200_rates; /* TODO: fetch from FW */
-       priv->mcs_rates = cw1200_n_rates;
-       if (cw1200_ba_rx_tids != -1)
-               priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
-       else
-               priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */
-       if (cw1200_ba_tx_tids != -1)
-               priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
-       else
-               priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */
-
-       ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
-       ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
-       ieee80211_hw_set(hw, AMPDU_AGGREGATION);
-       ieee80211_hw_set(hw, CONNECTION_MONITOR);
-       ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
-       ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
-       ieee80211_hw_set(hw, SIGNAL_DBM);
-       ieee80211_hw_set(hw, SUPPORTS_PS);
-
-       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-                                         BIT(NL80211_IFTYPE_ADHOC) |
-                                         BIT(NL80211_IFTYPE_AP) |
-                                         BIT(NL80211_IFTYPE_MESH_POINT) |
-                                         BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                                         BIT(NL80211_IFTYPE_P2P_GO);
-
-#ifdef CONFIG_PM
-       hw->wiphy->wowlan = &cw1200_wowlan_support;
-#endif
-
-       hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
-
-       hw->queues = 4;
-
-       priv->rts_threshold = -1;
-
-       hw->max_rates = 8;
-       hw->max_rate_tries = 15;
-       hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
-               8;  /* TKIP IV */
-
-       hw->sta_data_size = sizeof(struct cw1200_sta_priv);
-
-       hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz;
-       if (have_5ghz)
-               hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz;
-
-       /* Channel params have to be cleared before registering wiphy again */
-       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-               struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
-               if (!sband)
-                       continue;
-               for (i = 0; i < sband->n_channels; i++) {
-                       sband->channels[i].flags = 0;
-                       sband->channels[i].max_antenna_gain = 0;
-                       sband->channels[i].max_power = 30;
-               }
-       }
-
-       hw->wiphy->max_scan_ssids = 2;
-       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
-
-       if (macaddr)
-               SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
-       else
-               SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
-
-       /* Fix up mac address if necessary */
-       if (hw->wiphy->perm_addr[3] == 0 &&
-           hw->wiphy->perm_addr[4] == 0 &&
-           hw->wiphy->perm_addr[5] == 0) {
-               get_random_bytes(&hw->wiphy->perm_addr[3], 3);
-       }
-
-       mutex_init(&priv->wsm_cmd_mux);
-       mutex_init(&priv->conf_mutex);
-       priv->workqueue = create_singlethread_workqueue("cw1200_wq");
-       sema_init(&priv->scan.lock, 1);
-       INIT_WORK(&priv->scan.work, cw1200_scan_work);
-       INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
-       INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
-       INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
-                         cw1200_clear_recent_scan_work);
-       INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
-       INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
-       INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
-       INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
-       INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
-       spin_lock_init(&priv->event_queue_lock);
-       INIT_LIST_HEAD(&priv->event_queue);
-       INIT_WORK(&priv->event_handler, cw1200_event_handler);
-       INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
-       INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
-       spin_lock_init(&priv->bss_loss_lock);
-       spin_lock_init(&priv->ps_state_lock);
-       INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
-       INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
-       INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
-       INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
-       INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
-       INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
-       INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
-       INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
-       INIT_WORK(&priv->set_beacon_wakeup_period_work,
-                 cw1200_set_beacon_wakeup_period_work);
-       setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout,
-                   (unsigned long)priv);
-
-       if (cw1200_queue_stats_init(&priv->tx_queue_stats,
-                                   CW1200_LINK_ID_MAX,
-                                   cw1200_skb_dtor,
-                                   priv)) {
-               ieee80211_free_hw(hw);
-               return NULL;
-       }
-
-       for (i = 0; i < 4; ++i) {
-               if (cw1200_queue_init(&priv->tx_queue[i],
-                                     &priv->tx_queue_stats, i, 16,
-                                     cw1200_ttl[i])) {
-                       for (; i > 0; i--)
-                               cw1200_queue_deinit(&priv->tx_queue[i - 1]);
-                       cw1200_queue_stats_deinit(&priv->tx_queue_stats);
-                       ieee80211_free_hw(hw);
-                       return NULL;
-               }
-       }
-
-       init_waitqueue_head(&priv->channel_switch_done);
-       init_waitqueue_head(&priv->wsm_cmd_wq);
-       init_waitqueue_head(&priv->wsm_startup_done);
-       init_waitqueue_head(&priv->ps_mode_switch_done);
-       wsm_buf_init(&priv->wsm_cmd_buf);
-       spin_lock_init(&priv->wsm_cmd.lock);
-       priv->wsm_cmd.done = 1;
-       tx_policy_init(priv);
-
-       return hw;
-}
-
-static int cw1200_register_common(struct ieee80211_hw *dev)
-{
-       struct cw1200_common *priv = dev->priv;
-       int err;
-
-#ifdef CONFIG_PM
-       err = cw1200_pm_init(&priv->pm_state, priv);
-       if (err) {
-               pr_err("Cannot init PM. (%d).\n",
-                      err);
-               return err;
-       }
-#endif
-
-       err = ieee80211_register_hw(dev);
-       if (err) {
-               pr_err("Cannot register device (%d).\n",
-                      err);
-#ifdef CONFIG_PM
-               cw1200_pm_deinit(&priv->pm_state);
-#endif
-               return err;
-       }
-
-       cw1200_debug_init(priv);
-
-       pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
-       return 0;
-}
-
-static void cw1200_free_common(struct ieee80211_hw *dev)
-{
-       ieee80211_free_hw(dev);
-}
-
-static void cw1200_unregister_common(struct ieee80211_hw *dev)
-{
-       struct cw1200_common *priv = dev->priv;
-       int i;
-
-       ieee80211_unregister_hw(dev);
-
-       del_timer_sync(&priv->mcast_timeout);
-       cw1200_unregister_bh(priv);
-
-       cw1200_debug_release(priv);
-
-       mutex_destroy(&priv->conf_mutex);
-
-       wsm_buf_deinit(&priv->wsm_cmd_buf);
-
-       destroy_workqueue(priv->workqueue);
-       priv->workqueue = NULL;
-
-       if (priv->sdd) {
-               release_firmware(priv->sdd);
-               priv->sdd = NULL;
-       }
-
-       for (i = 0; i < 4; ++i)
-               cw1200_queue_deinit(&priv->tx_queue[i]);
-
-       cw1200_queue_stats_deinit(&priv->tx_queue_stats);
-#ifdef CONFIG_PM
-       cw1200_pm_deinit(&priv->pm_state);
-#endif
-}
-
-/* Clock is in KHz */
-u32 cw1200_dpll_from_clk(u16 clk_khz)
-{
-       switch (clk_khz) {
-       case 0x32C8: /* 13000 KHz */
-               return 0x1D89D241;
-       case 0x3E80: /* 16000 KHz */
-               return 0x000001E1;
-       case 0x41A0: /* 16800 KHz */
-               return 0x124931C1;
-       case 0x4B00: /* 19200 KHz */
-               return 0x00000191;
-       case 0x5DC0: /* 24000 KHz */
-               return 0x00000141;
-       case 0x6590: /* 26000 KHz */
-               return 0x0EC4F121;
-       case 0x8340: /* 33600 KHz */
-               return 0x092490E1;
-       case 0x9600: /* 38400 KHz */
-               return 0x100010C1;
-       case 0x9C40: /* 40000 KHz */
-               return 0x000000C1;
-       case 0xBB80: /* 48000 KHz */
-               return 0x000000A1;
-       case 0xCB20: /* 52000 KHz */
-               return 0x07627091;
-       default:
-               pr_err("Unknown Refclk freq (0x%04x), using 26000KHz\n",
-                      clk_khz);
-               return 0x0EC4F121;
-       }
-}
-
-int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
-                     struct hwbus_priv *hwbus,
-                     struct device *pdev,
-                     struct cw1200_common **core,
-                     int ref_clk, const u8 *macaddr,
-                     const char *sdd_path, bool have_5ghz)
-{
-       int err = -EINVAL;
-       struct ieee80211_hw *dev;
-       struct cw1200_common *priv;
-       struct wsm_operational_mode mode = {
-               .power_mode = cw1200_power_mode,
-               .disable_more_flag_usage = true,
-       };
-
-       dev = cw1200_init_common(macaddr, have_5ghz);
-       if (!dev)
-               goto err;
-
-       priv = dev->priv;
-       priv->hw_refclk = ref_clk;
-       if (cw1200_refclk)
-               priv->hw_refclk = cw1200_refclk;
-
-       priv->sdd_path = (char *)sdd_path;
-       if (cw1200_sdd_path)
-               priv->sdd_path = cw1200_sdd_path;
-
-       priv->hwbus_ops = hwbus_ops;
-       priv->hwbus_priv = hwbus;
-       priv->pdev = pdev;
-       SET_IEEE80211_DEV(priv->hw, pdev);
-
-       /* Pass struct cw1200_common back up */
-       *core = priv;
-
-       err = cw1200_register_bh(priv);
-       if (err)
-               goto err1;
-
-       err = cw1200_load_firmware(priv);
-       if (err)
-               goto err2;
-
-       if (wait_event_interruptible_timeout(priv->wsm_startup_done,
-                                            priv->firmware_ready,
-                                            3*HZ) <= 0) {
-               /* TODO: Need to find how to reset device
-                  in QUEUE mode properly.
-               */
-               pr_err("Timeout waiting on device startup\n");
-               err = -ETIMEDOUT;
-               goto err2;
-       }
-
-       /* Set low-power mode. */
-       wsm_set_operational_mode(priv, &mode);
-
-       /* Enable multi-TX confirmation */
-       wsm_use_multi_tx_conf(priv, true);
-
-       err = cw1200_register_common(dev);
-       if (err)
-               goto err2;
-
-       return err;
-
-err2:
-       cw1200_unregister_bh(priv);
-err1:
-       cw1200_free_common(dev);
-err:
-       *core = NULL;
-       return err;
-}
-EXPORT_SYMBOL_GPL(cw1200_core_probe);
-
-void cw1200_core_release(struct cw1200_common *self)
-{
-       /* Disable device interrupts */
-       self->hwbus_ops->lock(self->hwbus_priv);
-       __cw1200_irq_enable(self, 0);
-       self->hwbus_ops->unlock(self->hwbus_priv);
-
-       /* And then clean up */
-       cw1200_unregister_common(self->hw);
-       cw1200_free_common(self->hw);
-       return;
-}
-EXPORT_SYMBOL_GPL(cw1200_core_release);
diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c
deleted file mode 100644 (file)
index d2202ae..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Mac80211 power management API for ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2011, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/if_ether.h>
-#include "cw1200.h"
-#include "pm.h"
-#include "sta.h"
-#include "bh.h"
-#include "hwbus.h"
-
-#define CW1200_BEACON_SKIPPING_MULTIPLIER 3
-
-struct cw1200_udp_port_filter {
-       struct wsm_udp_port_filter_hdr hdr;
-       /* Up to 4 filters are allowed. */
-       struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS];
-} __packed;
-
-struct cw1200_ether_type_filter {
-       struct wsm_ether_type_filter_hdr hdr;
-       /* Up to 4 filters are allowed. */
-       struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS];
-} __packed;
-
-static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = {
-       .hdr.num = 2,
-       .filters = {
-               [0] = {
-                       .action = WSM_FILTER_ACTION_FILTER_OUT,
-                       .type = WSM_FILTER_PORT_TYPE_DST,
-                       .port = __cpu_to_le16(67), /* DHCP Bootps */
-               },
-               [1] = {
-                       .action = WSM_FILTER_ACTION_FILTER_OUT,
-                       .type = WSM_FILTER_PORT_TYPE_DST,
-                       .port = __cpu_to_le16(68), /* DHCP Bootpc */
-               },
-       }
-};
-
-static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = {
-       .num = 0,
-};
-
-#ifndef ETH_P_WAPI
-#define ETH_P_WAPI     0x88B4
-#endif
-
-static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = {
-       .hdr.num = 4,
-       .filters = {
-               [0] = {
-                       .action = WSM_FILTER_ACTION_FILTER_IN,
-                       .type = __cpu_to_le16(ETH_P_IP),
-               },
-               [1] = {
-                       .action = WSM_FILTER_ACTION_FILTER_IN,
-                       .type = __cpu_to_le16(ETH_P_PAE),
-               },
-               [2] = {
-                       .action = WSM_FILTER_ACTION_FILTER_IN,
-                       .type = __cpu_to_le16(ETH_P_WAPI),
-               },
-               [3] = {
-                       .action = WSM_FILTER_ACTION_FILTER_IN,
-                       .type = __cpu_to_le16(ETH_P_ARP),
-               },
-       },
-};
-
-static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = {
-       .num = 0,
-};
-
-/* private */
-struct cw1200_suspend_state {
-       unsigned long bss_loss_tmo;
-       unsigned long join_tmo;
-       unsigned long direct_probe;
-       unsigned long link_id_gc;
-       bool beacon_skipping;
-       u8 prev_ps_mode;
-};
-
-static void cw1200_pm_stay_awake_tmo(unsigned long arg)
-{
-       /* XXX what's the point of this ? */
-}
-
-int cw1200_pm_init(struct cw1200_pm_state *pm,
-                  struct cw1200_common *priv)
-{
-       spin_lock_init(&pm->lock);
-
-       setup_timer(&pm->stay_awake, cw1200_pm_stay_awake_tmo,
-                   (unsigned long)pm);
-
-       return 0;
-}
-
-void cw1200_pm_deinit(struct cw1200_pm_state *pm)
-{
-       del_timer_sync(&pm->stay_awake);
-}
-
-void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
-                         unsigned long tmo)
-{
-       long cur_tmo;
-       spin_lock_bh(&pm->lock);
-       cur_tmo = pm->stay_awake.expires - jiffies;
-       if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
-               mod_timer(&pm->stay_awake, jiffies + tmo);
-       spin_unlock_bh(&pm->lock);
-}
-
-static long cw1200_suspend_work(struct delayed_work *work)
-{
-       int ret = cancel_delayed_work(work);
-       long tmo;
-       if (ret > 0) {
-               /* Timer is pending */
-               tmo = work->timer.expires - jiffies;
-               if (tmo < 0)
-                       tmo = 0;
-       } else {
-               tmo = -1;
-       }
-       return tmo;
-}
-
-static int cw1200_resume_work(struct cw1200_common *priv,
-                              struct delayed_work *work,
-                              unsigned long tmo)
-{
-       if ((long)tmo < 0)
-               return 1;
-
-       return queue_delayed_work(priv->workqueue, work, tmo);
-}
-
-int cw1200_can_suspend(struct cw1200_common *priv)
-{
-       if (atomic_read(&priv->bh_rx)) {
-               wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
-               return 0;
-       }
-       return 1;
-}
-EXPORT_SYMBOL_GPL(cw1200_can_suspend);
-
-int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
-{
-       struct cw1200_common *priv = hw->priv;
-       struct cw1200_pm_state *pm_state = &priv->pm_state;
-       struct cw1200_suspend_state *state;
-       int ret;
-
-       spin_lock_bh(&pm_state->lock);
-       ret = timer_pending(&pm_state->stay_awake);
-       spin_unlock_bh(&pm_state->lock);
-       if (ret)
-               return -EAGAIN;
-
-       /* Do not suspend when datapath is not idle */
-       if (priv->tx_queue_stats.num_queued)
-               return -EBUSY;
-
-       /* Make sure there is no configuration requests in progress. */
-       if (!mutex_trylock(&priv->conf_mutex))
-               return -EBUSY;
-
-       /* Ensure pending operations are done.
-        * Note also that wow_suspend must return in ~2.5sec, before
-        * watchdog is triggered.
-        */
-       if (priv->channel_switch_in_progress)
-               goto revert1;
-
-       /* Do not suspend when join is pending */
-       if (priv->join_pending)
-               goto revert1;
-
-       /* Do not suspend when scanning */
-       if (down_trylock(&priv->scan.lock))
-               goto revert1;
-
-       /* Lock TX. */
-       wsm_lock_tx_async(priv);
-
-       /* Wait to avoid possible race with bh code.
-        * But do not wait too long...
-        */
-       if (wait_event_timeout(priv->bh_evt_wq,
-                              !priv->hw_bufs_used, HZ / 10) <= 0)
-               goto revert2;
-
-       /* Set UDP filter */
-       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr);
-
-       /* Set ethernet frame type filter */
-       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr);
-
-       /* Allocate state */
-       state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
-       if (!state)
-               goto revert3;
-
-       /* Change to legacy PS while going to suspend */
-       if (!priv->vif->p2p &&
-           priv->join_status == CW1200_JOIN_STATUS_STA &&
-           priv->powersave_mode.mode != WSM_PSM_PS) {
-               state->prev_ps_mode = priv->powersave_mode.mode;
-               priv->powersave_mode.mode = WSM_PSM_PS;
-               cw1200_set_pm(priv, &priv->powersave_mode);
-               if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
-                                                    !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
-                       goto revert4;
-               }
-       }
-
-       /* Store delayed work states. */
-       state->bss_loss_tmo =
-               cw1200_suspend_work(&priv->bss_loss_work);
-       state->join_tmo =
-               cw1200_suspend_work(&priv->join_timeout);
-       state->direct_probe =
-               cw1200_suspend_work(&priv->scan.probe_work);
-       state->link_id_gc =
-               cw1200_suspend_work(&priv->link_id_gc_work);
-
-       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
-       atomic_set(&priv->recent_scan, 0);
-
-       /* Enable beacon skipping */
-       if (priv->join_status == CW1200_JOIN_STATUS_STA &&
-           priv->join_dtim_period &&
-           !priv->has_multicast_subscription) {
-               state->beacon_skipping = true;
-               wsm_set_beacon_wakeup_period(priv,
-                                            priv->join_dtim_period,
-                                            CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period);
-       }
-
-       /* Stop serving thread */
-       if (cw1200_bh_suspend(priv))
-               goto revert5;
-
-       ret = timer_pending(&priv->mcast_timeout);
-       if (ret)
-               goto revert6;
-
-       /* Store suspend state */
-       pm_state->suspend_state = state;
-
-       /* Enable IRQ wake */
-       ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true);
-       if (ret) {
-               wiphy_err(priv->hw->wiphy,
-                         "PM request failed: %d. WoW is disabled.\n", ret);
-               cw1200_wow_resume(hw);
-               return -EBUSY;
-       }
-
-       /* Force resume if event is coming from the device. */
-       if (atomic_read(&priv->bh_rx)) {
-               cw1200_wow_resume(hw);
-               return -EAGAIN;
-       }
-
-       return 0;
-
-revert6:
-       WARN_ON(cw1200_bh_resume(priv));
-revert5:
-       cw1200_resume_work(priv, &priv->bss_loss_work,
-                          state->bss_loss_tmo);
-       cw1200_resume_work(priv, &priv->join_timeout,
-                          state->join_tmo);
-       cw1200_resume_work(priv, &priv->scan.probe_work,
-                          state->direct_probe);
-       cw1200_resume_work(priv, &priv->link_id_gc_work,
-                          state->link_id_gc);
-revert4:
-       kfree(state);
-revert3:
-       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
-       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
-revert2:
-       wsm_unlock_tx(priv);
-       up(&priv->scan.lock);
-revert1:
-       mutex_unlock(&priv->conf_mutex);
-       return -EBUSY;
-}
-
-int cw1200_wow_resume(struct ieee80211_hw *hw)
-{
-       struct cw1200_common *priv = hw->priv;
-       struct cw1200_pm_state *pm_state = &priv->pm_state;
-       struct cw1200_suspend_state *state;
-
-       state = pm_state->suspend_state;
-       pm_state->suspend_state = NULL;
-
-       /* Disable IRQ wake */
-       priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false);
-
-       /* Scan.lock must be released before BH is resumed other way
-        * in case when BSS_LOST command arrived the processing of the
-        * command will be delayed.
-        */
-       up(&priv->scan.lock);
-
-       /* Resume BH thread */
-       WARN_ON(cw1200_bh_resume(priv));
-
-       /* Restores previous PS mode */
-       if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) {
-               priv->powersave_mode.mode = state->prev_ps_mode;
-               cw1200_set_pm(priv, &priv->powersave_mode);
-       }
-
-       if (state->beacon_skipping) {
-               wsm_set_beacon_wakeup_period(priv, priv->beacon_int *
-                                            priv->join_dtim_period >
-                                            MAX_BEACON_SKIP_TIME_MS ? 1 :
-                                            priv->join_dtim_period, 0);
-               state->beacon_skipping = false;
-       }
-
-       /* Resume delayed work */
-       cw1200_resume_work(priv, &priv->bss_loss_work,
-                          state->bss_loss_tmo);
-       cw1200_resume_work(priv, &priv->join_timeout,
-                          state->join_tmo);
-       cw1200_resume_work(priv, &priv->scan.probe_work,
-                          state->direct_probe);
-       cw1200_resume_work(priv, &priv->link_id_gc_work,
-                          state->link_id_gc);
-
-       /* Remove UDP port filter */
-       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
-
-       /* Remove ethernet frame type filter */
-       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
-
-       /* Unlock datapath */
-       wsm_unlock_tx(priv);
-
-       /* Unlock configuration mutex */
-       mutex_unlock(&priv->conf_mutex);
-
-       /* Free memory */
-       kfree(state);
-
-       return 0;
-}
diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h
deleted file mode 100644 (file)
index 3ed90ff..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers
- *
- * Copyright (c) 2011, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef PM_H_INCLUDED
-#define PM_H_INCLUDED
-
-/* ******************************************************************** */
-/* mac80211 API                                                                */
-
-/* extern */  struct cw1200_common;
-/* private */ struct cw1200_suspend_state;
-
-struct cw1200_pm_state {
-       struct cw1200_suspend_state *suspend_state;
-       struct timer_list stay_awake;
-       struct platform_device *pm_dev;
-       spinlock_t lock; /* Protect access */
-};
-
-#ifdef CONFIG_PM
-int cw1200_pm_init(struct cw1200_pm_state *pm,
-                   struct cw1200_common *priv);
-void cw1200_pm_deinit(struct cw1200_pm_state *pm);
-int cw1200_wow_suspend(struct ieee80211_hw *hw,
-                      struct cfg80211_wowlan *wowlan);
-int cw1200_wow_resume(struct ieee80211_hw *hw);
-int cw1200_can_suspend(struct cw1200_common *priv);
-void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
-                         unsigned long tmo);
-#else
-static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
-                                       unsigned long tmo) {
-}
-#endif
-#endif
diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c
deleted file mode 100644 (file)
index 0ba5ef9..0000000
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <net/mac80211.h>
-#include <linux/sched.h>
-#include "queue.h"
-#include "cw1200.h"
-#include "debug.h"
-
-/* private */ struct cw1200_queue_item
-{
-       struct list_head        head;
-       struct sk_buff          *skb;
-       u32                     packet_id;
-       unsigned long           queue_timestamp;
-       unsigned long           xmit_timestamp;
-       struct cw1200_txpriv    txpriv;
-       u8                      generation;
-};
-
-static inline void __cw1200_queue_lock(struct cw1200_queue *queue)
-{
-       struct cw1200_queue_stats *stats = queue->stats;
-       if (queue->tx_locked_cnt++ == 0) {
-               pr_debug("[TX] Queue %d is locked.\n",
-                        queue->queue_id);
-               ieee80211_stop_queue(stats->priv->hw, queue->queue_id);
-       }
-}
-
-static inline void __cw1200_queue_unlock(struct cw1200_queue *queue)
-{
-       struct cw1200_queue_stats *stats = queue->stats;
-       BUG_ON(!queue->tx_locked_cnt);
-       if (--queue->tx_locked_cnt == 0) {
-               pr_debug("[TX] Queue %d is unlocked.\n",
-                        queue->queue_id);
-               ieee80211_wake_queue(stats->priv->hw, queue->queue_id);
-       }
-}
-
-static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation,
-                                        u8 *queue_id, u8 *item_generation,
-                                        u8 *item_id)
-{
-       *item_id                = (packet_id >>  0) & 0xFF;
-       *item_generation        = (packet_id >>  8) & 0xFF;
-       *queue_id               = (packet_id >> 16) & 0xFF;
-       *queue_generation       = (packet_id >> 24) & 0xFF;
-}
-
-static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id,
-                                           u8 item_generation, u8 item_id)
-{
-       return ((u32)item_id << 0) |
-               ((u32)item_generation << 8) |
-               ((u32)queue_id << 16) |
-               ((u32)queue_generation << 24);
-}
-
-static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats,
-                                struct list_head *gc_list)
-{
-       struct cw1200_queue_item *item, *tmp;
-
-       list_for_each_entry_safe(item, tmp, gc_list, head) {
-               list_del(&item->head);
-               stats->skb_dtor(stats->priv, item->skb, &item->txpriv);
-               kfree(item);
-       }
-}
-
-static void cw1200_queue_register_post_gc(struct list_head *gc_list,
-                                         struct cw1200_queue_item *item)
-{
-       struct cw1200_queue_item *gc_item;
-       gc_item = kmalloc(sizeof(struct cw1200_queue_item),
-                       GFP_ATOMIC);
-       BUG_ON(!gc_item);
-       memcpy(gc_item, item, sizeof(struct cw1200_queue_item));
-       list_add_tail(&gc_item->head, gc_list);
-}
-
-static void __cw1200_queue_gc(struct cw1200_queue *queue,
-                             struct list_head *head,
-                             bool unlock)
-{
-       struct cw1200_queue_stats *stats = queue->stats;
-       struct cw1200_queue_item *item = NULL, *tmp;
-       bool wakeup_stats = false;
-
-       list_for_each_entry_safe(item, tmp, &queue->queue, head) {
-               if (jiffies - item->queue_timestamp < queue->ttl)
-                       break;
-               --queue->num_queued;
-               --queue->link_map_cache[item->txpriv.link_id];
-               spin_lock_bh(&stats->lock);
-               --stats->num_queued;
-               if (!--stats->link_map_cache[item->txpriv.link_id])
-                       wakeup_stats = true;
-               spin_unlock_bh(&stats->lock);
-               cw1200_debug_tx_ttl(stats->priv);
-               cw1200_queue_register_post_gc(head, item);
-               item->skb = NULL;
-               list_move_tail(&item->head, &queue->free_pool);
-       }
-
-       if (wakeup_stats)
-               wake_up(&stats->wait_link_id_empty);
-
-       if (queue->overfull) {
-               if (queue->num_queued <= (queue->capacity >> 1)) {
-                       queue->overfull = false;
-                       if (unlock)
-                               __cw1200_queue_unlock(queue);
-               } else if (item) {
-                       unsigned long tmo = item->queue_timestamp + queue->ttl;
-                       mod_timer(&queue->gc, tmo);
-                       cw1200_pm_stay_awake(&stats->priv->pm_state,
-                                            tmo - jiffies);
-               }
-       }
-}
-
-static void cw1200_queue_gc(unsigned long arg)
-{
-       LIST_HEAD(list);
-       struct cw1200_queue *queue =
-               (struct cw1200_queue *)arg;
-
-       spin_lock_bh(&queue->lock);
-       __cw1200_queue_gc(queue, &list, true);
-       spin_unlock_bh(&queue->lock);
-       cw1200_queue_post_gc(queue->stats, &list);
-}
-
-int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
-                           size_t map_capacity,
-                           cw1200_queue_skb_dtor_t skb_dtor,
-                           struct cw1200_common *priv)
-{
-       memset(stats, 0, sizeof(*stats));
-       stats->map_capacity = map_capacity;
-       stats->skb_dtor = skb_dtor;
-       stats->priv = priv;
-       spin_lock_init(&stats->lock);
-       init_waitqueue_head(&stats->wait_link_id_empty);
-
-       stats->link_map_cache = kzalloc(sizeof(int) * map_capacity,
-                                       GFP_KERNEL);
-       if (!stats->link_map_cache)
-               return -ENOMEM;
-
-       return 0;
-}
-
-int cw1200_queue_init(struct cw1200_queue *queue,
-                     struct cw1200_queue_stats *stats,
-                     u8 queue_id,
-                     size_t capacity,
-                     unsigned long ttl)
-{
-       size_t i;
-
-       memset(queue, 0, sizeof(*queue));
-       queue->stats = stats;
-       queue->capacity = capacity;
-       queue->queue_id = queue_id;
-       queue->ttl = ttl;
-       INIT_LIST_HEAD(&queue->queue);
-       INIT_LIST_HEAD(&queue->pending);
-       INIT_LIST_HEAD(&queue->free_pool);
-       spin_lock_init(&queue->lock);
-       setup_timer(&queue->gc, cw1200_queue_gc, (unsigned long)queue);
-
-       queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
-                       GFP_KERNEL);
-       if (!queue->pool)
-               return -ENOMEM;
-
-       queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity,
-                       GFP_KERNEL);
-       if (!queue->link_map_cache) {
-               kfree(queue->pool);
-               queue->pool = NULL;
-               return -ENOMEM;
-       }
-
-       for (i = 0; i < capacity; ++i)
-               list_add_tail(&queue->pool[i].head, &queue->free_pool);
-
-       return 0;
-}
-
-int cw1200_queue_clear(struct cw1200_queue *queue)
-{
-       int i;
-       LIST_HEAD(gc_list);
-       struct cw1200_queue_stats *stats = queue->stats;
-       struct cw1200_queue_item *item, *tmp;
-
-       spin_lock_bh(&queue->lock);
-       queue->generation++;
-       list_splice_tail_init(&queue->queue, &queue->pending);
-       list_for_each_entry_safe(item, tmp, &queue->pending, head) {
-               WARN_ON(!item->skb);
-               cw1200_queue_register_post_gc(&gc_list, item);
-               item->skb = NULL;
-               list_move_tail(&item->head, &queue->free_pool);
-       }
-       queue->num_queued = 0;
-       queue->num_pending = 0;
-
-       spin_lock_bh(&stats->lock);
-       for (i = 0; i < stats->map_capacity; ++i) {
-               stats->num_queued -= queue->link_map_cache[i];
-               stats->link_map_cache[i] -= queue->link_map_cache[i];
-               queue->link_map_cache[i] = 0;
-       }
-       spin_unlock_bh(&stats->lock);
-       if (queue->overfull) {
-               queue->overfull = false;
-               __cw1200_queue_unlock(queue);
-       }
-       spin_unlock_bh(&queue->lock);
-       wake_up(&stats->wait_link_id_empty);
-       cw1200_queue_post_gc(stats, &gc_list);
-       return 0;
-}
-
-void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
-{
-       kfree(stats->link_map_cache);
-       stats->link_map_cache = NULL;
-}
-
-void cw1200_queue_deinit(struct cw1200_queue *queue)
-{
-       cw1200_queue_clear(queue);
-       del_timer_sync(&queue->gc);
-       INIT_LIST_HEAD(&queue->free_pool);
-       kfree(queue->pool);
-       kfree(queue->link_map_cache);
-       queue->pool = NULL;
-       queue->link_map_cache = NULL;
-       queue->capacity = 0;
-}
-
-size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
-                                  u32 link_id_map)
-{
-       size_t ret;
-       int i, bit;
-       size_t map_capacity = queue->stats->map_capacity;
-
-       if (!link_id_map)
-               return 0;
-
-       spin_lock_bh(&queue->lock);
-       if (link_id_map == (u32)-1) {
-               ret = queue->num_queued - queue->num_pending;
-       } else {
-               ret = 0;
-               for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
-                       if (link_id_map & bit)
-                               ret += queue->link_map_cache[i];
-               }
-       }
-       spin_unlock_bh(&queue->lock);
-       return ret;
-}
-
-int cw1200_queue_put(struct cw1200_queue *queue,
-                    struct sk_buff *skb,
-                    struct cw1200_txpriv *txpriv)
-{
-       int ret = 0;
-       LIST_HEAD(gc_list);
-       struct cw1200_queue_stats *stats = queue->stats;
-
-       if (txpriv->link_id >= queue->stats->map_capacity)
-               return -EINVAL;
-
-       spin_lock_bh(&queue->lock);
-       if (!WARN_ON(list_empty(&queue->free_pool))) {
-               struct cw1200_queue_item *item = list_first_entry(
-                       &queue->free_pool, struct cw1200_queue_item, head);
-               BUG_ON(item->skb);
-
-               list_move_tail(&item->head, &queue->queue);
-               item->skb = skb;
-               item->txpriv = *txpriv;
-               item->generation = 0;
-               item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
-                                                           queue->queue_id,
-                                                           item->generation,
-                                                           item - queue->pool);
-               item->queue_timestamp = jiffies;
-
-               ++queue->num_queued;
-               ++queue->link_map_cache[txpriv->link_id];
-
-               spin_lock_bh(&stats->lock);
-               ++stats->num_queued;
-               ++stats->link_map_cache[txpriv->link_id];
-               spin_unlock_bh(&stats->lock);
-
-               /* TX may happen in parallel sometimes.
-                * Leave extra queue slots so we don't overflow.
-                */
-               if (queue->overfull == false &&
-                   queue->num_queued >=
-                   (queue->capacity - (num_present_cpus() - 1))) {
-                       queue->overfull = true;
-                       __cw1200_queue_lock(queue);
-                       mod_timer(&queue->gc, jiffies);
-               }
-       } else {
-               ret = -ENOENT;
-       }
-       spin_unlock_bh(&queue->lock);
-       return ret;
-}
-
-int cw1200_queue_get(struct cw1200_queue *queue,
-                    u32 link_id_map,
-                    struct wsm_tx **tx,
-                    struct ieee80211_tx_info **tx_info,
-                    const struct cw1200_txpriv **txpriv)
-{
-       int ret = -ENOENT;
-       struct cw1200_queue_item *item;
-       struct cw1200_queue_stats *stats = queue->stats;
-       bool wakeup_stats = false;
-
-       spin_lock_bh(&queue->lock);
-       list_for_each_entry(item, &queue->queue, head) {
-               if (link_id_map & BIT(item->txpriv.link_id)) {
-                       ret = 0;
-                       break;
-               }
-       }
-
-       if (!WARN_ON(ret)) {
-               *tx = (struct wsm_tx *)item->skb->data;
-               *tx_info = IEEE80211_SKB_CB(item->skb);
-               *txpriv = &item->txpriv;
-               (*tx)->packet_id = item->packet_id;
-               list_move_tail(&item->head, &queue->pending);
-               ++queue->num_pending;
-               --queue->link_map_cache[item->txpriv.link_id];
-               item->xmit_timestamp = jiffies;
-
-               spin_lock_bh(&stats->lock);
-               --stats->num_queued;
-               if (!--stats->link_map_cache[item->txpriv.link_id])
-                       wakeup_stats = true;
-               spin_unlock_bh(&stats->lock);
-       }
-       spin_unlock_bh(&queue->lock);
-       if (wakeup_stats)
-               wake_up(&stats->wait_link_id_empty);
-       return ret;
-}
-
-int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id)
-{
-       int ret = 0;
-       u8 queue_generation, queue_id, item_generation, item_id;
-       struct cw1200_queue_item *item;
-       struct cw1200_queue_stats *stats = queue->stats;
-
-       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
-                             &item_generation, &item_id);
-
-       item = &queue->pool[item_id];
-
-       spin_lock_bh(&queue->lock);
-       BUG_ON(queue_id != queue->queue_id);
-       if (queue_generation != queue->generation) {
-               ret = -ENOENT;
-       } else if (item_id >= (unsigned) queue->capacity) {
-               WARN_ON(1);
-               ret = -EINVAL;
-       } else if (item->generation != item_generation) {
-               WARN_ON(1);
-               ret = -ENOENT;
-       } else {
-               --queue->num_pending;
-               ++queue->link_map_cache[item->txpriv.link_id];
-
-               spin_lock_bh(&stats->lock);
-               ++stats->num_queued;
-               ++stats->link_map_cache[item->txpriv.link_id];
-               spin_unlock_bh(&stats->lock);
-
-               item->generation = ++item_generation;
-               item->packet_id = cw1200_queue_mk_packet_id(queue_generation,
-                                                           queue_id,
-                                                           item_generation,
-                                                           item_id);
-               list_move(&item->head, &queue->queue);
-       }
-       spin_unlock_bh(&queue->lock);
-       return ret;
-}
-
-int cw1200_queue_requeue_all(struct cw1200_queue *queue)
-{
-       struct cw1200_queue_item *item, *tmp;
-       struct cw1200_queue_stats *stats = queue->stats;
-       spin_lock_bh(&queue->lock);
-
-       list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) {
-               --queue->num_pending;
-               ++queue->link_map_cache[item->txpriv.link_id];
-
-               spin_lock_bh(&stats->lock);
-               ++stats->num_queued;
-               ++stats->link_map_cache[item->txpriv.link_id];
-               spin_unlock_bh(&stats->lock);
-
-               ++item->generation;
-               item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
-                                                           queue->queue_id,
-                                                           item->generation,
-                                                           item - queue->pool);
-               list_move(&item->head, &queue->queue);
-       }
-       spin_unlock_bh(&queue->lock);
-
-       return 0;
-}
-
-int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id)
-{
-       int ret = 0;
-       u8 queue_generation, queue_id, item_generation, item_id;
-       struct cw1200_queue_item *item;
-       struct cw1200_queue_stats *stats = queue->stats;
-       struct sk_buff *gc_skb = NULL;
-       struct cw1200_txpriv gc_txpriv;
-
-       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
-                             &item_generation, &item_id);
-
-       item = &queue->pool[item_id];
-
-       spin_lock_bh(&queue->lock);
-       BUG_ON(queue_id != queue->queue_id);
-       if (queue_generation != queue->generation) {
-               ret = -ENOENT;
-       } else if (item_id >= (unsigned) queue->capacity) {
-               WARN_ON(1);
-               ret = -EINVAL;
-       } else if (item->generation != item_generation) {
-               WARN_ON(1);
-               ret = -ENOENT;
-       } else {
-               gc_txpriv = item->txpriv;
-               gc_skb = item->skb;
-               item->skb = NULL;
-               --queue->num_pending;
-               --queue->num_queued;
-               ++queue->num_sent;
-               ++item->generation;
-               /* Do not use list_move_tail here, but list_move:
-                * try to utilize cache row.
-                */
-               list_move(&item->head, &queue->free_pool);
-
-               if (queue->overfull &&
-                   (queue->num_queued <= (queue->capacity >> 1))) {
-                       queue->overfull = false;
-                       __cw1200_queue_unlock(queue);
-               }
-       }
-       spin_unlock_bh(&queue->lock);
-
-       if (gc_skb)
-               stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv);
-
-       return ret;
-}
-
-int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
-                        struct sk_buff **skb,
-                        const struct cw1200_txpriv **txpriv)
-{
-       int ret = 0;
-       u8 queue_generation, queue_id, item_generation, item_id;
-       struct cw1200_queue_item *item;
-       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
-                             &item_generation, &item_id);
-
-       item = &queue->pool[item_id];
-
-       spin_lock_bh(&queue->lock);
-       BUG_ON(queue_id != queue->queue_id);
-       if (queue_generation != queue->generation) {
-               ret = -ENOENT;
-       } else if (item_id >= (unsigned) queue->capacity) {
-               WARN_ON(1);
-               ret = -EINVAL;
-       } else if (item->generation != item_generation) {
-               WARN_ON(1);
-               ret = -ENOENT;
-       } else {
-               *skb = item->skb;
-               *txpriv = &item->txpriv;
-       }
-       spin_unlock_bh(&queue->lock);
-       return ret;
-}
-
-void cw1200_queue_lock(struct cw1200_queue *queue)
-{
-       spin_lock_bh(&queue->lock);
-       __cw1200_queue_lock(queue);
-       spin_unlock_bh(&queue->lock);
-}
-
-void cw1200_queue_unlock(struct cw1200_queue *queue)
-{
-       spin_lock_bh(&queue->lock);
-       __cw1200_queue_unlock(queue);
-       spin_unlock_bh(&queue->lock);
-}
-
-bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
-                                    unsigned long *timestamp,
-                                    u32 pending_frame_id)
-{
-       struct cw1200_queue_item *item;
-       bool ret;
-
-       spin_lock_bh(&queue->lock);
-       ret = !list_empty(&queue->pending);
-       if (ret) {
-               list_for_each_entry(item, &queue->pending, head) {
-                       if (item->packet_id != pending_frame_id)
-                               if (time_before(item->xmit_timestamp,
-                                               *timestamp))
-                                       *timestamp = item->xmit_timestamp;
-               }
-       }
-       spin_unlock_bh(&queue->lock);
-       return ret;
-}
-
-bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
-                                u32 link_id_map)
-{
-       bool empty = true;
-
-       spin_lock_bh(&stats->lock);
-       if (link_id_map == (u32)-1) {
-               empty = stats->num_queued == 0;
-       } else {
-               int i;
-               for (i = 0; i < stats->map_capacity; ++i) {
-                       if (link_id_map & BIT(i)) {
-                               if (stats->link_map_cache[i]) {
-                                       empty = false;
-                                       break;
-                               }
-                       }
-               }
-       }
-       spin_unlock_bh(&stats->lock);
-
-       return empty;
-}
diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/cw1200/queue.h
deleted file mode 100644 (file)
index 119f9c7..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef CW1200_QUEUE_H_INCLUDED
-#define CW1200_QUEUE_H_INCLUDED
-
-/* private */ struct cw1200_queue_item;
-
-/* extern */ struct sk_buff;
-/* extern */ struct wsm_tx;
-/* extern */ struct cw1200_common;
-/* extern */ struct ieee80211_tx_queue_stats;
-/* extern */ struct cw1200_txpriv;
-
-/* forward */ struct cw1200_queue_stats;
-
-typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv,
-                                       struct sk_buff *skb,
-                                       const struct cw1200_txpriv *txpriv);
-
-struct cw1200_queue {
-       struct cw1200_queue_stats *stats;
-       size_t                  capacity;
-       size_t                  num_queued;
-       size_t                  num_pending;
-       size_t                  num_sent;
-       struct cw1200_queue_item *pool;
-       struct list_head        queue;
-       struct list_head        free_pool;
-       struct list_head        pending;
-       int                     tx_locked_cnt;
-       int                     *link_map_cache;
-       bool                    overfull;
-       spinlock_t              lock; /* Protect queue entry */
-       u8                      queue_id;
-       u8                      generation;
-       struct timer_list       gc;
-       unsigned long           ttl;
-};
-
-struct cw1200_queue_stats {
-       spinlock_t              lock; /* Protect stats entry */
-       int                     *link_map_cache;
-       int                     num_queued;
-       size_t                  map_capacity;
-       wait_queue_head_t       wait_link_id_empty;
-       cw1200_queue_skb_dtor_t skb_dtor;
-       struct cw1200_common    *priv;
-};
-
-struct cw1200_txpriv {
-       u8 link_id;
-       u8 raw_link_id;
-       u8 tid;
-       u8 rate_id;
-       u8 offset;
-};
-
-int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
-                           size_t map_capacity,
-                           cw1200_queue_skb_dtor_t skb_dtor,
-                           struct cw1200_common *priv);
-int cw1200_queue_init(struct cw1200_queue *queue,
-                     struct cw1200_queue_stats *stats,
-                     u8 queue_id,
-                     size_t capacity,
-                     unsigned long ttl);
-int cw1200_queue_clear(struct cw1200_queue *queue);
-void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats);
-void cw1200_queue_deinit(struct cw1200_queue *queue);
-
-size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
-                                  u32 link_id_map);
-int cw1200_queue_put(struct cw1200_queue *queue,
-                    struct sk_buff *skb,
-                    struct cw1200_txpriv *txpriv);
-int cw1200_queue_get(struct cw1200_queue *queue,
-                    u32 link_id_map,
-                    struct wsm_tx **tx,
-                    struct ieee80211_tx_info **tx_info,
-                    const struct cw1200_txpriv **txpriv);
-int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id);
-int cw1200_queue_requeue_all(struct cw1200_queue *queue);
-int cw1200_queue_remove(struct cw1200_queue *queue,
-                       u32 packet_id);
-int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
-                        struct sk_buff **skb,
-                        const struct cw1200_txpriv **txpriv);
-void cw1200_queue_lock(struct cw1200_queue *queue);
-void cw1200_queue_unlock(struct cw1200_queue *queue);
-bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
-                                    unsigned long *timestamp,
-                                    u32 pending_frame_id);
-
-bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
-                                u32 link_id_map);
-
-static inline u8 cw1200_queue_get_queue_id(u32 packet_id)
-{
-       return (packet_id >> 16) & 0xFF;
-}
-
-static inline u8 cw1200_queue_get_generation(u32 packet_id)
-{
-       return (packet_id >>  8) & 0xFF;
-}
-
-#endif /* CW1200_QUEUE_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c
deleted file mode 100644 (file)
index bff81b8..0000000
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- * Scan implementation for ST-Ericsson CW1200 mac80211 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/sched.h>
-#include "cw1200.h"
-#include "scan.h"
-#include "sta.h"
-#include "pm.h"
-
-static void cw1200_scan_restart_delayed(struct cw1200_common *priv);
-
-static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
-{
-       int ret, i;
-       int tmo = 2000;
-
-       switch (priv->join_status) {
-       case CW1200_JOIN_STATUS_PRE_STA:
-       case CW1200_JOIN_STATUS_JOINING:
-               return -EBUSY;
-       default:
-               break;
-       }
-
-       wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
-                 scan->type, scan->num_channels, scan->flags);
-
-       for (i = 0; i < scan->num_channels; ++i)
-               tmo += scan->ch[i].max_chan_time + 10;
-
-       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
-       atomic_set(&priv->scan.in_progress, 1);
-       atomic_set(&priv->recent_scan, 1);
-       cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo));
-       queue_delayed_work(priv->workqueue, &priv->scan.timeout,
-                          msecs_to_jiffies(tmo));
-       ret = wsm_scan(priv, scan);
-       if (ret) {
-               atomic_set(&priv->scan.in_progress, 0);
-               cancel_delayed_work_sync(&priv->scan.timeout);
-               cw1200_scan_restart_delayed(priv);
-       }
-       return ret;
-}
-
-int cw1200_hw_scan(struct ieee80211_hw *hw,
-                  struct ieee80211_vif *vif,
-                  struct ieee80211_scan_request *hw_req)
-{
-       struct cw1200_common *priv = hw->priv;
-       struct cfg80211_scan_request *req = &hw_req->req;
-       struct wsm_template_frame frame = {
-               .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
-       };
-       int i, ret;
-
-       if (!priv->vif)
-               return -EINVAL;
-
-       /* Scan when P2P_GO corrupt firmware MiniAP mode */
-       if (priv->join_status == CW1200_JOIN_STATUS_AP)
-               return -EOPNOTSUPP;
-
-       if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
-               req->n_ssids = 0;
-
-       wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
-                 req->n_ssids);
-
-       if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
-               return -EINVAL;
-
-       frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
-               req->ie_len);
-       if (!frame.skb)
-               return -ENOMEM;
-
-       if (req->ie_len)
-               memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len);
-
-       /* will be unlocked in cw1200_scan_work() */
-       down(&priv->scan.lock);
-       mutex_lock(&priv->conf_mutex);
-
-       ret = wsm_set_template_frame(priv, &frame);
-       if (!ret) {
-               /* Host want to be the probe responder. */
-               ret = wsm_set_probe_responder(priv, true);
-       }
-       if (ret) {
-               mutex_unlock(&priv->conf_mutex);
-               up(&priv->scan.lock);
-               dev_kfree_skb(frame.skb);
-               return ret;
-       }
-
-       wsm_lock_tx(priv);
-
-       BUG_ON(priv->scan.req);
-       priv->scan.req = req;
-       priv->scan.n_ssids = 0;
-       priv->scan.status = 0;
-       priv->scan.begin = &req->channels[0];
-       priv->scan.curr = priv->scan.begin;
-       priv->scan.end = &req->channels[req->n_channels];
-       priv->scan.output_power = priv->output_power;
-
-       for (i = 0; i < req->n_ssids; ++i) {
-               struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
-               memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
-               dst->length = req->ssids[i].ssid_len;
-               ++priv->scan.n_ssids;
-       }
-
-       mutex_unlock(&priv->conf_mutex);
-
-       if (frame.skb)
-               dev_kfree_skb(frame.skb);
-       queue_work(priv->workqueue, &priv->scan.work);
-       return 0;
-}
-
-void cw1200_scan_work(struct work_struct *work)
-{
-       struct cw1200_common *priv = container_of(work, struct cw1200_common,
-                                                       scan.work);
-       struct ieee80211_channel **it;
-       struct wsm_scan scan = {
-               .type = WSM_SCAN_TYPE_FOREGROUND,
-               .flags = WSM_SCAN_FLAG_SPLIT_METHOD,
-       };
-       bool first_run = (priv->scan.begin == priv->scan.curr &&
-                         priv->scan.begin != priv->scan.end);
-       int i;
-
-       if (first_run) {
-               /* Firmware gets crazy if scan request is sent
-                * when STA is joined but not yet associated.
-                * Force unjoin in this case.
-                */
-               if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
-                       cw1200_join_timeout(&priv->join_timeout.work);
-       }
-
-       mutex_lock(&priv->conf_mutex);
-
-       if (first_run) {
-               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
-                   !(priv->powersave_mode.mode & WSM_PSM_PS)) {
-                       struct wsm_set_pm pm = priv->powersave_mode;
-                       pm.mode = WSM_PSM_PS;
-                       cw1200_set_pm(priv, &pm);
-               } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
-                       /* FW bug: driver has to restart p2p-dev mode
-                        * after scan
-                        */
-                       cw1200_disable_listening(priv);
-               }
-       }
-
-       if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
-               if (priv->scan.output_power != priv->output_power)
-                       wsm_set_output_power(priv, priv->output_power * 10);
-               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
-                   !(priv->powersave_mode.mode & WSM_PSM_PS))
-                       cw1200_set_pm(priv, &priv->powersave_mode);
-
-               if (priv->scan.status < 0)
-                       wiphy_warn(priv->hw->wiphy,
-                                  "[SCAN] Scan failed (%d).\n",
-                                  priv->scan.status);
-               else if (priv->scan.req)
-                       wiphy_dbg(priv->hw->wiphy,
-                                 "[SCAN] Scan completed.\n");
-               else
-                       wiphy_dbg(priv->hw->wiphy,
-                                 "[SCAN] Scan canceled.\n");
-
-               priv->scan.req = NULL;
-               cw1200_scan_restart_delayed(priv);
-               wsm_unlock_tx(priv);
-               mutex_unlock(&priv->conf_mutex);
-               ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0);
-               up(&priv->scan.lock);
-               return;
-       } else {
-               struct ieee80211_channel *first = *priv->scan.curr;
-               for (it = priv->scan.curr + 1, i = 1;
-                    it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
-                    ++it, ++i) {
-                       if ((*it)->band != first->band)
-                               break;
-                       if (((*it)->flags ^ first->flags) &
-                                       IEEE80211_CHAN_NO_IR)
-                               break;
-                       if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
-                           (*it)->max_power != first->max_power)
-                               break;
-               }
-               scan.band = first->band;
-
-               if (priv->scan.req->no_cck)
-                       scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
-               else
-                       scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
-               scan.num_probes =
-                       (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
-               scan.num_ssids = priv->scan.n_ssids;
-               scan.ssids = &priv->scan.ssids[0];
-               scan.num_channels = it - priv->scan.curr;
-               /* TODO: Is it optimal? */
-               scan.probe_delay = 100;
-               /* It is not stated in WSM specification, however
-                * FW team says that driver may not use FG scan
-                * when joined.
-                */
-               if (priv->join_status == CW1200_JOIN_STATUS_STA) {
-                       scan.type = WSM_SCAN_TYPE_BACKGROUND;
-                       scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
-               }
-               scan.ch = kzalloc(
-                       sizeof(struct wsm_scan_ch) * (it - priv->scan.curr),
-                       GFP_KERNEL);
-               if (!scan.ch) {
-                       priv->scan.status = -ENOMEM;
-                       goto fail;
-               }
-               for (i = 0; i < scan.num_channels; ++i) {
-                       scan.ch[i].number = priv->scan.curr[i]->hw_value;
-                       if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
-                               scan.ch[i].min_chan_time = 50;
-                               scan.ch[i].max_chan_time = 100;
-                       } else {
-                               scan.ch[i].min_chan_time = 10;
-                               scan.ch[i].max_chan_time = 25;
-                       }
-               }
-               if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
-                   priv->scan.output_power != first->max_power) {
-                       priv->scan.output_power = first->max_power;
-                       wsm_set_output_power(priv,
-                                            priv->scan.output_power * 10);
-               }
-               priv->scan.status = cw1200_scan_start(priv, &scan);
-               kfree(scan.ch);
-               if (priv->scan.status)
-                       goto fail;
-               priv->scan.curr = it;
-       }
-       mutex_unlock(&priv->conf_mutex);
-       return;
-
-fail:
-       priv->scan.curr = priv->scan.end;
-       mutex_unlock(&priv->conf_mutex);
-       queue_work(priv->workqueue, &priv->scan.work);
-       return;
-}
-
-static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
-{
-       /* FW bug: driver has to restart p2p-dev mode after scan. */
-       if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
-               cw1200_enable_listening(priv);
-               cw1200_update_filtering(priv);
-       }
-
-       if (priv->delayed_unjoin) {
-               priv->delayed_unjoin = false;
-               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
-                       wsm_unlock_tx(priv);
-       } else if (priv->delayed_link_loss) {
-                       wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
-                       priv->delayed_link_loss = 0;
-                       cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
-       }
-}
-
-static void cw1200_scan_complete(struct cw1200_common *priv)
-{
-       queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
-       if (priv->scan.direct_probe) {
-               wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
-               cw1200_scan_restart_delayed(priv);
-               priv->scan.direct_probe = 0;
-               up(&priv->scan.lock);
-               wsm_unlock_tx(priv);
-       } else {
-               cw1200_scan_work(&priv->scan.work);
-       }
-}
-
-void cw1200_scan_failed_cb(struct cw1200_common *priv)
-{
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
-               /* STA is stopped. */
-               return;
-
-       if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
-               priv->scan.status = -EIO;
-               queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
-       }
-}
-
-
-void cw1200_scan_complete_cb(struct cw1200_common *priv,
-                               struct wsm_scan_complete *arg)
-{
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
-               /* STA is stopped. */
-               return;
-
-       if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
-               priv->scan.status = 1;
-               queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
-       }
-}
-
-void cw1200_clear_recent_scan_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common,
-                            clear_recent_scan_work.work);
-       atomic_xchg(&priv->recent_scan, 0);
-}
-
-void cw1200_scan_timeout(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, scan.timeout.work);
-       if (atomic_xchg(&priv->scan.in_progress, 0)) {
-               if (priv->scan.status > 0) {
-                       priv->scan.status = 0;
-               } else if (!priv->scan.status) {
-                       wiphy_warn(priv->hw->wiphy,
-                                  "Timeout waiting for scan complete notification.\n");
-                       priv->scan.status = -ETIMEDOUT;
-                       priv->scan.curr = priv->scan.end;
-                       wsm_stop_scan(priv);
-               }
-               cw1200_scan_complete(priv);
-       }
-}
-
-void cw1200_probe_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, scan.probe_work.work);
-       u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
-       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
-       const struct cw1200_txpriv *txpriv;
-       struct wsm_tx *wsm;
-       struct wsm_template_frame frame = {
-               .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
-       };
-       struct wsm_ssid ssids[1] = {{
-               .length = 0,
-       } };
-       struct wsm_scan_ch ch[1] = {{
-               .min_chan_time = 0,
-               .max_chan_time = 10,
-       } };
-       struct wsm_scan scan = {
-               .type = WSM_SCAN_TYPE_FOREGROUND,
-               .num_probes = 1,
-               .probe_delay = 0,
-               .num_channels = 1,
-               .ssids = ssids,
-               .ch = ch,
-       };
-       u8 *ies;
-       size_t ies_len;
-       int ret;
-
-       wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
-
-       mutex_lock(&priv->conf_mutex);
-       if (down_trylock(&priv->scan.lock)) {
-               /* Scan is already in progress. Requeue self. */
-               schedule();
-               queue_delayed_work(priv->workqueue, &priv->scan.probe_work,
-                                  msecs_to_jiffies(100));
-               mutex_unlock(&priv->conf_mutex);
-               return;
-       }
-
-       /* Make sure we still have a pending probe req */
-       if (cw1200_queue_get_skb(queue, priv->pending_frame_id,
-                                &frame.skb, &txpriv)) {
-               up(&priv->scan.lock);
-               mutex_unlock(&priv->conf_mutex);
-               wsm_unlock_tx(priv);
-               return;
-       }
-       wsm = (struct wsm_tx *)frame.skb->data;
-       scan.max_tx_rate = wsm->max_tx_rate;
-       scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
-               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
-       if (priv->join_status == CW1200_JOIN_STATUS_STA ||
-           priv->join_status == CW1200_JOIN_STATUS_IBSS) {
-               scan.type = WSM_SCAN_TYPE_BACKGROUND;
-               scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
-       }
-       ch[0].number = priv->channel->hw_value;
-
-       skb_pull(frame.skb, txpriv->offset);
-
-       ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
-       ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
-
-       if (ies_len) {
-               u8 *ssidie =
-                       (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
-               if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
-                       u8 *nextie = &ssidie[2 + ssidie[1]];
-                       /* Remove SSID from the IE list. It has to be provided
-                        * as a separate argument in cw1200_scan_start call
-                        */
-
-                       /* Store SSID localy */
-                       ssids[0].length = ssidie[1];
-                       memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
-                       scan.num_ssids = 1;
-
-                       /* Remove SSID from IE list */
-                       ssidie[1] = 0;
-                       memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
-                       skb_trim(frame.skb, frame.skb->len - ssids[0].length);
-               }
-       }
-
-       /* FW bug: driver has to restart p2p-dev mode after scan */
-       if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
-               cw1200_disable_listening(priv);
-       ret = wsm_set_template_frame(priv, &frame);
-       priv->scan.direct_probe = 1;
-       if (!ret) {
-               wsm_flush_tx(priv);
-               ret = cw1200_scan_start(priv, &scan);
-       }
-       mutex_unlock(&priv->conf_mutex);
-
-       skb_push(frame.skb, txpriv->offset);
-       if (!ret)
-               IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
-       BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
-
-       if (ret) {
-               priv->scan.direct_probe = 0;
-               up(&priv->scan.lock);
-               wsm_unlock_tx(priv);
-       }
-
-       return;
-}
diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h
deleted file mode 100644 (file)
index cc75459..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Scan interface for ST-Ericsson CW1200 mac80211 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef SCAN_H_INCLUDED
-#define SCAN_H_INCLUDED
-
-#include <linux/semaphore.h>
-#include "wsm.h"
-
-/* external */ struct sk_buff;
-/* external */ struct cfg80211_scan_request;
-/* external */ struct ieee80211_channel;
-/* external */ struct ieee80211_hw;
-/* external */ struct work_struct;
-
-struct cw1200_scan {
-       struct semaphore lock;
-       struct work_struct work;
-       struct delayed_work timeout;
-       struct cfg80211_scan_request *req;
-       struct ieee80211_channel **begin;
-       struct ieee80211_channel **curr;
-       struct ieee80211_channel **end;
-       struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
-       int output_power;
-       int n_ssids;
-       int status;
-       atomic_t in_progress;
-       /* Direct probe requests workaround */
-       struct delayed_work probe_work;
-       int direct_probe;
-};
-
-int cw1200_hw_scan(struct ieee80211_hw *hw,
-                  struct ieee80211_vif *vif,
-                  struct ieee80211_scan_request *hw_req);
-void cw1200_scan_work(struct work_struct *work);
-void cw1200_scan_timeout(struct work_struct *work);
-void cw1200_clear_recent_scan_work(struct work_struct *work);
-void cw1200_scan_complete_cb(struct cw1200_common *priv,
-                            struct wsm_scan_complete *arg);
-void cw1200_scan_failed_cb(struct cw1200_common *priv);
-
-/* ******************************************************************** */
-/* Raw probe requests TX workaround                                    */
-void cw1200_probe_work(struct work_struct *work);
-
-#endif
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c
deleted file mode 100644 (file)
index 95a7fdb..0000000
+++ /dev/null
@@ -1,2399 +0,0 @@
-/*
- * Mac80211 STA API for ST-Ericsson CW1200 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/vmalloc.h>
-#include <linux/sched.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/etherdevice.h>
-
-#include "cw1200.h"
-#include "sta.h"
-#include "fwio.h"
-#include "bh.h"
-#include "debug.h"
-
-#ifndef ERP_INFO_BYTE_OFFSET
-#define ERP_INFO_BYTE_OFFSET 2
-#endif
-
-static void cw1200_do_join(struct cw1200_common *priv);
-static void cw1200_do_unjoin(struct cw1200_common *priv);
-
-static int cw1200_upload_beacon(struct cw1200_common *priv);
-static int cw1200_upload_pspoll(struct cw1200_common *priv);
-static int cw1200_upload_null(struct cw1200_common *priv);
-static int cw1200_upload_qosnull(struct cw1200_common *priv);
-static int cw1200_start_ap(struct cw1200_common *priv);
-static int cw1200_update_beaconing(struct cw1200_common *priv);
-static int cw1200_enable_beaconing(struct cw1200_common *priv,
-                                  bool enable);
-static void __cw1200_sta_notify(struct ieee80211_hw *dev,
-                               struct ieee80211_vif *vif,
-                               enum sta_notify_cmd notify_cmd,
-                               int link_id);
-static int __cw1200_flush(struct cw1200_common *priv, bool drop);
-
-static inline void __cw1200_free_event_queue(struct list_head *list)
-{
-       struct cw1200_wsm_event *event, *tmp;
-       list_for_each_entry_safe(event, tmp, list, link) {
-               list_del(&event->link);
-               kfree(event);
-       }
-}
-
-/* ******************************************************************** */
-/* STA API                                                             */
-
-int cw1200_start(struct ieee80211_hw *dev)
-{
-       struct cw1200_common *priv = dev->priv;
-       int ret = 0;
-
-       cw1200_pm_stay_awake(&priv->pm_state, HZ);
-
-       mutex_lock(&priv->conf_mutex);
-
-       /* default EDCA */
-       WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false);
-       WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false);
-       WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false);
-       WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false);
-       ret = wsm_set_edca_params(priv, &priv->edca);
-       if (ret)
-               goto out;
-
-       ret = cw1200_set_uapsd_param(priv, &priv->edca);
-       if (ret)
-               goto out;
-
-       priv->setbssparams_done = false;
-
-       memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
-       priv->mode = NL80211_IFTYPE_MONITOR;
-       priv->wep_default_key_id = -1;
-
-       priv->cqm_beacon_loss_count = 10;
-
-       ret = cw1200_setup_mac(priv);
-       if (ret)
-               goto out;
-
-out:
-       mutex_unlock(&priv->conf_mutex);
-       return ret;
-}
-
-void cw1200_stop(struct ieee80211_hw *dev)
-{
-       struct cw1200_common *priv = dev->priv;
-       LIST_HEAD(list);
-       int i;
-
-       wsm_lock_tx(priv);
-
-       while (down_trylock(&priv->scan.lock)) {
-               /* Scan is in progress. Force it to stop. */
-               priv->scan.req = NULL;
-               schedule();
-       }
-       up(&priv->scan.lock);
-
-       cancel_delayed_work_sync(&priv->scan.probe_work);
-       cancel_delayed_work_sync(&priv->scan.timeout);
-       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
-       cancel_delayed_work_sync(&priv->join_timeout);
-       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
-       cancel_work_sync(&priv->unjoin_work);
-       cancel_delayed_work_sync(&priv->link_id_gc_work);
-       flush_workqueue(priv->workqueue);
-       del_timer_sync(&priv->mcast_timeout);
-       mutex_lock(&priv->conf_mutex);
-       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
-       priv->listening = false;
-
-       spin_lock(&priv->event_queue_lock);
-       list_splice_init(&priv->event_queue, &list);
-       spin_unlock(&priv->event_queue_lock);
-       __cw1200_free_event_queue(&list);
-
-
-       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
-       priv->join_pending = false;
-
-       for (i = 0; i < 4; i++)
-               cw1200_queue_clear(&priv->tx_queue[i]);
-       mutex_unlock(&priv->conf_mutex);
-       tx_policy_clean(priv);
-
-       /* HACK! */
-       if (atomic_xchg(&priv->tx_lock, 1) != 1)
-               pr_debug("[STA] TX is force-unlocked due to stop request.\n");
-
-       wsm_unlock_tx(priv);
-       atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */
-}
-
-static int cw1200_bssloss_mitigation = 1;
-module_param(cw1200_bssloss_mitigation, int, 0644);
-MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)");
-
-
-void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
-                            int init, int good, int bad)
-{
-       int tx = 0;
-
-       priv->delayed_link_loss = 0;
-       cancel_work_sync(&priv->bss_params_work);
-
-       pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n",
-                priv->bss_loss_state,
-                init, good, bad,
-                atomic_read(&priv->tx_lock),
-                priv->delayed_unjoin);
-
-       /* If we have a pending unjoin */
-       if (priv->delayed_unjoin)
-               return;
-
-       if (init) {
-               queue_delayed_work(priv->workqueue,
-                                  &priv->bss_loss_work,
-                                  HZ);
-               priv->bss_loss_state = 0;
-
-               /* Skip the confimration procedure in P2P case */
-               if (!priv->vif->p2p && !atomic_read(&priv->tx_lock))
-                       tx = 1;
-       } else if (good) {
-               cancel_delayed_work_sync(&priv->bss_loss_work);
-               priv->bss_loss_state = 0;
-               queue_work(priv->workqueue, &priv->bss_params_work);
-       } else if (bad) {
-               /* XXX Should we just keep going until we time out? */
-               if (priv->bss_loss_state < 3)
-                       tx = 1;
-       } else {
-               cancel_delayed_work_sync(&priv->bss_loss_work);
-               priv->bss_loss_state = 0;
-       }
-
-       /* Bypass mitigation if it's disabled */
-       if (!cw1200_bssloss_mitigation)
-               tx = 0;
-
-       /* Spit out a NULL packet to our AP if necessary */
-       if (tx) {
-               struct sk_buff *skb;
-
-               priv->bss_loss_state++;
-
-               skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
-               WARN_ON(!skb);
-               if (skb)
-                       cw1200_tx(priv->hw, NULL, skb);
-       }
-}
-
-int cw1200_add_interface(struct ieee80211_hw *dev,
-                        struct ieee80211_vif *vif)
-{
-       int ret;
-       struct cw1200_common *priv = dev->priv;
-       /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
-
-       vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
-                            IEEE80211_VIF_SUPPORTS_UAPSD |
-                            IEEE80211_VIF_SUPPORTS_CQM_RSSI;
-
-       mutex_lock(&priv->conf_mutex);
-
-       if (priv->mode != NL80211_IFTYPE_MONITOR) {
-               mutex_unlock(&priv->conf_mutex);
-               return -EOPNOTSUPP;
-       }
-
-       switch (vif->type) {
-       case NL80211_IFTYPE_STATION:
-       case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_MESH_POINT:
-       case NL80211_IFTYPE_AP:
-               priv->mode = vif->type;
-               break;
-       default:
-               mutex_unlock(&priv->conf_mutex);
-               return -EOPNOTSUPP;
-       }
-
-       priv->vif = vif;
-       memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
-       ret = cw1200_setup_mac(priv);
-       /* Enable auto-calibration */
-       /* Exception in subsequent channel switch; disabled.
-        *  wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
-        *      &auto_calibration_mode, sizeof(auto_calibration_mode));
-       */
-
-       mutex_unlock(&priv->conf_mutex);
-       return ret;
-}
-
-void cw1200_remove_interface(struct ieee80211_hw *dev,
-                            struct ieee80211_vif *vif)
-{
-       struct cw1200_common *priv = dev->priv;
-       struct wsm_reset reset = {
-               .reset_statistics = true,
-       };
-       int i;
-
-       mutex_lock(&priv->conf_mutex);
-       switch (priv->join_status) {
-       case CW1200_JOIN_STATUS_JOINING:
-       case CW1200_JOIN_STATUS_PRE_STA:
-       case CW1200_JOIN_STATUS_STA:
-       case CW1200_JOIN_STATUS_IBSS:
-               wsm_lock_tx(priv);
-               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
-                       wsm_unlock_tx(priv);
-               break;
-       case CW1200_JOIN_STATUS_AP:
-               for (i = 0; priv->link_id_map; ++i) {
-                       if (priv->link_id_map & BIT(i)) {
-                               reset.link_id = i;
-                               wsm_reset(priv, &reset);
-                               priv->link_id_map &= ~BIT(i);
-                       }
-               }
-               memset(priv->link_id_db, 0, sizeof(priv->link_id_db));
-               priv->sta_asleep_mask = 0;
-               priv->enable_beacon = false;
-               priv->tx_multicast = false;
-               priv->aid0_bit_set = false;
-               priv->buffered_multicasts = false;
-               priv->pspoll_mask = 0;
-               reset.link_id = 0;
-               wsm_reset(priv, &reset);
-               break;
-       case CW1200_JOIN_STATUS_MONITOR:
-               cw1200_update_listening(priv, false);
-               break;
-       default:
-               break;
-       }
-       priv->vif = NULL;
-       priv->mode = NL80211_IFTYPE_MONITOR;
-       eth_zero_addr(priv->mac_addr);
-       memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
-       cw1200_free_keys(priv);
-       cw1200_setup_mac(priv);
-       priv->listening = false;
-       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
-       if (!__cw1200_flush(priv, true))
-               wsm_unlock_tx(priv);
-
-       mutex_unlock(&priv->conf_mutex);
-}
-
-int cw1200_change_interface(struct ieee80211_hw *dev,
-                           struct ieee80211_vif *vif,
-                           enum nl80211_iftype new_type,
-                           bool p2p)
-{
-       int ret = 0;
-       pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type,
-                p2p, vif->type, vif->p2p);
-
-       if (new_type != vif->type || vif->p2p != p2p) {
-               cw1200_remove_interface(dev, vif);
-               vif->type = new_type;
-               vif->p2p = p2p;
-               ret = cw1200_add_interface(dev, vif);
-       }
-
-       return ret;
-}
-
-int cw1200_config(struct ieee80211_hw *dev, u32 changed)
-{
-       int ret = 0;
-       struct cw1200_common *priv = dev->priv;
-       struct ieee80211_conf *conf = &dev->conf;
-
-       pr_debug("CONFIG CHANGED:  %08x\n", changed);
-
-       down(&priv->scan.lock);
-       mutex_lock(&priv->conf_mutex);
-       /* TODO: IEEE80211_CONF_CHANGE_QOS */
-       /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */
-
-       if (changed & IEEE80211_CONF_CHANGE_POWER) {
-               priv->output_power = conf->power_level;
-               pr_debug("[STA] TX power: %d\n", priv->output_power);
-               wsm_set_output_power(priv, priv->output_power * 10);
-       }
-
-       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
-           (priv->channel != conf->chandef.chan)) {
-               struct ieee80211_channel *ch = conf->chandef.chan;
-               struct wsm_switch_channel channel = {
-                       .channel_number = ch->hw_value,
-               };
-               pr_debug("[STA] Freq %d (wsm ch: %d).\n",
-                        ch->center_freq, ch->hw_value);
-
-               /* __cw1200_flush() implicitly locks tx, if successful */
-               if (!__cw1200_flush(priv, false)) {
-                       if (!wsm_switch_channel(priv, &channel)) {
-                               ret = wait_event_timeout(priv->channel_switch_done,
-                                                        !priv->channel_switch_in_progress,
-                                                        3 * HZ);
-                               if (ret) {
-                                       /* Already unlocks if successful */
-                                       priv->channel = ch;
-                                       ret = 0;
-                               } else {
-                                       ret = -ETIMEDOUT;
-                               }
-                       } else {
-                               /* Unlock if switch channel fails */
-                               wsm_unlock_tx(priv);
-                       }
-               }
-       }
-
-       if (changed & IEEE80211_CONF_CHANGE_PS) {
-               if (!(conf->flags & IEEE80211_CONF_PS))
-                       priv->powersave_mode.mode = WSM_PSM_ACTIVE;
-               else if (conf->dynamic_ps_timeout <= 0)
-                       priv->powersave_mode.mode = WSM_PSM_PS;
-               else
-                       priv->powersave_mode.mode = WSM_PSM_FAST_PS;
-
-               /* Firmware requires that value for this 1-byte field must
-                * be specified in units of 500us. Values above the 128ms
-                * threshold are not supported.
-                */
-               if (conf->dynamic_ps_timeout >= 0x80)
-                       priv->powersave_mode.fast_psm_idle_period = 0xFF;
-               else
-                       priv->powersave_mode.fast_psm_idle_period =
-                                       conf->dynamic_ps_timeout << 1;
-
-               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
-                   priv->bss_params.aid)
-                       cw1200_set_pm(priv, &priv->powersave_mode);
-       }
-
-       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
-               /* TBD: It looks like it's transparent
-                * there's a monitor interface present -- use this
-                * to determine for example whether to calculate
-                * timestamps for packets or not, do not use instead
-                * of filter flags!
-                */
-       }
-
-       if (changed & IEEE80211_CONF_CHANGE_IDLE) {
-               struct wsm_operational_mode mode = {
-                       .power_mode = cw1200_power_mode,
-                       .disable_more_flag_usage = true,
-               };
-
-               wsm_lock_tx(priv);
-               /* Disable p2p-dev mode forced by TX request */
-               if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) &&
-                   (conf->flags & IEEE80211_CONF_IDLE) &&
-                   !priv->listening) {
-                       cw1200_disable_listening(priv);
-                       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
-               }
-               wsm_set_operational_mode(priv, &mode);
-               wsm_unlock_tx(priv);
-       }
-
-       if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
-               pr_debug("[STA] Retry limits: %d (long), %d (short).\n",
-                        conf->long_frame_max_tx_count,
-                        conf->short_frame_max_tx_count);
-               spin_lock_bh(&priv->tx_policy_cache.lock);
-               priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
-               priv->short_frame_max_tx_count =
-                       (conf->short_frame_max_tx_count < 0x0F) ?
-                       conf->short_frame_max_tx_count : 0x0F;
-               priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
-               spin_unlock_bh(&priv->tx_policy_cache.lock);
-       }
-       mutex_unlock(&priv->conf_mutex);
-       up(&priv->scan.lock);
-       return ret;
-}
-
-void cw1200_update_filtering(struct cw1200_common *priv)
-{
-       int ret;
-       bool bssid_filtering = !priv->rx_filter.bssid;
-       bool is_p2p = priv->vif && priv->vif->p2p;
-       bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type;
-
-       static struct wsm_beacon_filter_control bf_ctrl;
-       static struct wsm_mib_beacon_filter_table bf_tbl = {
-               .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC,
-               .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
-                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
-                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
-               .entry[0].oui[0] = 0x50,
-               .entry[0].oui[1] = 0x6F,
-               .entry[0].oui[2] = 0x9A,
-               .entry[1].ie_id = WLAN_EID_HT_OPERATION,
-               .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
-                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
-                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
-               .entry[2].ie_id = WLAN_EID_ERP_INFO,
-               .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
-                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
-                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
-       };
-
-       if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE)
-               return;
-       else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
-               bssid_filtering = false;
-
-       if (priv->disable_beacon_filter) {
-               bf_ctrl.enabled = 0;
-               bf_ctrl.bcn_count = 1;
-               bf_tbl.num = __cpu_to_le32(0);
-       } else if (is_p2p || !is_sta) {
-               bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE |
-                       WSM_BEACON_FILTER_AUTO_ERP;
-               bf_ctrl.bcn_count = 0;
-               bf_tbl.num = __cpu_to_le32(2);
-       } else {
-               bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE;
-               bf_ctrl.bcn_count = 0;
-               bf_tbl.num = __cpu_to_le32(3);
-       }
-
-       /* When acting as p2p client being connected to p2p GO, in order to
-        * receive frames from a different p2p device, turn off bssid filter.
-        *
-        * WARNING: FW dependency!
-        * This can only be used with FW WSM371 and its successors.
-        * In that FW version even with bssid filter turned off,
-        * device will block most of the unwanted frames.
-        */
-       if (is_p2p)
-               bssid_filtering = false;
-
-       ret = wsm_set_rx_filter(priv, &priv->rx_filter);
-       if (!ret)
-               ret = wsm_set_beacon_filter_table(priv, &bf_tbl);
-       if (!ret)
-               ret = wsm_beacon_filter_control(priv, &bf_ctrl);
-       if (!ret)
-               ret = wsm_set_bssid_filtering(priv, bssid_filtering);
-       if (!ret)
-               ret = wsm_set_multicast_filter(priv, &priv->multicast_filter);
-       if (ret)
-               wiphy_err(priv->hw->wiphy,
-                         "Update filtering failed: %d.\n", ret);
-       return;
-}
-
-void cw1200_update_filtering_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common,
-                            update_filtering_work);
-
-       cw1200_update_filtering(priv);
-}
-
-void cw1200_set_beacon_wakeup_period_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common,
-                            set_beacon_wakeup_period_work);
-
-       wsm_set_beacon_wakeup_period(priv,
-                                    priv->beacon_int * priv->join_dtim_period >
-                                    MAX_BEACON_SKIP_TIME_MS ? 1 :
-                                    priv->join_dtim_period, 0);
-}
-
-u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
-                            struct netdev_hw_addr_list *mc_list)
-{
-       static u8 broadcast_ipv6[ETH_ALEN] = {
-               0x33, 0x33, 0x00, 0x00, 0x00, 0x01
-       };
-       static u8 broadcast_ipv4[ETH_ALEN] = {
-               0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
-       };
-       struct cw1200_common *priv = hw->priv;
-       struct netdev_hw_addr *ha;
-       int count = 0;
-
-       /* Disable multicast filtering */
-       priv->has_multicast_subscription = false;
-       memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
-
-       if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
-               return 0;
-
-       /* Enable if requested */
-       netdev_hw_addr_list_for_each(ha, mc_list) {
-               pr_debug("[STA] multicast: %pM\n", ha->addr);
-               memcpy(&priv->multicast_filter.macaddrs[count],
-                      ha->addr, ETH_ALEN);
-               if (!ether_addr_equal(ha->addr, broadcast_ipv4) &&
-                   !ether_addr_equal(ha->addr, broadcast_ipv6))
-                       priv->has_multicast_subscription = true;
-               count++;
-       }
-
-       if (count) {
-               priv->multicast_filter.enable = __cpu_to_le32(1);
-               priv->multicast_filter.num_addrs = __cpu_to_le32(count);
-       }
-
-       return netdev_hw_addr_list_count(mc_list);
-}
-
-void cw1200_configure_filter(struct ieee80211_hw *dev,
-                            unsigned int changed_flags,
-                            unsigned int *total_flags,
-                            u64 multicast)
-{
-       struct cw1200_common *priv = dev->priv;
-       bool listening = !!(*total_flags &
-                           (FIF_OTHER_BSS |
-                            FIF_BCN_PRBRESP_PROMISC |
-                            FIF_PROBE_REQ));
-
-       *total_flags &= FIF_OTHER_BSS |
-                       FIF_FCSFAIL |
-                       FIF_BCN_PRBRESP_PROMISC |
-                       FIF_PROBE_REQ;
-
-       down(&priv->scan.lock);
-       mutex_lock(&priv->conf_mutex);
-
-       priv->rx_filter.promiscuous = 0;
-       priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
-                       FIF_PROBE_REQ)) ? 1 : 0;
-       priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
-       priv->disable_beacon_filter = !(*total_flags &
-                                       (FIF_BCN_PRBRESP_PROMISC |
-                                        FIF_PROBE_REQ));
-       if (priv->listening != listening) {
-               priv->listening = listening;
-               wsm_lock_tx(priv);
-               cw1200_update_listening(priv, listening);
-               wsm_unlock_tx(priv);
-       }
-       cw1200_update_filtering(priv);
-       mutex_unlock(&priv->conf_mutex);
-       up(&priv->scan.lock);
-}
-
-int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
-                  u16 queue, const struct ieee80211_tx_queue_params *params)
-{
-       struct cw1200_common *priv = dev->priv;
-       int ret = 0;
-       /* To prevent re-applying PM request OID again and again*/
-       bool old_uapsd_flags;
-
-       mutex_lock(&priv->conf_mutex);
-
-       if (queue < dev->queues) {
-               old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags);
-
-               WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
-               ret = wsm_set_tx_queue_params(priv,
-                                             &priv->tx_queue_params.params[queue], queue);
-               if (ret) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               WSM_EDCA_SET(&priv->edca, queue, params->aifs,
-                            params->cw_min, params->cw_max,
-                            params->txop, 0xc8,
-                            params->uapsd);
-               ret = wsm_set_edca_params(priv, &priv->edca);
-               if (ret) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               if (priv->mode == NL80211_IFTYPE_STATION) {
-                       ret = cw1200_set_uapsd_param(priv, &priv->edca);
-                       if (!ret && priv->setbssparams_done &&
-                           (priv->join_status == CW1200_JOIN_STATUS_STA) &&
-                           (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags)))
-                               ret = cw1200_set_pm(priv, &priv->powersave_mode);
-               }
-       } else {
-               ret = -EINVAL;
-       }
-
-out:
-       mutex_unlock(&priv->conf_mutex);
-       return ret;
-}
-
-int cw1200_get_stats(struct ieee80211_hw *dev,
-                    struct ieee80211_low_level_stats *stats)
-{
-       struct cw1200_common *priv = dev->priv;
-
-       memcpy(stats, &priv->stats, sizeof(*stats));
-       return 0;
-}
-
-int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
-{
-       struct wsm_set_pm pm = *arg;
-
-       if (priv->uapsd_info.uapsd_flags != 0)
-               pm.mode &= ~WSM_PSM_FAST_PS_FLAG;
-
-       if (memcmp(&pm, &priv->firmware_ps_mode,
-                  sizeof(struct wsm_set_pm))) {
-               priv->firmware_ps_mode = pm;
-               return wsm_set_pm(priv, &pm);
-       } else {
-               return 0;
-       }
-}
-
-int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
-                  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
-                  struct ieee80211_key_conf *key)
-{
-       int ret = -EOPNOTSUPP;
-       struct cw1200_common *priv = dev->priv;
-       struct ieee80211_key_seq seq;
-
-       mutex_lock(&priv->conf_mutex);
-
-       if (cmd == SET_KEY) {
-               u8 *peer_addr = NULL;
-               int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
-                       1 : 0;
-               int idx = cw1200_alloc_key(priv);
-               struct wsm_add_key *wsm_key = &priv->keys[idx];
-
-               if (idx < 0) {
-                       ret = -EINVAL;
-                       goto finally;
-               }
-
-               if (sta)
-                       peer_addr = sta->addr;
-
-               key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE |
-                             IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
-
-               switch (key->cipher) {
-               case WLAN_CIPHER_SUITE_WEP40:
-               case WLAN_CIPHER_SUITE_WEP104:
-                       if (key->keylen > 16) {
-                               cw1200_free_key(priv, idx);
-                               ret = -EINVAL;
-                               goto finally;
-                       }
-
-                       if (pairwise) {
-                               wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
-                               memcpy(wsm_key->wep_pairwise.peer,
-                                      peer_addr, ETH_ALEN);
-                               memcpy(wsm_key->wep_pairwise.keydata,
-                                      &key->key[0], key->keylen);
-                               wsm_key->wep_pairwise.keylen = key->keylen;
-                       } else {
-                               wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
-                               memcpy(wsm_key->wep_group.keydata,
-                                      &key->key[0], key->keylen);
-                               wsm_key->wep_group.keylen = key->keylen;
-                               wsm_key->wep_group.keyid = key->keyidx;
-                       }
-                       break;
-               case WLAN_CIPHER_SUITE_TKIP:
-                       ieee80211_get_key_rx_seq(key, 0, &seq);
-                       if (pairwise) {
-                               wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
-                               memcpy(wsm_key->tkip_pairwise.peer,
-                                      peer_addr, ETH_ALEN);
-                               memcpy(wsm_key->tkip_pairwise.keydata,
-                                      &key->key[0], 16);
-                               memcpy(wsm_key->tkip_pairwise.tx_mic_key,
-                                      &key->key[16], 8);
-                               memcpy(wsm_key->tkip_pairwise.rx_mic_key,
-                                      &key->key[24], 8);
-                       } else {
-                               size_t mic_offset =
-                                       (priv->mode == NL80211_IFTYPE_AP) ?
-                                       16 : 24;
-                               wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
-                               memcpy(wsm_key->tkip_group.keydata,
-                                      &key->key[0], 16);
-                               memcpy(wsm_key->tkip_group.rx_mic_key,
-                                      &key->key[mic_offset], 8);
-
-                               wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff;
-                               wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff;
-                               wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff;
-                               wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff;
-                               wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff;
-                               wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff;
-                               wsm_key->tkip_group.rx_seqnum[6] = 0;
-                               wsm_key->tkip_group.rx_seqnum[7] = 0;
-
-                               wsm_key->tkip_group.keyid = key->keyidx;
-                       }
-                       break;
-               case WLAN_CIPHER_SUITE_CCMP:
-                       ieee80211_get_key_rx_seq(key, 0, &seq);
-                       if (pairwise) {
-                               wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
-                               memcpy(wsm_key->aes_pairwise.peer,
-                                      peer_addr, ETH_ALEN);
-                               memcpy(wsm_key->aes_pairwise.keydata,
-                                      &key->key[0], 16);
-                       } else {
-                               wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
-                               memcpy(wsm_key->aes_group.keydata,
-                                      &key->key[0], 16);
-
-                               wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5];
-                               wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4];
-                               wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3];
-                               wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2];
-                               wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1];
-                               wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0];
-                               wsm_key->aes_group.rx_seqnum[6] = 0;
-                               wsm_key->aes_group.rx_seqnum[7] = 0;
-                               wsm_key->aes_group.keyid = key->keyidx;
-                       }
-                       break;
-               case WLAN_CIPHER_SUITE_SMS4:
-                       if (pairwise) {
-                               wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
-                               memcpy(wsm_key->wapi_pairwise.peer,
-                                      peer_addr, ETH_ALEN);
-                               memcpy(wsm_key->wapi_pairwise.keydata,
-                                      &key->key[0], 16);
-                               memcpy(wsm_key->wapi_pairwise.mic_key,
-                                      &key->key[16], 16);
-                               wsm_key->wapi_pairwise.keyid = key->keyidx;
-                       } else {
-                               wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
-                               memcpy(wsm_key->wapi_group.keydata,
-                                      &key->key[0],  16);
-                               memcpy(wsm_key->wapi_group.mic_key,
-                                      &key->key[16], 16);
-                               wsm_key->wapi_group.keyid = key->keyidx;
-                       }
-                       break;
-               default:
-                       pr_warn("Unhandled key type %d\n", key->cipher);
-                       cw1200_free_key(priv, idx);
-                       ret = -EOPNOTSUPP;
-                       goto finally;
-               }
-               ret = wsm_add_key(priv, wsm_key);
-               if (!ret)
-                       key->hw_key_idx = idx;
-               else
-                       cw1200_free_key(priv, idx);
-       } else if (cmd == DISABLE_KEY) {
-               struct wsm_remove_key wsm_key = {
-                       .index = key->hw_key_idx,
-               };
-
-               if (wsm_key.index > WSM_KEY_MAX_INDEX) {
-                       ret = -EINVAL;
-                       goto finally;
-               }
-
-               cw1200_free_key(priv, wsm_key.index);
-               ret = wsm_remove_key(priv, &wsm_key);
-       } else {
-               pr_warn("Unhandled key command %d\n", cmd);
-       }
-
-finally:
-       mutex_unlock(&priv->conf_mutex);
-       return ret;
-}
-
-void cw1200_wep_key_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, wep_key_work);
-       u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
-       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
-       __le32 wep_default_key_id = __cpu_to_le32(
-               priv->wep_default_key_id);
-
-       pr_debug("[STA] Setting default WEP key: %d\n",
-                priv->wep_default_key_id);
-       wsm_flush_tx(priv);
-       wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
-                     &wep_default_key_id, sizeof(wep_default_key_id));
-       cw1200_queue_requeue(queue, priv->pending_frame_id);
-       wsm_unlock_tx(priv);
-}
-
-int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
-{
-       int ret = 0;
-       __le32 val32;
-       struct cw1200_common *priv = hw->priv;
-
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
-               return 0;
-
-       if (value != (u32) -1)
-               val32 = __cpu_to_le32(value);
-       else
-               val32 = 0; /* disabled */
-
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
-               /* device is down, can _not_ set threshold */
-               ret = -ENODEV;
-               goto out;
-       }
-
-       if (priv->rts_threshold == value)
-               goto out;
-
-       pr_debug("[STA] Setting RTS threshold: %d\n",
-                priv->rts_threshold);
-
-       /* mutex_lock(&priv->conf_mutex); */
-       ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
-                           &val32, sizeof(val32));
-       if (!ret)
-               priv->rts_threshold = value;
-       /* mutex_unlock(&priv->conf_mutex); */
-
-out:
-       return ret;
-}
-
-/* If successful, LOCKS the TX queue! */
-static int __cw1200_flush(struct cw1200_common *priv, bool drop)
-{
-       int i, ret;
-
-       for (;;) {
-               /* TODO: correct flush handling is required when dev_stop.
-                * Temporary workaround: 2s
-                */
-               if (drop) {
-                       for (i = 0; i < 4; ++i)
-                               cw1200_queue_clear(&priv->tx_queue[i]);
-               } else {
-                       ret = wait_event_timeout(
-                               priv->tx_queue_stats.wait_link_id_empty,
-                               cw1200_queue_stats_is_empty(
-                                       &priv->tx_queue_stats, -1),
-                               2 * HZ);
-               }
-
-               if (!drop && ret <= 0) {
-                       ret = -ETIMEDOUT;
-                       break;
-               } else {
-                       ret = 0;
-               }
-
-               wsm_lock_tx(priv);
-               if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) {
-                       /* Highly unlikely: WSM requeued frames. */
-                       wsm_unlock_tx(priv);
-                       continue;
-               }
-               break;
-       }
-       return ret;
-}
-
-void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                 u32 queues, bool drop)
-{
-       struct cw1200_common *priv = hw->priv;
-
-       switch (priv->mode) {
-       case NL80211_IFTYPE_MONITOR:
-               drop = true;
-               break;
-       case NL80211_IFTYPE_AP:
-               if (!priv->enable_beacon)
-                       drop = true;
-               break;
-       }
-
-       if (!__cw1200_flush(priv, drop))
-               wsm_unlock_tx(priv);
-
-       return;
-}
-
-/* ******************************************************************** */
-/* WSM callbacks                                                       */
-
-void cw1200_free_event_queue(struct cw1200_common *priv)
-{
-       LIST_HEAD(list);
-
-       spin_lock(&priv->event_queue_lock);
-       list_splice_init(&priv->event_queue, &list);
-       spin_unlock(&priv->event_queue_lock);
-
-       __cw1200_free_event_queue(&list);
-}
-
-void cw1200_event_handler(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, event_handler);
-       struct cw1200_wsm_event *event;
-       LIST_HEAD(list);
-
-       spin_lock(&priv->event_queue_lock);
-       list_splice_init(&priv->event_queue, &list);
-       spin_unlock(&priv->event_queue_lock);
-
-       list_for_each_entry(event, &list, link) {
-               switch (event->evt.id) {
-               case WSM_EVENT_ERROR:
-                       pr_err("Unhandled WSM Error from LMAC\n");
-                       break;
-               case WSM_EVENT_BSS_LOST:
-                       pr_debug("[CQM] BSS lost.\n");
-                       cancel_work_sync(&priv->unjoin_work);
-                       if (!down_trylock(&priv->scan.lock)) {
-                               cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
-                               up(&priv->scan.lock);
-                       } else {
-                               /* Scan is in progress. Delay reporting.
-                                * Scan complete will trigger bss_loss_work
-                                */
-                               priv->delayed_link_loss = 1;
-                               /* Also start a watchdog. */
-                               queue_delayed_work(priv->workqueue,
-                                                  &priv->bss_loss_work, 5*HZ);
-                       }
-                       break;
-               case WSM_EVENT_BSS_REGAINED:
-                       pr_debug("[CQM] BSS regained.\n");
-                       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
-                       cancel_work_sync(&priv->unjoin_work);
-                       break;
-               case WSM_EVENT_RADAR_DETECTED:
-                       wiphy_info(priv->hw->wiphy, "radar pulse detected\n");
-                       break;
-               case WSM_EVENT_RCPI_RSSI:
-               {
-                       /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
-                        * RSSI = RCPI / 2 - 110
-                        */
-                       int rcpi_rssi = (int)(event->evt.data & 0xFF);
-                       int cqm_evt;
-                       if (priv->cqm_use_rssi)
-                               rcpi_rssi = (s8)rcpi_rssi;
-                       else
-                               rcpi_rssi =  rcpi_rssi / 2 - 110;
-
-                       cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
-                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
-                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
-                       pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi);
-                       ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
-                                                 GFP_KERNEL);
-                       break;
-               }
-               case WSM_EVENT_BT_INACTIVE:
-                       pr_warn("Unhandled BT INACTIVE from LMAC\n");
-                       break;
-               case WSM_EVENT_BT_ACTIVE:
-                       pr_warn("Unhandled BT ACTIVE from LMAC\n");
-                       break;
-               }
-       }
-       __cw1200_free_event_queue(&list);
-}
-
-void cw1200_bss_loss_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, bss_loss_work.work);
-
-       pr_debug("[CQM] Reporting connection loss.\n");
-       wsm_lock_tx(priv);
-       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
-               wsm_unlock_tx(priv);
-}
-
-void cw1200_bss_params_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, bss_params_work);
-       mutex_lock(&priv->conf_mutex);
-
-       priv->bss_params.reset_beacon_loss = 1;
-       wsm_set_bss_params(priv, &priv->bss_params);
-       priv->bss_params.reset_beacon_loss = 0;
-
-       mutex_unlock(&priv->conf_mutex);
-}
-
-/* ******************************************************************** */
-/* Internal API                                                                */
-
-/* This function is called to Parse the SDD file
- * to extract listen_interval and PTA related information
- * sdd is a TLV: u8 id, u8 len, u8 data[]
- */
-static int cw1200_parse_sdd_file(struct cw1200_common *priv)
-{
-       const u8 *p = priv->sdd->data;
-       int ret = 0;
-
-       while (p + 2 <= priv->sdd->data + priv->sdd->size) {
-               if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) {
-                       pr_warn("Malformed sdd structure\n");
-                       return -1;
-               }
-               switch (p[0]) {
-               case SDD_PTA_CFG_ELT_ID: {
-                       u16 v;
-                       if (p[1] < 4) {
-                               pr_warn("SDD_PTA_CFG_ELT_ID malformed\n");
-                               ret = -1;
-                               break;
-                       }
-                       v = le16_to_cpu(*((__le16 *)(p + 2)));
-                       if (!v)  /* non-zero means this is enabled */
-                               break;
-
-                       v = le16_to_cpu(*((__le16 *)(p + 4)));
-                       priv->conf_listen_interval = (v >> 7) & 0x1F;
-                       pr_debug("PTA found; Listen Interval %d\n",
-                                priv->conf_listen_interval);
-                       break;
-               }
-               case SDD_REFERENCE_FREQUENCY_ELT_ID: {
-                       u16 clk = le16_to_cpu(*((__le16 *)(p + 2)));
-                       if (clk != priv->hw_refclk)
-                               pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n",
-                                       clk, priv->hw_refclk);
-                       break;
-               }
-               default:
-                       break;
-               }
-               p += p[1] + 2;
-       }
-
-       if (!priv->bt_present) {
-               pr_debug("PTA element NOT found.\n");
-               priv->conf_listen_interval = 0;
-       }
-       return ret;
-}
-
-int cw1200_setup_mac(struct cw1200_common *priv)
-{
-       int ret = 0;
-
-       /* NOTE: There is a bug in FW: it reports signal
-        * as RSSI if RSSI subscription is enabled.
-        * It's not enough to set WSM_RCPI_RSSI_USE_RSSI.
-        *
-        * NOTE2: RSSI based reports have been switched to RCPI, since
-        * FW has a bug and RSSI reported values are not stable,
-        * what can leads to signal level oscilations in user-end applications
-        */
-       struct wsm_rcpi_rssi_threshold threshold = {
-               .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
-               WSM_RCPI_RSSI_DONT_USE_UPPER |
-               WSM_RCPI_RSSI_DONT_USE_LOWER,
-               .rollingAverageCount = 16,
-       };
-
-       struct wsm_configuration cfg = {
-               .dot11StationId = &priv->mac_addr[0],
-       };
-
-       /* Remember the decission here to make sure, we will handle
-        * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS
-        */
-       if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
-               priv->cqm_use_rssi = true;
-
-       if (!priv->sdd) {
-               ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev);
-               if (ret) {
-                       pr_err("Can't load sdd file %s.\n", priv->sdd_path);
-                       return ret;
-               }
-               cw1200_parse_sdd_file(priv);
-       }
-
-       cfg.dpdData = priv->sdd->data;
-       cfg.dpdData_size = priv->sdd->size;
-       ret = wsm_configuration(priv, &cfg);
-       if (ret)
-               return ret;
-
-       /* Configure RSSI/SCPI reporting as RSSI. */
-       wsm_set_rcpi_rssi_threshold(priv, &threshold);
-
-       return 0;
-}
-
-static void cw1200_join_complete(struct cw1200_common *priv)
-{
-       pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status);
-
-       priv->join_pending = false;
-       if (priv->join_complete_status) {
-               priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
-               cw1200_update_listening(priv, priv->listening);
-               cw1200_do_unjoin(priv);
-               ieee80211_connection_loss(priv->vif);
-       } else {
-               if (priv->mode == NL80211_IFTYPE_ADHOC)
-                       priv->join_status = CW1200_JOIN_STATUS_IBSS;
-               else
-                       priv->join_status = CW1200_JOIN_STATUS_PRE_STA;
-       }
-       wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */
-}
-
-void cw1200_join_complete_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, join_complete_work);
-       mutex_lock(&priv->conf_mutex);
-       cw1200_join_complete(priv);
-       mutex_unlock(&priv->conf_mutex);
-}
-
-void cw1200_join_complete_cb(struct cw1200_common *priv,
-                            struct wsm_join_complete *arg)
-{
-       pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n",
-                arg->status);
-
-       if (cancel_delayed_work(&priv->join_timeout)) {
-               priv->join_complete_status = arg->status;
-               queue_work(priv->workqueue, &priv->join_complete_work);
-       }
-}
-
-/* MUST be called with tx_lock held!  It will be unlocked for us. */
-static void cw1200_do_join(struct cw1200_common *priv)
-{
-       const u8 *bssid;
-       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
-       struct cfg80211_bss *bss = NULL;
-       struct wsm_protected_mgmt_policy mgmt_policy;
-       struct wsm_join join = {
-               .mode = conf->ibss_joined ?
-                               WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
-               .preamble_type = WSM_JOIN_PREAMBLE_LONG,
-               .probe_for_join = 1,
-               .atim_window = 0,
-               .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
-                                                         conf->basic_rates),
-       };
-       if (delayed_work_pending(&priv->join_timeout)) {
-               pr_warn("[STA] - Join request already pending, skipping..\n");
-               wsm_unlock_tx(priv);
-               return;
-       }
-
-       if (priv->join_status)
-               cw1200_do_unjoin(priv);
-
-       bssid = priv->vif->bss_conf.bssid;
-
-       bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0,
-                              IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
-
-       if (!bss && !conf->ibss_joined) {
-               wsm_unlock_tx(priv);
-               return;
-       }
-
-       mutex_lock(&priv->conf_mutex);
-
-       /* Under the conf lock: check scan status and
-        * bail out if it is in progress.
-        */
-       if (atomic_read(&priv->scan.in_progress)) {
-               wsm_unlock_tx(priv);
-               goto done_put;
-       }
-
-       priv->join_pending = true;
-
-       /* Sanity check basic rates */
-       if (!join.basic_rate_set)
-               join.basic_rate_set = 7;
-
-       /* Sanity check beacon interval */
-       if (!priv->beacon_int)
-               priv->beacon_int = 1;
-
-       join.beacon_interval = priv->beacon_int;
-
-       /* BT Coex related changes */
-       if (priv->bt_present) {
-               if (((priv->conf_listen_interval * 100) %
-                    priv->beacon_int) == 0)
-                       priv->listen_interval =
-                               ((priv->conf_listen_interval * 100) /
-                                priv->beacon_int);
-               else
-                       priv->listen_interval =
-                               ((priv->conf_listen_interval * 100) /
-                                priv->beacon_int + 1);
-       }
-
-       if (priv->hw->conf.ps_dtim_period)
-               priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
-       join.dtim_period = priv->join_dtim_period;
-
-       join.channel_number = priv->channel->hw_value;
-       join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
-               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
-
-       memcpy(join.bssid, bssid, sizeof(join.bssid));
-
-       pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n",
-                join.bssid,
-                join.dtim_period, priv->beacon_int);
-
-       if (!conf->ibss_joined) {
-               const u8 *ssidie;
-               rcu_read_lock();
-               ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
-               if (ssidie) {
-                       join.ssid_len = ssidie[1];
-                       memcpy(join.ssid, &ssidie[2], join.ssid_len);
-               }
-               rcu_read_unlock();
-       }
-
-       if (priv->vif->p2p) {
-               join.flags |= WSM_JOIN_FLAGS_P2P_GO;
-               join.basic_rate_set =
-                       cw1200_rate_mask_to_wsm(priv, 0xFF0);
-       }
-
-       /* Enable asynchronous join calls */
-       if (!conf->ibss_joined) {
-               join.flags |= WSM_JOIN_FLAGS_FORCE;
-               join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND;
-       }
-
-       wsm_flush_tx(priv);
-
-       /* Stay Awake for Join and Auth Timeouts and a bit more */
-       cw1200_pm_stay_awake(&priv->pm_state,
-                            CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT);
-
-       cw1200_update_listening(priv, false);
-
-       /* Turn on Block ACKs */
-       wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask,
-                                priv->ba_rx_tid_mask);
-
-       /* Set up timeout */
-       if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) {
-               priv->join_status = CW1200_JOIN_STATUS_JOINING;
-               queue_delayed_work(priv->workqueue,
-                                  &priv->join_timeout,
-                                  CW1200_JOIN_TIMEOUT);
-       }
-
-       /* 802.11w protected mgmt frames */
-       mgmt_policy.protectedMgmtEnable = 0;
-       mgmt_policy.unprotectedMgmtFramesAllowed = 1;
-       mgmt_policy.encryptionForAuthFrame = 1;
-       wsm_set_protected_mgmt_policy(priv, &mgmt_policy);
-
-       /* Perform actual join */
-       if (wsm_join(priv, &join)) {
-               pr_err("[STA] cw1200_join_work: wsm_join failed!\n");
-               cancel_delayed_work_sync(&priv->join_timeout);
-               cw1200_update_listening(priv, priv->listening);
-               /* Tx lock still held, unjoin will clear it. */
-               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
-                       wsm_unlock_tx(priv);
-       } else {
-               if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND))
-                       cw1200_join_complete(priv); /* Will clear tx_lock */
-
-               /* Upload keys */
-               cw1200_upload_keys(priv);
-
-               /* Due to beacon filtering it is possible that the
-                * AP's beacon is not known for the mac80211 stack.
-                * Disable filtering temporary to make sure the stack
-                * receives at least one
-                */
-               priv->disable_beacon_filter = true;
-       }
-       cw1200_update_filtering(priv);
-
-done_put:
-       mutex_unlock(&priv->conf_mutex);
-       if (bss)
-               cfg80211_put_bss(priv->hw->wiphy, bss);
-}
-
-void cw1200_join_timeout(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, join_timeout.work);
-       pr_debug("[WSM] Join timed out.\n");
-       wsm_lock_tx(priv);
-       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
-               wsm_unlock_tx(priv);
-}
-
-static void cw1200_do_unjoin(struct cw1200_common *priv)
-{
-       struct wsm_reset reset = {
-               .reset_statistics = true,
-       };
-
-       cancel_delayed_work_sync(&priv->join_timeout);
-
-       mutex_lock(&priv->conf_mutex);
-       priv->join_pending = false;
-
-       if (atomic_read(&priv->scan.in_progress)) {
-               if (priv->delayed_unjoin)
-                       wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n");
-               else
-                       priv->delayed_unjoin = true;
-               goto done;
-       }
-
-       priv->delayed_link_loss = false;
-
-       if (!priv->join_status)
-               goto done;
-
-       if (priv->join_status == CW1200_JOIN_STATUS_AP)
-               goto done;
-
-       cancel_work_sync(&priv->update_filtering_work);
-       cancel_work_sync(&priv->set_beacon_wakeup_period_work);
-       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
-
-       /* Unjoin is a reset. */
-       wsm_flush_tx(priv);
-       wsm_keep_alive_period(priv, 0);
-       wsm_reset(priv, &reset);
-       wsm_set_output_power(priv, priv->output_power * 10);
-       priv->join_dtim_period = 0;
-       cw1200_setup_mac(priv);
-       cw1200_free_event_queue(priv);
-       cancel_work_sync(&priv->event_handler);
-       cw1200_update_listening(priv, priv->listening);
-       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
-
-       /* Disable Block ACKs */
-       wsm_set_block_ack_policy(priv, 0, 0);
-
-       priv->disable_beacon_filter = false;
-       cw1200_update_filtering(priv);
-       memset(&priv->association_mode, 0,
-              sizeof(priv->association_mode));
-       memset(&priv->bss_params, 0, sizeof(priv->bss_params));
-       priv->setbssparams_done = false;
-       memset(&priv->firmware_ps_mode, 0,
-              sizeof(priv->firmware_ps_mode));
-
-       pr_debug("[STA] Unjoin completed.\n");
-
-done:
-       mutex_unlock(&priv->conf_mutex);
-}
-
-void cw1200_unjoin_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, unjoin_work);
-
-       cw1200_do_unjoin(priv);
-
-       /* Tell the stack we're dead */
-       ieee80211_connection_loss(priv->vif);
-
-       wsm_unlock_tx(priv);
-}
-
-int cw1200_enable_listening(struct cw1200_common *priv)
-{
-       struct wsm_start start = {
-               .mode = WSM_START_MODE_P2P_DEV,
-               .band = WSM_PHY_BAND_2_4G,
-               .beacon_interval = 100,
-               .dtim_period = 1,
-               .probe_delay = 0,
-               .basic_rate_set = 0x0F,
-       };
-
-       if (priv->channel) {
-               start.band = priv->channel->band == IEEE80211_BAND_5GHZ ?
-                            WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
-               start.channel_number = priv->channel->hw_value;
-       } else {
-               start.band = WSM_PHY_BAND_2_4G;
-               start.channel_number = 1;
-       }
-
-       return wsm_start(priv, &start);
-}
-
-int cw1200_disable_listening(struct cw1200_common *priv)
-{
-       int ret;
-       struct wsm_reset reset = {
-               .reset_statistics = true,
-       };
-       ret = wsm_reset(priv, &reset);
-       return ret;
-}
-
-void cw1200_update_listening(struct cw1200_common *priv, bool enabled)
-{
-       if (enabled) {
-               if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) {
-                       if (!cw1200_enable_listening(priv))
-                               priv->join_status = CW1200_JOIN_STATUS_MONITOR;
-                       wsm_set_probe_responder(priv, true);
-               }
-       } else {
-               if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
-                       if (!cw1200_disable_listening(priv))
-                               priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
-                       wsm_set_probe_responder(priv, false);
-               }
-       }
-}
-
-int cw1200_set_uapsd_param(struct cw1200_common *priv,
-                          const struct wsm_edca_params *arg)
-{
-       int ret;
-       u16 uapsd_flags = 0;
-
-       /* Here's the mapping AC [queue, bit]
-        *  VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]
-        */
-
-       if (arg->uapsd_enable[0])
-               uapsd_flags |= 1 << 3;
-
-       if (arg->uapsd_enable[1])
-               uapsd_flags |= 1 << 2;
-
-       if (arg->uapsd_enable[2])
-               uapsd_flags |= 1 << 1;
-
-       if (arg->uapsd_enable[3])
-               uapsd_flags |= 1;
-
-       /* Currently pseudo U-APSD operation is not supported, so setting
-        * MinAutoTriggerInterval, MaxAutoTriggerInterval and
-        * AutoTriggerStep to 0
-        */
-
-       priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags);
-       priv->uapsd_info.min_auto_trigger_interval = 0;
-       priv->uapsd_info.max_auto_trigger_interval = 0;
-       priv->uapsd_info.auto_trigger_step = 0;
-
-       ret = wsm_set_uapsd_info(priv, &priv->uapsd_info);
-       return ret;
-}
-
-/* ******************************************************************** */
-/* AP API                                                              */
-
-int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                  struct ieee80211_sta *sta)
-{
-       struct cw1200_common *priv = hw->priv;
-       struct cw1200_sta_priv *sta_priv =
-                       (struct cw1200_sta_priv *)&sta->drv_priv;
-       struct cw1200_link_entry *entry;
-       struct sk_buff *skb;
-
-       if (priv->mode != NL80211_IFTYPE_AP)
-               return 0;
-
-       sta_priv->link_id = cw1200_find_link_id(priv, sta->addr);
-       if (WARN_ON(!sta_priv->link_id)) {
-               wiphy_info(priv->hw->wiphy,
-                          "[AP] No more link IDs available.\n");
-               return -ENOENT;
-       }
-
-       entry = &priv->link_id_db[sta_priv->link_id - 1];
-       spin_lock_bh(&priv->ps_state_lock);
-       if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
-                                       IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
-               priv->sta_asleep_mask |= BIT(sta_priv->link_id);
-       entry->status = CW1200_LINK_HARD;
-       while ((skb = skb_dequeue(&entry->rx_queue)))
-               ieee80211_rx_irqsafe(priv->hw, skb);
-       spin_unlock_bh(&priv->ps_state_lock);
-       return 0;
-}
-
-int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                     struct ieee80211_sta *sta)
-{
-       struct cw1200_common *priv = hw->priv;
-       struct cw1200_sta_priv *sta_priv =
-                       (struct cw1200_sta_priv *)&sta->drv_priv;
-       struct cw1200_link_entry *entry;
-
-       if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
-               return 0;
-
-       entry = &priv->link_id_db[sta_priv->link_id - 1];
-       spin_lock_bh(&priv->ps_state_lock);
-       entry->status = CW1200_LINK_RESERVE;
-       entry->timestamp = jiffies;
-       wsm_lock_tx_async(priv);
-       if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
-               wsm_unlock_tx(priv);
-       spin_unlock_bh(&priv->ps_state_lock);
-       flush_workqueue(priv->workqueue);
-       return 0;
-}
-
-static void __cw1200_sta_notify(struct ieee80211_hw *dev,
-                               struct ieee80211_vif *vif,
-                               enum sta_notify_cmd notify_cmd,
-                               int link_id)
-{
-       struct cw1200_common *priv = dev->priv;
-       u32 bit, prev;
-
-       /* Zero link id means "for all link IDs" */
-       if (link_id)
-               bit = BIT(link_id);
-       else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
-               bit = 0;
-       else
-               bit = priv->link_id_map;
-       prev = priv->sta_asleep_mask & bit;
-
-       switch (notify_cmd) {
-       case STA_NOTIFY_SLEEP:
-               if (!prev) {
-                       if (priv->buffered_multicasts &&
-                           !priv->sta_asleep_mask)
-                               queue_work(priv->workqueue,
-                                          &priv->multicast_start_work);
-                       priv->sta_asleep_mask |= bit;
-               }
-               break;
-       case STA_NOTIFY_AWAKE:
-               if (prev) {
-                       priv->sta_asleep_mask &= ~bit;
-                       priv->pspoll_mask &= ~bit;
-                       if (priv->tx_multicast && link_id &&
-                           !priv->sta_asleep_mask)
-                               queue_work(priv->workqueue,
-                                          &priv->multicast_stop_work);
-                       cw1200_bh_wakeup(priv);
-               }
-               break;
-       }
-}
-
-void cw1200_sta_notify(struct ieee80211_hw *dev,
-                      struct ieee80211_vif *vif,
-                      enum sta_notify_cmd notify_cmd,
-                      struct ieee80211_sta *sta)
-{
-       struct cw1200_common *priv = dev->priv;
-       struct cw1200_sta_priv *sta_priv =
-               (struct cw1200_sta_priv *)&sta->drv_priv;
-
-       spin_lock_bh(&priv->ps_state_lock);
-       __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id);
-       spin_unlock_bh(&priv->ps_state_lock);
-}
-
-static void cw1200_ps_notify(struct cw1200_common *priv,
-                     int link_id, bool ps)
-{
-       if (link_id > CW1200_MAX_STA_IN_AP_MODE)
-               return;
-
-       pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n",
-                ps ? "Stop" : "Start",
-                link_id, priv->sta_asleep_mask);
-
-       __cw1200_sta_notify(priv->hw, priv->vif,
-                           ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
-}
-
-static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set)
-{
-       struct sk_buff *skb;
-       struct wsm_update_ie update_ie = {
-               .what = WSM_UPDATE_IE_BEACON,
-               .count = 1,
-       };
-       u16 tim_offset, tim_length;
-
-       pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis");
-
-       skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
-                       &tim_offset, &tim_length);
-       if (!skb) {
-               if (!__cw1200_flush(priv, true))
-                       wsm_unlock_tx(priv);
-               return -ENOENT;
-       }
-
-       if (tim_offset && tim_length >= 6) {
-               /* Ignore DTIM count from mac80211:
-                * firmware handles DTIM internally.
-                */
-               skb->data[tim_offset + 2] = 0;
-
-               /* Set/reset aid0 bit */
-               if (aid0_bit_set)
-                       skb->data[tim_offset + 4] |= 1;
-               else
-                       skb->data[tim_offset + 4] &= ~1;
-       }
-
-       update_ie.ies = &skb->data[tim_offset];
-       update_ie.length = tim_length;
-       wsm_update_ie(priv, &update_ie);
-
-       dev_kfree_skb(skb);
-
-       return 0;
-}
-
-void cw1200_set_tim_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, set_tim_work);
-       (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set);
-}
-
-int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
-                  bool set)
-{
-       struct cw1200_common *priv = dev->priv;
-       queue_work(priv->workqueue, &priv->set_tim_work);
-       return 0;
-}
-
-void cw1200_set_cts_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, set_cts_work);
-
-       u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
-       struct wsm_update_ie update_ie = {
-               .what = WSM_UPDATE_IE_BEACON,
-               .count = 1,
-               .ies = erp_ie,
-               .length = 3,
-       };
-       u32 erp_info;
-       __le32 use_cts_prot;
-       mutex_lock(&priv->conf_mutex);
-       erp_info = priv->erp_info;
-       mutex_unlock(&priv->conf_mutex);
-       use_cts_prot =
-               erp_info & WLAN_ERP_USE_PROTECTION ?
-               __cpu_to_le32(1) : 0;
-
-       erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
-
-       pr_debug("[STA] ERP information 0x%x\n", erp_info);
-
-       wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION,
-                     &use_cts_prot, sizeof(use_cts_prot));
-       wsm_update_ie(priv, &update_ie);
-
-       return;
-}
-
-static int cw1200_set_btcoexinfo(struct cw1200_common *priv)
-{
-       struct wsm_override_internal_txrate arg;
-       int ret = 0;
-
-       if (priv->mode == NL80211_IFTYPE_STATION) {
-               /* Plumb PSPOLL and NULL template */
-               cw1200_upload_pspoll(priv);
-               cw1200_upload_null(priv);
-               cw1200_upload_qosnull(priv);
-       } else {
-               return 0;
-       }
-
-       memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
-
-       if (!priv->vif->p2p) {
-               /* STATION mode */
-               if (priv->bss_params.operational_rate_set & ~0xF) {
-                       pr_debug("[STA] STA has ERP rates\n");
-                       /* G or BG mode */
-                       arg.internalTxRate = (__ffs(
-                       priv->bss_params.operational_rate_set & ~0xF));
-               } else {
-                       pr_debug("[STA] STA has non ERP rates\n");
-                       /* B only mode */
-                       arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
-               }
-               arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
-       } else {
-               /* P2P mode */
-               arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
-               arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
-       }
-
-       pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
-                priv->mode,
-                arg.internalTxRate,
-                arg.nonErpInternalTxRate);
-
-       ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
-                           &arg, sizeof(arg));
-
-       return ret;
-}
-
-void cw1200_bss_info_changed(struct ieee80211_hw *dev,
-                            struct ieee80211_vif *vif,
-                            struct ieee80211_bss_conf *info,
-                            u32 changed)
-{
-       struct cw1200_common *priv = dev->priv;
-       bool do_join = false;
-
-       mutex_lock(&priv->conf_mutex);
-
-       pr_debug("BSS CHANGED:  %08x\n", changed);
-
-       /* TODO: BSS_CHANGED_QOS */
-       /* TODO: BSS_CHANGED_TXPOWER */
-
-       if (changed & BSS_CHANGED_ARP_FILTER) {
-               struct wsm_mib_arp_ipv4_filter filter = {0};
-               int i;
-
-               pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
-                        info->arp_addr_cnt);
-
-               /* Currently only one IP address is supported by firmware.
-                * In case of more IPs arp filtering will be disabled.
-                */
-               if (info->arp_addr_cnt > 0 &&
-                   info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
-                       for (i = 0; i < info->arp_addr_cnt; i++) {
-                               filter.ipv4addrs[i] = info->arp_addr_list[i];
-                               pr_debug("[STA] addr[%d]: 0x%X\n",
-                                        i, filter.ipv4addrs[i]);
-                       }
-                       filter.enable = __cpu_to_le32(1);
-               }
-
-               pr_debug("[STA] arp ip filter enable: %d\n",
-                        __le32_to_cpu(filter.enable));
-
-               wsm_set_arp_ipv4_filter(priv, &filter);
-       }
-
-       if (changed &
-           (BSS_CHANGED_BEACON |
-            BSS_CHANGED_AP_PROBE_RESP |
-            BSS_CHANGED_BSSID |
-            BSS_CHANGED_SSID |
-            BSS_CHANGED_IBSS)) {
-               pr_debug("BSS_CHANGED_BEACON\n");
-               priv->beacon_int = info->beacon_int;
-               cw1200_update_beaconing(priv);
-               cw1200_upload_beacon(priv);
-       }
-
-       if (changed & BSS_CHANGED_BEACON_ENABLED) {
-               pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon);
-
-               if (priv->enable_beacon != info->enable_beacon) {
-                       cw1200_enable_beaconing(priv, info->enable_beacon);
-                       priv->enable_beacon = info->enable_beacon;
-               }
-       }
-
-       if (changed & BSS_CHANGED_BEACON_INT) {
-               pr_debug("CHANGED_BEACON_INT\n");
-               if (info->ibss_joined)
-                       do_join = true;
-               else if (priv->join_status == CW1200_JOIN_STATUS_AP)
-                       cw1200_update_beaconing(priv);
-       }
-
-       /* assoc/disassoc, or maybe AID changed */
-       if (changed & BSS_CHANGED_ASSOC) {
-               wsm_lock_tx(priv);
-               priv->wep_default_key_id = -1;
-               wsm_unlock_tx(priv);
-       }
-
-       if (changed & BSS_CHANGED_BSSID) {
-               pr_debug("BSS_CHANGED_BSSID\n");
-               do_join = true;
-       }
-
-       if (changed &
-           (BSS_CHANGED_ASSOC |
-            BSS_CHANGED_BSSID |
-            BSS_CHANGED_IBSS |
-            BSS_CHANGED_BASIC_RATES |
-            BSS_CHANGED_HT)) {
-               pr_debug("BSS_CHANGED_ASSOC\n");
-               if (info->assoc) {
-                       if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) {
-                               ieee80211_connection_loss(vif);
-                               mutex_unlock(&priv->conf_mutex);
-                               return;
-                       } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) {
-                               priv->join_status = CW1200_JOIN_STATUS_STA;
-                       }
-               } else {
-                       do_join = true;
-               }
-
-               if (info->assoc || info->ibss_joined) {
-                       struct ieee80211_sta *sta = NULL;
-                       __le32 htprot = 0;
-
-                       if (info->dtim_period)
-                               priv->join_dtim_period = info->dtim_period;
-                       priv->beacon_int = info->beacon_int;
-
-                       rcu_read_lock();
-
-                       if (info->bssid && !info->ibss_joined)
-                               sta = ieee80211_find_sta(vif, info->bssid);
-                       if (sta) {
-                               priv->ht_info.ht_cap = sta->ht_cap;
-                               priv->bss_params.operational_rate_set =
-                                       cw1200_rate_mask_to_wsm(priv,
-                                                               sta->supp_rates[priv->channel->band]);
-                               priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef);
-                               priv->ht_info.operation_mode = info->ht_operation_mode;
-                       } else {
-                               memset(&priv->ht_info, 0,
-                                      sizeof(priv->ht_info));
-                               priv->bss_params.operational_rate_set = -1;
-                       }
-                       rcu_read_unlock();
-
-                       /* Non Greenfield stations present */
-                       if (priv->ht_info.operation_mode &
-                           IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
-                               htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT);
-
-                       /* Set HT protection method */
-                       htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2);
-
-                       /* TODO:
-                        * STBC_param.dual_cts
-                        *  STBC_param.LSIG_TXOP_FILL
-                        */
-
-                       wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION,
-                                     &htprot, sizeof(htprot));
-
-                       priv->association_mode.greenfield =
-                               cw1200_ht_greenfield(&priv->ht_info);
-                       priv->association_mode.flags =
-                               WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
-                               WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
-                               WSM_ASSOCIATION_MODE_USE_HT_MODE |
-                               WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
-                               WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
-                       priv->association_mode.preamble =
-                               info->use_short_preamble ?
-                               WSM_JOIN_PREAMBLE_SHORT :
-                               WSM_JOIN_PREAMBLE_LONG;
-                       priv->association_mode.basic_rate_set = __cpu_to_le32(
-                               cw1200_rate_mask_to_wsm(priv,
-                                                       info->basic_rates));
-                       priv->association_mode.mpdu_start_spacing =
-                               cw1200_ht_ampdu_density(&priv->ht_info);
-
-                       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
-                       cancel_work_sync(&priv->unjoin_work);
-
-                       priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count;
-                       priv->bss_params.aid = info->aid;
-
-                       if (priv->join_dtim_period < 1)
-                               priv->join_dtim_period = 1;
-
-                       pr_debug("[STA] DTIM %d, interval: %d\n",
-                                priv->join_dtim_period, priv->beacon_int);
-                       pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
-                                priv->association_mode.preamble,
-                                priv->association_mode.greenfield,
-                                priv->bss_params.aid,
-                                priv->bss_params.operational_rate_set,
-                                priv->association_mode.basic_rate_set);
-                       wsm_set_association_mode(priv, &priv->association_mode);
-
-                       if (!info->ibss_joined) {
-                               wsm_keep_alive_period(priv, 30 /* sec */);
-                               wsm_set_bss_params(priv, &priv->bss_params);
-                               priv->setbssparams_done = true;
-                               cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work);
-                               cw1200_set_pm(priv, &priv->powersave_mode);
-                       }
-                       if (priv->vif->p2p) {
-                               pr_debug("[STA] Setting p2p powersave configuration.\n");
-                               wsm_set_p2p_ps_modeinfo(priv,
-                                                       &priv->p2p_ps_modeinfo);
-                       }
-                       if (priv->bt_present)
-                               cw1200_set_btcoexinfo(priv);
-               } else {
-                       memset(&priv->association_mode, 0,
-                              sizeof(priv->association_mode));
-                       memset(&priv->bss_params, 0, sizeof(priv->bss_params));
-               }
-       }
-
-       /* ERP Protection */
-       if (changed & (BSS_CHANGED_ASSOC |
-                      BSS_CHANGED_ERP_CTS_PROT |
-                      BSS_CHANGED_ERP_PREAMBLE)) {
-               u32 prev_erp_info = priv->erp_info;
-               if (info->use_cts_prot)
-                       priv->erp_info |= WLAN_ERP_USE_PROTECTION;
-               else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
-                       priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
-
-               if (info->use_short_preamble)
-                       priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
-               else
-                       priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
-
-               pr_debug("[STA] ERP Protection: %x\n", priv->erp_info);
-
-               if (prev_erp_info != priv->erp_info)
-                       queue_work(priv->workqueue, &priv->set_cts_work);
-       }
-
-       /* ERP Slottime */
-       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
-               __le32 slot_time = info->use_short_slot ?
-                       __cpu_to_le32(9) : __cpu_to_le32(20);
-               pr_debug("[STA] Slot time: %d us.\n",
-                        __le32_to_cpu(slot_time));
-               wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME,
-                             &slot_time, sizeof(slot_time));
-       }
-
-       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
-               struct wsm_rcpi_rssi_threshold threshold = {
-                       .rollingAverageCount = 8,
-               };
-               pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n",
-                        info->cqm_rssi_thold, info->cqm_rssi_hyst);
-               priv->cqm_rssi_thold = info->cqm_rssi_thold;
-               priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
-
-               if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
-                       /* RSSI subscription enabled */
-                       /* TODO: It's not a correct way of setting threshold.
-                        * Upper and lower must be set equal here and adjusted
-                        * in callback. However current implementation is much
-                        * more relaible and stable.
-                        */
-
-                       /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
-                        * RSSI = RCPI / 2 - 110
-                        */
-                       if (priv->cqm_use_rssi) {
-                               threshold.upperThreshold =
-                                       info->cqm_rssi_thold + info->cqm_rssi_hyst;
-                               threshold.lowerThreshold =
-                                       info->cqm_rssi_thold;
-                               threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
-                       } else {
-                               threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2;
-                               threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2;
-                       }
-                       threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
-               } else {
-                       /* There is a bug in FW, see sta.c. We have to enable
-                        * dummy subscription to get correct RSSI values.
-                        */
-                       threshold.rssiRcpiMode |=
-                               WSM_RCPI_RSSI_THRESHOLD_ENABLE |
-                               WSM_RCPI_RSSI_DONT_USE_UPPER |
-                               WSM_RCPI_RSSI_DONT_USE_LOWER;
-                       if (priv->cqm_use_rssi)
-                               threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
-               }
-               wsm_set_rcpi_rssi_threshold(priv, &threshold);
-       }
-       mutex_unlock(&priv->conf_mutex);
-
-       if (do_join) {
-               wsm_lock_tx(priv);
-               cw1200_do_join(priv); /* Will unlock it for us */
-       }
-}
-
-void cw1200_multicast_start_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, multicast_start_work);
-       long tmo = priv->join_dtim_period *
-                       (priv->beacon_int + 20) * HZ / 1024;
-
-       cancel_work_sync(&priv->multicast_stop_work);
-
-       if (!priv->aid0_bit_set) {
-               wsm_lock_tx(priv);
-               cw1200_set_tim_impl(priv, true);
-               priv->aid0_bit_set = true;
-               mod_timer(&priv->mcast_timeout, jiffies + tmo);
-               wsm_unlock_tx(priv);
-       }
-}
-
-void cw1200_multicast_stop_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, multicast_stop_work);
-
-       if (priv->aid0_bit_set) {
-               del_timer_sync(&priv->mcast_timeout);
-               wsm_lock_tx(priv);
-               priv->aid0_bit_set = false;
-               cw1200_set_tim_impl(priv, false);
-               wsm_unlock_tx(priv);
-       }
-}
-
-void cw1200_mcast_timeout(unsigned long arg)
-{
-       struct cw1200_common *priv =
-               (struct cw1200_common *)arg;
-
-       wiphy_warn(priv->hw->wiphy,
-                  "Multicast delivery timeout.\n");
-       spin_lock_bh(&priv->ps_state_lock);
-       priv->tx_multicast = priv->aid0_bit_set &&
-                       priv->buffered_multicasts;
-       if (priv->tx_multicast)
-               cw1200_bh_wakeup(priv);
-       spin_unlock_bh(&priv->ps_state_lock);
-}
-
-int cw1200_ampdu_action(struct ieee80211_hw *hw,
-                       struct ieee80211_vif *vif,
-                       enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size, bool amsdu)
-{
-       /* Aggregation is implemented fully in firmware,
-        * including block ack negotiation. Do not allow
-        * mac80211 stack to do anything: it interferes with
-        * the firmware.
-        */
-
-       /* Note that we still need this function stubbed. */
-       return -ENOTSUPP;
-}
-
-/* ******************************************************************** */
-/* WSM callback                                                                */
-void cw1200_suspend_resume(struct cw1200_common *priv,
-                         struct wsm_suspend_resume *arg)
-{
-       pr_debug("[AP] %s: %s\n",
-                arg->stop ? "stop" : "start",
-                arg->multicast ? "broadcast" : "unicast");
-
-       if (arg->multicast) {
-               bool cancel_tmo = false;
-               spin_lock_bh(&priv->ps_state_lock);
-               if (arg->stop) {
-                       priv->tx_multicast = false;
-               } else {
-                       /* Firmware sends this indication every DTIM if there
-                        * is a STA in powersave connected. There is no reason
-                        * to suspend, following wakeup will consume much more
-                        * power than it could be saved.
-                        */
-                       cw1200_pm_stay_awake(&priv->pm_state,
-                                            priv->join_dtim_period *
-                                            (priv->beacon_int + 20) * HZ / 1024);
-                       priv->tx_multicast = (priv->aid0_bit_set &&
-                                             priv->buffered_multicasts);
-                       if (priv->tx_multicast) {
-                               cancel_tmo = true;
-                               cw1200_bh_wakeup(priv);
-                       }
-               }
-               spin_unlock_bh(&priv->ps_state_lock);
-               if (cancel_tmo)
-                       del_timer_sync(&priv->mcast_timeout);
-       } else {
-               spin_lock_bh(&priv->ps_state_lock);
-               cw1200_ps_notify(priv, arg->link_id, arg->stop);
-               spin_unlock_bh(&priv->ps_state_lock);
-               if (!arg->stop)
-                       cw1200_bh_wakeup(priv);
-       }
-       return;
-}
-
-/* ******************************************************************** */
-/* AP privates                                                         */
-
-static int cw1200_upload_beacon(struct cw1200_common *priv)
-{
-       int ret = 0;
-       struct ieee80211_mgmt *mgmt;
-       struct wsm_template_frame frame = {
-               .frame_type = WSM_FRAME_TYPE_BEACON,
-       };
-
-       u16 tim_offset;
-       u16 tim_len;
-
-       if (priv->mode == NL80211_IFTYPE_STATION ||
-           priv->mode == NL80211_IFTYPE_MONITOR ||
-           priv->mode == NL80211_IFTYPE_UNSPECIFIED)
-               goto done;
-
-       if (priv->vif->p2p)
-               frame.rate = WSM_TRANSMIT_RATE_6;
-
-       frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
-                                            &tim_offset, &tim_len);
-       if (!frame.skb)
-               return -ENOMEM;
-
-       ret = wsm_set_template_frame(priv, &frame);
-
-       if (ret)
-               goto done;
-
-       /* TODO: Distill probe resp; remove TIM
-        * and any other beacon-specific IEs
-        */
-       mgmt = (void *)frame.skb->data;
-       mgmt->frame_control =
-               __cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                             IEEE80211_STYPE_PROBE_RESP);
-
-       frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
-       if (priv->vif->p2p) {
-               ret = wsm_set_probe_responder(priv, true);
-       } else {
-               ret = wsm_set_template_frame(priv, &frame);
-               wsm_set_probe_responder(priv, false);
-       }
-
-done:
-       dev_kfree_skb(frame.skb);
-
-       return ret;
-}
-
-static int cw1200_upload_pspoll(struct cw1200_common *priv)
-{
-       int ret = 0;
-       struct wsm_template_frame frame = {
-               .frame_type = WSM_FRAME_TYPE_PS_POLL,
-               .rate = 0xFF,
-       };
-
-
-       frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
-       if (!frame.skb)
-               return -ENOMEM;
-
-       ret = wsm_set_template_frame(priv, &frame);
-
-       dev_kfree_skb(frame.skb);
-
-       return ret;
-}
-
-static int cw1200_upload_null(struct cw1200_common *priv)
-{
-       int ret = 0;
-       struct wsm_template_frame frame = {
-               .frame_type = WSM_FRAME_TYPE_NULL,
-               .rate = 0xFF,
-       };
-
-       frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
-       if (!frame.skb)
-               return -ENOMEM;
-
-       ret = wsm_set_template_frame(priv, &frame);
-
-       dev_kfree_skb(frame.skb);
-
-       return ret;
-}
-
-static int cw1200_upload_qosnull(struct cw1200_common *priv)
-{
-       /* TODO:  This needs to be implemented
-
-       struct wsm_template_frame frame = {
-               .frame_type = WSM_FRAME_TYPE_QOS_NULL,
-               .rate = 0xFF,
-       };
-
-       frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
-       if (!frame.skb)
-               return -ENOMEM;
-
-       ret = wsm_set_template_frame(priv, &frame);
-
-       dev_kfree_skb(frame.skb);
-
-       */
-       return 0;
-}
-
-static int cw1200_enable_beaconing(struct cw1200_common *priv,
-                                  bool enable)
-{
-       struct wsm_beacon_transmit transmit = {
-               .enable_beaconing = enable,
-       };
-
-       return wsm_beacon_transmit(priv, &transmit);
-}
-
-static int cw1200_start_ap(struct cw1200_common *priv)
-{
-       int ret;
-       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
-       struct wsm_start start = {
-               .mode = priv->vif->p2p ?
-                               WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
-               .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
-                               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
-               .channel_number = priv->channel->hw_value,
-               .beacon_interval = conf->beacon_int,
-               .dtim_period = conf->dtim_period,
-               .preamble = conf->use_short_preamble ?
-                               WSM_JOIN_PREAMBLE_SHORT :
-                               WSM_JOIN_PREAMBLE_LONG,
-               .probe_delay = 100,
-               .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
-                               conf->basic_rates),
-       };
-       struct wsm_operational_mode mode = {
-               .power_mode = cw1200_power_mode,
-               .disable_more_flag_usage = true,
-       };
-
-       memset(start.ssid, 0, sizeof(start.ssid));
-       if (!conf->hidden_ssid) {
-               start.ssid_len = conf->ssid_len;
-               memcpy(start.ssid, conf->ssid, start.ssid_len);
-       }
-
-       priv->beacon_int = conf->beacon_int;
-       priv->join_dtim_period = conf->dtim_period;
-
-       memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
-
-       pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n",
-                start.channel_number, start.band,
-                start.beacon_interval, start.dtim_period,
-                start.basic_rate_set,
-                start.ssid_len, start.ssid);
-       ret = wsm_start(priv, &start);
-       if (!ret)
-               ret = cw1200_upload_keys(priv);
-       if (!ret && priv->vif->p2p) {
-               pr_debug("[AP] Setting p2p powersave configuration.\n");
-               wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo);
-       }
-       if (!ret) {
-               wsm_set_block_ack_policy(priv, 0, 0);
-               priv->join_status = CW1200_JOIN_STATUS_AP;
-               cw1200_update_filtering(priv);
-       }
-       wsm_set_operational_mode(priv, &mode);
-       return ret;
-}
-
-static int cw1200_update_beaconing(struct cw1200_common *priv)
-{
-       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
-       struct wsm_reset reset = {
-               .link_id = 0,
-               .reset_statistics = true,
-       };
-
-       if (priv->mode == NL80211_IFTYPE_AP) {
-               /* TODO: check if changed channel, band */
-               if (priv->join_status != CW1200_JOIN_STATUS_AP ||
-                   priv->beacon_int != conf->beacon_int) {
-                       pr_debug("ap restarting\n");
-                       wsm_lock_tx(priv);
-                       if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE)
-                               wsm_reset(priv, &reset);
-                       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
-                       cw1200_start_ap(priv);
-                       wsm_unlock_tx(priv);
-               } else
-                       pr_debug("ap started join_status: %d\n",
-                                priv->join_status);
-       }
-       return 0;
-}
diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h
deleted file mode 100644 (file)
index bebb337..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef STA_H_INCLUDED
-#define STA_H_INCLUDED
-
-/* ******************************************************************** */
-/* mac80211 API                                                                */
-
-int cw1200_start(struct ieee80211_hw *dev);
-void cw1200_stop(struct ieee80211_hw *dev);
-int cw1200_add_interface(struct ieee80211_hw *dev,
-                        struct ieee80211_vif *vif);
-void cw1200_remove_interface(struct ieee80211_hw *dev,
-                            struct ieee80211_vif *vif);
-int cw1200_change_interface(struct ieee80211_hw *dev,
-                           struct ieee80211_vif *vif,
-                           enum nl80211_iftype new_type,
-                           bool p2p);
-int cw1200_config(struct ieee80211_hw *dev, u32 changed);
-void cw1200_configure_filter(struct ieee80211_hw *dev,
-                            unsigned int changed_flags,
-                            unsigned int *total_flags,
-                            u64 multicast);
-int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
-                  u16 queue, const struct ieee80211_tx_queue_params *params);
-int cw1200_get_stats(struct ieee80211_hw *dev,
-                    struct ieee80211_low_level_stats *stats);
-int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
-                  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
-                  struct ieee80211_key_conf *key);
-
-int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
-
-void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                 u32 queues, bool drop);
-
-u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
-                            struct netdev_hw_addr_list *mc_list);
-
-int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
-
-/* ******************************************************************** */
-/* WSM callbacks                                                       */
-
-void cw1200_join_complete_cb(struct cw1200_common *priv,
-                               struct wsm_join_complete *arg);
-
-/* ******************************************************************** */
-/* WSM events                                                          */
-
-void cw1200_free_event_queue(struct cw1200_common *priv);
-void cw1200_event_handler(struct work_struct *work);
-void cw1200_bss_loss_work(struct work_struct *work);
-void cw1200_bss_params_work(struct work_struct *work);
-void cw1200_keep_alive_work(struct work_struct *work);
-void cw1200_tx_failure_work(struct work_struct *work);
-
-void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good,
-                            int bad);
-static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
-                                        int init, int good, int bad)
-{
-       spin_lock(&priv->bss_loss_lock);
-       __cw1200_cqm_bssloss_sm(priv, init, good, bad);
-       spin_unlock(&priv->bss_loss_lock);
-}
-
-/* ******************************************************************** */
-/* Internal API                                                                */
-
-int cw1200_setup_mac(struct cw1200_common *priv);
-void cw1200_join_timeout(struct work_struct *work);
-void cw1200_unjoin_work(struct work_struct *work);
-void cw1200_join_complete_work(struct work_struct *work);
-void cw1200_wep_key_work(struct work_struct *work);
-void cw1200_update_listening(struct cw1200_common *priv, bool enabled);
-void cw1200_update_filtering(struct cw1200_common *priv);
-void cw1200_update_filtering_work(struct work_struct *work);
-void cw1200_set_beacon_wakeup_period_work(struct work_struct *work);
-int cw1200_enable_listening(struct cw1200_common *priv);
-int cw1200_disable_listening(struct cw1200_common *priv);
-int cw1200_set_uapsd_param(struct cw1200_common *priv,
-                               const struct wsm_edca_params *arg);
-void cw1200_ba_work(struct work_struct *work);
-void cw1200_ba_timer(unsigned long arg);
-
-/* AP stuffs */
-int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
-                  bool set);
-int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                  struct ieee80211_sta *sta);
-int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                     struct ieee80211_sta *sta);
-void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
-                      enum sta_notify_cmd notify_cmd,
-                      struct ieee80211_sta *sta);
-void cw1200_bss_info_changed(struct ieee80211_hw *dev,
-                            struct ieee80211_vif *vif,
-                            struct ieee80211_bss_conf *info,
-                            u32 changed);
-int cw1200_ampdu_action(struct ieee80211_hw *hw,
-                       struct ieee80211_vif *vif,
-                       enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size, bool amsdu);
-
-void cw1200_suspend_resume(struct cw1200_common *priv,
-                         struct wsm_suspend_resume *arg);
-void cw1200_set_tim_work(struct work_struct *work);
-void cw1200_set_cts_work(struct work_struct *work);
-void cw1200_multicast_start_work(struct work_struct *work);
-void cw1200_multicast_stop_work(struct work_struct *work);
-void cw1200_mcast_timeout(unsigned long arg);
-
-#endif
diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c
deleted file mode 100644 (file)
index d28bd49..0000000
+++ /dev/null
@@ -1,1472 +0,0 @@
-/*
- * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <net/mac80211.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-#include "cw1200.h"
-#include "wsm.h"
-#include "bh.h"
-#include "sta.h"
-#include "debug.h"
-
-#define CW1200_INVALID_RATE_ID (0xFF)
-
-static int cw1200_handle_action_rx(struct cw1200_common *priv,
-                                  struct sk_buff *skb);
-static const struct ieee80211_rate *
-cw1200_get_tx_rate(const struct cw1200_common *priv,
-                  const struct ieee80211_tx_rate *rate);
-
-/* ******************************************************************** */
-/* TX queue lock / unlock                                              */
-
-static inline void cw1200_tx_queues_lock(struct cw1200_common *priv)
-{
-       int i;
-       for (i = 0; i < 4; ++i)
-               cw1200_queue_lock(&priv->tx_queue[i]);
-}
-
-static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv)
-{
-       int i;
-       for (i = 0; i < 4; ++i)
-               cw1200_queue_unlock(&priv->tx_queue[i]);
-}
-
-/* ******************************************************************** */
-/* TX policy cache implementation                                      */
-
-static void tx_policy_dump(struct tx_policy *policy)
-{
-       pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n",
-                policy->raw[0] & 0x0F,  policy->raw[0] >> 4,
-                policy->raw[1] & 0x0F,  policy->raw[1] >> 4,
-                policy->raw[2] & 0x0F,  policy->raw[2] >> 4,
-                policy->raw[3] & 0x0F,  policy->raw[3] >> 4,
-                policy->raw[4] & 0x0F,  policy->raw[4] >> 4,
-                policy->raw[5] & 0x0F,  policy->raw[5] >> 4,
-                policy->raw[6] & 0x0F,  policy->raw[6] >> 4,
-                policy->raw[7] & 0x0F,  policy->raw[7] >> 4,
-                policy->raw[8] & 0x0F,  policy->raw[8] >> 4,
-                policy->raw[9] & 0x0F,  policy->raw[9] >> 4,
-                policy->raw[10] & 0x0F,  policy->raw[10] >> 4,
-                policy->raw[11] & 0x0F,  policy->raw[11] >> 4,
-                policy->defined);
-}
-
-static void tx_policy_build(const struct cw1200_common *priv,
-       /* [out] */ struct tx_policy *policy,
-       struct ieee80211_tx_rate *rates, size_t count)
-{
-       int i, j;
-       unsigned limit = priv->short_frame_max_tx_count;
-       unsigned total = 0;
-       BUG_ON(rates[0].idx < 0);
-       memset(policy, 0, sizeof(*policy));
-
-       /* Sort rates in descending order. */
-       for (i = 1; i < count; ++i) {
-               if (rates[i].idx < 0) {
-                       count = i;
-                       break;
-               }
-               if (rates[i].idx > rates[i - 1].idx) {
-                       struct ieee80211_tx_rate tmp = rates[i - 1];
-                       rates[i - 1] = rates[i];
-                       rates[i] = tmp;
-               }
-       }
-
-       /* Eliminate duplicates. */
-       total = rates[0].count;
-       for (i = 0, j = 1; j < count; ++j) {
-               if (rates[j].idx == rates[i].idx) {
-                       rates[i].count += rates[j].count;
-               } else if (rates[j].idx > rates[i].idx) {
-                       break;
-               } else {
-                       ++i;
-                       if (i != j)
-                               rates[i] = rates[j];
-               }
-               total += rates[j].count;
-       }
-       count = i + 1;
-
-       /* Re-fill policy trying to keep every requested rate and with
-        * respect to the global max tx retransmission count.
-        */
-       if (limit < count)
-               limit = count;
-       if (total > limit) {
-               for (i = 0; i < count; ++i) {
-                       int left = count - i - 1;
-                       if (rates[i].count > limit - left)
-                               rates[i].count = limit - left;
-                       limit -= rates[i].count;
-               }
-       }
-
-       /* HACK!!! Device has problems (at least) switching from
-        * 54Mbps CTS to 1Mbps. This switch takes enormous amount
-        * of time (100-200 ms), leading to valuable throughput drop.
-        * As a workaround, additional g-rates are injected to the
-        * policy.
-        */
-       if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
-           rates[0].idx > 4 && rates[0].count > 2 &&
-           rates[1].idx < 2) {
-               int mid_rate = (rates[0].idx + 4) >> 1;
-
-               /* Decrease number of retries for the initial rate */
-               rates[0].count -= 2;
-
-               if (mid_rate != 4) {
-                       /* Keep fallback rate at 1Mbps. */
-                       rates[3] = rates[1];
-
-                       /* Inject 1 transmission on lowest g-rate */
-                       rates[2].idx = 4;
-                       rates[2].count = 1;
-                       rates[2].flags = rates[1].flags;
-
-                       /* Inject 1 transmission on mid-rate */
-                       rates[1].idx = mid_rate;
-                       rates[1].count = 1;
-
-                       /* Fallback to 1 Mbps is a really bad thing,
-                        * so let's try to increase probability of
-                        * successful transmission on the lowest g rate
-                        * even more
-                        */
-                       if (rates[0].count >= 3) {
-                               --rates[0].count;
-                               ++rates[2].count;
-                       }
-
-                       /* Adjust amount of rates defined */
-                       count += 2;
-               } else {
-                       /* Keep fallback rate at 1Mbps. */
-                       rates[2] = rates[1];
-
-                       /* Inject 2 transmissions on lowest g-rate */
-                       rates[1].idx = 4;
-                       rates[1].count = 2;
-
-                       /* Adjust amount of rates defined */
-                       count += 1;
-               }
-       }
-
-       policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1;
-
-       for (i = 0; i < count; ++i) {
-               register unsigned rateid, off, shift, retries;
-
-               rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value;
-               off = rateid >> 3;              /* eq. rateid / 8 */
-               shift = (rateid & 0x07) << 2;   /* eq. (rateid % 8) * 4 */
-
-               retries = rates[i].count;
-               if (retries > 0x0F) {
-                       rates[i].count = 0x0f;
-                       retries = 0x0F;
-               }
-               policy->tbl[off] |= __cpu_to_le32(retries << shift);
-               policy->retry_count += retries;
-       }
-
-       pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d\n",
-                count,
-                rates[0].idx, rates[0].count,
-                rates[1].idx, rates[1].count,
-                rates[2].idx, rates[2].count,
-                rates[3].idx, rates[3].count);
-}
-
-static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
-                                       const struct tx_policy *cached)
-{
-       size_t count = wanted->defined >> 1;
-       if (wanted->defined > cached->defined)
-               return false;
-       if (count) {
-               if (memcmp(wanted->raw, cached->raw, count))
-                       return false;
-       }
-       if (wanted->defined & 1) {
-               if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F))
-                       return false;
-       }
-       return true;
-}
-
-static int tx_policy_find(struct tx_policy_cache *cache,
-                               const struct tx_policy *wanted)
-{
-       /* O(n) complexity. Not so good, but there's only 8 entries in
-        * the cache.
-        * Also lru helps to reduce search time.
-        */
-       struct tx_policy_cache_entry *it;
-       /* First search for policy in "used" list */
-       list_for_each_entry(it, &cache->used, link) {
-               if (tx_policy_is_equal(wanted, &it->policy))
-                       return it - cache->cache;
-       }
-       /* Then - in "free list" */
-       list_for_each_entry(it, &cache->free, link) {
-               if (tx_policy_is_equal(wanted, &it->policy))
-                       return it - cache->cache;
-       }
-       return -1;
-}
-
-static inline void tx_policy_use(struct tx_policy_cache *cache,
-                                struct tx_policy_cache_entry *entry)
-{
-       ++entry->policy.usage_count;
-       list_move(&entry->link, &cache->used);
-}
-
-static inline int tx_policy_release(struct tx_policy_cache *cache,
-                                   struct tx_policy_cache_entry *entry)
-{
-       int ret = --entry->policy.usage_count;
-       if (!ret)
-               list_move(&entry->link, &cache->free);
-       return ret;
-}
-
-void tx_policy_clean(struct cw1200_common *priv)
-{
-       int idx, locked;
-       struct tx_policy_cache *cache = &priv->tx_policy_cache;
-       struct tx_policy_cache_entry *entry;
-
-       cw1200_tx_queues_lock(priv);
-       spin_lock_bh(&cache->lock);
-       locked = list_empty(&cache->free);
-
-       for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) {
-               entry = &cache->cache[idx];
-               /* Policy usage count should be 0 at this time as all queues
-                  should be empty
-                */
-               if (WARN_ON(entry->policy.usage_count)) {
-                       entry->policy.usage_count = 0;
-                       list_move(&entry->link, &cache->free);
-               }
-               memset(&entry->policy, 0, sizeof(entry->policy));
-       }
-       if (locked)
-               cw1200_tx_queues_unlock(priv);
-
-       cw1200_tx_queues_unlock(priv);
-       spin_unlock_bh(&cache->lock);
-}
-
-/* ******************************************************************** */
-/* External TX policy cache API                                                */
-
-void tx_policy_init(struct cw1200_common *priv)
-{
-       struct tx_policy_cache *cache = &priv->tx_policy_cache;
-       int i;
-
-       memset(cache, 0, sizeof(*cache));
-
-       spin_lock_init(&cache->lock);
-       INIT_LIST_HEAD(&cache->used);
-       INIT_LIST_HEAD(&cache->free);
-
-       for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i)
-               list_add(&cache->cache[i].link, &cache->free);
-}
-
-static int tx_policy_get(struct cw1200_common *priv,
-                 struct ieee80211_tx_rate *rates,
-                 size_t count, bool *renew)
-{
-       int idx;
-       struct tx_policy_cache *cache = &priv->tx_policy_cache;
-       struct tx_policy wanted;
-
-       tx_policy_build(priv, &wanted, rates, count);
-
-       spin_lock_bh(&cache->lock);
-       if (WARN_ON_ONCE(list_empty(&cache->free))) {
-               spin_unlock_bh(&cache->lock);
-               return CW1200_INVALID_RATE_ID;
-       }
-       idx = tx_policy_find(cache, &wanted);
-       if (idx >= 0) {
-               pr_debug("[TX policy] Used TX policy: %d\n", idx);
-               *renew = false;
-       } else {
-               struct tx_policy_cache_entry *entry;
-               *renew = true;
-               /* If policy is not found create a new one
-                * using the oldest entry in "free" list
-                */
-               entry = list_entry(cache->free.prev,
-                       struct tx_policy_cache_entry, link);
-               entry->policy = wanted;
-               idx = entry - cache->cache;
-               pr_debug("[TX policy] New TX policy: %d\n", idx);
-               tx_policy_dump(&entry->policy);
-       }
-       tx_policy_use(cache, &cache->cache[idx]);
-       if (list_empty(&cache->free)) {
-               /* Lock TX queues. */
-               cw1200_tx_queues_lock(priv);
-       }
-       spin_unlock_bh(&cache->lock);
-       return idx;
-}
-
-static void tx_policy_put(struct cw1200_common *priv, int idx)
-{
-       int usage, locked;
-       struct tx_policy_cache *cache = &priv->tx_policy_cache;
-
-       spin_lock_bh(&cache->lock);
-       locked = list_empty(&cache->free);
-       usage = tx_policy_release(cache, &cache->cache[idx]);
-       if (locked && !usage) {
-               /* Unlock TX queues. */
-               cw1200_tx_queues_unlock(priv);
-       }
-       spin_unlock_bh(&cache->lock);
-}
-
-static int tx_policy_upload(struct cw1200_common *priv)
-{
-       struct tx_policy_cache *cache = &priv->tx_policy_cache;
-       int i;
-       struct wsm_set_tx_rate_retry_policy arg = {
-               .num = 0,
-       };
-       spin_lock_bh(&cache->lock);
-
-       /* Upload only modified entries. */
-       for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) {
-               struct tx_policy *src = &cache->cache[i].policy;
-               if (src->retry_count && !src->uploaded) {
-                       struct wsm_tx_rate_retry_policy *dst =
-                               &arg.tbl[arg.num];
-                       dst->index = i;
-                       dst->short_retries = priv->short_frame_max_tx_count;
-                       dst->long_retries = priv->long_frame_max_tx_count;
-
-                       dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED |
-                               WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT;
-                       memcpy(dst->rate_count_indices, src->tbl,
-                              sizeof(dst->rate_count_indices));
-                       src->uploaded = 1;
-                       ++arg.num;
-               }
-       }
-       spin_unlock_bh(&cache->lock);
-       cw1200_debug_tx_cache_miss(priv);
-       pr_debug("[TX policy] Upload %d policies\n", arg.num);
-       return wsm_set_tx_rate_retry_policy(priv, &arg);
-}
-
-void tx_policy_upload_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, tx_policy_upload_work);
-
-       pr_debug("[TX] TX policy upload.\n");
-       tx_policy_upload(priv);
-
-       wsm_unlock_tx(priv);
-       cw1200_tx_queues_unlock(priv);
-}
-
-/* ******************************************************************** */
-/* cw1200 TX implementation                                            */
-
-struct cw1200_txinfo {
-       struct sk_buff *skb;
-       unsigned queue;
-       struct ieee80211_tx_info *tx_info;
-       const struct ieee80211_rate *rate;
-       struct ieee80211_hdr *hdr;
-       size_t hdrlen;
-       const u8 *da;
-       struct cw1200_sta_priv *sta_priv;
-       struct ieee80211_sta *sta;
-       struct cw1200_txpriv txpriv;
-};
-
-u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates)
-{
-       u32 ret = 0;
-       int i;
-       for (i = 0; i < 32; ++i) {
-               if (rates & BIT(i))
-                       ret |= BIT(priv->rates[i].hw_value);
-       }
-       return ret;
-}
-
-static const struct ieee80211_rate *
-cw1200_get_tx_rate(const struct cw1200_common *priv,
-                  const struct ieee80211_tx_rate *rate)
-{
-       if (rate->idx < 0)
-               return NULL;
-       if (rate->flags & IEEE80211_TX_RC_MCS)
-               return &priv->mcs_rates[rate->idx];
-       return &priv->hw->wiphy->bands[priv->channel->band]->
-               bitrates[rate->idx];
-}
-
-static int
-cw1200_tx_h_calc_link_ids(struct cw1200_common *priv,
-                         struct cw1200_txinfo *t)
-{
-       if (t->sta && t->sta_priv->link_id)
-               t->txpriv.raw_link_id =
-                               t->txpriv.link_id =
-                               t->sta_priv->link_id;
-       else if (priv->mode != NL80211_IFTYPE_AP)
-               t->txpriv.raw_link_id =
-                               t->txpriv.link_id = 0;
-       else if (is_multicast_ether_addr(t->da)) {
-               if (priv->enable_beacon) {
-                       t->txpriv.raw_link_id = 0;
-                       t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM;
-               } else {
-                       t->txpriv.raw_link_id = 0;
-                       t->txpriv.link_id = 0;
-               }
-       } else {
-               t->txpriv.link_id = cw1200_find_link_id(priv, t->da);
-               if (!t->txpriv.link_id)
-                       t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da);
-               if (!t->txpriv.link_id) {
-                       wiphy_err(priv->hw->wiphy,
-                                 "No more link IDs available.\n");
-                       return -ENOENT;
-               }
-               t->txpriv.raw_link_id = t->txpriv.link_id;
-       }
-       if (t->txpriv.raw_link_id)
-               priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp =
-                               jiffies;
-       if (t->sta && (t->sta->uapsd_queues & BIT(t->queue)))
-               t->txpriv.link_id = CW1200_LINK_ID_UAPSD;
-       return 0;
-}
-
-static void
-cw1200_tx_h_pm(struct cw1200_common *priv,
-              struct cw1200_txinfo *t)
-{
-       if (ieee80211_is_auth(t->hdr->frame_control)) {
-               u32 mask = ~BIT(t->txpriv.raw_link_id);
-               spin_lock_bh(&priv->ps_state_lock);
-               priv->sta_asleep_mask &= mask;
-               priv->pspoll_mask &= mask;
-               spin_unlock_bh(&priv->ps_state_lock);
-       }
-}
-
-static void
-cw1200_tx_h_calc_tid(struct cw1200_common *priv,
-                    struct cw1200_txinfo *t)
-{
-       if (ieee80211_is_data_qos(t->hdr->frame_control)) {
-               u8 *qos = ieee80211_get_qos_ctl(t->hdr);
-               t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK;
-       } else if (ieee80211_is_data(t->hdr->frame_control)) {
-               t->txpriv.tid = 0;
-       }
-}
-
-static int
-cw1200_tx_h_crypt(struct cw1200_common *priv,
-                 struct cw1200_txinfo *t)
-{
-       if (!t->tx_info->control.hw_key ||
-           !ieee80211_has_protected(t->hdr->frame_control))
-               return 0;
-
-       t->hdrlen += t->tx_info->control.hw_key->iv_len;
-       skb_put(t->skb, t->tx_info->control.hw_key->icv_len);
-
-       if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
-               skb_put(t->skb, 8); /* MIC space */
-
-       return 0;
-}
-
-static int
-cw1200_tx_h_align(struct cw1200_common *priv,
-                 struct cw1200_txinfo *t,
-                 u8 *flags)
-{
-       size_t offset = (size_t)t->skb->data & 3;
-
-       if (!offset)
-               return 0;
-
-       if (offset & 1) {
-               wiphy_err(priv->hw->wiphy,
-                         "Bug: attempt to transmit a frame with wrong alignment: %zu\n",
-                         offset);
-               return -EINVAL;
-       }
-
-       if (skb_headroom(t->skb) < offset) {
-               wiphy_err(priv->hw->wiphy,
-                         "Bug: no space allocated for DMA alignment. headroom: %d\n",
-                         skb_headroom(t->skb));
-               return -ENOMEM;
-       }
-       skb_push(t->skb, offset);
-       t->hdrlen += offset;
-       t->txpriv.offset += offset;
-       *flags |= WSM_TX_2BYTES_SHIFT;
-       cw1200_debug_tx_align(priv);
-       return 0;
-}
-
-static int
-cw1200_tx_h_action(struct cw1200_common *priv,
-                  struct cw1200_txinfo *t)
-{
-       struct ieee80211_mgmt *mgmt =
-               (struct ieee80211_mgmt *)t->hdr;
-       if (ieee80211_is_action(t->hdr->frame_control) &&
-           mgmt->u.action.category == WLAN_CATEGORY_BACK)
-               return 1;
-       else
-               return 0;
-}
-
-/* Add WSM header */
-static struct wsm_tx *
-cw1200_tx_h_wsm(struct cw1200_common *priv,
-               struct cw1200_txinfo *t)
-{
-       struct wsm_tx *wsm;
-
-       if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) {
-               wiphy_err(priv->hw->wiphy,
-                         "Bug: no space allocated for WSM header. headroom: %d\n",
-                         skb_headroom(t->skb));
-               return NULL;
-       }
-
-       wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx));
-       t->txpriv.offset += sizeof(struct wsm_tx);
-       memset(wsm, 0, sizeof(*wsm));
-       wsm->hdr.len = __cpu_to_le16(t->skb->len);
-       wsm->hdr.id = __cpu_to_le16(0x0004);
-       wsm->queue_id = wsm_queue_id_to_wsm(t->queue);
-       return wsm;
-}
-
-/* BT Coex specific handling */
-static void
-cw1200_tx_h_bt(struct cw1200_common *priv,
-              struct cw1200_txinfo *t,
-              struct wsm_tx *wsm)
-{
-       u8 priority = 0;
-
-       if (!priv->bt_present)
-               return;
-
-       if (ieee80211_is_nullfunc(t->hdr->frame_control)) {
-               priority = WSM_EPTA_PRIORITY_MGT;
-       } else if (ieee80211_is_data(t->hdr->frame_control)) {
-               /* Skip LLC SNAP header (+6) */
-               u8 *payload = &t->skb->data[t->hdrlen];
-               __be16 *ethertype = (__be16 *)&payload[6];
-               if (be16_to_cpu(*ethertype) == ETH_P_PAE)
-                       priority = WSM_EPTA_PRIORITY_EAPOL;
-       } else if (ieee80211_is_assoc_req(t->hdr->frame_control) ||
-               ieee80211_is_reassoc_req(t->hdr->frame_control)) {
-               struct ieee80211_mgmt *mgt_frame =
-                               (struct ieee80211_mgmt *)t->hdr;
-
-               if (le16_to_cpu(mgt_frame->u.assoc_req.listen_interval) <
-                                               priv->listen_interval) {
-                       pr_debug("Modified Listen Interval to %d from %d\n",
-                                priv->listen_interval,
-                                mgt_frame->u.assoc_req.listen_interval);
-                       /* Replace listen interval derieved from
-                        * the one read from SDD
-                        */
-                       mgt_frame->u.assoc_req.listen_interval = cpu_to_le16(priv->listen_interval);
-               }
-       }
-
-       if (!priority) {
-               if (ieee80211_is_action(t->hdr->frame_control))
-                       priority = WSM_EPTA_PRIORITY_ACTION;
-               else if (ieee80211_is_mgmt(t->hdr->frame_control))
-                       priority = WSM_EPTA_PRIORITY_MGT;
-               else if ((wsm->queue_id == WSM_QUEUE_VOICE))
-                       priority = WSM_EPTA_PRIORITY_VOICE;
-               else if ((wsm->queue_id == WSM_QUEUE_VIDEO))
-                       priority = WSM_EPTA_PRIORITY_VIDEO;
-               else
-                       priority = WSM_EPTA_PRIORITY_DATA;
-       }
-
-       pr_debug("[TX] EPTA priority %d.\n", priority);
-
-       wsm->flags |= priority << 1;
-}
-
-static int
-cw1200_tx_h_rate_policy(struct cw1200_common *priv,
-                       struct cw1200_txinfo *t,
-                       struct wsm_tx *wsm)
-{
-       bool tx_policy_renew = false;
-
-       t->txpriv.rate_id = tx_policy_get(priv,
-               t->tx_info->control.rates, IEEE80211_TX_MAX_RATES,
-               &tx_policy_renew);
-       if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID)
-               return -EFAULT;
-
-       wsm->flags |= t->txpriv.rate_id << 4;
-
-       t->rate = cw1200_get_tx_rate(priv,
-               &t->tx_info->control.rates[0]),
-       wsm->max_tx_rate = t->rate->hw_value;
-       if (t->rate->flags & IEEE80211_TX_RC_MCS) {
-               if (cw1200_ht_greenfield(&priv->ht_info))
-                       wsm->ht_tx_parameters |=
-                               __cpu_to_le32(WSM_HT_TX_GREENFIELD);
-               else
-                       wsm->ht_tx_parameters |=
-                               __cpu_to_le32(WSM_HT_TX_MIXED);
-       }
-
-       if (tx_policy_renew) {
-               pr_debug("[TX] TX policy renew.\n");
-               /* It's not so optimal to stop TX queues every now and then.
-                * Better to reimplement task scheduling with
-                * a counter. TODO.
-                */
-               wsm_lock_tx_async(priv);
-               cw1200_tx_queues_lock(priv);
-               if (queue_work(priv->workqueue,
-                              &priv->tx_policy_upload_work) <= 0) {
-                       cw1200_tx_queues_unlock(priv);
-                       wsm_unlock_tx(priv);
-               }
-       }
-       return 0;
-}
-
-static bool
-cw1200_tx_h_pm_state(struct cw1200_common *priv,
-                    struct cw1200_txinfo *t)
-{
-       int was_buffered = 1;
-
-       if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM &&
-           !priv->buffered_multicasts) {
-               priv->buffered_multicasts = true;
-               if (priv->sta_asleep_mask)
-                       queue_work(priv->workqueue,
-                                  &priv->multicast_start_work);
-       }
-
-       if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID)
-               was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++;
-
-       return !was_buffered;
-}
-
-/* ******************************************************************** */
-
-void cw1200_tx(struct ieee80211_hw *dev,
-              struct ieee80211_tx_control *control,
-              struct sk_buff *skb)
-{
-       struct cw1200_common *priv = dev->priv;
-       struct cw1200_txinfo t = {
-               .skb = skb,
-               .queue = skb_get_queue_mapping(skb),
-               .tx_info = IEEE80211_SKB_CB(skb),
-               .hdr = (struct ieee80211_hdr *)skb->data,
-               .txpriv.tid = CW1200_MAX_TID,
-               .txpriv.rate_id = CW1200_INVALID_RATE_ID,
-       };
-       struct ieee80211_sta *sta;
-       struct wsm_tx *wsm;
-       bool tid_update = 0;
-       u8 flags = 0;
-       int ret;
-
-       if (priv->bh_error)
-               goto drop;
-
-       t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control);
-       t.da = ieee80211_get_DA(t.hdr);
-       if (control) {
-               t.sta = control->sta;
-               t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv;
-       }
-
-       if (WARN_ON(t.queue >= 4))
-               goto drop;
-
-       ret = cw1200_tx_h_calc_link_ids(priv, &t);
-       if (ret)
-               goto drop;
-
-       pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n",
-                skb->len, t.queue, t.txpriv.link_id,
-                t.txpriv.raw_link_id);
-
-       cw1200_tx_h_pm(priv, &t);
-       cw1200_tx_h_calc_tid(priv, &t);
-       ret = cw1200_tx_h_crypt(priv, &t);
-       if (ret)
-               goto drop;
-       ret = cw1200_tx_h_align(priv, &t, &flags);
-       if (ret)
-               goto drop;
-       ret = cw1200_tx_h_action(priv, &t);
-       if (ret)
-               goto drop;
-       wsm = cw1200_tx_h_wsm(priv, &t);
-       if (!wsm) {
-               ret = -ENOMEM;
-               goto drop;
-       }
-       wsm->flags |= flags;
-       cw1200_tx_h_bt(priv, &t, wsm);
-       ret = cw1200_tx_h_rate_policy(priv, &t, wsm);
-       if (ret)
-               goto drop;
-
-       rcu_read_lock();
-       sta = rcu_dereference(t.sta);
-
-       spin_lock_bh(&priv->ps_state_lock);
-       {
-               tid_update = cw1200_tx_h_pm_state(priv, &t);
-               BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue],
-                                       t.skb, &t.txpriv));
-       }
-       spin_unlock_bh(&priv->ps_state_lock);
-
-       if (tid_update && sta)
-               ieee80211_sta_set_buffered(sta, t.txpriv.tid, true);
-
-       rcu_read_unlock();
-
-       cw1200_bh_wakeup(priv);
-
-       return;
-
-drop:
-       cw1200_skb_dtor(priv, skb, &t.txpriv);
-       return;
-}
-
-/* ******************************************************************** */
-
-static int cw1200_handle_action_rx(struct cw1200_common *priv,
-                                  struct sk_buff *skb)
-{
-       struct ieee80211_mgmt *mgmt = (void *)skb->data;
-
-       /* Filter block ACK negotiation: fully controlled by firmware */
-       if (mgmt->u.action.category == WLAN_CATEGORY_BACK)
-               return 1;
-
-       return 0;
-}
-
-static int cw1200_handle_pspoll(struct cw1200_common *priv,
-                               struct sk_buff *skb)
-{
-       struct ieee80211_sta *sta;
-       struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data;
-       int link_id = 0;
-       u32 pspoll_mask = 0;
-       int drop = 1;
-       int i;
-
-       if (priv->join_status != CW1200_JOIN_STATUS_AP)
-               goto done;
-       if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN))
-               goto done;
-
-       rcu_read_lock();
-       sta = ieee80211_find_sta(priv->vif, pspoll->ta);
-       if (sta) {
-               struct cw1200_sta_priv *sta_priv;
-               sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv;
-               link_id = sta_priv->link_id;
-               pspoll_mask = BIT(sta_priv->link_id);
-       }
-       rcu_read_unlock();
-       if (!link_id)
-               goto done;
-
-       priv->pspoll_mask |= pspoll_mask;
-       drop = 0;
-
-       /* Do not report pspols if data for given link id is queued already. */
-       for (i = 0; i < 4; ++i) {
-               if (cw1200_queue_get_num_queued(&priv->tx_queue[i],
-                                               pspoll_mask)) {
-                       cw1200_bh_wakeup(priv);
-                       drop = 1;
-                       break;
-               }
-       }
-       pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd");
-done:
-       return drop;
-}
-
-/* ******************************************************************** */
-
-void cw1200_tx_confirm_cb(struct cw1200_common *priv,
-                         int link_id,
-                         struct wsm_tx_confirm *arg)
-{
-       u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id);
-       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
-       struct sk_buff *skb;
-       const struct cw1200_txpriv *txpriv;
-
-       pr_debug("[TX] TX confirm: %d, %d.\n",
-                arg->status, arg->ack_failures);
-
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
-               /* STA is stopped. */
-               return;
-       }
-
-       if (WARN_ON(queue_id >= 4))
-               return;
-
-       if (arg->status)
-               pr_debug("TX failed: %d.\n", arg->status);
-
-       if ((arg->status == WSM_REQUEUE) &&
-           (arg->flags & WSM_TX_STATUS_REQUEUE)) {
-               /* "Requeue" means "implicit suspend" */
-               struct wsm_suspend_resume suspend = {
-                       .link_id = link_id,
-                       .stop = 1,
-                       .multicast = !link_id,
-               };
-               cw1200_suspend_resume(priv, &suspend);
-               wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n",
-                          link_id,
-                          cw1200_queue_get_generation(arg->packet_id) + 1,
-                          priv->sta_asleep_mask);
-               cw1200_queue_requeue(queue, arg->packet_id);
-               spin_lock_bh(&priv->ps_state_lock);
-               if (!link_id) {
-                       priv->buffered_multicasts = true;
-                       if (priv->sta_asleep_mask) {
-                               queue_work(priv->workqueue,
-                                          &priv->multicast_start_work);
-                       }
-               }
-               spin_unlock_bh(&priv->ps_state_lock);
-       } else if (!cw1200_queue_get_skb(queue, arg->packet_id,
-                                        &skb, &txpriv)) {
-               struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb);
-               int tx_count = arg->ack_failures;
-               u8 ht_flags = 0;
-               int i;
-
-               if (cw1200_ht_greenfield(&priv->ht_info))
-                       ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
-
-               spin_lock(&priv->bss_loss_lock);
-               if (priv->bss_loss_state &&
-                   arg->packet_id == priv->bss_loss_confirm_id) {
-                       if (arg->status) {
-                               /* Recovery failed */
-                               __cw1200_cqm_bssloss_sm(priv, 0, 0, 1);
-                       } else {
-                               /* Recovery succeeded */
-                               __cw1200_cqm_bssloss_sm(priv, 0, 1, 0);
-                       }
-               }
-               spin_unlock(&priv->bss_loss_lock);
-
-               if (!arg->status) {
-                       tx->flags |= IEEE80211_TX_STAT_ACK;
-                       ++tx_count;
-                       cw1200_debug_txed(priv);
-                       if (arg->flags & WSM_TX_STATUS_AGGREGATION) {
-                               /* Do not report aggregation to mac80211:
-                                * it confuses minstrel a lot.
-                                */
-                               /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */
-                               cw1200_debug_txed_agg(priv);
-                       }
-               } else {
-                       if (tx_count)
-                               ++tx_count;
-               }
-
-               for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
-                       if (tx->status.rates[i].count >= tx_count) {
-                               tx->status.rates[i].count = tx_count;
-                               break;
-                       }
-                       tx_count -= tx->status.rates[i].count;
-                       if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS)
-                               tx->status.rates[i].flags |= ht_flags;
-               }
-
-               for (++i; i < IEEE80211_TX_MAX_RATES; ++i) {
-                       tx->status.rates[i].count = 0;
-                       tx->status.rates[i].idx = -1;
-               }
-
-               /* Pull off any crypto trailers that we added on */
-               if (tx->control.hw_key) {
-                       skb_trim(skb, skb->len - tx->control.hw_key->icv_len);
-                       if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
-                               skb_trim(skb, skb->len - 8); /* MIC space */
-               }
-               cw1200_queue_remove(queue, arg->packet_id);
-       }
-       /* XXX TODO:  Only wake if there are pending transmits.. */
-       cw1200_bh_wakeup(priv);
-}
-
-static void cw1200_notify_buffered_tx(struct cw1200_common *priv,
-                              struct sk_buff *skb, int link_id, int tid)
-{
-       struct ieee80211_sta *sta;
-       struct ieee80211_hdr *hdr;
-       u8 *buffered;
-       u8 still_buffered = 0;
-
-       if (link_id && tid < CW1200_MAX_TID) {
-               buffered = priv->link_id_db
-                               [link_id - 1].buffered;
-
-               spin_lock_bh(&priv->ps_state_lock);
-               if (!WARN_ON(!buffered[tid]))
-                       still_buffered = --buffered[tid];
-               spin_unlock_bh(&priv->ps_state_lock);
-
-               if (!still_buffered && tid < CW1200_MAX_TID) {
-                       hdr = (struct ieee80211_hdr *)skb->data;
-                       rcu_read_lock();
-                       sta = ieee80211_find_sta(priv->vif, hdr->addr1);
-                       if (sta)
-                               ieee80211_sta_set_buffered(sta, tid, false);
-                       rcu_read_unlock();
-               }
-       }
-}
-
-void cw1200_skb_dtor(struct cw1200_common *priv,
-                    struct sk_buff *skb,
-                    const struct cw1200_txpriv *txpriv)
-{
-       skb_pull(skb, txpriv->offset);
-       if (txpriv->rate_id != CW1200_INVALID_RATE_ID) {
-               cw1200_notify_buffered_tx(priv, skb,
-                                         txpriv->raw_link_id, txpriv->tid);
-               tx_policy_put(priv, txpriv->rate_id);
-       }
-       ieee80211_tx_status(priv->hw, skb);
-}
-
-void cw1200_rx_cb(struct cw1200_common *priv,
-                 struct wsm_rx *arg,
-                 int link_id,
-                 struct sk_buff **skb_p)
-{
-       struct sk_buff *skb = *skb_p;
-       struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
-       struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
-       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
-       struct cw1200_link_entry *entry = NULL;
-       unsigned long grace_period;
-
-       bool early_data = false;
-       bool p2p = priv->vif && priv->vif->p2p;
-       size_t hdrlen;
-       hdr->flag = 0;
-
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
-               /* STA is stopped. */
-               goto drop;
-       }
-
-       if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) {
-               entry = &priv->link_id_db[link_id - 1];
-               if (entry->status == CW1200_LINK_SOFT &&
-                   ieee80211_is_data(frame->frame_control))
-                       early_data = true;
-               entry->timestamp = jiffies;
-       } else if (p2p &&
-                  ieee80211_is_action(frame->frame_control) &&
-                  (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
-               pr_debug("[RX] Going to MAP&RESET link ID\n");
-               WARN_ON(work_pending(&priv->linkid_reset_work));
-               memcpy(&priv->action_frame_sa[0],
-                      ieee80211_get_SA(frame), ETH_ALEN);
-               priv->action_linkid = 0;
-               schedule_work(&priv->linkid_reset_work);
-       }
-
-       if (link_id && p2p &&
-           ieee80211_is_action(frame->frame_control) &&
-           (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
-               /* Link ID already exists for the ACTION frame.
-                * Reset and Remap
-                */
-               WARN_ON(work_pending(&priv->linkid_reset_work));
-               memcpy(&priv->action_frame_sa[0],
-                      ieee80211_get_SA(frame), ETH_ALEN);
-               priv->action_linkid = link_id;
-               schedule_work(&priv->linkid_reset_work);
-       }
-       if (arg->status) {
-               if (arg->status == WSM_STATUS_MICFAILURE) {
-                       pr_debug("[RX] MIC failure.\n");
-                       hdr->flag |= RX_FLAG_MMIC_ERROR;
-               } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
-                       pr_debug("[RX] No key found.\n");
-                       goto drop;
-               } else {
-                       pr_debug("[RX] Receive failure: %d.\n",
-                                arg->status);
-                       goto drop;
-               }
-       }
-
-       if (skb->len < sizeof(struct ieee80211_pspoll)) {
-               wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n");
-               goto drop;
-       }
-
-       if (ieee80211_is_pspoll(frame->frame_control))
-               if (cw1200_handle_pspoll(priv, skb))
-                       goto drop;
-
-       hdr->band = ((arg->channel_number & 0xff00) ||
-                    (arg->channel_number > 14)) ?
-                       IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
-       hdr->freq = ieee80211_channel_to_frequency(
-                       arg->channel_number,
-                       hdr->band);
-
-       if (arg->rx_rate >= 14) {
-               hdr->flag |= RX_FLAG_HT;
-               hdr->rate_idx = arg->rx_rate - 14;
-       } else if (arg->rx_rate >= 4) {
-               hdr->rate_idx = arg->rx_rate - 2;
-       } else {
-               hdr->rate_idx = arg->rx_rate;
-       }
-
-       hdr->signal = (s8)arg->rcpi_rssi;
-       hdr->antenna = 0;
-
-       hdrlen = ieee80211_hdrlen(frame->frame_control);
-
-       if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
-               size_t iv_len = 0, icv_len = 0;
-
-               hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED;
-
-               /* Oops... There is no fast way to ask mac80211 about
-                * IV/ICV lengths. Even defineas are not exposed.
-                */
-               switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
-               case WSM_RX_STATUS_WEP:
-                       iv_len = 4 /* WEP_IV_LEN */;
-                       icv_len = 4 /* WEP_ICV_LEN */;
-                       break;
-               case WSM_RX_STATUS_TKIP:
-                       iv_len = 8 /* TKIP_IV_LEN */;
-                       icv_len = 4 /* TKIP_ICV_LEN */
-                               + 8 /*MICHAEL_MIC_LEN*/;
-                       hdr->flag |= RX_FLAG_MMIC_STRIPPED;
-                       break;
-               case WSM_RX_STATUS_AES:
-                       iv_len = 8 /* CCMP_HDR_LEN */;
-                       icv_len = 8 /* CCMP_MIC_LEN */;
-                       break;
-               case WSM_RX_STATUS_WAPI:
-                       iv_len = 18 /* WAPI_HDR_LEN */;
-                       icv_len = 16 /* WAPI_MIC_LEN */;
-                       break;
-               default:
-                       pr_warn("Unknown encryption type %d\n",
-                               WSM_RX_STATUS_ENCRYPTION(arg->flags));
-                       goto drop;
-               }
-
-               /* Firmware strips ICV in case of MIC failure. */
-               if (arg->status == WSM_STATUS_MICFAILURE)
-                       icv_len = 0;
-
-               if (skb->len < hdrlen + iv_len + icv_len) {
-                       wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n");
-                       goto drop;
-               }
-
-               /* Remove IV, ICV and MIC */
-               skb_trim(skb, skb->len - icv_len);
-               memmove(skb->data + iv_len, skb->data, hdrlen);
-               skb_pull(skb, iv_len);
-       }
-
-       /* Remove TSF from the end of frame */
-       if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) {
-               memcpy(&hdr->mactime, skb->data + skb->len - 8, 8);
-               hdr->mactime = le64_to_cpu(hdr->mactime);
-               if (skb->len >= 8)
-                       skb_trim(skb, skb->len - 8);
-       } else {
-               hdr->mactime = 0;
-       }
-
-       cw1200_debug_rxed(priv);
-       if (arg->flags & WSM_RX_STATUS_AGGREGATE)
-               cw1200_debug_rxed_agg(priv);
-
-       if (ieee80211_is_action(frame->frame_control) &&
-           (arg->flags & WSM_RX_STATUS_ADDRESS1)) {
-               if (cw1200_handle_action_rx(priv, skb))
-                       return;
-       } else if (ieee80211_is_beacon(frame->frame_control) &&
-                  !arg->status && priv->vif &&
-                  ether_addr_equal(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid)) {
-               const u8 *tim_ie;
-               u8 *ies = ((struct ieee80211_mgmt *)
-                         (skb->data))->u.beacon.variable;
-               size_t ies_len = skb->len - (ies - (u8 *)(skb->data));
-
-               tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len);
-               if (tim_ie) {
-                       struct ieee80211_tim_ie *tim =
-                               (struct ieee80211_tim_ie *)&tim_ie[2];
-
-                       if (priv->join_dtim_period != tim->dtim_period) {
-                               priv->join_dtim_period = tim->dtim_period;
-                               queue_work(priv->workqueue,
-                                          &priv->set_beacon_wakeup_period_work);
-                       }
-               }
-
-               /* Disable beacon filter once we're associated... */
-               if (priv->disable_beacon_filter &&
-                   (priv->vif->bss_conf.assoc ||
-                    priv->vif->bss_conf.ibss_joined)) {
-                       priv->disable_beacon_filter = false;
-                       queue_work(priv->workqueue,
-                                  &priv->update_filtering_work);
-               }
-       }
-
-       /* Stay awake after frame is received to give
-        * userspace chance to react and acquire appropriate
-        * wakelock.
-        */
-       if (ieee80211_is_auth(frame->frame_control))
-               grace_period = 5 * HZ;
-       else if (ieee80211_is_deauth(frame->frame_control))
-               grace_period = 5 * HZ;
-       else
-               grace_period = 1 * HZ;
-       cw1200_pm_stay_awake(&priv->pm_state, grace_period);
-
-       if (early_data) {
-               spin_lock_bh(&priv->ps_state_lock);
-               /* Double-check status with lock held */
-               if (entry->status == CW1200_LINK_SOFT)
-                       skb_queue_tail(&entry->rx_queue, skb);
-               else
-                       ieee80211_rx_irqsafe(priv->hw, skb);
-               spin_unlock_bh(&priv->ps_state_lock);
-       } else {
-               ieee80211_rx_irqsafe(priv->hw, skb);
-       }
-       *skb_p = NULL;
-
-       return;
-
-drop:
-       /* TODO: update failure counters */
-       return;
-}
-
-/* ******************************************************************** */
-/* Security                                                            */
-
-int cw1200_alloc_key(struct cw1200_common *priv)
-{
-       int idx;
-
-       idx = ffs(~priv->key_map) - 1;
-       if (idx < 0 || idx > WSM_KEY_MAX_INDEX)
-               return -1;
-
-       priv->key_map |= BIT(idx);
-       priv->keys[idx].index = idx;
-       return idx;
-}
-
-void cw1200_free_key(struct cw1200_common *priv, int idx)
-{
-       BUG_ON(!(priv->key_map & BIT(idx)));
-       memset(&priv->keys[idx], 0, sizeof(priv->keys[idx]));
-       priv->key_map &= ~BIT(idx);
-}
-
-void cw1200_free_keys(struct cw1200_common *priv)
-{
-       memset(&priv->keys, 0, sizeof(priv->keys));
-       priv->key_map = 0;
-}
-
-int cw1200_upload_keys(struct cw1200_common *priv)
-{
-       int idx, ret = 0;
-       for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx)
-               if (priv->key_map & BIT(idx)) {
-                       ret = wsm_add_key(priv, &priv->keys[idx]);
-                       if (ret < 0)
-                               break;
-               }
-       return ret;
-}
-
-/* Workaround for WFD test case 6.1.10 */
-void cw1200_link_id_reset(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, linkid_reset_work);
-       int temp_linkid;
-
-       if (!priv->action_linkid) {
-               /* In GO mode we can receive ACTION frames without a linkID */
-               temp_linkid = cw1200_alloc_link_id(priv,
-                               &priv->action_frame_sa[0]);
-               WARN_ON(!temp_linkid);
-               if (temp_linkid) {
-                       /* Make sure we execute the WQ */
-                       flush_workqueue(priv->workqueue);
-                       /* Release the link ID */
-                       spin_lock_bh(&priv->ps_state_lock);
-                       priv->link_id_db[temp_linkid - 1].prev_status =
-                               priv->link_id_db[temp_linkid - 1].status;
-                       priv->link_id_db[temp_linkid - 1].status =
-                               CW1200_LINK_RESET;
-                       spin_unlock_bh(&priv->ps_state_lock);
-                       wsm_lock_tx_async(priv);
-                       if (queue_work(priv->workqueue,
-                                      &priv->link_id_work) <= 0)
-                               wsm_unlock_tx(priv);
-               }
-       } else {
-               spin_lock_bh(&priv->ps_state_lock);
-               priv->link_id_db[priv->action_linkid - 1].prev_status =
-                       priv->link_id_db[priv->action_linkid - 1].status;
-               priv->link_id_db[priv->action_linkid - 1].status =
-                       CW1200_LINK_RESET_REMAP;
-               spin_unlock_bh(&priv->ps_state_lock);
-               wsm_lock_tx_async(priv);
-               if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
-                       wsm_unlock_tx(priv);
-               flush_workqueue(priv->workqueue);
-       }
-}
-
-int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac)
-{
-       int i, ret = 0;
-       spin_lock_bh(&priv->ps_state_lock);
-       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
-               if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) &&
-                   priv->link_id_db[i].status) {
-                       priv->link_id_db[i].timestamp = jiffies;
-                       ret = i + 1;
-                       break;
-               }
-       }
-       spin_unlock_bh(&priv->ps_state_lock);
-       return ret;
-}
-
-int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac)
-{
-       int i, ret = 0;
-       unsigned long max_inactivity = 0;
-       unsigned long now = jiffies;
-
-       spin_lock_bh(&priv->ps_state_lock);
-       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
-               if (!priv->link_id_db[i].status) {
-                       ret = i + 1;
-                       break;
-               } else if (priv->link_id_db[i].status != CW1200_LINK_HARD &&
-                          !priv->tx_queue_stats.link_map_cache[i + 1]) {
-                       unsigned long inactivity =
-                               now - priv->link_id_db[i].timestamp;
-                       if (inactivity < max_inactivity)
-                               continue;
-                       max_inactivity = inactivity;
-                       ret = i + 1;
-               }
-       }
-       if (ret) {
-               struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1];
-               pr_debug("[AP] STA added, link_id: %d\n", ret);
-               entry->status = CW1200_LINK_RESERVE;
-               memcpy(&entry->mac, mac, ETH_ALEN);
-               memset(&entry->buffered, 0, CW1200_MAX_TID);
-               skb_queue_head_init(&entry->rx_queue);
-               wsm_lock_tx_async(priv);
-               if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
-                       wsm_unlock_tx(priv);
-       } else {
-               wiphy_info(priv->hw->wiphy,
-                          "[AP] Early: no more link IDs available.\n");
-       }
-
-       spin_unlock_bh(&priv->ps_state_lock);
-       return ret;
-}
-
-void cw1200_link_id_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, link_id_work);
-       wsm_flush_tx(priv);
-       cw1200_link_id_gc_work(&priv->link_id_gc_work.work);
-       wsm_unlock_tx(priv);
-}
-
-void cw1200_link_id_gc_work(struct work_struct *work)
-{
-       struct cw1200_common *priv =
-               container_of(work, struct cw1200_common, link_id_gc_work.work);
-       struct wsm_reset reset = {
-               .reset_statistics = false,
-       };
-       struct wsm_map_link map_link = {
-               .link_id = 0,
-       };
-       unsigned long now = jiffies;
-       unsigned long next_gc = -1;
-       long ttl;
-       bool need_reset;
-       u32 mask;
-       int i;
-
-       if (priv->join_status != CW1200_JOIN_STATUS_AP)
-               return;
-
-       wsm_lock_tx(priv);
-       spin_lock_bh(&priv->ps_state_lock);
-       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
-               need_reset = false;
-               mask = BIT(i + 1);
-               if (priv->link_id_db[i].status == CW1200_LINK_RESERVE ||
-                   (priv->link_id_db[i].status == CW1200_LINK_HARD &&
-                    !(priv->link_id_map & mask))) {
-                       if (priv->link_id_map & mask) {
-                               priv->sta_asleep_mask &= ~mask;
-                               priv->pspoll_mask &= ~mask;
-                               need_reset = true;
-                       }
-                       priv->link_id_map |= mask;
-                       if (priv->link_id_db[i].status != CW1200_LINK_HARD)
-                               priv->link_id_db[i].status = CW1200_LINK_SOFT;
-                       memcpy(map_link.mac_addr, priv->link_id_db[i].mac,
-                              ETH_ALEN);
-                       spin_unlock_bh(&priv->ps_state_lock);
-                       if (need_reset) {
-                               reset.link_id = i + 1;
-                               wsm_reset(priv, &reset);
-                       }
-                       map_link.link_id = i + 1;
-                       wsm_map_link(priv, &map_link);
-                       next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT);
-                       spin_lock_bh(&priv->ps_state_lock);
-               } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) {
-                       ttl = priv->link_id_db[i].timestamp - now +
-                                       CW1200_LINK_ID_GC_TIMEOUT;
-                       if (ttl <= 0) {
-                               need_reset = true;
-                               priv->link_id_db[i].status = CW1200_LINK_OFF;
-                               priv->link_id_map &= ~mask;
-                               priv->sta_asleep_mask &= ~mask;
-                               priv->pspoll_mask &= ~mask;
-                               eth_zero_addr(map_link.mac_addr);
-                               spin_unlock_bh(&priv->ps_state_lock);
-                               reset.link_id = i + 1;
-                               wsm_reset(priv, &reset);
-                               spin_lock_bh(&priv->ps_state_lock);
-                       } else {
-                               next_gc = min_t(unsigned long, next_gc, ttl);
-                       }
-               } else if (priv->link_id_db[i].status == CW1200_LINK_RESET ||
-                               priv->link_id_db[i].status ==
-                               CW1200_LINK_RESET_REMAP) {
-                       int status = priv->link_id_db[i].status;
-                       priv->link_id_db[i].status =
-                                       priv->link_id_db[i].prev_status;
-                       priv->link_id_db[i].timestamp = now;
-                       reset.link_id = i + 1;
-                       spin_unlock_bh(&priv->ps_state_lock);
-                       wsm_reset(priv, &reset);
-                       if (status == CW1200_LINK_RESET_REMAP) {
-                               memcpy(map_link.mac_addr,
-                                      priv->link_id_db[i].mac,
-                                      ETH_ALEN);
-                               map_link.link_id = i + 1;
-                               wsm_map_link(priv, &map_link);
-                               next_gc = min(next_gc,
-                                               CW1200_LINK_ID_GC_TIMEOUT);
-                       }
-                       spin_lock_bh(&priv->ps_state_lock);
-               }
-               if (need_reset) {
-                       skb_queue_purge(&priv->link_id_db[i].rx_queue);
-                       pr_debug("[AP] STA removed, link_id: %d\n",
-                                reset.link_id);
-               }
-       }
-       spin_unlock_bh(&priv->ps_state_lock);
-       if (next_gc != -1)
-               queue_delayed_work(priv->workqueue,
-                                  &priv->link_id_gc_work, next_gc);
-       wsm_unlock_tx(priv);
-}
diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/cw1200/txrx.h
deleted file mode 100644 (file)
index 492a4e1..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Datapath interface for ST-Ericsson CW1200 mac80211 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef CW1200_TXRX_H
-#define CW1200_TXRX_H
-
-#include <linux/list.h>
-
-/* extern */ struct ieee80211_hw;
-/* extern */ struct sk_buff;
-/* extern */ struct wsm_tx;
-/* extern */ struct wsm_rx;
-/* extern */ struct wsm_tx_confirm;
-/* extern */ struct cw1200_txpriv;
-
-struct tx_policy {
-       union {
-               __le32 tbl[3];
-               u8 raw[12];
-       };
-       u8  defined;
-       u8  usage_count;
-       u8  retry_count;
-       u8  uploaded;
-};
-
-struct tx_policy_cache_entry {
-       struct tx_policy policy;
-       struct list_head link;
-};
-
-#define TX_POLICY_CACHE_SIZE   (8)
-struct tx_policy_cache {
-       struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
-       struct list_head used;
-       struct list_head free;
-       spinlock_t lock; /* Protect policy cache */
-};
-
-/* ******************************************************************** */
-/* TX policy cache                                                     */
-/* Intention of TX policy cache is an overcomplicated WSM API.
- * Device does not accept per-PDU tx retry sequence.
- * It uses "tx retry policy id" instead, so driver code has to sync
- * linux tx retry sequences with a retry policy table in the device.
- */
-void tx_policy_init(struct cw1200_common *priv);
-void tx_policy_upload_work(struct work_struct *work);
-void tx_policy_clean(struct cw1200_common *priv);
-
-/* ******************************************************************** */
-/* TX implementation                                                   */
-
-u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv,
-                              u32 rates);
-void cw1200_tx(struct ieee80211_hw *dev,
-              struct ieee80211_tx_control *control,
-              struct sk_buff *skb);
-void cw1200_skb_dtor(struct cw1200_common *priv,
-                    struct sk_buff *skb,
-                    const struct cw1200_txpriv *txpriv);
-
-/* ******************************************************************** */
-/* WSM callbacks                                                       */
-
-void cw1200_tx_confirm_cb(struct cw1200_common *priv,
-                         int link_id,
-                         struct wsm_tx_confirm *arg);
-void cw1200_rx_cb(struct cw1200_common *priv,
-                 struct wsm_rx *arg,
-                 int link_id,
-                 struct sk_buff **skb_p);
-
-/* ******************************************************************** */
-/* Timeout                                                             */
-
-void cw1200_tx_timeout(struct work_struct *work);
-
-/* ******************************************************************** */
-/* Security                                                            */
-int cw1200_alloc_key(struct cw1200_common *priv);
-void cw1200_free_key(struct cw1200_common *priv, int idx);
-void cw1200_free_keys(struct cw1200_common *priv);
-int cw1200_upload_keys(struct cw1200_common *priv);
-
-/* ******************************************************************** */
-/* Workaround for WFD test case 6.1.10                                 */
-void cw1200_link_id_reset(struct work_struct *work);
-
-#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
-
-int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac);
-int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac);
-void cw1200_link_id_work(struct work_struct *work);
-void cw1200_link_id_gc_work(struct work_struct *work);
-
-
-#endif /* CW1200_TXRX_H */
diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c
deleted file mode 100644 (file)
index 9e0ca30..0000000
+++ /dev/null
@@ -1,1822 +0,0 @@
-/*
- * WSM host interface (HI) implementation for
- * ST-Ericsson CW1200 mac80211 drivers.
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/skbuff.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/random.h>
-
-#include "cw1200.h"
-#include "wsm.h"
-#include "bh.h"
-#include "sta.h"
-#include "debug.h"
-
-#define WSM_CMD_TIMEOUT                (2 * HZ) /* With respect to interrupt loss */
-#define WSM_CMD_START_TIMEOUT  (7 * HZ)
-#define WSM_CMD_RESET_TIMEOUT  (3 * HZ) /* 2 sec. timeout was observed.   */
-#define WSM_CMD_MAX_TIMEOUT    (3 * HZ)
-
-#define WSM_SKIP(buf, size)                                            \
-       do {                                                            \
-               if ((buf)->data + size > (buf)->end)                    \
-                       goto underflow;                                 \
-               (buf)->data += size;                                    \
-       } while (0)
-
-#define WSM_GET(buf, ptr, size)                                                \
-       do {                                                            \
-               if ((buf)->data + size > (buf)->end)                    \
-                       goto underflow;                                 \
-               memcpy(ptr, (buf)->data, size);                         \
-               (buf)->data += size;                                    \
-       } while (0)
-
-#define __WSM_GET(buf, type, type2, cvt)                               \
-       ({                                                              \
-               type val;                                               \
-               if ((buf)->data + sizeof(type) > (buf)->end)            \
-                       goto underflow;                                 \
-               val = cvt(*(type2 *)(buf)->data);                       \
-               (buf)->data += sizeof(type);                            \
-               val;                                                    \
-       })
-
-#define WSM_GET8(buf)  __WSM_GET(buf, u8, u8, (u8))
-#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16, __le16_to_cpu)
-#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32, __le32_to_cpu)
-
-#define WSM_PUT(buf, ptr, size)                                                \
-       do {                                                            \
-               if ((buf)->data + size > (buf)->end)            \
-                       if (wsm_buf_reserve((buf), size))       \
-                               goto nomem;                             \
-               memcpy((buf)->data, ptr, size);                         \
-               (buf)->data += size;                                    \
-       } while (0)
-
-#define __WSM_PUT(buf, val, type, type2, cvt)                          \
-       do {                                                            \
-               if ((buf)->data + sizeof(type) > (buf)->end)            \
-                       if (wsm_buf_reserve((buf), sizeof(type))) \
-                               goto nomem;                             \
-               *(type2 *)(buf)->data = cvt(val);                       \
-               (buf)->data += sizeof(type);                            \
-       } while (0)
-
-#define WSM_PUT8(buf, val)  __WSM_PUT(buf, val, u8, u8, (u8))
-#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __le16, __cpu_to_le16)
-#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __le32, __cpu_to_le32)
-
-static void wsm_buf_reset(struct wsm_buf *buf);
-static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size);
-
-static int wsm_cmd_send(struct cw1200_common *priv,
-                       struct wsm_buf *buf,
-                       void *arg, u16 cmd, long tmo);
-
-#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux))
-#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux))
-
-/* ******************************************************************** */
-/* WSM API implementation                                              */
-
-static int wsm_generic_confirm(struct cw1200_common *priv,
-                            void *arg,
-                            struct wsm_buf *buf)
-{
-       u32 status = WSM_GET32(buf);
-       if (status != WSM_STATUS_SUCCESS)
-               return -EINVAL;
-       return 0;
-
-underflow:
-       WARN_ON(1);
-       return -EINVAL;
-}
-
-int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime);
-       WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime);
-       WSM_PUT32(buf, arg->dot11RtsThreshold);
-
-       /* DPD block. */
-       WSM_PUT16(buf, arg->dpdData_size + 12);
-       WSM_PUT16(buf, 1); /* DPD version */
-       WSM_PUT(buf, arg->dot11StationId, ETH_ALEN);
-       WSM_PUT16(buf, 5); /* DPD flags */
-       WSM_PUT(buf, arg->dpdData, arg->dpdData_size);
-
-       ret = wsm_cmd_send(priv, buf, arg,
-                          WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-static int wsm_configuration_confirm(struct cw1200_common *priv,
-                                    struct wsm_configuration *arg,
-                                    struct wsm_buf *buf)
-{
-       int i;
-       int status;
-
-       status = WSM_GET32(buf);
-       if (WARN_ON(status != WSM_STATUS_SUCCESS))
-               return -EINVAL;
-
-       WSM_GET(buf, arg->dot11StationId, ETH_ALEN);
-       arg->dot11FrequencyBandsSupported = WSM_GET8(buf);
-       WSM_SKIP(buf, 1);
-       arg->supportedRateMask = WSM_GET32(buf);
-       for (i = 0; i < 2; ++i) {
-               arg->txPowerRange[i].min_power_level = WSM_GET32(buf);
-               arg->txPowerRange[i].max_power_level = WSM_GET32(buf);
-               arg->txPowerRange[i].stepping = WSM_GET32(buf);
-       }
-       return 0;
-
-underflow:
-       WARN_ON(1);
-       return -EINVAL;
-}
-
-/* ******************************************************************** */
-
-int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-       u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id);
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT32(buf, arg->reset_statistics ? 0 : 1);
-       ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT);
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-struct wsm_mib {
-       u16 mib_id;
-       void *buf;
-       size_t buf_size;
-};
-
-int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
-                       size_t buf_size)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-       struct wsm_mib mib_buf = {
-               .mib_id = mib_id,
-               .buf = _buf,
-               .buf_size = buf_size,
-       };
-       wsm_cmd_lock(priv);
-
-       WSM_PUT16(buf, mib_id);
-       WSM_PUT16(buf, 0);
-
-       ret = wsm_cmd_send(priv, buf, &mib_buf,
-                          WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT);
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-static int wsm_read_mib_confirm(struct cw1200_common *priv,
-                               struct wsm_mib *arg,
-                               struct wsm_buf *buf)
-{
-       u16 size;
-       if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS))
-               return -EINVAL;
-
-       if (WARN_ON(WSM_GET16(buf) != arg->mib_id))
-               return -EINVAL;
-
-       size = WSM_GET16(buf);
-       if (size > arg->buf_size)
-               size = arg->buf_size;
-
-       WSM_GET(buf, arg->buf, size);
-       arg->buf_size = size;
-       return 0;
-
-underflow:
-       WARN_ON(1);
-       return -EINVAL;
-}
-
-/* ******************************************************************** */
-
-int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
-                       size_t buf_size)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-       struct wsm_mib mib_buf = {
-               .mib_id = mib_id,
-               .buf = _buf,
-               .buf_size = buf_size,
-       };
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT16(buf, mib_id);
-       WSM_PUT16(buf, buf_size);
-       WSM_PUT(buf, _buf, buf_size);
-
-       ret = wsm_cmd_send(priv, buf, &mib_buf,
-                          WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT);
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-static int wsm_write_mib_confirm(struct cw1200_common *priv,
-                               struct wsm_mib *arg,
-                               struct wsm_buf *buf)
-{
-       int ret;
-
-       ret = wsm_generic_confirm(priv, arg, buf);
-       if (ret)
-               return ret;
-
-       if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) {
-               /* OperationalMode: update PM status. */
-               const char *p = arg->buf;
-               cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false);
-       }
-       return 0;
-}
-
-/* ******************************************************************** */
-
-int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg)
-{
-       int i;
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       if (arg->num_channels > 48)
-               return -EINVAL;
-
-       if (arg->num_ssids > 2)
-               return -EINVAL;
-
-       if (arg->band > 1)
-               return -EINVAL;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT8(buf, arg->band);
-       WSM_PUT8(buf, arg->type);
-       WSM_PUT8(buf, arg->flags);
-       WSM_PUT8(buf, arg->max_tx_rate);
-       WSM_PUT32(buf, arg->auto_scan_interval);
-       WSM_PUT8(buf, arg->num_probes);
-       WSM_PUT8(buf, arg->num_channels);
-       WSM_PUT8(buf, arg->num_ssids);
-       WSM_PUT8(buf, arg->probe_delay);
-
-       for (i = 0; i < arg->num_channels; ++i) {
-               WSM_PUT16(buf, arg->ch[i].number);
-               WSM_PUT16(buf, 0);
-               WSM_PUT32(buf, arg->ch[i].min_chan_time);
-               WSM_PUT32(buf, arg->ch[i].max_chan_time);
-               WSM_PUT32(buf, 0);
-       }
-
-       for (i = 0; i < arg->num_ssids; ++i) {
-               WSM_PUT32(buf, arg->ssids[i].length);
-               WSM_PUT(buf, &arg->ssids[i].ssid[0],
-                       sizeof(arg->ssids[i].ssid));
-       }
-
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_stop_scan(struct cw1200_common *priv)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-       wsm_cmd_lock(priv);
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
-       wsm_cmd_unlock(priv);
-       return ret;
-}
-
-
-static int wsm_tx_confirm(struct cw1200_common *priv,
-                         struct wsm_buf *buf,
-                         int link_id)
-{
-       struct wsm_tx_confirm tx_confirm;
-
-       tx_confirm.packet_id = WSM_GET32(buf);
-       tx_confirm.status = WSM_GET32(buf);
-       tx_confirm.tx_rate = WSM_GET8(buf);
-       tx_confirm.ack_failures = WSM_GET8(buf);
-       tx_confirm.flags = WSM_GET16(buf);
-       tx_confirm.media_delay = WSM_GET32(buf);
-       tx_confirm.tx_queue_delay = WSM_GET32(buf);
-
-       cw1200_tx_confirm_cb(priv, link_id, &tx_confirm);
-       return 0;
-
-underflow:
-       WARN_ON(1);
-       return -EINVAL;
-}
-
-static int wsm_multi_tx_confirm(struct cw1200_common *priv,
-                               struct wsm_buf *buf, int link_id)
-{
-       int ret;
-       int count;
-       int i;
-
-       count = WSM_GET32(buf);
-       if (WARN_ON(count <= 0))
-               return -EINVAL;
-
-       if (count > 1) {
-               /* We already released one buffer, now for the rest */
-               ret = wsm_release_tx_buffer(priv, count - 1);
-               if (ret < 0)
-                       return ret;
-               else if (ret > 0)
-                       cw1200_bh_wakeup(priv);
-       }
-
-       cw1200_debug_txed_multi(priv, count);
-       for (i = 0; i < count; ++i) {
-               ret = wsm_tx_confirm(priv, buf, link_id);
-               if (ret)
-                       return ret;
-       }
-       return ret;
-
-underflow:
-       WARN_ON(1);
-       return -EINVAL;
-}
-
-/* ******************************************************************** */
-
-static int wsm_join_confirm(struct cw1200_common *priv,
-                           struct wsm_join_cnf *arg,
-                           struct wsm_buf *buf)
-{
-       arg->status = WSM_GET32(buf);
-       if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS)
-               return -EINVAL;
-
-       arg->min_power_level = WSM_GET32(buf);
-       arg->max_power_level = WSM_GET32(buf);
-
-       return 0;
-
-underflow:
-       WARN_ON(1);
-       return -EINVAL;
-}
-
-int wsm_join(struct cw1200_common *priv, struct wsm_join *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-       struct wsm_join_cnf resp;
-       wsm_cmd_lock(priv);
-
-       WSM_PUT8(buf, arg->mode);
-       WSM_PUT8(buf, arg->band);
-       WSM_PUT16(buf, arg->channel_number);
-       WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid));
-       WSM_PUT16(buf, arg->atim_window);
-       WSM_PUT8(buf, arg->preamble_type);
-       WSM_PUT8(buf, arg->probe_for_join);
-       WSM_PUT8(buf, arg->dtim_period);
-       WSM_PUT8(buf, arg->flags);
-       WSM_PUT32(buf, arg->ssid_len);
-       WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid));
-       WSM_PUT32(buf, arg->beacon_interval);
-       WSM_PUT32(buf, arg->basic_rate_set);
-
-       priv->tx_burst_idx = -1;
-       ret = wsm_cmd_send(priv, buf, &resp,
-                          WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT);
-       /* TODO:  Update state based on resp.min|max_power_level */
-
-       priv->join_complete_status = resp.status;
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_set_bss_params(struct cw1200_common *priv,
-                      const struct wsm_set_bss_params *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT8(buf, (arg->reset_beacon_loss ?  0x1 : 0));
-       WSM_PUT8(buf, arg->beacon_lost_count);
-       WSM_PUT16(buf, arg->aid);
-       WSM_PUT32(buf, arg->operational_rate_set);
-
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT(buf, arg, sizeof(*arg));
-
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT8(buf, arg->index);
-       WSM_PUT8(buf, 0);
-       WSM_PUT16(buf, 0);
-
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_set_tx_queue_params(struct cw1200_common *priv,
-               const struct wsm_set_tx_queue_params *arg, u8 id)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-       u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1};
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT8(buf, queue_id_to_wmm_aci[id]);
-       WSM_PUT8(buf, 0);
-       WSM_PUT8(buf, arg->ackPolicy);
-       WSM_PUT8(buf, 0);
-       WSM_PUT32(buf, arg->maxTransmitLifetime);
-       WSM_PUT16(buf, arg->allowedMediumTime);
-       WSM_PUT16(buf, 0);
-
-       ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_set_edca_params(struct cw1200_common *priv,
-                               const struct wsm_edca_params *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       /* Implemented according to specification. */
-
-       WSM_PUT16(buf, arg->params[3].cwmin);
-       WSM_PUT16(buf, arg->params[2].cwmin);
-       WSM_PUT16(buf, arg->params[1].cwmin);
-       WSM_PUT16(buf, arg->params[0].cwmin);
-
-       WSM_PUT16(buf, arg->params[3].cwmax);
-       WSM_PUT16(buf, arg->params[2].cwmax);
-       WSM_PUT16(buf, arg->params[1].cwmax);
-       WSM_PUT16(buf, arg->params[0].cwmax);
-
-       WSM_PUT8(buf, arg->params[3].aifns);
-       WSM_PUT8(buf, arg->params[2].aifns);
-       WSM_PUT8(buf, arg->params[1].aifns);
-       WSM_PUT8(buf, arg->params[0].aifns);
-
-       WSM_PUT16(buf, arg->params[3].txop_limit);
-       WSM_PUT16(buf, arg->params[2].txop_limit);
-       WSM_PUT16(buf, arg->params[1].txop_limit);
-       WSM_PUT16(buf, arg->params[0].txop_limit);
-
-       WSM_PUT32(buf, arg->params[3].max_rx_lifetime);
-       WSM_PUT32(buf, arg->params[2].max_rx_lifetime);
-       WSM_PUT32(buf, arg->params[1].max_rx_lifetime);
-       WSM_PUT32(buf, arg->params[0].max_rx_lifetime);
-
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_switch_channel(struct cw1200_common *priv,
-                       const struct wsm_switch_channel *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT8(buf, arg->mode);
-       WSM_PUT8(buf, arg->switch_count);
-       WSM_PUT16(buf, arg->channel_number);
-
-       priv->channel_switch_in_progress = 1;
-
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT);
-       if (ret)
-               priv->channel_switch_in_progress = 0;
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-       priv->ps_mode_switch_in_progress = 1;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT8(buf, arg->mode);
-       WSM_PUT8(buf, arg->fast_psm_idle_period);
-       WSM_PUT8(buf, arg->ap_psm_change_period);
-       WSM_PUT8(buf, arg->min_auto_pspoll_period);
-
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT8(buf, arg->mode);
-       WSM_PUT8(buf, arg->band);
-       WSM_PUT16(buf, arg->channel_number);
-       WSM_PUT32(buf, arg->ct_window);
-       WSM_PUT32(buf, arg->beacon_interval);
-       WSM_PUT8(buf, arg->dtim_period);
-       WSM_PUT8(buf, arg->preamble);
-       WSM_PUT8(buf, arg->probe_delay);
-       WSM_PUT8(buf, arg->ssid_len);
-       WSM_PUT(buf, arg->ssid, sizeof(arg->ssid));
-       WSM_PUT32(buf, arg->basic_rate_set);
-
-       priv->tx_burst_idx = -1;
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_beacon_transmit(struct cw1200_common *priv,
-                       const struct wsm_beacon_transmit *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0);
-
-       ret = wsm_cmd_send(priv, buf, NULL,
-                          WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_start_find(struct cw1200_common *priv)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-       ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT);
-       wsm_cmd_unlock(priv);
-       return ret;
-}
-
-/* ******************************************************************** */
-
-int wsm_stop_find(struct cw1200_common *priv)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-       ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT);
-       wsm_cmd_unlock(priv);
-       return ret;
-}
-
-/* ******************************************************************** */
-
-int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-       u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id);
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr));
-       WSM_PUT16(buf, 0);
-
-       ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-
-int wsm_update_ie(struct cw1200_common *priv,
-                 const struct wsm_update_ie *arg)
-{
-       int ret;
-       struct wsm_buf *buf = &priv->wsm_cmd_buf;
-
-       wsm_cmd_lock(priv);
-
-       WSM_PUT16(buf, arg->what);
-       WSM_PUT16(buf, arg->count);
-       WSM_PUT(buf, arg->ies, arg->length);
-
-       ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT);
-
-       wsm_cmd_unlock(priv);
-       return ret;
-
-nomem:
-       wsm_cmd_unlock(priv);
-       return -ENOMEM;
-}
-
-/* ******************************************************************** */
-int wsm_set_probe_responder(struct cw1200_common *priv, bool enable)
-{
-       priv->rx_filter.probeResponder = enable;
-       return wsm_set_rx_filter(priv, &priv->rx_filter);
-}
-
-/* ******************************************************************** */
-/* WSM indication events implementation                                        */
-const char * const cw1200_fw_types[] = {
-       "ETF",
-       "WFM",
-       "WSM",
-       "HI test",
-       "Platform test"
-};
-
-static int wsm_startup_indication(struct cw1200_common *priv,
-                                       struct wsm_buf *buf)
-{
-       priv->wsm_caps.input_buffers     = WSM_GET16(buf);
-       priv->wsm_caps.input_buffer_size = WSM_GET16(buf);
-       priv->wsm_caps.hw_id      = WSM_GET16(buf);
-       priv->wsm_caps.hw_subid   = WSM_GET16(buf);
-       priv->wsm_caps.status     = WSM_GET16(buf);
-       priv->wsm_caps.fw_cap     = WSM_GET16(buf);
-       priv->wsm_caps.fw_type    = WSM_GET16(buf);
-       priv->wsm_caps.fw_api     = WSM_GET16(buf);
-       priv->wsm_caps.fw_build   = WSM_GET16(buf);
-       priv->wsm_caps.fw_ver     = WSM_GET16(buf);
-       WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label));
-       priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */
-
-       if (WARN_ON(priv->wsm_caps.status))
-               return -EINVAL;
-
-       if (WARN_ON(priv->wsm_caps.fw_type > 4))
-               return -EINVAL;
-
-       pr_info("CW1200 WSM init done.\n"
-               "   Input buffers: %d x %d bytes\n"
-               "   Hardware: %d.%d\n"
-               "   %s firmware [%s], ver: %d, build: %d,"
-               "   api: %d, cap: 0x%.4X\n",
-               priv->wsm_caps.input_buffers,
-               priv->wsm_caps.input_buffer_size,
-               priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid,
-               cw1200_fw_types[priv->wsm_caps.fw_type],
-               priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver,
-               priv->wsm_caps.fw_build,
-               priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap);
-
-       /* Disable unsupported frequency bands */
-       if (!(priv->wsm_caps.fw_cap & 0x1))
-               priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
-       if (!(priv->wsm_caps.fw_cap & 0x2))
-               priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
-
-       priv->firmware_ready = 1;
-       wake_up(&priv->wsm_startup_done);
-       return 0;
-
-underflow:
-       WARN_ON(1);
-       return -EINVAL;
-}
-
-static int wsm_receive_indication(struct cw1200_common *priv,
-                                 int link_id,
-                                 struct wsm_buf *buf,
-                                 struct sk_buff **skb_p)
-{
-       struct wsm_rx rx;
-       struct ieee80211_hdr *hdr;
-       size_t hdr_len;
-       __le16 fctl;
-
-       rx.status = WSM_GET32(buf);
-       rx.channel_number = WSM_GET16(buf);
-       rx.rx_rate = WSM_GET8(buf);
-       rx.rcpi_rssi = WSM_GET8(buf);
-       rx.flags = WSM_GET32(buf);
-
-       /* FW Workaround: Drop probe resp or
-          beacon when RSSI is 0
-       */
-       hdr = (struct ieee80211_hdr *)(*skb_p)->data;
-
-       if (!rx.rcpi_rssi &&
-           (ieee80211_is_probe_resp(hdr->frame_control) ||
-            ieee80211_is_beacon(hdr->frame_control)))
-               return 0;
-
-       /* If no RSSI subscription has been made,
-        * convert RCPI to RSSI here
-        */
-       if (!priv->cqm_use_rssi)
-               rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110;
-
-       fctl = *(__le16 *)buf->data;
-       hdr_len = buf->data - buf->begin;
-       skb_pull(*skb_p, hdr_len);
-       if (!rx.status && ieee80211_is_deauth(fctl)) {
-               if (priv->join_status == CW1200_JOIN_STATUS_STA) {
-                       /* Shedule unjoin work */
-                       pr_debug("[WSM] Issue unjoin command (RX).\n");
-                       wsm_lock_tx_async(priv);
-                       if (queue_work(priv->workqueue,
-                                      &priv->unjoin_work) <= 0)
-                               wsm_unlock_tx(priv);
-               }
-       }
-       cw1200_rx_cb(priv, &rx, link_id, skb_p);
-       if (*skb_p)
-               skb_push(*skb_p, hdr_len);
-
-       return 0;
-
-underflow:
-       return -EINVAL;
-}
-
-static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf)
-{
-       int first;
-       struct cw1200_wsm_event *event;
-
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
-               /* STA is stopped. */
-               return 0;
-       }
-
-       event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL);
-       if (!event)
-               return -ENOMEM;
-
-       event->evt.id = WSM_GET32(buf);
-       event->evt.data = WSM_GET32(buf);
-
-       pr_debug("[WSM] Event: %d(%d)\n",
-                event->evt.id, event->evt.data);
-
-       spin_lock(&priv->event_queue_lock);
-       first = list_empty(&priv->event_queue);
-       list_add_tail(&event->link, &priv->event_queue);
-       spin_unlock(&priv->event_queue_lock);
-
-       if (first)
-               queue_work(priv->workqueue, &priv->event_handler);
-
-       return 0;
-
-underflow:
-       kfree(event);
-       return -EINVAL;
-}
-
-static int wsm_channel_switch_indication(struct cw1200_common *priv,
-                                        struct wsm_buf *buf)
-{
-       WARN_ON(WSM_GET32(buf));
-
-       priv->channel_switch_in_progress = 0;
-       wake_up(&priv->channel_switch_done);
-
-       wsm_unlock_tx(priv);
-
-       return 0;
-
-underflow:
-       return -EINVAL;
-}
-
-static int wsm_set_pm_indication(struct cw1200_common *priv,
-                                struct wsm_buf *buf)
-{
-       /* TODO:  Check buf (struct wsm_set_pm_complete) for validity */
-       if (priv->ps_mode_switch_in_progress) {
-               priv->ps_mode_switch_in_progress = 0;
-               wake_up(&priv->ps_mode_switch_done);
-       }
-       return 0;
-}
-
-static int wsm_scan_started(struct cw1200_common *priv, void *arg,
-                           struct wsm_buf *buf)
-{
-       u32 status = WSM_GET32(buf);
-       if (status != WSM_STATUS_SUCCESS) {
-               cw1200_scan_failed_cb(priv);
-               return -EINVAL;
-       }
-       return 0;
-
-underflow:
-       WARN_ON(1);
-       return -EINVAL;
-}
-
-static int wsm_scan_complete_indication(struct cw1200_common *priv,
-                                       struct wsm_buf *buf)
-{
-       struct wsm_scan_complete arg;
-       arg.status = WSM_GET32(buf);
-       arg.psm = WSM_GET8(buf);
-       arg.num_channels = WSM_GET8(buf);
-       cw1200_scan_complete_cb(priv, &arg);
-
-       return 0;
-
-underflow:
-       return -EINVAL;
-}
-
-static int wsm_join_complete_indication(struct cw1200_common *priv,
-                                       struct wsm_buf *buf)
-{
-       struct wsm_join_complete arg;
-       arg.status = WSM_GET32(buf);
-       pr_debug("[WSM] Join complete indication, status: %d\n", arg.status);
-       cw1200_join_complete_cb(priv, &arg);
-
-       return 0;
-
-underflow:
-       return -EINVAL;
-}
-
-static int wsm_find_complete_indication(struct cw1200_common *priv,
-                                       struct wsm_buf *buf)
-{
-       pr_warn("Implement find_complete_indication\n");
-       return 0;
-}
-
-static int wsm_ba_timeout_indication(struct cw1200_common *priv,
-                                    struct wsm_buf *buf)
-{
-       u32 dummy;
-       u8 tid;
-       u8 dummy2;
-       u8 addr[ETH_ALEN];
-
-       dummy = WSM_GET32(buf);
-       tid = WSM_GET8(buf);
-       dummy2 = WSM_GET8(buf);
-       WSM_GET(buf, addr, ETH_ALEN);
-
-       pr_info("BlockACK timeout, tid %d, addr %pM\n",
-               tid, addr);
-
-       return 0;
-
-underflow:
-       return -EINVAL;
-}
-
-static int wsm_suspend_resume_indication(struct cw1200_common *priv,
-                                        int link_id, struct wsm_buf *buf)
-{
-       u32 flags;
-       struct wsm_suspend_resume arg;
-
-       flags = WSM_GET32(buf);
-       arg.link_id = link_id;
-       arg.stop = !(flags & 1);
-       arg.multicast = !!(flags & 8);
-       arg.queue = (flags >> 1) & 3;
-
-       cw1200_suspend_resume(priv, &arg);
-
-       return 0;
-
-underflow:
-       return -EINVAL;
-}
-
-
-/* ******************************************************************** */
-/* WSM TX                                                              */
-
-static int wsm_cmd_send(struct cw1200_common *priv,
-                       struct wsm_buf *buf,
-                       void *arg, u16 cmd, long tmo)
-{
-       size_t buf_len = buf->data - buf->begin;
-       int ret;
-
-       /* Don't bother if we're dead. */
-       if (priv->bh_error) {
-               ret = 0;
-               goto done;
-       }
-
-       /* Block until the cmd buffer is completed.  Tortuous. */
-       spin_lock(&priv->wsm_cmd.lock);
-       while (!priv->wsm_cmd.done) {
-               spin_unlock(&priv->wsm_cmd.lock);
-               spin_lock(&priv->wsm_cmd.lock);
-       }
-       priv->wsm_cmd.done = 0;
-       spin_unlock(&priv->wsm_cmd.lock);
-
-       if (cmd == WSM_WRITE_MIB_REQ_ID ||
-           cmd == WSM_READ_MIB_REQ_ID)
-               pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n",
-                        cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]),
-                        buf_len);
-       else
-               pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len);
-
-       /* Due to buggy SPI on CW1200, we need to
-        * pad the message by a few bytes to ensure
-        * that it's completely received.
-        */
-       buf_len += 4;
-
-       /* Fill HI message header */
-       /* BH will add sequence number */
-       ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
-       ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
-
-       spin_lock(&priv->wsm_cmd.lock);
-       BUG_ON(priv->wsm_cmd.ptr);
-       priv->wsm_cmd.ptr = buf->begin;
-       priv->wsm_cmd.len = buf_len;
-       priv->wsm_cmd.arg = arg;
-       priv->wsm_cmd.cmd = cmd;
-       spin_unlock(&priv->wsm_cmd.lock);
-
-       cw1200_bh_wakeup(priv);
-
-       /* Wait for command completion */
-       ret = wait_event_timeout(priv->wsm_cmd_wq,
-                                priv->wsm_cmd.done, tmo);
-
-       if (!ret && !priv->wsm_cmd.done) {
-               spin_lock(&priv->wsm_cmd.lock);
-               priv->wsm_cmd.done = 1;
-               priv->wsm_cmd.ptr = NULL;
-               spin_unlock(&priv->wsm_cmd.lock);
-               if (priv->bh_error) {
-                       /* Return ok to help system cleanup */
-                       ret = 0;
-               } else {
-                       pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd);
-                       print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE,
-                                            buf->begin, buf_len);
-                       pr_err("Outstanding outgoing frames:  %d\n", priv->hw_bufs_used);
-
-                       /* Kill BH thread to report the error to the top layer. */
-                       atomic_add(1, &priv->bh_term);
-                       wake_up(&priv->bh_wq);
-                       ret = -ETIMEDOUT;
-               }
-       } else {
-               spin_lock(&priv->wsm_cmd.lock);
-               BUG_ON(!priv->wsm_cmd.done);
-               ret = priv->wsm_cmd.ret;
-               spin_unlock(&priv->wsm_cmd.lock);
-       }
-done:
-       wsm_buf_reset(buf);
-       return ret;
-}
-
-/* ******************************************************************** */
-/* WSM TX port control                                                 */
-
-void wsm_lock_tx(struct cw1200_common *priv)
-{
-       wsm_cmd_lock(priv);
-       if (atomic_add_return(1, &priv->tx_lock) == 1) {
-               if (wsm_flush_tx(priv))
-                       pr_debug("[WSM] TX is locked.\n");
-       }
-       wsm_cmd_unlock(priv);
-}
-
-void wsm_lock_tx_async(struct cw1200_common *priv)
-{
-       if (atomic_add_return(1, &priv->tx_lock) == 1)
-               pr_debug("[WSM] TX is locked (async).\n");
-}
-
-bool wsm_flush_tx(struct cw1200_common *priv)
-{
-       unsigned long timestamp = jiffies;
-       bool pending = false;
-       long timeout;
-       int i;
-
-       /* Flush must be called with TX lock held. */
-       BUG_ON(!atomic_read(&priv->tx_lock));
-
-       /* First check if we really need to do something.
-        * It is safe to use unprotected access, as hw_bufs_used
-        * can only decrements.
-        */
-       if (!priv->hw_bufs_used)
-               return true;
-
-       if (priv->bh_error) {
-               /* In case of failure do not wait for magic. */
-               pr_err("[WSM] Fatal error occurred, will not flush TX.\n");
-               return false;
-       } else {
-               /* Get a timestamp of "oldest" frame */
-               for (i = 0; i < 4; ++i)
-                       pending |= cw1200_queue_get_xmit_timestamp(
-                                       &priv->tx_queue[i],
-                                       &timestamp, 0xffffffff);
-               /* If there's nothing pending, we're good */
-               if (!pending)
-                       return true;
-
-               timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
-               if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq,
-                                                     !priv->hw_bufs_used,
-                                                     timeout) <= 0) {
-                       /* Hmmm... Not good. Frame had stuck in firmware. */
-                       priv->bh_error = 1;
-                       wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used);
-                       wake_up(&priv->bh_wq);
-                       return false;
-               }
-
-               /* Ok, everything is flushed. */
-               return true;
-       }
-}
-
-void wsm_unlock_tx(struct cw1200_common *priv)
-{
-       int tx_lock;
-       tx_lock = atomic_sub_return(1, &priv->tx_lock);
-       BUG_ON(tx_lock < 0);
-
-       if (tx_lock == 0) {
-               if (!priv->bh_error)
-                       cw1200_bh_wakeup(priv);
-               pr_debug("[WSM] TX is unlocked.\n");
-       }
-}
-
-/* ******************************************************************** */
-/* WSM RX                                                              */
-
-int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len)
-{
-       struct wsm_buf buf;
-       u32 reason;
-       u32 reg[18];
-       char fname[48];
-       unsigned int i;
-
-       static const char * const reason_str[] = {
-               "undefined instruction",
-               "prefetch abort",
-               "data abort",
-               "unknown error",
-       };
-
-       buf.begin = buf.data = data;
-       buf.end = &buf.begin[len];
-
-       reason = WSM_GET32(&buf);
-       for (i = 0; i < ARRAY_SIZE(reg); ++i)
-               reg[i] = WSM_GET32(&buf);
-       WSM_GET(&buf, fname, sizeof(fname));
-
-       if (reason < 4)
-               wiphy_err(priv->hw->wiphy,
-                         "Firmware exception: %s.\n",
-                         reason_str[reason]);
-       else
-               wiphy_err(priv->hw->wiphy,
-                         "Firmware assert at %.*s, line %d\n",
-                         (int) sizeof(fname), fname, reg[1]);
-
-       for (i = 0; i < 12; i += 4)
-               wiphy_err(priv->hw->wiphy,
-                         "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n",
-                         i + 0, reg[i + 0], i + 1, reg[i + 1],
-                         i + 2, reg[i + 2], i + 3, reg[i + 3]);
-       wiphy_err(priv->hw->wiphy,
-                 "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n",
-                 reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]);
-       i += 4;
-       wiphy_err(priv->hw->wiphy,
-                 "CPSR: 0x%.8X, SPSR: 0x%.8X\n",
-                 reg[i + 0], reg[i + 1]);
-
-       print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE,
-                            fname, sizeof(fname));
-       return 0;
-
-underflow:
-       wiphy_err(priv->hw->wiphy, "Firmware exception.\n");
-       print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE,
-                            data, len);
-       return -EINVAL;
-}
-
-int wsm_handle_rx(struct cw1200_common *priv, u16 id,
-                 struct wsm_hdr *wsm, struct sk_buff **skb_p)
-{
-       int ret = 0;
-       struct wsm_buf wsm_buf;
-       int link_id = (id >> 6) & 0x0F;
-
-       /* Strip link id. */
-       id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
-
-       wsm_buf.begin = (u8 *)&wsm[0];
-       wsm_buf.data = (u8 *)&wsm[1];
-       wsm_buf.end = &wsm_buf.begin[__le16_to_cpu(wsm->len)];
-
-       pr_debug("[WSM] <<< 0x%.4X (%td)\n", id,
-                wsm_buf.end - wsm_buf.begin);
-
-       if (id == WSM_TX_CONFIRM_IND_ID) {
-               ret = wsm_tx_confirm(priv, &wsm_buf, link_id);
-       } else if (id == WSM_MULTI_TX_CONFIRM_ID) {
-               ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id);
-       } else if (id & 0x0400) {
-               void *wsm_arg;
-               u16 wsm_cmd;
-
-               /* Do not trust FW too much. Protection against repeated
-                * response and race condition removal (see above).
-                */
-               spin_lock(&priv->wsm_cmd.lock);
-               wsm_arg = priv->wsm_cmd.arg;
-               wsm_cmd = priv->wsm_cmd.cmd &
-                               ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
-               priv->wsm_cmd.cmd = 0xFFFF;
-               spin_unlock(&priv->wsm_cmd.lock);
-
-               if (WARN_ON((id & ~0x0400) != wsm_cmd)) {
-                       /* Note that any non-zero is a fatal retcode. */
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               /* Note that wsm_arg can be NULL in case of timeout in
-                * wsm_cmd_send().
-                */
-
-               switch (id) {
-               case WSM_READ_MIB_RESP_ID:
-                       if (wsm_arg)
-                               ret = wsm_read_mib_confirm(priv, wsm_arg,
-                                                               &wsm_buf);
-                       break;
-               case WSM_WRITE_MIB_RESP_ID:
-                       if (wsm_arg)
-                               ret = wsm_write_mib_confirm(priv, wsm_arg,
-                                                           &wsm_buf);
-                       break;
-               case WSM_START_SCAN_RESP_ID:
-                       if (wsm_arg)
-                               ret = wsm_scan_started(priv, wsm_arg, &wsm_buf);
-                       break;
-               case WSM_CONFIGURATION_RESP_ID:
-                       if (wsm_arg)
-                               ret = wsm_configuration_confirm(priv, wsm_arg,
-                                                               &wsm_buf);
-                       break;
-               case WSM_JOIN_RESP_ID:
-                       if (wsm_arg)
-                               ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf);
-                       break;
-               case WSM_STOP_SCAN_RESP_ID:
-               case WSM_RESET_RESP_ID:
-               case WSM_ADD_KEY_RESP_ID:
-               case WSM_REMOVE_KEY_RESP_ID:
-               case WSM_SET_PM_RESP_ID:
-               case WSM_SET_BSS_PARAMS_RESP_ID:
-               case 0x0412: /* set_tx_queue_params */
-               case WSM_EDCA_PARAMS_RESP_ID:
-               case WSM_SWITCH_CHANNEL_RESP_ID:
-               case WSM_START_RESP_ID:
-               case WSM_BEACON_TRANSMIT_RESP_ID:
-               case 0x0419: /* start_find */
-               case 0x041A: /* stop_find */
-               case 0x041B: /* update_ie */
-               case 0x041C: /* map_link */
-                       WARN_ON(wsm_arg != NULL);
-                       ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf);
-                       if (ret) {
-                               wiphy_warn(priv->hw->wiphy,
-                                          "wsm_generic_confirm failed for request 0x%04x.\n",
-                                          id & ~0x0400);
-
-                               /* often 0x407 and 0x410 occur, this means we're dead.. */
-                               if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) {
-                                       wsm_lock_tx(priv);
-                                       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
-                                               wsm_unlock_tx(priv);
-                               }
-                       }
-                       break;
-               default:
-                       wiphy_warn(priv->hw->wiphy,
-                                  "Unrecognized confirmation 0x%04x\n",
-                                  id & ~0x0400);
-               }
-
-               spin_lock(&priv->wsm_cmd.lock);
-               priv->wsm_cmd.ret = ret;
-               priv->wsm_cmd.done = 1;
-               spin_unlock(&priv->wsm_cmd.lock);
-
-               ret = 0; /* Error response from device should ne stop BH. */
-
-               wake_up(&priv->wsm_cmd_wq);
-       } else if (id & 0x0800) {
-               switch (id) {
-               case WSM_STARTUP_IND_ID:
-                       ret = wsm_startup_indication(priv, &wsm_buf);
-                       break;
-               case WSM_RECEIVE_IND_ID:
-                       ret = wsm_receive_indication(priv, link_id,
-                                                    &wsm_buf, skb_p);
-                       break;
-               case 0x0805:
-                       ret = wsm_event_indication(priv, &wsm_buf);
-                       break;
-               case WSM_SCAN_COMPLETE_IND_ID:
-                       ret = wsm_scan_complete_indication(priv, &wsm_buf);
-                       break;
-               case 0x0808:
-                       ret = wsm_ba_timeout_indication(priv, &wsm_buf);
-                       break;
-               case 0x0809:
-                       ret = wsm_set_pm_indication(priv, &wsm_buf);
-                       break;
-               case 0x080A:
-                       ret = wsm_channel_switch_indication(priv, &wsm_buf);
-                       break;
-               case 0x080B:
-                       ret = wsm_find_complete_indication(priv, &wsm_buf);
-                       break;
-               case 0x080C:
-                       ret = wsm_suspend_resume_indication(priv,
-                                       link_id, &wsm_buf);
-                       break;
-               case 0x080F:
-                       ret = wsm_join_complete_indication(priv, &wsm_buf);
-                       break;
-               default:
-                       pr_warn("Unrecognised WSM ID %04x\n", id);
-               }
-       } else {
-               WARN_ON(1);
-               ret = -EINVAL;
-       }
-out:
-       return ret;
-}
-
-static bool wsm_handle_tx_data(struct cw1200_common *priv,
-                              struct wsm_tx *wsm,
-                              const struct ieee80211_tx_info *tx_info,
-                              const struct cw1200_txpriv *txpriv,
-                              struct cw1200_queue *queue)
-{
-       bool handled = false;
-       const struct ieee80211_hdr *frame =
-               (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset];
-       __le16 fctl = frame->frame_control;
-       enum {
-               do_probe,
-               do_drop,
-               do_wep,
-               do_tx,
-       } action = do_tx;
-
-       switch (priv->mode) {
-       case NL80211_IFTYPE_STATION:
-               if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
-                       action = do_tx;
-               else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA)
-                       action = do_drop;
-               break;
-       case NL80211_IFTYPE_AP:
-               if (!priv->join_status) {
-                       action = do_drop;
-               } else if (!(BIT(txpriv->raw_link_id) &
-                            (BIT(0) | priv->link_id_map))) {
-                       wiphy_warn(priv->hw->wiphy,
-                                  "A frame with expired link id is dropped.\n");
-                       action = do_drop;
-               }
-               if (cw1200_queue_get_generation(wsm->packet_id) >
-                               CW1200_MAX_REQUEUE_ATTEMPTS) {
-                       /* HACK!!! WSM324 firmware has tendency to requeue
-                        * multicast frames in a loop, causing performance
-                        * drop and high power consumption of the driver.
-                        * In this situation it is better just to drop
-                        * the problematic frame.
-                        */
-                       wiphy_warn(priv->hw->wiphy,
-                                  "Too many attempts to requeue a frame; dropped.\n");
-                       action = do_drop;
-               }
-               break;
-       case NL80211_IFTYPE_ADHOC:
-               if (priv->join_status != CW1200_JOIN_STATUS_IBSS)
-                       action = do_drop;
-               break;
-       case NL80211_IFTYPE_MESH_POINT:
-               action = do_tx; /* TODO:  Test me! */
-               break;
-       case NL80211_IFTYPE_MONITOR:
-       default:
-               action = do_drop;
-               break;
-       }
-
-       if (action == do_tx) {
-               if (ieee80211_is_nullfunc(fctl)) {
-                       spin_lock(&priv->bss_loss_lock);
-                       if (priv->bss_loss_state) {
-                               priv->bss_loss_confirm_id = wsm->packet_id;
-                               wsm->queue_id = WSM_QUEUE_VOICE;
-                       }
-                       spin_unlock(&priv->bss_loss_lock);
-               } else if (ieee80211_is_probe_req(fctl)) {
-                       action = do_probe;
-               } else if (ieee80211_is_deauth(fctl) &&
-                          priv->mode != NL80211_IFTYPE_AP) {
-                       pr_debug("[WSM] Issue unjoin command due to tx deauth.\n");
-                       wsm_lock_tx_async(priv);
-                       if (queue_work(priv->workqueue,
-                                      &priv->unjoin_work) <= 0)
-                               wsm_unlock_tx(priv);
-               } else if (ieee80211_has_protected(fctl) &&
-                          tx_info->control.hw_key &&
-                          tx_info->control.hw_key->keyidx != priv->wep_default_key_id &&
-                          (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
-                           tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
-                       action = do_wep;
-               }
-       }
-
-       switch (action) {
-       case do_probe:
-               /* An interesting FW "feature". Device filters probe responses.
-                * The easiest way to get it back is to convert
-                * probe request into WSM start_scan command.
-                */
-               pr_debug("[WSM] Convert probe request to scan.\n");
-               wsm_lock_tx_async(priv);
-               priv->pending_frame_id = wsm->packet_id;
-               if (queue_delayed_work(priv->workqueue,
-                                      &priv->scan.probe_work, 0) <= 0)
-                       wsm_unlock_tx(priv);
-               handled = true;
-               break;
-       case do_drop:
-               pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl);
-               BUG_ON(cw1200_queue_remove(queue, wsm->packet_id));
-               handled = true;
-               break;
-       case do_wep:
-               pr_debug("[WSM] Issue set_default_wep_key.\n");
-               wsm_lock_tx_async(priv);
-               priv->wep_default_key_id = tx_info->control.hw_key->keyidx;
-               priv->pending_frame_id = wsm->packet_id;
-               if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0)
-                       wsm_unlock_tx(priv);
-               handled = true;
-               break;
-       case do_tx:
-               pr_debug("[WSM] Transmit frame.\n");
-               break;
-       default:
-               /* Do nothing */
-               break;
-       }
-       return handled;
-}
-
-static int cw1200_get_prio_queue(struct cw1200_common *priv,
-                                u32 link_id_map, int *total)
-{
-       static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) |
-               BIT(CW1200_LINK_ID_UAPSD);
-       struct wsm_edca_queue_params *edca;
-       unsigned score, best = -1;
-       int winner = -1;
-       int queued;
-       int i;
-
-       /* search for a winner using edca params */
-       for (i = 0; i < 4; ++i) {
-               queued = cw1200_queue_get_num_queued(&priv->tx_queue[i],
-                               link_id_map);
-               if (!queued)
-                       continue;
-               *total += queued;
-               edca = &priv->edca.params[i];
-               score = ((edca->aifns + edca->cwmin) << 16) +
-                       ((edca->cwmax - edca->cwmin) *
-                        (get_random_int() & 0xFFFF));
-               if (score < best && (winner < 0 || i != 3)) {
-                       best = score;
-                       winner = i;
-               }
-       }
-
-       /* override winner if bursting */
-       if (winner >= 0 && priv->tx_burst_idx >= 0 &&
-           winner != priv->tx_burst_idx &&
-           !cw1200_queue_get_num_queued(
-                   &priv->tx_queue[winner],
-                   link_id_map & urgent) &&
-           cw1200_queue_get_num_queued(
-                   &priv->tx_queue[priv->tx_burst_idx],
-                   link_id_map))
-               winner = priv->tx_burst_idx;
-
-       return winner;
-}
-
-static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
-                                    struct cw1200_queue **queue_p,
-                                    u32 *tx_allowed_mask_p,
-                                    bool *more)
-{
-       int idx;
-       u32 tx_allowed_mask;
-       int total = 0;
-
-       /* Search for a queue with multicast frames buffered */
-       if (priv->tx_multicast) {
-               tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM);
-               idx = cw1200_get_prio_queue(priv,
-                               tx_allowed_mask, &total);
-               if (idx >= 0) {
-                       *more = total > 1;
-                       goto found;
-               }
-       }
-
-       /* Search for unicast traffic */
-       tx_allowed_mask = ~priv->sta_asleep_mask;
-       tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD);
-       if (priv->sta_asleep_mask) {
-               tx_allowed_mask |= priv->pspoll_mask;
-               tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM);
-       } else {
-               tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM);
-       }
-       idx = cw1200_get_prio_queue(priv,
-                       tx_allowed_mask, &total);
-       if (idx < 0)
-               return -ENOENT;
-
-found:
-       *queue_p = &priv->tx_queue[idx];
-       *tx_allowed_mask_p = tx_allowed_mask;
-       return 0;
-}
-
-int wsm_get_tx(struct cw1200_common *priv, u8 **data,
-              size_t *tx_len, int *burst)
-{
-       struct wsm_tx *wsm = NULL;
-       struct ieee80211_tx_info *tx_info;
-       struct cw1200_queue *queue = NULL;
-       int queue_num;
-       u32 tx_allowed_mask = 0;
-       const struct cw1200_txpriv *txpriv = NULL;
-       int count = 0;
-
-       /* More is used only for broadcasts. */
-       bool more = false;
-
-       if (priv->wsm_cmd.ptr) { /* CMD request */
-               ++count;
-               spin_lock(&priv->wsm_cmd.lock);
-               BUG_ON(!priv->wsm_cmd.ptr);
-               *data = priv->wsm_cmd.ptr;
-               *tx_len = priv->wsm_cmd.len;
-               *burst = 1;
-               spin_unlock(&priv->wsm_cmd.lock);
-       } else {
-               for (;;) {
-                       int ret;
-
-                       if (atomic_add_return(0, &priv->tx_lock))
-                               break;
-
-                       spin_lock_bh(&priv->ps_state_lock);
-
-                       ret = wsm_get_tx_queue_and_mask(priv, &queue,
-                                                       &tx_allowed_mask, &more);
-                       queue_num = queue - priv->tx_queue;
-
-                       if (priv->buffered_multicasts &&
-                           (ret || !more) &&
-                           (priv->tx_multicast || !priv->sta_asleep_mask)) {
-                               priv->buffered_multicasts = false;
-                               if (priv->tx_multicast) {
-                                       priv->tx_multicast = false;
-                                       queue_work(priv->workqueue,
-                                                  &priv->multicast_stop_work);
-                               }
-                       }
-
-                       spin_unlock_bh(&priv->ps_state_lock);
-
-                       if (ret)
-                               break;
-
-                       if (cw1200_queue_get(queue,
-                                            tx_allowed_mask,
-                                            &wsm, &tx_info, &txpriv))
-                               continue;
-
-                       if (wsm_handle_tx_data(priv, wsm,
-                                              tx_info, txpriv, queue))
-                               continue;  /* Handled by WSM */
-
-                       wsm->hdr.id &= __cpu_to_le16(
-                               ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX));
-                       wsm->hdr.id |= cpu_to_le16(
-                               WSM_TX_LINK_ID(txpriv->raw_link_id));
-                       priv->pspoll_mask &= ~BIT(txpriv->raw_link_id);
-
-                       *data = (u8 *)wsm;
-                       *tx_len = __le16_to_cpu(wsm->hdr.len);
-
-                       /* allow bursting if txop is set */
-                       if (priv->edca.params[queue_num].txop_limit)
-                               *burst = min(*burst,
-                                            (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1);
-                       else
-                               *burst = 1;
-
-                       /* store index of bursting queue */
-                       if (*burst > 1)
-                               priv->tx_burst_idx = queue_num;
-                       else
-                               priv->tx_burst_idx = -1;
-
-                       if (more) {
-                               struct ieee80211_hdr *hdr =
-                                       (struct ieee80211_hdr *)
-                                       &((u8 *)wsm)[txpriv->offset];
-                               /* more buffered multicast/broadcast frames
-                                *  ==> set MoreData flag in IEEE 802.11 header
-                                *  to inform PS STAs
-                                */
-                               hdr->frame_control |=
-                                       cpu_to_le16(IEEE80211_FCTL_MOREDATA);
-                       }
-
-                       pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n",
-                                0x0004, *tx_len, *data,
-                                wsm->more ? 'M' : ' ');
-                       ++count;
-                       break;
-               }
-       }
-
-       return count;
-}
-
-void wsm_txed(struct cw1200_common *priv, u8 *data)
-{
-       if (data == priv->wsm_cmd.ptr) {
-               spin_lock(&priv->wsm_cmd.lock);
-               priv->wsm_cmd.ptr = NULL;
-               spin_unlock(&priv->wsm_cmd.lock);
-       }
-}
-
-/* ******************************************************************** */
-/* WSM buffer                                                          */
-
-void wsm_buf_init(struct wsm_buf *buf)
-{
-       BUG_ON(buf->begin);
-       buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
-       buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin;
-       wsm_buf_reset(buf);
-}
-
-void wsm_buf_deinit(struct wsm_buf *buf)
-{
-       kfree(buf->begin);
-       buf->begin = buf->data = buf->end = NULL;
-}
-
-static void wsm_buf_reset(struct wsm_buf *buf)
-{
-       if (buf->begin) {
-               buf->data = &buf->begin[4];
-               *(u32 *)buf->begin = 0;
-       } else {
-               buf->data = buf->begin;
-       }
-}
-
-static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size)
-{
-       size_t pos = buf->data - buf->begin;
-       size_t size = pos + extra_size;
-
-       size = round_up(size, FWLOAD_BLOCK_SIZE);
-
-       buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA);
-       if (buf->begin) {
-               buf->data = &buf->begin[pos];
-               buf->end = &buf->begin[size];
-               return 0;
-       } else {
-               buf->end = buf->data = buf->begin;
-               return -ENOMEM;
-       }
-}
diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h
deleted file mode 100644 (file)
index 48086e8..0000000
+++ /dev/null
@@ -1,1870 +0,0 @@
-/*
- * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers
- *
- * Copyright (c) 2010, ST-Ericsson
- * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- *
- * Based on CW1200 UMAC WSM API, which is
- * Copyright (C) ST-Ericsson SA 2010
- * Author: Stewart Mathers <stewart.mathers@stericsson.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef CW1200_WSM_H_INCLUDED
-#define CW1200_WSM_H_INCLUDED
-
-#include <linux/spinlock.h>
-
-struct cw1200_common;
-
-/* Bands */
-/* Radio band 2.412 -2.484 GHz. */
-#define WSM_PHY_BAND_2_4G              (0)
-
-/* Radio band 4.9375-5.8250 GHz. */
-#define WSM_PHY_BAND_5G                        (1)
-
-/* Transmit rates */
-/* 1   Mbps            ERP-DSSS */
-#define WSM_TRANSMIT_RATE_1            (0)
-
-/* 2   Mbps            ERP-DSSS */
-#define WSM_TRANSMIT_RATE_2            (1)
-
-/* 5.5 Mbps            ERP-CCK */
-#define WSM_TRANSMIT_RATE_5            (2)
-
-/* 11  Mbps            ERP-CCK */
-#define WSM_TRANSMIT_RATE_11           (3)
-
-/* 22  Mbps            ERP-PBCC (Not supported) */
-/* #define WSM_TRANSMIT_RATE_22                (4) */
-
-/* 33  Mbps            ERP-PBCC (Not supported) */
-/* #define WSM_TRANSMIT_RATE_33                (5) */
-
-/* 6   Mbps   (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */
-#define WSM_TRANSMIT_RATE_6            (6)
-
-/* 9   Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */
-#define WSM_TRANSMIT_RATE_9            (7)
-
-/* 12  Mbps  (6 Mbps)  ERP-OFDM, QPSK coding rate 1/2 */
-#define WSM_TRANSMIT_RATE_12           (8)
-
-/* 18  Mbps  (9 Mbps)  ERP-OFDM, QPSK coding rate 3/4 */
-#define WSM_TRANSMIT_RATE_18           (9)
-
-/* 24  Mbps (12 Mbps)  ERP-OFDM, 16QAM coding rate 1/2 */
-#define WSM_TRANSMIT_RATE_24           (10)
-
-/* 36  Mbps (18 Mbps)  ERP-OFDM, 16QAM coding rate 3/4 */
-#define WSM_TRANSMIT_RATE_36           (11)
-
-/* 48  Mbps (24 Mbps)  ERP-OFDM, 64QAM coding rate 1/2 */
-#define WSM_TRANSMIT_RATE_48           (12)
-
-/* 54  Mbps (27 Mbps)  ERP-OFDM, 64QAM coding rate 3/4 */
-#define WSM_TRANSMIT_RATE_54           (13)
-
-/* 6.5 Mbps            HT-OFDM, BPSK coding rate 1/2 */
-#define WSM_TRANSMIT_RATE_HT_6         (14)
-
-/* 13  Mbps            HT-OFDM, QPSK coding rate 1/2 */
-#define WSM_TRANSMIT_RATE_HT_13                (15)
-
-/* 19.5 Mbps           HT-OFDM, QPSK coding rate 3/4 */
-#define WSM_TRANSMIT_RATE_HT_19                (16)
-
-/* 26  Mbps            HT-OFDM, 16QAM coding rate 1/2 */
-#define WSM_TRANSMIT_RATE_HT_26                (17)
-
-/* 39  Mbps            HT-OFDM, 16QAM coding rate 3/4 */
-#define WSM_TRANSMIT_RATE_HT_39                (18)
-
-/* 52  Mbps            HT-OFDM, 64QAM coding rate 2/3 */
-#define WSM_TRANSMIT_RATE_HT_52                (19)
-
-/* 58.5 Mbps           HT-OFDM, 64QAM coding rate 3/4 */
-#define WSM_TRANSMIT_RATE_HT_58                (20)
-
-/* 65  Mbps            HT-OFDM, 64QAM coding rate 5/6 */
-#define WSM_TRANSMIT_RATE_HT_65                (21)
-
-/* Scan types */
-/* Foreground scan */
-#define WSM_SCAN_TYPE_FOREGROUND       (0)
-
-/* Background scan */
-#define WSM_SCAN_TYPE_BACKGROUND       (1)
-
-/* Auto scan */
-#define WSM_SCAN_TYPE_AUTO             (2)
-
-/* Scan flags */
-/* Forced background scan means if the station cannot */
-/* enter the power-save mode, it shall force to perform a */
-/* background scan. Only valid when ScanType is */
-/* background scan. */
-#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0))
-
-/* The WLAN device scans one channel at a time so */
-/* that disturbance to the data traffic is minimized. */
-#define WSM_SCAN_FLAG_SPLIT_METHOD     (BIT(1))
-
-/* Preamble Type. Long if not set. */
-#define WSM_SCAN_FLAG_SHORT_PREAMBLE   (BIT(2))
-
-/* 11n Tx Mode. Mixed if not set. */
-#define WSM_SCAN_FLAG_11N_GREENFIELD   (BIT(3))
-
-/* Scan constraints */
-/* Maximum number of channels to be scanned. */
-#define WSM_SCAN_MAX_NUM_OF_CHANNELS   (48)
-
-/* The maximum number of SSIDs that the device can scan for. */
-#define WSM_SCAN_MAX_NUM_OF_SSIDS      (2)
-
-/* Power management modes */
-/* 802.11 Active mode */
-#define WSM_PSM_ACTIVE                 (0)
-
-/* 802.11 PS mode */
-#define WSM_PSM_PS                     BIT(0)
-
-/* Fast Power Save bit */
-#define WSM_PSM_FAST_PS_FLAG           BIT(7)
-
-/* Dynamic aka Fast power save */
-#define WSM_PSM_FAST_PS                        (BIT(0) | BIT(7))
-
-/* Undetermined */
-/* Note : Undetermined status is reported when the */
-/* NULL data frame used to advertise the PM mode to */
-/* the AP at Pre or Post Background Scan is not Acknowledged */
-#define WSM_PSM_UNKNOWN                        BIT(1)
-
-/* Queue IDs */
-/* best effort/legacy */
-#define WSM_QUEUE_BEST_EFFORT          (0)
-
-/* background */
-#define WSM_QUEUE_BACKGROUND           (1)
-
-/* video */
-#define WSM_QUEUE_VIDEO                        (2)
-
-/* voice */
-#define WSM_QUEUE_VOICE                        (3)
-
-/* HT TX parameters */
-/* Non-HT */
-#define WSM_HT_TX_NON_HT               (0)
-
-/* Mixed format */
-#define WSM_HT_TX_MIXED                        (1)
-
-/* Greenfield format */
-#define WSM_HT_TX_GREENFIELD           (2)
-
-/* STBC allowed */
-#define WSM_HT_TX_STBC                 (BIT(7))
-
-/* EPTA prioirty flags for BT Coex */
-/* default epta priority */
-#define WSM_EPTA_PRIORITY_DEFAULT      4
-/* use for normal data */
-#define WSM_EPTA_PRIORITY_DATA         4
-/* use for connect/disconnect/roaming*/
-#define WSM_EPTA_PRIORITY_MGT          5
-/* use for action frames */
-#define WSM_EPTA_PRIORITY_ACTION       5
-/* use for AC_VI data */
-#define WSM_EPTA_PRIORITY_VIDEO                5
-/* use for AC_VO data */
-#define WSM_EPTA_PRIORITY_VOICE                6
-/* use for EAPOL exchange */
-#define WSM_EPTA_PRIORITY_EAPOL                7
-
-/* TX status */
-/* Frame was sent aggregated */
-/* Only valid for WSM_SUCCESS status. */
-#define WSM_TX_STATUS_AGGREGATION      (BIT(0))
-
-/* Host should requeue this frame later. */
-/* Valid only when status is WSM_REQUEUE. */
-#define WSM_TX_STATUS_REQUEUE          (BIT(1))
-
-/* Normal Ack */
-#define WSM_TX_STATUS_NORMAL_ACK       (0<<2)
-
-/* No Ack */
-#define WSM_TX_STATUS_NO_ACK           (1<<2)
-
-/* No explicit acknowledgement */
-#define WSM_TX_STATUS_NO_EXPLICIT_ACK  (2<<2)
-
-/* Block Ack */
-/* Only valid for WSM_SUCCESS status. */
-#define WSM_TX_STATUS_BLOCK_ACK                (3<<2)
-
-/* RX status */
-/* Unencrypted */
-#define WSM_RX_STATUS_UNENCRYPTED      (0<<0)
-
-/* WEP */
-#define WSM_RX_STATUS_WEP              (1<<0)
-
-/* TKIP */
-#define WSM_RX_STATUS_TKIP             (2<<0)
-
-/* AES */
-#define WSM_RX_STATUS_AES              (3<<0)
-
-/* WAPI */
-#define WSM_RX_STATUS_WAPI             (4<<0)
-
-/* Macro to fetch encryption subfield. */
-#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07)
-
-/* Frame was part of an aggregation */
-#define WSM_RX_STATUS_AGGREGATE                (BIT(3))
-
-/* Frame was first in the aggregation */
-#define WSM_RX_STATUS_AGGREGATE_FIRST  (BIT(4))
-
-/* Frame was last in the aggregation */
-#define WSM_RX_STATUS_AGGREGATE_LAST   (BIT(5))
-
-/* Indicates a defragmented frame */
-#define WSM_RX_STATUS_DEFRAGMENTED     (BIT(6))
-
-/* Indicates a Beacon frame */
-#define WSM_RX_STATUS_BEACON           (BIT(7))
-
-/* Indicates STA bit beacon TIM field */
-#define WSM_RX_STATUS_TIM              (BIT(8))
-
-/* Indicates Beacon frame's virtual bitmap contains multicast bit */
-#define WSM_RX_STATUS_MULTICAST                (BIT(9))
-
-/* Indicates frame contains a matching SSID */
-#define WSM_RX_STATUS_MATCHING_SSID    (BIT(10))
-
-/* Indicates frame contains a matching BSSI */
-#define WSM_RX_STATUS_MATCHING_BSSI    (BIT(11))
-
-/* Indicates More bit set in Framectl field */
-#define WSM_RX_STATUS_MORE_DATA                (BIT(12))
-
-/* Indicates frame received during a measurement process */
-#define WSM_RX_STATUS_MEASUREMENT      (BIT(13))
-
-/* Indicates frame received as an HT packet */
-#define WSM_RX_STATUS_HT               (BIT(14))
-
-/* Indicates frame received with STBC */
-#define WSM_RX_STATUS_STBC             (BIT(15))
-
-/* Indicates Address 1 field matches dot11StationId */
-#define WSM_RX_STATUS_ADDRESS1         (BIT(16))
-
-/* Indicates Group address present in the Address 1 field */
-#define WSM_RX_STATUS_GROUP            (BIT(17))
-
-/* Indicates Broadcast address present in the Address 1 field */
-#define WSM_RX_STATUS_BROADCAST                (BIT(18))
-
-/* Indicates group key used with encrypted frames */
-#define WSM_RX_STATUS_GROUP_KEY                (BIT(19))
-
-/* Macro to fetch encryption key index. */
-#define WSM_RX_STATUS_KEY_IDX(status)  (((status >> 20)) & 0x0F)
-
-/* Indicates TSF inclusion after 802.11 frame body */
-#define WSM_RX_STATUS_TSF_INCLUDED     (BIT(24))
-
-/* Frame Control field starts at Frame offset + 2 */
-#define WSM_TX_2BYTES_SHIFT            (BIT(7))
-
-/* Join mode */
-/* IBSS */
-#define WSM_JOIN_MODE_IBSS             (0)
-
-/* BSS */
-#define WSM_JOIN_MODE_BSS              (1)
-
-/* PLCP preamble type */
-/* For long preamble */
-#define WSM_JOIN_PREAMBLE_LONG         (0)
-
-/* For short preamble (Long for 1Mbps) */
-#define WSM_JOIN_PREAMBLE_SHORT                (1)
-
-/* For short preamble (Long for 1 and 2Mbps) */
-#define WSM_JOIN_PREAMBLE_SHORT_2      (2)
-
-/* Join flags */
-/* Unsynchronized */
-#define WSM_JOIN_FLAGS_UNSYNCRONIZED   BIT(0)
-/* The BSS owner is a P2P GO */
-#define WSM_JOIN_FLAGS_P2P_GO          BIT(1)
-/* Force to join BSS with the BSSID and the
- * SSID specified without waiting for beacons. The
- * ProbeForJoin parameter is ignored.
- */
-#define WSM_JOIN_FLAGS_FORCE           BIT(2)
-/* Give probe request/response higher
- * priority over the BT traffic
- */
-#define WSM_JOIN_FLAGS_PRIO            BIT(3)
-/* Issue immediate join confirmation and use
- * join complete to notify about completion
- */
-#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5)
-
-/* Key types */
-#define WSM_KEY_TYPE_WEP_DEFAULT       (0)
-#define WSM_KEY_TYPE_WEP_PAIRWISE      (1)
-#define WSM_KEY_TYPE_TKIP_GROUP                (2)
-#define WSM_KEY_TYPE_TKIP_PAIRWISE     (3)
-#define WSM_KEY_TYPE_AES_GROUP         (4)
-#define WSM_KEY_TYPE_AES_PAIRWISE      (5)
-#define WSM_KEY_TYPE_WAPI_GROUP                (6)
-#define WSM_KEY_TYPE_WAPI_PAIRWISE     (7)
-
-/* Key indexes */
-#define WSM_KEY_MAX_INDEX              (10)
-
-/* ACK policy */
-#define WSM_ACK_POLICY_NORMAL          (0)
-#define WSM_ACK_POLICY_NO_ACK          (1)
-
-/* Start modes */
-#define WSM_START_MODE_AP              (0)     /* Mini AP */
-#define WSM_START_MODE_P2P_GO          (1)     /* P2P GO */
-#define WSM_START_MODE_P2P_DEV         (2)     /* P2P device */
-
-/* SetAssociationMode MIB flags */
-#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE         (BIT(0))
-#define WSM_ASSOCIATION_MODE_USE_HT_MODE               (BIT(1))
-#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET                (BIT(2))
-#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING    (BIT(3))
-#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES                (BIT(4))
-
-/* RcpiRssiThreshold MIB flags */
-#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0))
-#define WSM_RCPI_RSSI_USE_RSSI         (BIT(1))
-#define WSM_RCPI_RSSI_DONT_USE_UPPER   (BIT(2))
-#define WSM_RCPI_RSSI_DONT_USE_LOWER   (BIT(3))
-
-/* Update-ie constants */
-#define WSM_UPDATE_IE_BEACON           (BIT(0))
-#define WSM_UPDATE_IE_PROBE_RESP       (BIT(1))
-#define WSM_UPDATE_IE_PROBE_REQ                (BIT(2))
-
-/* WSM events */
-/* Error */
-#define WSM_EVENT_ERROR                        (0)
-
-/* BSS lost */
-#define WSM_EVENT_BSS_LOST             (1)
-
-/* BSS regained */
-#define WSM_EVENT_BSS_REGAINED         (2)
-
-/* Radar detected */
-#define WSM_EVENT_RADAR_DETECTED       (3)
-
-/* RCPI or RSSI threshold triggered */
-#define WSM_EVENT_RCPI_RSSI            (4)
-
-/* BT inactive */
-#define WSM_EVENT_BT_INACTIVE          (5)
-
-/* BT active */
-#define WSM_EVENT_BT_ACTIVE            (6)
-
-/* MIB IDs */
-/* 4.1  dot11StationId */
-#define WSM_MIB_ID_DOT11_STATION_ID            0x0000
-
-/* 4.2  dot11MaxtransmitMsduLifeTime */
-#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME  0x0001
-
-/* 4.3  dot11MaxReceiveLifeTime */
-#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME  0x0002
-
-/* 4.4  dot11SlotTime */
-#define WSM_MIB_ID_DOT11_SLOT_TIME             0x0003
-
-/* 4.5  dot11GroupAddressesTable */
-#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004
-#define WSM_MAX_GRP_ADDRTABLE_ENTRIES          8
-
-/* 4.6  dot11WepDefaultKeyId */
-#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID    0x0005
-
-/* 4.7  dot11CurrentTxPowerLevel */
-#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL        0x0006
-
-/* 4.8  dot11RTSThreshold */
-#define WSM_MIB_ID_DOT11_RTS_THRESHOLD         0x0007
-
-/* 4.9  NonErpProtection */
-#define WSM_MIB_ID_NON_ERP_PROTECTION          0x1000
-
-/* 4.10 ArpIpAddressesTable */
-#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE      0x1001
-#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES       1
-
-/* 4.11 TemplateFrame */
-#define WSM_MIB_ID_TEMPLATE_FRAME              0x1002
-
-/* 4.12 RxFilter */
-#define WSM_MIB_ID_RX_FILTER                   0x1003
-
-/* 4.13 BeaconFilterTable */
-#define WSM_MIB_ID_BEACON_FILTER_TABLE         0x1004
-
-/* 4.14 BeaconFilterEnable */
-#define WSM_MIB_ID_BEACON_FILTER_ENABLE                0x1005
-
-/* 4.15 OperationalPowerMode */
-#define WSM_MIB_ID_OPERATIONAL_POWER_MODE      0x1006
-
-/* 4.16 BeaconWakeUpPeriod */
-#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD                0x1007
-
-/* 4.17 RcpiRssiThreshold */
-#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD         0x1009
-
-/* 4.18 StatisticsTable */
-#define WSM_MIB_ID_STATISTICS_TABLE            0x100A
-
-/* 4.19 IbssPsConfig */
-#define WSM_MIB_ID_IBSS_PS_CONFIG              0x100B
-
-/* 4.20 CountersTable */
-#define WSM_MIB_ID_COUNTERS_TABLE              0x100C
-
-/* 4.21 BlockAckPolicy */
-#define WSM_MIB_ID_BLOCK_ACK_POLICY            0x100E
-
-/* 4.22 OverrideInternalTxRate */
-#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE   0x100F
-
-/* 4.23 SetAssociationMode */
-#define WSM_MIB_ID_SET_ASSOCIATION_MODE                0x1010
-
-/* 4.24 UpdateEptaConfigData */
-#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA     0x1011
-
-/* 4.25 SelectCcaMethod */
-#define WSM_MIB_ID_SELECT_CCA_METHOD           0x1012
-
-/* 4.26 SetUpasdInformation */
-#define WSM_MIB_ID_SET_UAPSD_INFORMATION       0x1013
-
-/* 4.27 SetAutoCalibrationMode  WBF00004073 */
-#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE   0x1015
-
-/* 4.28 SetTxRateRetryPolicy */
-#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY    0x1016
-
-/* 4.29 SetHostMessageTypeFilter */
-#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER    0x1017
-
-/* 4.30 P2PFindInfo */
-#define WSM_MIB_ID_P2P_FIND_INFO               0x1018
-
-/* 4.31 P2PPsModeInfo */
-#define WSM_MIB_ID_P2P_PS_MODE_INFO            0x1019
-
-/* 4.32 SetEtherTypeDataFrameFilter */
-#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A
-
-/* 4.33 SetUDPPortDataFrameFilter */
-#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER        0x101B
-
-/* 4.34 SetMagicDataFrameFilter */
-#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER  0x101C
-
-/* 4.35 P2PDeviceInfo */
-#define WSM_MIB_ID_P2P_DEVICE_INFO             0x101D
-
-/* 4.36 SetWCDMABand */
-#define WSM_MIB_ID_SET_WCDMA_BAND              0x101E
-
-/* 4.37 GroupTxSequenceCounter */
-#define WSM_MIB_ID_GRP_SEQ_COUNTER             0x101F
-
-/* 4.38 ProtectedMgmtPolicy */
-#define WSM_MIB_ID_PROTECTED_MGMT_POLICY       0x1020
-
-/* 4.39 SetHtProtection */
-#define WSM_MIB_ID_SET_HT_PROTECTION           0x1021
-
-/* 4.40 GPIO Command */
-#define WSM_MIB_ID_GPIO_COMMAND                        0x1022
-
-/* 4.41 TSF Counter Value */
-#define WSM_MIB_ID_TSF_COUNTER                 0x1023
-
-/* Test Purposes Only */
-#define WSM_MIB_ID_BLOCK_ACK_INFO              0x100D
-
-/* 4.42 UseMultiTxConfMessage */
-#define WSM_MIB_USE_MULTI_TX_CONF              0x1024
-
-/* 4.43 Keep-alive period */
-#define WSM_MIB_ID_KEEP_ALIVE_PERIOD           0x1025
-
-/* 4.44 Disable BSSID filter */
-#define WSM_MIB_ID_DISABLE_BSSID_FILTER                0x1026
-
-/* Frame template types */
-#define WSM_FRAME_TYPE_PROBE_REQUEST   (0)
-#define WSM_FRAME_TYPE_BEACON          (1)
-#define WSM_FRAME_TYPE_NULL            (2)
-#define WSM_FRAME_TYPE_QOS_NULL                (3)
-#define WSM_FRAME_TYPE_PS_POLL         (4)
-#define WSM_FRAME_TYPE_PROBE_RESPONSE  (5)
-
-#define WSM_FRAME_GREENFIELD           (0x80)  /* See 4.11 */
-
-/* Status */
-/* The WSM firmware has completed a request */
-/* successfully. */
-#define WSM_STATUS_SUCCESS              (0)
-
-/* This is a generic failure code if other error codes do */
-/* not apply. */
-#define WSM_STATUS_FAILURE              (1)
-
-/* A request contains one or more invalid parameters. */
-#define WSM_INVALID_PARAMETER           (2)
-
-/* The request cannot perform because the device is in */
-/* an inappropriate mode. */
-#define WSM_ACCESS_DENIED               (3)
-
-/* The frame received includes a decryption error. */
-#define WSM_STATUS_DECRYPTFAILURE       (4)
-
-/* A MIC failure is detected in the received packets. */
-#define WSM_STATUS_MICFAILURE           (5)
-
-/* The transmit request failed due to retry limit being */
-/* exceeded. */
-#define WSM_STATUS_RETRY_EXCEEDED       (6)
-
-/* The transmit request failed due to MSDU life time */
-/* being exceeded. */
-#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7)
-
-/* The link to the AP is lost. */
-#define WSM_STATUS_LINK_LOST            (8)
-
-/* No key was found for the encrypted frame */
-#define WSM_STATUS_NO_KEY_FOUND         (9)
-
-/* Jammer was detected when transmitting this frame */
-#define WSM_STATUS_JAMMER_DETECTED      (10)
-
-/* The message should be requeued later. */
-/* This is applicable only to Transmit */
-#define WSM_REQUEUE                     (11)
-
-/* Advanced filtering options */
-#define WSM_MAX_FILTER_ELEMENTS                (4)
-
-#define WSM_FILTER_ACTION_IGNORE       (0)
-#define WSM_FILTER_ACTION_FILTER_IN    (1)
-#define WSM_FILTER_ACTION_FILTER_OUT   (2)
-
-#define WSM_FILTER_PORT_TYPE_DST       (0)
-#define WSM_FILTER_PORT_TYPE_SRC       (1)
-
-/* Actual header of WSM messages */
-struct wsm_hdr {
-       __le16 len;
-       __le16 id;
-};
-
-#define WSM_TX_SEQ_MAX                 (7)
-#define WSM_TX_SEQ(seq)                        \
-               ((seq & WSM_TX_SEQ_MAX) << 13)
-#define WSM_TX_LINK_ID_MAX             (0x0F)
-#define WSM_TX_LINK_ID(link_id)                \
-               ((link_id & WSM_TX_LINK_ID_MAX) << 6)
-
-#define MAX_BEACON_SKIP_TIME_MS 1000
-
-#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2)
-
-/* ******************************************************************** */
-/* WSM capability                                                      */
-
-#define WSM_STARTUP_IND_ID 0x0801
-
-struct wsm_startup_ind {
-       u16 input_buffers;
-       u16 input_buffer_size;
-       u16 status;
-       u16 hw_id;
-       u16 hw_subid;
-       u16 fw_cap;
-       u16 fw_type;
-       u16 fw_api;
-       u16 fw_build;
-       u16 fw_ver;
-       char fw_label[128];
-       u32 config[4];
-};
-
-/* ******************************************************************** */
-/* WSM commands                                                                */
-
-/* 3.1 */
-#define WSM_CONFIGURATION_REQ_ID 0x0009
-#define WSM_CONFIGURATION_RESP_ID 0x0409
-
-struct wsm_tx_power_range {
-       int min_power_level;
-       int max_power_level;
-       u32 stepping;
-};
-
-struct wsm_configuration {
-       /* [in] */ u32 dot11MaxTransmitMsduLifeTime;
-       /* [in] */ u32 dot11MaxReceiveLifeTime;
-       /* [in] */ u32 dot11RtsThreshold;
-       /* [in, out] */ u8 *dot11StationId;
-       /* [in] */ const void *dpdData;
-       /* [in] */ size_t dpdData_size;
-       /* [out] */ u8 dot11FrequencyBandsSupported;
-       /* [out] */ u32 supportedRateMask;
-       /* [out] */ struct wsm_tx_power_range txPowerRange[2];
-};
-
-int wsm_configuration(struct cw1200_common *priv,
-                     struct wsm_configuration *arg);
-
-/* 3.3 */
-#define WSM_RESET_REQ_ID 0x000A
-#define WSM_RESET_RESP_ID 0x040A
-struct wsm_reset {
-       /* [in] */ int link_id;
-       /* [in] */ bool reset_statistics;
-};
-
-int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg);
-
-/* 3.5 */
-#define WSM_READ_MIB_REQ_ID 0x0005
-#define WSM_READ_MIB_RESP_ID 0x0405
-int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
-                size_t buf_size);
-
-/* 3.7 */
-#define WSM_WRITE_MIB_REQ_ID 0x0006
-#define WSM_WRITE_MIB_RESP_ID 0x0406
-int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
-                 size_t buf_size);
-
-/* 3.9 */
-#define WSM_START_SCAN_REQ_ID 0x0007
-#define WSM_START_SCAN_RESP_ID 0x0407
-
-struct wsm_ssid {
-       u8 ssid[32];
-       u32 length;
-};
-
-struct wsm_scan_ch {
-       u16 number;
-       u32 min_chan_time;
-       u32 max_chan_time;
-       u32 tx_power_level;
-};
-
-struct wsm_scan {
-       /* WSM_PHY_BAND_... */
-       u8 band;
-
-       /* WSM_SCAN_TYPE_... */
-       u8 type;
-
-       /* WSM_SCAN_FLAG_... */
-       u8 flags;
-
-       /* WSM_TRANSMIT_RATE_... */
-       u8 max_tx_rate;
-
-       /* Interval period in TUs that the device shall the re- */
-       /* execute the requested scan. Max value supported by the device */
-       /* is 256s. */
-       u32 auto_scan_interval;
-
-       /* Number of probe requests (per SSID) sent to one (1) */
-       /* channel. Zero (0) means that none is send, which */
-       /* means that a passive scan is to be done. Value */
-       /* greater than zero (0) means that an active scan is to */
-       /* be done. */
-       u32 num_probes;
-
-       /* Number of channels to be scanned. */
-       /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */
-       u8 num_channels;
-
-       /* Number of SSID provided in the scan command (this */
-       /* is zero (0) in broadcast scan) */
-       /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */
-       u8 num_ssids;
-
-       /* The delay time (in microseconds) period */
-       /* before sending a probe-request. */
-       u8 probe_delay;
-
-       /* SSIDs to be scanned [numOfSSIDs]; */
-       struct wsm_ssid *ssids;
-
-       /* Channels to be scanned [numOfChannels]; */
-       struct wsm_scan_ch *ch;
-};
-
-int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg);
-
-/* 3.11 */
-#define WSM_STOP_SCAN_REQ_ID 0x0008
-#define WSM_STOP_SCAN_RESP_ID 0x0408
-int wsm_stop_scan(struct cw1200_common *priv);
-
-/* 3.13 */
-#define WSM_SCAN_COMPLETE_IND_ID 0x0806
-struct wsm_scan_complete {
-       /* WSM_STATUS_... */
-       u32 status;
-
-       /* WSM_PSM_... */
-       u8 psm;
-
-       /* Number of channels that the scan operation completed. */
-       u8 num_channels;
-};
-
-/* 3.14 */
-#define WSM_TX_CONFIRM_IND_ID 0x0404
-#define WSM_MULTI_TX_CONFIRM_ID 0x041E
-
-struct wsm_tx_confirm {
-       /* Packet identifier used in wsm_tx. */
-       u32 packet_id;
-
-       /* WSM_STATUS_... */
-       u32 status;
-
-       /* WSM_TRANSMIT_RATE_... */
-       u8 tx_rate;
-
-       /* The number of times the frame was transmitted */
-       /* without receiving an acknowledgement. */
-       u8 ack_failures;
-
-       /* WSM_TX_STATUS_... */
-       u16 flags;
-
-       /* The total time in microseconds that the frame spent in */
-       /* the WLAN device before transmission as completed. */
-       u32 media_delay;
-
-       /* The total time in microseconds that the frame spent in */
-       /* the WLAN device before transmission was started. */
-       u32 tx_queue_delay;
-};
-
-/* 3.15 */
-typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv,
-                                  struct wsm_tx_confirm *arg);
-
-/* Note that ideology of wsm_tx struct is different against the rest of
- * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input
- * argument for WSM call, but a prepared bytestream to be sent to firmware.
- * It is filled partly in cw1200_tx, partly in low-level WSM code.
- * Please pay attention once again: ideology is different.
- *
- * Legend:
- * - [in]: cw1200_tx must fill this field.
- * - [wsm]: the field is filled by low-level WSM.
- */
-struct wsm_tx {
-       /* common WSM header */
-       struct wsm_hdr hdr;
-
-       /* Packet identifier that meant to be used in completion. */
-       u32 packet_id;  /* Note this is actually a cookie */
-
-       /* WSM_TRANSMIT_RATE_... */
-       u8 max_tx_rate;
-
-       /* WSM_QUEUE_... */
-       u8 queue_id;
-
-       /* True: another packet is pending on the host for transmission. */
-       u8 more;
-
-       /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */
-       /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */
-       /* Bits 3:1  - PTA Priority */
-       /* Bits 6:4  - Tx Rate Retry Policy */
-       /* Bit 7 - Reserved */
-       u8 flags;
-
-       /* Should be 0. */
-       u32 reserved;
-
-       /* The elapsed time in TUs, after the initial transmission */
-       /* of an MSDU, after which further attempts to transmit */
-       /* the MSDU shall be terminated. Overrides the global */
-       /* dot11MaxTransmitMsduLifeTime setting [optional] */
-       /* Device will set the default value if this is 0. */
-       __le32 expire_time;
-
-       /* WSM_HT_TX_... */
-       __le32 ht_tx_parameters;
-} __packed;
-
-/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */
-#define WSM_TX_EXTRA_HEADROOM (28)
-
-/* 3.16 */
-#define WSM_RECEIVE_IND_ID 0x0804
-
-struct wsm_rx {
-       /* WSM_STATUS_... */
-       u32 status;
-
-       /* Specifies the channel of the received packet. */
-       u16 channel_number;
-
-       /* WSM_TRANSMIT_RATE_... */
-       u8 rx_rate;
-
-       /* This value is expressed in signed Q8.0 format for */
-       /* RSSI and unsigned Q7.1 format for RCPI. */
-       u8 rcpi_rssi;
-
-       /* WSM_RX_STATUS_... */
-       u32 flags;
-};
-
-/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */
-#define WSM_RX_EXTRA_HEADROOM (16)
-
-typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg,
-                          struct sk_buff **skb_p);
-
-/* 3.17 */
-struct wsm_event {
-       /* WSM_STATUS_... */
-       /* [out] */ u32 id;
-
-       /* Indication parameters. */
-       /* For error indication, this shall be a 32-bit WSM status. */
-       /* For RCPI or RSSI indication, this should be an 8-bit */
-       /* RCPI or RSSI value. */
-       /* [out] */ u32 data;
-};
-
-struct cw1200_wsm_event {
-       struct list_head link;
-       struct wsm_event evt;
-};
-
-/* 3.18 - 3.22 */
-/* Measurement. Skipped for now. Irrelevent. */
-
-typedef void (*wsm_event_cb) (struct cw1200_common *priv,
-                             struct wsm_event *arg);
-
-/* 3.23 */
-#define WSM_JOIN_REQ_ID 0x000B
-#define WSM_JOIN_RESP_ID 0x040B
-
-struct wsm_join {
-       /* WSM_JOIN_MODE_... */
-       u8 mode;
-
-       /* WSM_PHY_BAND_... */
-       u8 band;
-
-       /* Specifies the channel number to join. The channel */
-       /* number will be mapped to an actual frequency */
-       /* according to the band */
-       u16 channel_number;
-
-       /* Specifies the BSSID of the BSS or IBSS to be joined */
-       /* or the IBSS to be started. */
-       u8 bssid[6];
-
-       /* ATIM window of IBSS */
-       /* When ATIM window is zero the initiated IBSS does */
-       /* not support power saving. */
-       u16 atim_window;
-
-       /* WSM_JOIN_PREAMBLE_... */
-       u8 preamble_type;
-
-       /* Specifies if a probe request should be send with the */
-       /* specified SSID when joining to the network. */
-       u8 probe_for_join;
-
-       /* DTIM Period (In multiples of beacon interval) */
-       u8 dtim_period;
-
-       /* WSM_JOIN_FLAGS_... */
-       u8 flags;
-
-       /* Length of the SSID */
-       u32 ssid_len;
-
-       /* Specifies the SSID of the IBSS to join or start */
-       u8 ssid[32];
-
-       /* Specifies the time between TBTTs in TUs */
-       u32 beacon_interval;
-
-       /* A bit mask that defines the BSS basic rate set. */
-       u32 basic_rate_set;
-};
-
-struct wsm_join_cnf {
-       u32 status;
-
-       /* Minimum transmission power level in units of 0.1dBm */
-       u32 min_power_level;
-
-       /* Maximum transmission power level in units of 0.1dBm */
-       u32 max_power_level;
-};
-
-int wsm_join(struct cw1200_common *priv, struct wsm_join *arg);
-
-/* 3.24 */
-struct wsm_join_complete {
-       /* WSM_STATUS_... */
-       u32 status;
-};
-
-/* 3.25 */
-#define WSM_SET_PM_REQ_ID 0x0010
-#define WSM_SET_PM_RESP_ID 0x0410
-struct wsm_set_pm {
-       /* WSM_PSM_... */
-       u8 mode;
-
-       /* in unit of 500us; 0 to use default */
-       u8 fast_psm_idle_period;
-
-       /* in unit of 500us; 0 to use default */
-       u8 ap_psm_change_period;
-
-       /* in unit of 500us; 0 to disable auto-pspoll */
-       u8 min_auto_pspoll_period;
-};
-
-int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
-
-/* 3.27 */
-struct wsm_set_pm_complete {
-       u8 psm;                 /* WSM_PSM_... */
-};
-
-/* 3.28 */
-#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011
-#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411
-struct wsm_set_bss_params {
-       /* This resets the beacon loss counters only */
-       u8 reset_beacon_loss;
-
-       /* The number of lost consecutive beacons after which */
-       /* the WLAN device should indicate the BSS-Lost event */
-       /* to the WLAN host driver. */
-       u8 beacon_lost_count;
-
-       /* The AID received during the association process. */
-       u16 aid;
-
-       /* The operational rate set mask */
-       u32 operational_rate_set;
-};
-
-int wsm_set_bss_params(struct cw1200_common *priv,
-                      const struct wsm_set_bss_params *arg);
-
-/* 3.30 */
-#define WSM_ADD_KEY_REQ_ID         0x000C
-#define WSM_ADD_KEY_RESP_ID        0x040C
-struct wsm_add_key {
-       u8 type;                /* WSM_KEY_TYPE_... */
-       u8 index;               /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */
-       u16 reserved;
-       union {
-               struct {
-                       u8 peer[6];     /* MAC address of the peer station */
-                       u8 reserved;
-                       u8 keylen;              /* Key length in bytes */
-                       u8 keydata[16];         /* Key data */
-               } __packed wep_pairwise;
-               struct {
-                       u8 keyid;       /* Unique per key identifier (0..3) */
-                       u8 keylen;              /* Key length in bytes */
-                       u16 reserved;
-                       u8 keydata[16];         /* Key data */
-               } __packed wep_group;
-               struct {
-                       u8 peer[6];     /* MAC address of the peer station */
-                       u16 reserved;
-                       u8 keydata[16]; /* TKIP key data */
-                       u8 rx_mic_key[8];               /* Rx MIC key */
-                       u8 tx_mic_key[8];               /* Tx MIC key */
-               } __packed tkip_pairwise;
-               struct {
-                       u8 keydata[16]; /* TKIP key data */
-                       u8 rx_mic_key[8];               /* Rx MIC key */
-                       u8 keyid;               /* Key ID */
-                       u8 reserved[3];
-                       u8 rx_seqnum[8];        /* Receive Sequence Counter */
-               } __packed tkip_group;
-               struct {
-                       u8 peer[6];     /* MAC address of the peer station */
-                       u16 reserved;
-                       u8 keydata[16]; /* AES key data */
-               } __packed aes_pairwise;
-               struct {
-                       u8 keydata[16]; /* AES key data */
-                       u8 keyid;               /* Key ID */
-                       u8 reserved[3];
-                       u8 rx_seqnum[8];        /* Receive Sequence Counter */
-               } __packed aes_group;
-               struct {
-                       u8 peer[6];     /* MAC address of the peer station */
-                       u8 keyid;               /* Key ID */
-                       u8 reserved;
-                       u8 keydata[16]; /* WAPI key data */
-                       u8 mic_key[16]; /* MIC key data */
-               } __packed wapi_pairwise;
-               struct {
-                       u8 keydata[16]; /* WAPI key data */
-                       u8 mic_key[16]; /* MIC key data */
-                       u8 keyid;               /* Key ID */
-                       u8 reserved[3];
-               } __packed wapi_group;
-       } __packed;
-} __packed;
-
-int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg);
-
-/* 3.32 */
-#define WSM_REMOVE_KEY_REQ_ID         0x000D
-#define WSM_REMOVE_KEY_RESP_ID        0x040D
-struct wsm_remove_key {
-       u8 index; /* Key entry index : 0-10 */
-};
-
-int wsm_remove_key(struct cw1200_common *priv,
-                  const struct wsm_remove_key *arg);
-
-/* 3.34 */
-struct wsm_set_tx_queue_params {
-       /* WSM_ACK_POLICY_... */
-       u8 ackPolicy;
-
-       /* Medium Time of TSPEC (in 32us units) allowed per */
-       /* One Second Averaging Period for this queue. */
-       u16 allowedMediumTime;
-
-       /* dot11MaxTransmitMsduLifetime to be used for the */
-       /* specified queue. */
-       u32 maxTransmitLifetime;
-};
-
-struct wsm_tx_queue_params {
-       /* NOTE: index is a linux queue id. */
-       struct wsm_set_tx_queue_params params[4];
-};
-
-
-#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\
-               max_life_time)  \
-do {                                                   \
-       struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \
-       p->ackPolicy = (ack_policy);                            \
-       p->allowedMediumTime = (allowed_time);                          \
-       p->maxTransmitLifetime = (max_life_time);                       \
-} while (0)
-
-int wsm_set_tx_queue_params(struct cw1200_common *priv,
-                           const struct wsm_set_tx_queue_params *arg, u8 id);
-
-/* 3.36 */
-#define WSM_EDCA_PARAMS_REQ_ID 0x0013
-#define WSM_EDCA_PARAMS_RESP_ID 0x0413
-struct wsm_edca_queue_params {
-       /* CWmin (in slots) for the access class. */
-       u16 cwmin;
-
-       /* CWmax (in slots) for the access class. */
-       u16 cwmax;
-
-       /* AIFS (in slots) for the access class. */
-       u16 aifns;
-
-       /* TX OP Limit (in microseconds) for the access class. */
-       u16 txop_limit;
-
-       /* dot11MaxReceiveLifetime to be used for the specified */
-       /* the access class. Overrides the global */
-       /* dot11MaxReceiveLifetime value */
-       u32 max_rx_lifetime;
-};
-
-struct wsm_edca_params {
-       /* NOTE: index is a linux queue id. */
-       struct wsm_edca_queue_params params[4];
-       bool uapsd_enable[4];
-};
-
-#define TXOP_UNIT 32
-#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\
-                    __uapsd) \
-       do {                                                    \
-               struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \
-               p->cwmin = __cw_min;                                    \
-               p->cwmax = __cw_max;                                    \
-               p->aifns = __aifs;                                      \
-               p->txop_limit = ((__txop) * TXOP_UNIT);                 \
-               p->max_rx_lifetime = __lifetime;                        \
-               (__edca)->uapsd_enable[__queue] = (__uapsd);            \
-       } while (0)
-
-int wsm_set_edca_params(struct cw1200_common *priv,
-                       const struct wsm_edca_params *arg);
-
-int wsm_set_uapsd_param(struct cw1200_common *priv,
-                       const struct wsm_edca_params *arg);
-
-/* 3.38 */
-/* Set-System info. Skipped for now. Irrelevent. */
-
-/* 3.40 */
-#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016
-#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416
-
-struct wsm_switch_channel {
-       /* 1 - means the STA shall not transmit any further */
-       /* frames until the channel switch has completed */
-       u8 mode;
-
-       /* Number of TBTTs until channel switch occurs. */
-       /* 0 - indicates switch shall occur at any time */
-       /* 1 - occurs immediately before the next TBTT */
-       u8 switch_count;
-
-       /* The new channel number to switch to. */
-       /* Note this is defined as per section 2.7. */
-       u16 channel_number;
-};
-
-int wsm_switch_channel(struct cw1200_common *priv,
-                      const struct wsm_switch_channel *arg);
-
-typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv);
-
-#define WSM_START_REQ_ID 0x0017
-#define WSM_START_RESP_ID 0x0417
-
-struct wsm_start {
-       /* WSM_START_MODE_... */
-       /* [in] */ u8 mode;
-
-       /* WSM_PHY_BAND_... */
-       /* [in] */ u8 band;
-
-       /* Channel number */
-       /* [in] */ u16 channel_number;
-
-       /* Client Traffic window in units of TU */
-       /* Valid only when mode == ..._P2P */
-       /* [in] */ u32 ct_window;
-
-       /* Interval between two consecutive */
-       /* beacon transmissions in TU. */
-       /* [in] */ u32 beacon_interval;
-
-       /* DTIM period in terms of beacon intervals */
-       /* [in] */ u8 dtim_period;
-
-       /* WSM_JOIN_PREAMBLE_... */
-       /* [in] */ u8 preamble;
-
-       /* The delay time (in microseconds) period */
-       /* before sending a probe-request. */
-       /* [in] */ u8 probe_delay;
-
-       /* Length of the SSID */
-       /* [in] */ u8 ssid_len;
-
-       /* SSID of the BSS or P2P_GO to be started now. */
-       /* [in] */ u8 ssid[32];
-
-       /* The basic supported rates for the MiniAP. */
-       /* [in] */ u32 basic_rate_set;
-};
-
-int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg);
-
-#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018
-#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418
-
-struct wsm_beacon_transmit {
-       /* 1: enable; 0: disable */
-       /* [in] */ u8 enable_beaconing;
-};
-
-int wsm_beacon_transmit(struct cw1200_common *priv,
-                       const struct wsm_beacon_transmit *arg);
-
-int wsm_start_find(struct cw1200_common *priv);
-
-int wsm_stop_find(struct cw1200_common *priv);
-
-typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status);
-
-struct wsm_suspend_resume {
-       /* See 3.52 */
-       /* Link ID */
-       /* [out] */ int link_id;
-       /* Stop sending further Tx requests down to device for this link */
-       /* [out] */ bool stop;
-       /* Transmit multicast Frames */
-       /* [out] */ bool multicast;
-       /* The AC on which Tx to be suspended /resumed. */
-       /* This is applicable only for U-APSD */
-       /* WSM_QUEUE_... */
-       /* [out] */ int queue;
-};
-
-typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv,
-                                      struct wsm_suspend_resume *arg);
-
-/* 3.54 Update-IE request. */
-struct wsm_update_ie {
-       /* WSM_UPDATE_IE_... */
-       /* [in] */ u16 what;
-       /* [in] */ u16 count;
-       /* [in] */ u8 *ies;
-       /* [in] */ size_t length;
-};
-
-int wsm_update_ie(struct cw1200_common *priv,
-                 const struct wsm_update_ie *arg);
-
-/* 3.56 */
-struct wsm_map_link {
-       /* MAC address of the remote device */
-       /* [in] */ u8 mac_addr[6];
-       /* [in] */ u8 link_id;
-};
-
-int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg);
-
-/* ******************************************************************** */
-/* MIB shortcats                                                       */
-
-static inline int wsm_set_output_power(struct cw1200_common *priv,
-                                      int power_level)
-{
-       __le32 val = __cpu_to_le32(power_level);
-       return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL,
-                            &val, sizeof(val));
-}
-
-static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv,
-                                              unsigned dtim_interval,
-                                              unsigned listen_interval)
-{
-       struct {
-               u8 numBeaconPeriods;
-               u8 reserved;
-               __le16 listenInterval;
-       } val = {
-               dtim_interval, 0, __cpu_to_le16(listen_interval)
-       };
-
-       if (dtim_interval > 0xFF || listen_interval > 0xFFFF)
-               return -EINVAL;
-       else
-               return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD,
-                                    &val, sizeof(val));
-}
-
-struct wsm_rcpi_rssi_threshold {
-       u8 rssiRcpiMode;        /* WSM_RCPI_RSSI_... */
-       u8 lowerThreshold;
-       u8 upperThreshold;
-       u8 rollingAverageCount;
-};
-
-static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv,
-                                       struct wsm_rcpi_rssi_threshold *arg)
-{
-       return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg,
-                            sizeof(*arg));
-}
-
-struct wsm_mib_counters_table {
-       __le32 plcp_errors;
-       __le32 fcs_errors;
-       __le32 tx_packets;
-       __le32 rx_packets;
-       __le32 rx_packet_errors;
-       __le32 rx_decryption_failures;
-       __le32 rx_mic_failures;
-       __le32 rx_no_key_failures;
-       __le32 tx_multicast_frames;
-       __le32 tx_frames_success;
-       __le32 tx_frame_failures;
-       __le32 tx_frames_retried;
-       __le32 tx_frames_multi_retried;
-       __le32 rx_frame_duplicates;
-       __le32 rts_success;
-       __le32 rts_failures;
-       __le32 ack_failures;
-       __le32 rx_multicast_frames;
-       __le32 rx_frames_success;
-       __le32 rx_cmac_icv_errors;
-       __le32 rx_cmac_replays;
-       __le32 rx_mgmt_ccmp_replays;
-} __packed;
-
-static inline int wsm_get_counters_table(struct cw1200_common *priv,
-                                        struct wsm_mib_counters_table *arg)
-{
-       return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE,
-                           arg, sizeof(*arg));
-}
-
-static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac)
-{
-       return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN);
-}
-
-struct wsm_rx_filter {
-       bool promiscuous;
-       bool bssid;
-       bool fcs;
-       bool probeResponder;
-};
-
-static inline int wsm_set_rx_filter(struct cw1200_common *priv,
-                                   const struct wsm_rx_filter *arg)
-{
-       __le32 val = 0;
-       if (arg->promiscuous)
-               val |= __cpu_to_le32(BIT(0));
-       if (arg->bssid)
-               val |= __cpu_to_le32(BIT(1));
-       if (arg->fcs)
-               val |= __cpu_to_le32(BIT(2));
-       if (arg->probeResponder)
-               val |= __cpu_to_le32(BIT(3));
-       return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val));
-}
-
-int wsm_set_probe_responder(struct cw1200_common *priv, bool enable);
-
-#define WSM_BEACON_FILTER_IE_HAS_CHANGED       BIT(0)
-#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1)
-#define WSM_BEACON_FILTER_IE_HAS_APPEARED      BIT(2)
-
-struct wsm_beacon_filter_table_entry {
-       u8      ie_id;
-       u8      flags;
-       u8      oui[3];
-       u8      match_data[3];
-} __packed;
-
-struct wsm_mib_beacon_filter_table {
-       __le32 num;
-       struct wsm_beacon_filter_table_entry entry[10];
-} __packed;
-
-static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv,
-                                             struct wsm_mib_beacon_filter_table *ft)
-{
-       size_t size = __le32_to_cpu(ft->num) *
-                    sizeof(struct wsm_beacon_filter_table_entry) +
-                    sizeof(__le32);
-
-       return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size);
-}
-
-#define WSM_BEACON_FILTER_ENABLE       BIT(0) /* Enable/disable beacon filtering */
-#define WSM_BEACON_FILTER_AUTO_ERP     BIT(1) /* If 1 FW will handle ERP IE changes internally */
-
-struct wsm_beacon_filter_control {
-       int enabled;
-       int bcn_count;
-};
-
-static inline int wsm_beacon_filter_control(struct cw1200_common *priv,
-                                       struct wsm_beacon_filter_control *arg)
-{
-       struct {
-               __le32 enabled;
-               __le32 bcn_count;
-       } val;
-       val.enabled = __cpu_to_le32(arg->enabled);
-       val.bcn_count = __cpu_to_le32(arg->bcn_count);
-       return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val,
-                            sizeof(val));
-}
-
-enum wsm_power_mode {
-       wsm_power_mode_active = 0,
-       wsm_power_mode_doze = 1,
-       wsm_power_mode_quiescent = 2,
-};
-
-struct wsm_operational_mode {
-       enum wsm_power_mode power_mode;
-       int disable_more_flag_usage;
-       int perform_ant_diversity;
-};
-
-static inline int wsm_set_operational_mode(struct cw1200_common *priv,
-                                       const struct wsm_operational_mode *arg)
-{
-       u8 val = arg->power_mode;
-       if (arg->disable_more_flag_usage)
-               val |= BIT(4);
-       if (arg->perform_ant_diversity)
-               val |= BIT(5);
-       return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val,
-                            sizeof(val));
-}
-
-struct wsm_template_frame {
-       u8 frame_type;
-       u8 rate;
-       struct sk_buff *skb;
-};
-
-static inline int wsm_set_template_frame(struct cw1200_common *priv,
-                                        struct wsm_template_frame *arg)
-{
-       int ret;
-       u8 *p = skb_push(arg->skb, 4);
-       p[0] = arg->frame_type;
-       p[1] = arg->rate;
-       ((__le16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4);
-       ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len);
-       skb_pull(arg->skb, 4);
-       return ret;
-}
-
-
-struct wsm_protected_mgmt_policy {
-       bool protectedMgmtEnable;
-       bool unprotectedMgmtFramesAllowed;
-       bool encryptionForAuthFrame;
-};
-
-static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv,
-               struct wsm_protected_mgmt_policy *arg)
-{
-       __le32 val = 0;
-       int ret;
-       if (arg->protectedMgmtEnable)
-               val |= __cpu_to_le32(BIT(0));
-       if (arg->unprotectedMgmtFramesAllowed)
-               val |= __cpu_to_le32(BIT(1));
-       if (arg->encryptionForAuthFrame)
-               val |= __cpu_to_le32(BIT(2));
-       ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY,
-                       &val, sizeof(val));
-       return ret;
-}
-
-struct wsm_mib_block_ack_policy {
-       u8 tx_tid;
-       u8 reserved1;
-       u8 rx_tid;
-       u8 reserved2;
-} __packed;
-
-static inline int wsm_set_block_ack_policy(struct cw1200_common *priv,
-                                          u8 tx_tid_policy,
-                                          u8 rx_tid_policy)
-{
-       struct wsm_mib_block_ack_policy val = {
-               .tx_tid = tx_tid_policy,
-               .rx_tid = rx_tid_policy,
-       };
-       return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val,
-                            sizeof(val));
-}
-
-struct wsm_mib_association_mode {
-       u8 flags;               /* WSM_ASSOCIATION_MODE_... */
-       u8 preamble;    /* WSM_JOIN_PREAMBLE_... */
-       u8 greenfield;  /* 1 for greenfield */
-       u8 mpdu_start_spacing;
-       __le32 basic_rate_set;
-} __packed;
-
-static inline int wsm_set_association_mode(struct cw1200_common *priv,
-                                          struct wsm_mib_association_mode *arg)
-{
-       return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg,
-                            sizeof(*arg));
-}
-
-#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2)
-#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3)
-struct wsm_tx_rate_retry_policy {
-       u8 index;
-       u8 short_retries;
-       u8 long_retries;
-       /* BIT(2) - Terminate retries when Tx rate retry policy
-        *          finishes.
-        * BIT(3) - Count initial frame transmission as part of
-        *          rate retry counting but not as a retry
-        *          attempt
-        */
-       u8 flags;
-       u8 rate_recoveries;
-       u8 reserved[3];
-       __le32 rate_count_indices[3];
-} __packed;
-
-struct wsm_set_tx_rate_retry_policy {
-       u8 num;
-       u8 reserved[3];
-       struct wsm_tx_rate_retry_policy tbl[8];
-} __packed;
-
-static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv,
-                               struct wsm_set_tx_rate_retry_policy *arg)
-{
-       size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy);
-       return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg,
-                            size);
-}
-
-/* 4.32 SetEtherTypeDataFrameFilter */
-struct wsm_ether_type_filter_hdr {
-       u8 num;         /* Up to WSM_MAX_FILTER_ELEMENTS */
-       u8 reserved[3];
-} __packed;
-
-struct wsm_ether_type_filter {
-       u8 action;      /* WSM_FILTER_ACTION_XXX */
-       u8 reserved;
-       __le16 type;    /* Type of ethernet frame */
-} __packed;
-
-static inline int wsm_set_ether_type_filter(struct cw1200_common *priv,
-                               struct wsm_ether_type_filter_hdr *arg)
-{
-       size_t size = sizeof(struct wsm_ether_type_filter_hdr) +
-               arg->num * sizeof(struct wsm_ether_type_filter);
-       return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER,
-               arg, size);
-}
-
-/* 4.33 SetUDPPortDataFrameFilter */
-struct wsm_udp_port_filter_hdr {
-       u8 num;         /* Up to WSM_MAX_FILTER_ELEMENTS */
-       u8 reserved[3];
-} __packed;
-
-struct wsm_udp_port_filter {
-       u8 action;      /* WSM_FILTER_ACTION_XXX */
-       u8 type;                /* WSM_FILTER_PORT_TYPE_XXX */
-       __le16 port;            /* Port number */
-} __packed;
-
-static inline int wsm_set_udp_port_filter(struct cw1200_common *priv,
-                               struct wsm_udp_port_filter_hdr *arg)
-{
-       size_t size = sizeof(struct wsm_udp_port_filter_hdr) +
-               arg->num * sizeof(struct wsm_udp_port_filter);
-       return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER,
-               arg, size);
-}
-
-/* Undocumented MIBs: */
-/* 4.35 P2PDeviceInfo */
-#define D11_MAX_SSID_LEN               (32)
-
-struct wsm_p2p_device_type {
-       __le16 category_id;
-       u8 oui[4];
-       __le16 subcategory_id;
-} __packed;
-
-struct wsm_p2p_device_info {
-       struct wsm_p2p_device_type primaryDevice;
-       u8 reserved1[3];
-       u8 devname_size;
-       u8 local_devname[D11_MAX_SSID_LEN];
-       u8 reserved2[3];
-       u8 num_secdev_supported;
-       struct wsm_p2p_device_type secdevs[0];
-} __packed;
-
-/* 4.36 SetWCDMABand - WO */
-struct wsm_cdma_band {
-       u8 wcdma_band;
-       u8 reserved[3];
-} __packed;
-
-/* 4.37 GroupTxSequenceCounter - RO */
-struct wsm_group_tx_seq {
-       __le32 bits_47_16;
-       __le16 bits_15_00;
-       __le16 reserved;
-} __packed;
-
-/* 4.39 SetHtProtection - WO */
-#define WSM_DUAL_CTS_PROT_ENB          (1 << 0)
-#define WSM_NON_GREENFIELD_STA_PRESENT  (1 << 1)
-#define WSM_HT_PROT_MODE__NO_PROT      (0 << 2)
-#define WSM_HT_PROT_MODE__NON_MEMBER   (1 << 2)
-#define WSM_HT_PROT_MODE__20_MHZ       (2 << 2)
-#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2)
-#define WSM_LSIG_TXOP_PROT_FULL                (1 << 4)
-#define WSM_LARGE_L_LENGTH_PROT                (1 << 5)
-
-struct wsm_ht_protection {
-       __le32 flags;
-} __packed;
-
-/* 4.40 GPIO Command - R/W */
-#define WSM_GPIO_COMMAND_SETUP 0
-#define WSM_GPIO_COMMAND_READ  1
-#define WSM_GPIO_COMMAND_WRITE 2
-#define WSM_GPIO_COMMAND_RESET 3
-#define WSM_GPIO_ALL_PINS      0xFF
-
-struct wsm_gpio_command {
-       u8 command;
-       u8 pin;
-       __le16 config;
-} __packed;
-
-/* 4.41 TSFCounter - RO */
-struct wsm_tsf_counter {
-       __le64 tsf_counter;
-} __packed;
-
-/* 4.43 Keep alive period */
-struct wsm_keep_alive_period {
-       __le16 period;
-       u8 reserved[2];
-} __packed;
-
-static inline int wsm_keep_alive_period(struct cw1200_common *priv,
-                                       int period)
-{
-       struct wsm_keep_alive_period arg = {
-               .period = __cpu_to_le16(period),
-       };
-       return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD,
-                       &arg, sizeof(arg));
-};
-
-/* BSSID filtering */
-struct wsm_set_bssid_filtering {
-       u8 filter;
-       u8 reserved[3];
-} __packed;
-
-static inline int wsm_set_bssid_filtering(struct cw1200_common *priv,
-                                         bool enabled)
-{
-       struct wsm_set_bssid_filtering arg = {
-               .filter = !enabled,
-       };
-       return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER,
-                       &arg, sizeof(arg));
-}
-
-/* Multicast filtering - 4.5 */
-struct wsm_mib_multicast_filter {
-       __le32 enable;
-       __le32 num_addrs;
-       u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN];
-} __packed;
-
-static inline int wsm_set_multicast_filter(struct cw1200_common *priv,
-                                          struct wsm_mib_multicast_filter *fp)
-{
-       return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE,
-                            fp, sizeof(*fp));
-}
-
-/* ARP IPv4 filtering - 4.10 */
-struct wsm_mib_arp_ipv4_filter {
-       __le32 enable;
-       __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES];
-} __packed;
-
-static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv,
-                                         struct wsm_mib_arp_ipv4_filter *fp)
-{
-       return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE,
-                           fp, sizeof(*fp));
-}
-
-/* P2P Power Save Mode Info - 4.31 */
-struct wsm_p2p_ps_modeinfo {
-       u8      opp_ps_ct_window;
-       u8      count;
-       u8      reserved;
-       u8      dtim_count;
-       __le32  duration;
-       __le32  interval;
-       __le32  start_time;
-} __packed;
-
-static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv,
-                                         struct wsm_p2p_ps_modeinfo *mi)
-{
-       return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
-                            mi, sizeof(*mi));
-}
-
-static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv,
-                                         struct wsm_p2p_ps_modeinfo *mi)
-{
-       return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
-                           mi, sizeof(*mi));
-}
-
-/* UseMultiTxConfMessage */
-
-static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv,
-                                       bool enabled)
-{
-       __le32 arg = enabled ? __cpu_to_le32(1) : 0;
-
-       return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF,
-                       &arg, sizeof(arg));
-}
-
-
-/* 4.26 SetUpasdInformation */
-struct wsm_uapsd_info {
-       __le16 uapsd_flags;
-       __le16 min_auto_trigger_interval;
-       __le16 max_auto_trigger_interval;
-       __le16 auto_trigger_step;
-};
-
-static inline int wsm_set_uapsd_info(struct cw1200_common *priv,
-                                    struct wsm_uapsd_info *arg)
-{
-       return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION,
-                               arg, sizeof(*arg));
-}
-
-/* 4.22 OverrideInternalTxRate */
-struct wsm_override_internal_txrate {
-       u8 internalTxRate;
-       u8 nonErpInternalTxRate;
-       u8 reserved[2];
-} __packed;
-
-static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv,
-                                    struct wsm_override_internal_txrate *arg)
-{
-       return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
-                               arg, sizeof(*arg));
-}
-
-/* ******************************************************************** */
-/* WSM TX port control                                                 */
-
-void wsm_lock_tx(struct cw1200_common *priv);
-void wsm_lock_tx_async(struct cw1200_common *priv);
-bool wsm_flush_tx(struct cw1200_common *priv);
-void wsm_unlock_tx(struct cw1200_common *priv);
-
-/* ******************************************************************** */
-/* WSM / BH API                                                                */
-
-int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len);
-int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm,
-                 struct sk_buff **skb_p);
-
-/* ******************************************************************** */
-/* wsm_buf API                                                         */
-
-struct wsm_buf {
-       u8 *begin;
-       u8 *data;
-       u8 *end;
-};
-
-void wsm_buf_init(struct wsm_buf *buf);
-void wsm_buf_deinit(struct wsm_buf *buf);
-
-/* ******************************************************************** */
-/* wsm_cmd API                                                         */
-
-struct wsm_cmd {
-       spinlock_t lock; /* Protect structure from multiple access */
-       int done;
-       u8 *ptr;
-       size_t len;
-       void *arg;
-       int ret;
-       u16 cmd;
-};
-
-/* ******************************************************************** */
-/* WSM TX buffer access                                                        */
-
-int wsm_get_tx(struct cw1200_common *priv, u8 **data,
-              size_t *tx_len, int *burst);
-void wsm_txed(struct cw1200_common *priv, u8 *data);
-
-/* ******************************************************************** */
-/* Queue mapping: WSM <---> linux                                      */
-/* Linux: VO VI BE BK                                                  */
-/* WSM:   BE BK VI VO                                                  */
-
-static inline u8 wsm_queue_id_to_linux(u8 queue_id)
-{
-       static const u8 queue_mapping[] = {
-               2, 3, 1, 0
-       };
-       return queue_mapping[queue_id];
-}
-
-static inline u8 wsm_queue_id_to_wsm(u8 queue_id)
-{
-       static const u8 queue_mapping[] = {
-               3, 2, 0, 1
-       };
-       return queue_mapping[queue_id];
-}
-
-#endif /* CW1200_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/st/Kconfig b/drivers/net/wireless/st/Kconfig
new file mode 100644 (file)
index 0000000..969b4f6
--- /dev/null
@@ -0,0 +1,16 @@
+config WLAN_VENDOR_ST
+       bool "STMicroelectronics devices"
+       default y
+       ---help---
+         If you have a wireless card belonging to this class, say Y.
+
+         Note that the answer to this question doesn't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about  cards. If you say Y, you will be asked for
+         your specific card in the following questions.
+
+if WLAN_VENDOR_ST
+
+source "drivers/net/wireless/st/cw1200/Kconfig"
+
+endif # WLAN_VENDOR_ST
diff --git a/drivers/net/wireless/st/Makefile b/drivers/net/wireless/st/Makefile
new file mode 100644 (file)
index 0000000..a60d635
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_CW1200)   += cw1200/
diff --git a/drivers/net/wireless/st/cw1200/Kconfig b/drivers/net/wireless/st/cw1200/Kconfig
new file mode 100644 (file)
index 0000000..0880742
--- /dev/null
@@ -0,0 +1,30 @@
+config CW1200
+       tristate "CW1200 WLAN support"
+       depends on MAC80211 && CFG80211
+       help
+         This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets.
+         This option just enables the driver core, see below for
+         specific bus support.
+
+if CW1200
+
+config CW1200_WLAN_SDIO
+       tristate "Support SDIO platforms"
+       depends on CW1200 && MMC
+       help
+         Enable support for the CW1200 connected via an SDIO bus.
+         By default this driver only supports the Sagrad SG901-1091/1098 EVK
+         and similar designs that utilize a hardware reset circuit. To
+         support different CW1200 SDIO designs you will need to override
+         the default platform data by calling cw1200_sdio_set_platform_data()
+         in your board setup file.
+
+config CW1200_WLAN_SPI
+       tristate "Support SPI platforms"
+       depends on CW1200 && SPI
+       help
+         Enables support for the CW1200 connected via a SPI bus.  You will
+         need to add appropriate platform data glue in your board setup
+         file.
+
+endif
diff --git a/drivers/net/wireless/st/cw1200/Makefile b/drivers/net/wireless/st/cw1200/Makefile
new file mode 100644 (file)
index 0000000..b086aac
--- /dev/null
@@ -0,0 +1,21 @@
+cw1200_core-y := \
+               fwio.o \
+               txrx.o \
+               main.o \
+               queue.o \
+               hwio.o \
+               bh.o \
+               wsm.o \
+               sta.o \
+               scan.o \
+               debug.o
+cw1200_core-$(CONFIG_PM)       += pm.o
+
+# CFLAGS_sta.o += -DDEBUG
+
+cw1200_wlan_sdio-y := cw1200_sdio.o
+cw1200_wlan_spi-y := cw1200_spi.o
+
+obj-$(CONFIG_CW1200) += cw1200_core.o
+obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o
+obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o
diff --git a/drivers/net/wireless/st/cw1200/bh.c b/drivers/net/wireless/st/cw1200/bh.c
new file mode 100644 (file)
index 0000000..92d299a
--- /dev/null
@@ -0,0 +1,619 @@
+/*
+ * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver, which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+
+#include "cw1200.h"
+#include "bh.h"
+#include "hwio.h"
+#include "wsm.h"
+#include "hwbus.h"
+#include "debug.h"
+#include "fwio.h"
+
+static int cw1200_bh(void *arg);
+
+#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4)
+/* an SPI message cannot be bigger than (2"12-1)*2 bytes
+ * "*2" to cvt to bytes
+ */
+#define MAX_SZ_RD_WR_BUFFERS   (DOWNLOAD_BLOCK_SIZE_WR*2)
+#define PIGGYBACK_CTRL_REG     (2)
+#define EFFECTIVE_BUF_SIZE     (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
+
+/* Suspend state privates */
+enum cw1200_bh_pm_state {
+       CW1200_BH_RESUMED = 0,
+       CW1200_BH_SUSPEND,
+       CW1200_BH_SUSPENDED,
+       CW1200_BH_RESUME,
+};
+
+typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
+       u8 *data, size_t size);
+
+static void cw1200_bh_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+       container_of(work, struct cw1200_common, bh_work);
+       cw1200_bh(priv);
+}
+
+int cw1200_register_bh(struct cw1200_common *priv)
+{
+       int err = 0;
+       /* Realtime workqueue */
+       priv->bh_workqueue = alloc_workqueue("cw1200_bh",
+                               WQ_MEM_RECLAIM | WQ_HIGHPRI
+                               | WQ_CPU_INTENSIVE, 1);
+
+       if (!priv->bh_workqueue)
+               return -ENOMEM;
+
+       INIT_WORK(&priv->bh_work, cw1200_bh_work);
+
+       pr_debug("[BH] register.\n");
+
+       atomic_set(&priv->bh_rx, 0);
+       atomic_set(&priv->bh_tx, 0);
+       atomic_set(&priv->bh_term, 0);
+       atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+       priv->bh_error = 0;
+       priv->hw_bufs_used = 0;
+       priv->buf_id_tx = 0;
+       priv->buf_id_rx = 0;
+       init_waitqueue_head(&priv->bh_wq);
+       init_waitqueue_head(&priv->bh_evt_wq);
+
+       err = !queue_work(priv->bh_workqueue, &priv->bh_work);
+       WARN_ON(err);
+       return err;
+}
+
+void cw1200_unregister_bh(struct cw1200_common *priv)
+{
+       atomic_add(1, &priv->bh_term);
+       wake_up(&priv->bh_wq);
+
+       flush_workqueue(priv->bh_workqueue);
+
+       destroy_workqueue(priv->bh_workqueue);
+       priv->bh_workqueue = NULL;
+
+       pr_debug("[BH] unregistered.\n");
+}
+
+void cw1200_irq_handler(struct cw1200_common *priv)
+{
+       pr_debug("[BH] irq.\n");
+
+       /* Disable Interrupts! */
+       /* NOTE:  hwbus_ops->lock already held */
+       __cw1200_irq_enable(priv, 0);
+
+       if (/* WARN_ON */(priv->bh_error))
+               return;
+
+       if (atomic_add_return(1, &priv->bh_rx) == 1)
+               wake_up(&priv->bh_wq);
+}
+EXPORT_SYMBOL_GPL(cw1200_irq_handler);
+
+void cw1200_bh_wakeup(struct cw1200_common *priv)
+{
+       pr_debug("[BH] wakeup.\n");
+       if (priv->bh_error) {
+               pr_err("[BH] wakeup failed (BH error)\n");
+               return;
+       }
+
+       if (atomic_add_return(1, &priv->bh_tx) == 1)
+               wake_up(&priv->bh_wq);
+}
+
+int cw1200_bh_suspend(struct cw1200_common *priv)
+{
+       pr_debug("[BH] suspend.\n");
+       if (priv->bh_error) {
+               wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n");
+               return -EINVAL;
+       }
+
+       atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
+       wake_up(&priv->bh_wq);
+       return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
+               (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)),
+                1 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+int cw1200_bh_resume(struct cw1200_common *priv)
+{
+       pr_debug("[BH] resume.\n");
+       if (priv->bh_error) {
+               wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n");
+               return -EINVAL;
+       }
+
+       atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
+       wake_up(&priv->bh_wq);
+       return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
+               (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)),
+               1 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
+{
+       ++priv->hw_bufs_used;
+}
+
+int wsm_release_tx_buffer(struct cw1200_common *priv, int count)
+{
+       int ret = 0;
+       int hw_bufs_used = priv->hw_bufs_used;
+
+       priv->hw_bufs_used -= count;
+       if (WARN_ON(priv->hw_bufs_used < 0))
+               ret = -1;
+       else if (hw_bufs_used >= priv->wsm_caps.input_buffers)
+               ret = 1;
+       if (!priv->hw_bufs_used)
+               wake_up(&priv->bh_evt_wq);
+       return ret;
+}
+
+static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv,
+                                         u16 *ctrl_reg)
+{
+       int ret;
+
+       ret = cw1200_reg_read_16(priv,
+                       ST90TDS_CONTROL_REG_ID, ctrl_reg);
+       if (ret) {
+               ret = cw1200_reg_read_16(priv,
+                               ST90TDS_CONTROL_REG_ID, ctrl_reg);
+               if (ret)
+                       pr_err("[BH] Failed to read control register.\n");
+       }
+
+       return ret;
+}
+
+static int cw1200_device_wakeup(struct cw1200_common *priv)
+{
+       u16 ctrl_reg;
+       int ret;
+
+       pr_debug("[BH] Device wakeup.\n");
+
+       /* First, set the dpll register */
+       ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
+                                 cw1200_dpll_from_clk(priv->hw_refclk));
+       if (WARN_ON(ret))
+               return ret;
+
+       /* To force the device to be always-on, the host sets WLAN_UP to 1 */
+       ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
+                       ST90TDS_CONT_WUP_BIT);
+       if (WARN_ON(ret))
+               return ret;
+
+       ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg);
+       if (WARN_ON(ret))
+               return ret;
+
+       /* If the device returns WLAN_RDY as 1, the device is active and will
+        * remain active.
+        */
+       if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
+               pr_debug("[BH] Device awake.\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+/* Must be called from BH thraed. */
+void cw1200_enable_powersave(struct cw1200_common *priv,
+                            bool enable)
+{
+       pr_debug("[BH] Powerave is %s.\n",
+                enable ? "enabled" : "disabled");
+       priv->powersave_enabled = enable;
+}
+
+static int cw1200_bh_rx_helper(struct cw1200_common *priv,
+                              uint16_t *ctrl_reg,
+                              int *tx)
+{
+       size_t read_len = 0;
+       struct sk_buff *skb_rx = NULL;
+       struct wsm_hdr *wsm;
+       size_t wsm_len;
+       u16 wsm_id;
+       u8 wsm_seq;
+       int rx_resync = 1;
+
+       size_t alloc_len;
+       u8 *data;
+
+       read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
+       if (!read_len)
+               return 0; /* No more work */
+
+       if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
+                   (read_len > EFFECTIVE_BUF_SIZE))) {
+               pr_debug("Invalid read len: %zu (%04x)",
+                        read_len, *ctrl_reg);
+               goto err;
+       }
+
+       /* Add SIZE of PIGGYBACK reg (CONTROL Reg)
+        * to the NEXT Message length + 2 Bytes for SKB
+        */
+       read_len = read_len + 2;
+
+       alloc_len = priv->hwbus_ops->align_size(
+               priv->hwbus_priv, read_len);
+
+       /* Check if not exceeding CW1200 capabilities */
+       if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
+               pr_debug("Read aligned len: %zu\n",
+                        alloc_len);
+       }
+
+       skb_rx = dev_alloc_skb(alloc_len);
+       if (WARN_ON(!skb_rx))
+               goto err;
+
+       skb_trim(skb_rx, 0);
+       skb_put(skb_rx, read_len);
+       data = skb_rx->data;
+       if (WARN_ON(!data))
+               goto err;
+
+       if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) {
+               pr_err("rx blew up, len %zu\n", alloc_len);
+               goto err;
+       }
+
+       /* Piggyback */
+       *ctrl_reg = __le16_to_cpu(
+               ((__le16 *)data)[alloc_len / 2 - 1]);
+
+       wsm = (struct wsm_hdr *)data;
+       wsm_len = __le16_to_cpu(wsm->len);
+       if (WARN_ON(wsm_len > read_len))
+               goto err;
+
+       if (priv->wsm_enable_wsm_dumps)
+               print_hex_dump_bytes("<-- ",
+                                    DUMP_PREFIX_NONE,
+                                    data, wsm_len);
+
+       wsm_id  = __le16_to_cpu(wsm->id) & 0xFFF;
+       wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
+
+       skb_trim(skb_rx, wsm_len);
+
+       if (wsm_id == 0x0800) {
+               wsm_handle_exception(priv,
+                                    &data[sizeof(*wsm)],
+                                    wsm_len - sizeof(*wsm));
+               goto err;
+       } else if (!rx_resync) {
+               if (WARN_ON(wsm_seq != priv->wsm_rx_seq))
+                       goto err;
+       }
+       priv->wsm_rx_seq = (wsm_seq + 1) & 7;
+       rx_resync = 0;
+
+       if (wsm_id & 0x0400) {
+               int rc = wsm_release_tx_buffer(priv, 1);
+               if (WARN_ON(rc < 0))
+                       return rc;
+               else if (rc > 0)
+                       *tx = 1;
+       }
+
+       /* cw1200_wsm_rx takes care on SKB livetime */
+       if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx)))
+               goto err;
+
+       if (skb_rx) {
+               dev_kfree_skb(skb_rx);
+               skb_rx = NULL;
+       }
+
+       return 0;
+
+err:
+       if (skb_rx) {
+               dev_kfree_skb(skb_rx);
+               skb_rx = NULL;
+       }
+       return -1;
+}
+
+static int cw1200_bh_tx_helper(struct cw1200_common *priv,
+                              int *pending_tx,
+                              int *tx_burst)
+{
+       size_t tx_len;
+       u8 *data;
+       int ret;
+       struct wsm_hdr *wsm;
+
+       if (priv->device_can_sleep) {
+               ret = cw1200_device_wakeup(priv);
+               if (WARN_ON(ret < 0)) { /* Error in wakeup */
+                       *pending_tx = 1;
+                       return 0;
+               } else if (ret) { /* Woke up */
+                       priv->device_can_sleep = false;
+               } else { /* Did not awake */
+                       *pending_tx = 1;
+                       return 0;
+               }
+       }
+
+       wsm_alloc_tx_buffer(priv);
+       ret = wsm_get_tx(priv, &data, &tx_len, tx_burst);
+       if (ret <= 0) {
+               wsm_release_tx_buffer(priv, 1);
+               if (WARN_ON(ret < 0))
+                       return ret; /* Error */
+               return 0; /* No work */
+       }
+
+       wsm = (struct wsm_hdr *)data;
+       BUG_ON(tx_len < sizeof(*wsm));
+       BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
+
+       atomic_add(1, &priv->bh_tx);
+
+       tx_len = priv->hwbus_ops->align_size(
+               priv->hwbus_priv, tx_len);
+
+       /* Check if not exceeding CW1200 capabilities */
+       if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
+               pr_debug("Write aligned len: %zu\n", tx_len);
+
+       wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+       wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq));
+
+       if (WARN_ON(cw1200_data_write(priv, data, tx_len))) {
+               pr_err("tx blew up, len %zu\n", tx_len);
+               wsm_release_tx_buffer(priv, 1);
+               return -1; /* Error */
+       }
+
+       if (priv->wsm_enable_wsm_dumps)
+               print_hex_dump_bytes("--> ",
+                                    DUMP_PREFIX_NONE,
+                                    data,
+                                    __le16_to_cpu(wsm->len));
+
+       wsm_txed(priv, data);
+       priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
+
+       if (*tx_burst > 1) {
+               cw1200_debug_tx_burst(priv);
+               return 1; /* Work remains */
+       }
+
+       return 0;
+}
+
+static int cw1200_bh(void *arg)
+{
+       struct cw1200_common *priv = arg;
+       int rx, tx, term, suspend;
+       u16 ctrl_reg = 0;
+       int tx_allowed;
+       int pending_tx = 0;
+       int tx_burst;
+       long status;
+       u32 dummy;
+       int ret;
+
+       for (;;) {
+               if (!priv->hw_bufs_used &&
+                   priv->powersave_enabled &&
+                   !priv->device_can_sleep &&
+                   !atomic_read(&priv->recent_scan)) {
+                       status = 1 * HZ;
+                       pr_debug("[BH] Device wakedown. No data.\n");
+                       cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0);
+                       priv->device_can_sleep = true;
+               } else if (priv->hw_bufs_used) {
+                       /* Interrupt loss detection */
+                       status = 1 * HZ;
+               } else {
+                       status = MAX_SCHEDULE_TIMEOUT;
+               }
+
+               /* Dummy Read for SDIO retry mechanism*/
+               if ((priv->hw_type != -1) &&
+                   (atomic_read(&priv->bh_rx) == 0) &&
+                   (atomic_read(&priv->bh_tx) == 0))
+                       cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID,
+                                       &dummy, sizeof(dummy));
+
+               pr_debug("[BH] waiting ...\n");
+               status = wait_event_interruptible_timeout(priv->bh_wq, ({
+                               rx = atomic_xchg(&priv->bh_rx, 0);
+                               tx = atomic_xchg(&priv->bh_tx, 0);
+                               term = atomic_xchg(&priv->bh_term, 0);
+                               suspend = pending_tx ?
+                                       0 : atomic_read(&priv->bh_suspend);
+                               (rx || tx || term || suspend || priv->bh_error);
+                       }), status);
+
+               pr_debug("[BH] - rx: %d, tx: %d, term: %d, bh_err: %d, suspend: %d, status: %ld\n",
+                        rx, tx, term, suspend, priv->bh_error, status);
+
+               /* Did an error occur? */
+               if ((status < 0 && status != -ERESTARTSYS) ||
+                   term || priv->bh_error) {
+                       break;
+               }
+               if (!status) {  /* wait_event timed out */
+                       unsigned long timestamp = jiffies;
+                       long timeout;
+                       int pending = 0;
+                       int i;
+
+                       /* Check to see if we have any outstanding frames */
+                       if (priv->hw_bufs_used && (!rx || !tx)) {
+                               wiphy_warn(priv->hw->wiphy,
+                                          "Missed interrupt? (%d frames outstanding)\n",
+                                          priv->hw_bufs_used);
+                               rx = 1;
+
+                               /* Get a timestamp of "oldest" frame */
+                               for (i = 0; i < 4; ++i)
+                                       pending += cw1200_queue_get_xmit_timestamp(
+                                               &priv->tx_queue[i],
+                                               &timestamp,
+                                               priv->pending_frame_id);
+
+                               /* Check if frame transmission is timed out.
+                                * Add an extra second with respect to possible
+                                * interrupt loss.
+                                */
+                               timeout = timestamp +
+                                       WSM_CMD_LAST_CHANCE_TIMEOUT +
+                                       1 * HZ  -
+                                       jiffies;
+
+                               /* And terminate BH thread if the frame is "stuck" */
+                               if (pending && timeout < 0) {
+                                       wiphy_warn(priv->hw->wiphy,
+                                                  "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
+                                                  priv->hw_bufs_used, pending,
+                                                  timestamp, jiffies);
+                                       break;
+                               }
+                       } else if (!priv->device_can_sleep &&
+                                  !atomic_read(&priv->recent_scan)) {
+                               pr_debug("[BH] Device wakedown. Timeout.\n");
+                               cw1200_reg_write_16(priv,
+                                                   ST90TDS_CONTROL_REG_ID, 0);
+                               priv->device_can_sleep = true;
+                       }
+                       goto done;
+               } else if (suspend) {
+                       pr_debug("[BH] Device suspend.\n");
+                       if (priv->powersave_enabled) {
+                               pr_debug("[BH] Device wakedown. Suspend.\n");
+                               cw1200_reg_write_16(priv,
+                                                   ST90TDS_CONTROL_REG_ID, 0);
+                               priv->device_can_sleep = true;
+                       }
+
+                       atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
+                       wake_up(&priv->bh_evt_wq);
+                       status = wait_event_interruptible(priv->bh_wq,
+                                                         CW1200_BH_RESUME == atomic_read(&priv->bh_suspend));
+                       if (status < 0) {
+                               wiphy_err(priv->hw->wiphy,
+                                         "Failed to wait for resume: %ld.\n",
+                                         status);
+                               break;
+                       }
+                       pr_debug("[BH] Device resume.\n");
+                       atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+                       wake_up(&priv->bh_evt_wq);
+                       atomic_add(1, &priv->bh_rx);
+                       goto done;
+               }
+
+       rx:
+               tx += pending_tx;
+               pending_tx = 0;
+
+               if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
+                       break;
+
+               /* Don't bother trying to rx unless we have data to read */
+               if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
+                       ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
+                       if (ret < 0)
+                               break;
+                       /* Double up here if there's more data.. */
+                       if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
+                               ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
+                               if (ret < 0)
+                                       break;
+                       }
+               }
+
+       tx:
+               if (tx) {
+                       tx = 0;
+
+                       BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers);
+                       tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used;
+                       tx_allowed = tx_burst > 0;
+
+                       if (!tx_allowed) {
+                               /* Buffers full.  Ensure we process tx
+                                * after we handle rx..
+                                */
+                               pending_tx = tx;
+                               goto done_rx;
+                       }
+                       ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst);
+                       if (ret < 0)
+                               break;
+                       if (ret > 0) /* More to transmit */
+                               tx = ret;
+
+                       /* Re-read ctrl reg */
+                       if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
+                               break;
+               }
+
+       done_rx:
+               if (priv->bh_error)
+                       break;
+               if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+                       goto rx;
+               if (tx)
+                       goto tx;
+
+       done:
+               /* Re-enable device interrupts */
+               priv->hwbus_ops->lock(priv->hwbus_priv);
+               __cw1200_irq_enable(priv, 1);
+               priv->hwbus_ops->unlock(priv->hwbus_priv);
+       }
+
+       /* Explicitly disable device interrupts */
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       __cw1200_irq_enable(priv, 0);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+
+       if (!term) {
+               pr_err("[BH] Fatal error, exiting.\n");
+               priv->bh_error = 1;
+               /* TODO: schedule_work(recovery) */
+       }
+       return 0;
+}
diff --git a/drivers/net/wireless/st/cw1200/bh.h b/drivers/net/wireless/st/cw1200/bh.h
new file mode 100644 (file)
index 0000000..af6a485
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_BH_H
+#define CW1200_BH_H
+
+/* extern */ struct cw1200_common;
+
+int cw1200_register_bh(struct cw1200_common *priv);
+void cw1200_unregister_bh(struct cw1200_common *priv);
+void cw1200_irq_handler(struct cw1200_common *priv);
+void cw1200_bh_wakeup(struct cw1200_common *priv);
+int cw1200_bh_suspend(struct cw1200_common *priv);
+int cw1200_bh_resume(struct cw1200_common *priv);
+/* Must be called from BH thread. */
+void cw1200_enable_powersave(struct cw1200_common *priv,
+                            bool enable);
+int wsm_release_tx_buffer(struct cw1200_common *priv, int count);
+
+#endif /* CW1200_BH_H */
diff --git a/drivers/net/wireless/st/cw1200/cw1200.h b/drivers/net/wireless/st/cw1200/cw1200.h
new file mode 100644 (file)
index 0000000..1ad7d36
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * Common private data for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on the mac80211 Prism54 code, which is
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_H
+#define CW1200_H
+
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <net/mac80211.h>
+
+#include "queue.h"
+#include "wsm.h"
+#include "scan.h"
+#include "txrx.h"
+#include "pm.h"
+
+/* Forward declarations */
+struct hwbus_ops;
+struct task_struct;
+struct cw1200_debug_priv;
+struct firmware;
+
+#define CW1200_MAX_CTRL_FRAME_LEN      (0x1000)
+
+#define CW1200_MAX_STA_IN_AP_MODE      (5)
+#define CW1200_LINK_ID_AFTER_DTIM      (CW1200_MAX_STA_IN_AP_MODE + 1)
+#define CW1200_LINK_ID_UAPSD           (CW1200_MAX_STA_IN_AP_MODE + 2)
+#define CW1200_LINK_ID_MAX             (CW1200_MAX_STA_IN_AP_MODE + 3)
+#define CW1200_MAX_REQUEUE_ATTEMPTS    (5)
+
+#define CW1200_MAX_TID                 (8)
+
+#define CW1200_BLOCK_ACK_CNT           (30)
+#define CW1200_BLOCK_ACK_THLD          (800)
+#define CW1200_BLOCK_ACK_HIST          (3)
+#define CW1200_BLOCK_ACK_INTERVAL      (1 * HZ / CW1200_BLOCK_ACK_HIST)
+
+#define CW1200_JOIN_TIMEOUT            (1 * HZ)
+#define CW1200_AUTH_TIMEOUT            (5 * HZ)
+
+struct cw1200_ht_info {
+       struct ieee80211_sta_ht_cap     ht_cap;
+       enum nl80211_channel_type       channel_type;
+       u16                             operation_mode;
+};
+
+/* Please keep order */
+enum cw1200_join_status {
+       CW1200_JOIN_STATUS_PASSIVE = 0,
+       CW1200_JOIN_STATUS_MONITOR,
+       CW1200_JOIN_STATUS_JOINING,
+       CW1200_JOIN_STATUS_PRE_STA,
+       CW1200_JOIN_STATUS_STA,
+       CW1200_JOIN_STATUS_IBSS,
+       CW1200_JOIN_STATUS_AP,
+};
+
+enum cw1200_link_status {
+       CW1200_LINK_OFF,
+       CW1200_LINK_RESERVE,
+       CW1200_LINK_SOFT,
+       CW1200_LINK_HARD,
+       CW1200_LINK_RESET,
+       CW1200_LINK_RESET_REMAP,
+};
+
+extern int cw1200_power_mode;
+extern const char * const cw1200_fw_types[];
+
+struct cw1200_link_entry {
+       unsigned long                   timestamp;
+       enum cw1200_link_status         status;
+       enum cw1200_link_status         prev_status;
+       u8                              mac[ETH_ALEN];
+       u8                              buffered[CW1200_MAX_TID];
+       struct sk_buff_head             rx_queue;
+};
+
+struct cw1200_common {
+       /* interfaces to the rest of the stack */
+       struct ieee80211_hw             *hw;
+       struct ieee80211_vif            *vif;
+       struct device                   *pdev;
+
+       /* Statistics */
+       struct ieee80211_low_level_stats stats;
+
+       /* Our macaddr */
+       u8 mac_addr[ETH_ALEN];
+
+       /* Hardware interface */
+       const struct hwbus_ops          *hwbus_ops;
+       struct hwbus_priv               *hwbus_priv;
+
+       /* Hardware information */
+       enum {
+               HIF_9000_SILICON_VERSATILE = 0,
+               HIF_8601_VERSATILE,
+               HIF_8601_SILICON,
+       } hw_type;
+       enum {
+               CW1200_HW_REV_CUT10 = 10,
+               CW1200_HW_REV_CUT11 = 11,
+               CW1200_HW_REV_CUT20 = 20,
+               CW1200_HW_REV_CUT22 = 22,
+               CW1X60_HW_REV       = 40,
+       } hw_revision;
+       int                             hw_refclk;
+       bool                            hw_have_5ghz;
+       const struct firmware           *sdd;
+       char                            *sdd_path;
+
+       struct cw1200_debug_priv        *debug;
+
+       struct workqueue_struct         *workqueue;
+       struct mutex                    conf_mutex;
+
+       struct cw1200_queue             tx_queue[4];
+       struct cw1200_queue_stats       tx_queue_stats;
+       int                             tx_burst_idx;
+
+       /* firmware/hardware info */
+       unsigned int tx_hdr_len;
+
+       /* Radio data */
+       int output_power;
+
+       /* BBP/MAC state */
+       struct ieee80211_rate           *rates;
+       struct ieee80211_rate           *mcs_rates;
+       struct ieee80211_channel        *channel;
+       struct wsm_edca_params          edca;
+       struct wsm_tx_queue_params      tx_queue_params;
+       struct wsm_mib_association_mode association_mode;
+       struct wsm_set_bss_params       bss_params;
+       struct cw1200_ht_info           ht_info;
+       struct wsm_set_pm               powersave_mode;
+       struct wsm_set_pm               firmware_ps_mode;
+       int                             cqm_rssi_thold;
+       unsigned                        cqm_rssi_hyst;
+       bool                            cqm_use_rssi;
+       int                             cqm_beacon_loss_count;
+       int                             channel_switch_in_progress;
+       wait_queue_head_t               channel_switch_done;
+       u8                              long_frame_max_tx_count;
+       u8                              short_frame_max_tx_count;
+       int                             mode;
+       bool                            enable_beacon;
+       int                             beacon_int;
+       bool                            listening;
+       struct wsm_rx_filter            rx_filter;
+       struct wsm_mib_multicast_filter multicast_filter;
+       bool                            has_multicast_subscription;
+       bool                            disable_beacon_filter;
+       struct work_struct              update_filtering_work;
+       struct work_struct              set_beacon_wakeup_period_work;
+
+       u8                              ba_rx_tid_mask;
+       u8                              ba_tx_tid_mask;
+
+       struct cw1200_pm_state          pm_state;
+
+       struct wsm_p2p_ps_modeinfo      p2p_ps_modeinfo;
+       struct wsm_uapsd_info           uapsd_info;
+       bool                            setbssparams_done;
+       bool                            bt_present;
+       u8                              conf_listen_interval;
+       u32                             listen_interval;
+       u32                             erp_info;
+       u32                             rts_threshold;
+
+       /* BH */
+       atomic_t                        bh_rx;
+       atomic_t                        bh_tx;
+       atomic_t                        bh_term;
+       atomic_t                        bh_suspend;
+
+       struct workqueue_struct         *bh_workqueue;
+       struct work_struct              bh_work;
+
+       int                             bh_error;
+       wait_queue_head_t               bh_wq;
+       wait_queue_head_t               bh_evt_wq;
+       u8                              buf_id_tx;
+       u8                              buf_id_rx;
+       u8                              wsm_rx_seq;
+       u8                              wsm_tx_seq;
+       int                             hw_bufs_used;
+       bool                            powersave_enabled;
+       bool                            device_can_sleep;
+
+       /* Scan status */
+       struct cw1200_scan scan;
+       /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid
+        * FW issue with sleeping/waking up.
+        */
+       atomic_t                        recent_scan;
+       struct delayed_work             clear_recent_scan_work;
+
+       /* WSM */
+       struct wsm_startup_ind          wsm_caps;
+       struct mutex                    wsm_cmd_mux;
+       struct wsm_buf                  wsm_cmd_buf;
+       struct wsm_cmd                  wsm_cmd;
+       wait_queue_head_t               wsm_cmd_wq;
+       wait_queue_head_t               wsm_startup_done;
+       int                             firmware_ready;
+       atomic_t                        tx_lock;
+
+       /* WSM debug */
+       int                             wsm_enable_wsm_dumps;
+
+       /* WSM Join */
+       enum cw1200_join_status join_status;
+       u32                     pending_frame_id;
+       bool                    join_pending;
+       struct delayed_work     join_timeout;
+       struct work_struct      unjoin_work;
+       struct work_struct      join_complete_work;
+       int                     join_complete_status;
+       int                     join_dtim_period;
+       bool                    delayed_unjoin;
+
+       /* TX/RX and security */
+       s8                      wep_default_key_id;
+       struct work_struct      wep_key_work;
+       u32                     key_map;
+       struct wsm_add_key      keys[WSM_KEY_MAX_INDEX + 1];
+
+       /* AP powersave */
+       u32                     link_id_map;
+       struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE];
+       struct work_struct      link_id_work;
+       struct delayed_work     link_id_gc_work;
+       u32                     sta_asleep_mask;
+       u32                     pspoll_mask;
+       bool                    aid0_bit_set;
+       spinlock_t              ps_state_lock; /* Protect power save state */
+       bool                    buffered_multicasts;
+       bool                    tx_multicast;
+       struct work_struct      set_tim_work;
+       struct work_struct      set_cts_work;
+       struct work_struct      multicast_start_work;
+       struct work_struct      multicast_stop_work;
+       struct timer_list       mcast_timeout;
+
+       /* WSM events and CQM implementation */
+       spinlock_t              event_queue_lock; /* Protect event queue */
+       struct list_head        event_queue;
+       struct work_struct      event_handler;
+
+       struct delayed_work     bss_loss_work;
+       spinlock_t              bss_loss_lock; /* Protect BSS loss state */
+       int                     bss_loss_state;
+       u32                     bss_loss_confirm_id;
+       int                     delayed_link_loss;
+       struct work_struct      bss_params_work;
+
+       /* TX rate policy cache */
+       struct tx_policy_cache tx_policy_cache;
+       struct work_struct tx_policy_upload_work;
+
+       /* legacy PS mode switch in suspend */
+       int                     ps_mode_switch_in_progress;
+       wait_queue_head_t       ps_mode_switch_done;
+
+       /* Workaround for WFD testcase 6.1.10*/
+       struct work_struct      linkid_reset_work;
+       u8                      action_frame_sa[ETH_ALEN];
+       u8                      action_linkid;
+};
+
+struct cw1200_sta_priv {
+       int link_id;
+};
+
+/* interfaces for the drivers */
+int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
+                     struct hwbus_priv *hwbus,
+                     struct device *pdev,
+                     struct cw1200_common **pself,
+                     int ref_clk, const u8 *macaddr,
+                     const char *sdd_path, bool have_5ghz);
+void cw1200_core_release(struct cw1200_common *self);
+
+#define FWLOAD_BLOCK_SIZE (1024)
+
+static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info)
+{
+       return ht_info->channel_type != NL80211_CHAN_NO_HT;
+}
+
+static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info)
+{
+       return cw1200_is_ht(ht_info) &&
+               (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
+               !(ht_info->operation_mode &
+                 IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+}
+
+static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info)
+{
+       if (!cw1200_is_ht(ht_info))
+               return 0;
+       return ht_info->ht_cap.ampdu_density;
+}
+
+#endif /* CW1200_H */
diff --git a/drivers/net/wireless/st/cw1200/cw1200_sdio.c b/drivers/net/wireless/st/cw1200/cw1200_sdio.c
new file mode 100644 (file)
index 0000000..d3acc85
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * Mac80211 SDIO driver for ST-Ericsson CW1200 device
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <net/mac80211.h>
+
+#include "cw1200.h"
+#include "hwbus.h"
+#include <linux/platform_data/net-cw1200.h>
+#include "hwio.h"
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
+MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
+MODULE_LICENSE("GPL");
+
+#define SDIO_BLOCK_SIZE (512)
+
+/* Default platform data for Sagrad modules */
+static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = {
+       .ref_clk = 38400,
+       .have_5ghz = false,
+       .sdd_file = "sdd_sagrad_1091_1098.bin",
+};
+
+/* Allow platform data to be overridden */
+static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data;
+
+void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata)
+{
+       global_plat_data = pdata;
+}
+
+struct hwbus_priv {
+       struct sdio_func        *func;
+       struct cw1200_common    *core;
+       const struct cw1200_platform_data_sdio *pdata;
+};
+
+#ifndef SDIO_VENDOR_ID_STE
+#define SDIO_VENDOR_ID_STE             0x0020
+#endif
+
+#ifndef SDIO_DEVICE_ID_STE_CW1200
+#define SDIO_DEVICE_ID_STE_CW1200      0x2280
+#endif
+
+static const struct sdio_device_id cw1200_sdio_ids[] = {
+       { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
+       { /* end: all zeroes */                 },
+};
+
+/* hwbus_ops implemetation */
+
+static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self,
+                                    unsigned int addr,
+                                    void *dst, int count)
+{
+       return sdio_memcpy_fromio(self->func, dst, addr, count);
+}
+
+static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self,
+                                  unsigned int addr,
+                                  const void *src, int count)
+{
+       return sdio_memcpy_toio(self->func, addr, (void *)src, count);
+}
+
+static void cw1200_sdio_lock(struct hwbus_priv *self)
+{
+       sdio_claim_host(self->func);
+}
+
+static void cw1200_sdio_unlock(struct hwbus_priv *self)
+{
+       sdio_release_host(self->func);
+}
+
+static void cw1200_sdio_irq_handler(struct sdio_func *func)
+{
+       struct hwbus_priv *self = sdio_get_drvdata(func);
+
+       /* note:  sdio_host already claimed here. */
+       if (self->core)
+               cw1200_irq_handler(self->core);
+}
+
+static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
+{
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
+{
+       struct hwbus_priv *self = dev_id;
+
+       if (self->core) {
+               cw1200_sdio_lock(self);
+               cw1200_irq_handler(self->core);
+               cw1200_sdio_unlock(self);
+               return IRQ_HANDLED;
+       } else {
+               return IRQ_NONE;
+       }
+}
+
+static int cw1200_request_irq(struct hwbus_priv *self)
+{
+       int ret;
+       u8 cccr;
+
+       cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
+       if (WARN_ON(ret))
+               goto err;
+
+       /* Master interrupt enable ... */
+       cccr |= BIT(0);
+
+       /* ... for our function */
+       cccr |= BIT(self->func->num);
+
+       sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
+       if (WARN_ON(ret))
+               goto err;
+
+       ret = enable_irq_wake(self->pdata->irq);
+       if (WARN_ON(ret))
+               goto err;
+
+       /* Request the IRQ */
+       ret =  request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq,
+                                   cw1200_gpio_irq,
+                                   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                   "cw1200_wlan_irq", self);
+       if (WARN_ON(ret))
+               goto err;
+
+       return 0;
+
+err:
+       return ret;
+}
+
+static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self)
+{
+       int ret = 0;
+
+       pr_debug("SW IRQ subscribe\n");
+       sdio_claim_host(self->func);
+       if (self->pdata->irq)
+               ret = cw1200_request_irq(self);
+       else
+               ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
+
+       sdio_release_host(self->func);
+       return ret;
+}
+
+static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self)
+{
+       int ret = 0;
+
+       pr_debug("SW IRQ unsubscribe\n");
+
+       if (self->pdata->irq) {
+               disable_irq_wake(self->pdata->irq);
+               free_irq(self->pdata->irq, self);
+       } else {
+               sdio_claim_host(self->func);
+               ret = sdio_release_irq(self->func);
+               sdio_release_host(self->func);
+       }
+       return ret;
+}
+
+static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
+{
+       if (pdata->reset) {
+               gpio_set_value(pdata->reset, 0);
+               msleep(30); /* Min is 2 * CLK32K cycles */
+               gpio_free(pdata->reset);
+       }
+
+       if (pdata->power_ctrl)
+               pdata->power_ctrl(pdata, false);
+       if (pdata->clk_ctrl)
+               pdata->clk_ctrl(pdata, false);
+
+       return 0;
+}
+
+static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
+{
+       /* Ensure I/Os are pulled low */
+       if (pdata->reset) {
+               gpio_request(pdata->reset, "cw1200_wlan_reset");
+               gpio_direction_output(pdata->reset, 0);
+       }
+       if (pdata->powerup) {
+               gpio_request(pdata->powerup, "cw1200_wlan_powerup");
+               gpio_direction_output(pdata->powerup, 0);
+       }
+       if (pdata->reset || pdata->powerup)
+               msleep(10); /* Settle time? */
+
+       /* Enable 3v3 and 1v8 to hardware */
+       if (pdata->power_ctrl) {
+               if (pdata->power_ctrl(pdata, true)) {
+                       pr_err("power_ctrl() failed!\n");
+                       return -1;
+               }
+       }
+
+       /* Enable CLK32K */
+       if (pdata->clk_ctrl) {
+               if (pdata->clk_ctrl(pdata, true)) {
+                       pr_err("clk_ctrl() failed!\n");
+                       return -1;
+               }
+               msleep(10); /* Delay until clock is stable for 2 cycles */
+       }
+
+       /* Enable POWERUP signal */
+       if (pdata->powerup) {
+               gpio_set_value(pdata->powerup, 1);
+               msleep(250); /* or more..? */
+       }
+       /* Enable RSTn signal */
+       if (pdata->reset) {
+               gpio_set_value(pdata->reset, 1);
+               msleep(50); /* Or more..? */
+       }
+       return 0;
+}
+
+static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size)
+{
+       if (self->pdata->no_nptb)
+               size = round_up(size, SDIO_BLOCK_SIZE);
+       else
+               size = sdio_align_size(self->func, size);
+
+       return size;
+}
+
+static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend)
+{
+       int ret = 0;
+
+       if (self->pdata->irq)
+               ret = irq_set_irq_wake(self->pdata->irq, suspend);
+       return ret;
+}
+
+static struct hwbus_ops cw1200_sdio_hwbus_ops = {
+       .hwbus_memcpy_fromio    = cw1200_sdio_memcpy_fromio,
+       .hwbus_memcpy_toio      = cw1200_sdio_memcpy_toio,
+       .lock                   = cw1200_sdio_lock,
+       .unlock                 = cw1200_sdio_unlock,
+       .align_size             = cw1200_sdio_align_size,
+       .power_mgmt             = cw1200_sdio_pm,
+};
+
+/* Probe Function to be called by SDIO stack when device is discovered */
+static int cw1200_sdio_probe(struct sdio_func *func,
+                            const struct sdio_device_id *id)
+{
+       struct hwbus_priv *self;
+       int status;
+
+       pr_info("cw1200_wlan_sdio: Probe called\n");
+
+       /* We are only able to handle the wlan function */
+       if (func->num != 0x01)
+               return -ENODEV;
+
+       self = kzalloc(sizeof(*self), GFP_KERNEL);
+       if (!self) {
+               pr_err("Can't allocate SDIO hwbus_priv.\n");
+               return -ENOMEM;
+       }
+
+       func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+       self->pdata = global_plat_data; /* FIXME */
+       self->func = func;
+       sdio_set_drvdata(func, self);
+       sdio_claim_host(func);
+       sdio_enable_func(func);
+       sdio_release_host(func);
+
+       status = cw1200_sdio_irq_subscribe(self);
+
+       status = cw1200_core_probe(&cw1200_sdio_hwbus_ops,
+                                  self, &func->dev, &self->core,
+                                  self->pdata->ref_clk,
+                                  self->pdata->macaddr,
+                                  self->pdata->sdd_file,
+                                  self->pdata->have_5ghz);
+       if (status) {
+               cw1200_sdio_irq_unsubscribe(self);
+               sdio_claim_host(func);
+               sdio_disable_func(func);
+               sdio_release_host(func);
+               sdio_set_drvdata(func, NULL);
+               kfree(self);
+       }
+
+       return status;
+}
+
+/* Disconnect Function to be called by SDIO stack when
+ * device is disconnected
+ */
+static void cw1200_sdio_disconnect(struct sdio_func *func)
+{
+       struct hwbus_priv *self = sdio_get_drvdata(func);
+
+       if (self) {
+               cw1200_sdio_irq_unsubscribe(self);
+               if (self->core) {
+                       cw1200_core_release(self->core);
+                       self->core = NULL;
+               }
+               sdio_claim_host(func);
+               sdio_disable_func(func);
+               sdio_release_host(func);
+               sdio_set_drvdata(func, NULL);
+               kfree(self);
+       }
+}
+
+#ifdef CONFIG_PM
+static int cw1200_sdio_suspend(struct device *dev)
+{
+       int ret;
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       struct hwbus_priv *self = sdio_get_drvdata(func);
+
+       if (!cw1200_can_suspend(self->core))
+               return -EAGAIN;
+
+       /* Notify SDIO that CW1200 will remain powered during suspend */
+       ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+       if (ret)
+               pr_err("Error setting SDIO pm flags: %i\n", ret);
+
+       return ret;
+}
+
+static int cw1200_sdio_resume(struct device *dev)
+{
+       return 0;
+}
+
+static const struct dev_pm_ops cw1200_pm_ops = {
+       .suspend = cw1200_sdio_suspend,
+       .resume = cw1200_sdio_resume,
+};
+#endif
+
+static struct sdio_driver sdio_driver = {
+       .name           = "cw1200_wlan_sdio",
+       .id_table       = cw1200_sdio_ids,
+       .probe          = cw1200_sdio_probe,
+       .remove         = cw1200_sdio_disconnect,
+#ifdef CONFIG_PM
+       .drv = {
+               .pm = &cw1200_pm_ops,
+       }
+#endif
+};
+
+/* Init Module function -> Called by insmod */
+static int __init cw1200_sdio_init(void)
+{
+       const struct cw1200_platform_data_sdio *pdata;
+       int ret;
+
+       /* FIXME -- this won't support multiple devices */
+       pdata = global_plat_data;
+
+       if (cw1200_sdio_on(pdata)) {
+               ret = -1;
+               goto err;
+       }
+
+       ret = sdio_register_driver(&sdio_driver);
+       if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       cw1200_sdio_off(pdata);
+       return ret;
+}
+
+/* Called at Driver Unloading */
+static void __exit cw1200_sdio_exit(void)
+{
+       const struct cw1200_platform_data_sdio *pdata;
+
+       /* FIXME -- this won't support multiple devices */
+       pdata = global_plat_data;
+       sdio_unregister_driver(&sdio_driver);
+       cw1200_sdio_off(pdata);
+}
+
+
+module_init(cw1200_sdio_init);
+module_exit(cw1200_sdio_exit);
diff --git a/drivers/net/wireless/st/cw1200/cw1200_spi.c b/drivers/net/wireless/st/cw1200/cw1200_spi.c
new file mode 100644 (file)
index 0000000..a740083
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Mac80211 SPI driver for ST-Ericsson CW1200 device
+ *
+ * Copyright (c) 2011, Sagrad Inc.
+ * Author:  Solomon Peachy <speachy@sagrad.com>
+ *
+ * Based on cw1200_sdio.c
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <net/mac80211.h>
+
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+
+#include "cw1200.h"
+#include "hwbus.h"
+#include <linux/platform_data/net-cw1200.h>
+#include "hwio.h"
+
+MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
+MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:cw1200_wlan_spi");
+
+/* #define SPI_DEBUG */
+
+struct hwbus_priv {
+       struct spi_device       *func;
+       struct cw1200_common    *core;
+       const struct cw1200_platform_data_spi *pdata;
+       spinlock_t              lock; /* Serialize all bus operations */
+       wait_queue_head_t       wq;
+       int claimed;
+};
+
+#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
+#define SET_WRITE 0x7FFF /* usage: and operation */
+#define SET_READ 0x8000  /* usage: or operation */
+
+/* Notes on byte ordering:
+   LE:  B0 B1 B2 B3
+   BE:  B3 B2 B1 B0
+
+   Hardware expects 32-bit data to be written as 16-bit BE words:
+
+   B1 B0 B3 B2
+*/
+
+static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self,
+                                    unsigned int addr,
+                                    void *dst, int count)
+{
+       int ret, i;
+       u16 regaddr;
+       struct spi_message      m;
+
+       struct spi_transfer     t_addr = {
+               .tx_buf         = &regaddr,
+               .len            = sizeof(regaddr),
+       };
+       struct spi_transfer     t_msg = {
+               .rx_buf         = dst,
+               .len            = count,
+       };
+
+       regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
+       regaddr |= SET_READ;
+       regaddr |= (count>>1);
+
+#ifdef SPI_DEBUG
+       pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr);
+#endif
+
+       /* Header is LE16 */
+       regaddr = cpu_to_le16(regaddr);
+
+       /* We have to byteswap if the SPI bus is limited to 8b operation
+          or we are running on a Big Endian system
+       */
+#if defined(__LITTLE_ENDIAN)
+       if (self->func->bits_per_word == 8)
+#endif
+               regaddr = swab16(regaddr);
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t_addr, &m);
+       spi_message_add_tail(&t_msg, &m);
+       ret = spi_sync(self->func, &m);
+
+#ifdef SPI_DEBUG
+       pr_info("READ : ");
+       for (i = 0; i < t_addr.len; i++)
+               printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
+       printk(" : ");
+       for (i = 0; i < t_msg.len; i++)
+               printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
+       printk("\n");
+#endif
+
+       /* We have to byteswap if the SPI bus is limited to 8b operation
+          or we are running on a Big Endian system
+       */
+#if defined(__LITTLE_ENDIAN)
+       if (self->func->bits_per_word == 8)
+#endif
+       {
+               uint16_t *buf = (uint16_t *)dst;
+               for (i = 0; i < ((count + 1) >> 1); i++)
+                       buf[i] = swab16(buf[i]);
+       }
+
+       return ret;
+}
+
+static int cw1200_spi_memcpy_toio(struct hwbus_priv *self,
+                                  unsigned int addr,
+                                  const void *src, int count)
+{
+       int rval, i;
+       u16 regaddr;
+       struct spi_transfer     t_addr = {
+               .tx_buf         = &regaddr,
+               .len            = sizeof(regaddr),
+       };
+       struct spi_transfer     t_msg = {
+               .tx_buf         = src,
+               .len            = count,
+       };
+       struct spi_message      m;
+
+       regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
+       regaddr &= SET_WRITE;
+       regaddr |= (count>>1);
+
+#ifdef SPI_DEBUG
+       pr_info("WRITE: %04d  to  0x%02x (%04x)\n", count, addr, regaddr);
+#endif
+
+       /* Header is LE16 */
+       regaddr = cpu_to_le16(regaddr);
+
+       /* We have to byteswap if the SPI bus is limited to 8b operation
+          or we are running on a Big Endian system
+       */
+#if defined(__LITTLE_ENDIAN)
+       if (self->func->bits_per_word == 8)
+#endif
+       {
+               uint16_t *buf = (uint16_t *)src;
+               regaddr = swab16(regaddr);
+               for (i = 0; i < ((count + 1) >> 1); i++)
+                       buf[i] = swab16(buf[i]);
+       }
+
+#ifdef SPI_DEBUG
+       pr_info("WRITE: ");
+       for (i = 0; i < t_addr.len; i++)
+               printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
+       printk(" : ");
+       for (i = 0; i < t_msg.len; i++)
+               printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
+       printk("\n");
+#endif
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t_addr, &m);
+       spi_message_add_tail(&t_msg, &m);
+       rval = spi_sync(self->func, &m);
+
+#ifdef SPI_DEBUG
+       pr_info("WROTE: %d\n", m.actual_length);
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+       /* We have to byteswap if the SPI bus is limited to 8b operation */
+       if (self->func->bits_per_word == 8)
+#endif
+       {
+               uint16_t *buf = (uint16_t *)src;
+               for (i = 0; i < ((count + 1) >> 1); i++)
+                       buf[i] = swab16(buf[i]);
+       }
+       return rval;
+}
+
+static void cw1200_spi_lock(struct hwbus_priv *self)
+{
+       unsigned long flags;
+
+       DECLARE_WAITQUEUE(wait, current);
+
+       might_sleep();
+
+       add_wait_queue(&self->wq, &wait);
+       spin_lock_irqsave(&self->lock, flags);
+       while (1) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               if (!self->claimed)
+                       break;
+               spin_unlock_irqrestore(&self->lock, flags);
+               schedule();
+               spin_lock_irqsave(&self->lock, flags);
+       }
+       set_current_state(TASK_RUNNING);
+       self->claimed = 1;
+       spin_unlock_irqrestore(&self->lock, flags);
+       remove_wait_queue(&self->wq, &wait);
+
+       return;
+}
+
+static void cw1200_spi_unlock(struct hwbus_priv *self)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&self->lock, flags);
+       self->claimed = 0;
+       spin_unlock_irqrestore(&self->lock, flags);
+       wake_up(&self->wq);
+
+       return;
+}
+
+static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
+{
+       struct hwbus_priv *self = dev_id;
+
+       if (self->core) {
+               cw1200_spi_lock(self);
+               cw1200_irq_handler(self->core);
+               cw1200_spi_unlock(self);
+               return IRQ_HANDLED;
+       } else {
+               return IRQ_NONE;
+       }
+}
+
+static int cw1200_spi_irq_subscribe(struct hwbus_priv *self)
+{
+       int ret;
+
+       pr_debug("SW IRQ subscribe\n");
+
+       ret = request_threaded_irq(self->func->irq, NULL,
+                                  cw1200_spi_irq_handler,
+                                  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                  "cw1200_wlan_irq", self);
+       if (WARN_ON(ret < 0))
+               goto exit;
+
+       ret = enable_irq_wake(self->func->irq);
+       if (WARN_ON(ret))
+               goto free_irq;
+
+       return 0;
+
+free_irq:
+       free_irq(self->func->irq, self);
+exit:
+       return ret;
+}
+
+static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
+{
+       int ret = 0;
+
+       pr_debug("SW IRQ unsubscribe\n");
+       disable_irq_wake(self->func->irq);
+       free_irq(self->func->irq, self);
+
+       return ret;
+}
+
+static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
+{
+       if (pdata->reset) {
+               gpio_set_value(pdata->reset, 0);
+               msleep(30); /* Min is 2 * CLK32K cycles */
+               gpio_free(pdata->reset);
+       }
+
+       if (pdata->power_ctrl)
+               pdata->power_ctrl(pdata, false);
+       if (pdata->clk_ctrl)
+               pdata->clk_ctrl(pdata, false);
+
+       return 0;
+}
+
+static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
+{
+       /* Ensure I/Os are pulled low */
+       if (pdata->reset) {
+               gpio_request(pdata->reset, "cw1200_wlan_reset");
+               gpio_direction_output(pdata->reset, 0);
+       }
+       if (pdata->powerup) {
+               gpio_request(pdata->powerup, "cw1200_wlan_powerup");
+               gpio_direction_output(pdata->powerup, 0);
+       }
+       if (pdata->reset || pdata->powerup)
+               msleep(10); /* Settle time? */
+
+       /* Enable 3v3 and 1v8 to hardware */
+       if (pdata->power_ctrl) {
+               if (pdata->power_ctrl(pdata, true)) {
+                       pr_err("power_ctrl() failed!\n");
+                       return -1;
+               }
+       }
+
+       /* Enable CLK32K */
+       if (pdata->clk_ctrl) {
+               if (pdata->clk_ctrl(pdata, true)) {
+                       pr_err("clk_ctrl() failed!\n");
+                       return -1;
+               }
+               msleep(10); /* Delay until clock is stable for 2 cycles */
+       }
+
+       /* Enable POWERUP signal */
+       if (pdata->powerup) {
+               gpio_set_value(pdata->powerup, 1);
+               msleep(250); /* or more..? */
+       }
+       /* Enable RSTn signal */
+       if (pdata->reset) {
+               gpio_set_value(pdata->reset, 1);
+               msleep(50); /* Or more..? */
+       }
+       return 0;
+}
+
+static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size)
+{
+       return size & 1 ? size + 1 : size;
+}
+
+static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend)
+{
+       return irq_set_irq_wake(self->func->irq, suspend);
+}
+
+static struct hwbus_ops cw1200_spi_hwbus_ops = {
+       .hwbus_memcpy_fromio    = cw1200_spi_memcpy_fromio,
+       .hwbus_memcpy_toio      = cw1200_spi_memcpy_toio,
+       .lock                   = cw1200_spi_lock,
+       .unlock                 = cw1200_spi_unlock,
+       .align_size             = cw1200_spi_align_size,
+       .power_mgmt             = cw1200_spi_pm,
+};
+
+/* Probe Function to be called by SPI stack when device is discovered */
+static int cw1200_spi_probe(struct spi_device *func)
+{
+       const struct cw1200_platform_data_spi *plat_data =
+               dev_get_platdata(&func->dev);
+       struct hwbus_priv *self;
+       int status;
+
+       /* Sanity check speed */
+       if (func->max_speed_hz > 52000000)
+               func->max_speed_hz = 52000000;
+       if (func->max_speed_hz < 1000000)
+               func->max_speed_hz = 1000000;
+
+       /* Fix up transfer size */
+       if (plat_data->spi_bits_per_word)
+               func->bits_per_word = plat_data->spi_bits_per_word;
+       if (!func->bits_per_word)
+               func->bits_per_word = 16;
+
+       /* And finally.. */
+       func->mode = SPI_MODE_0;
+
+       pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
+               func->chip_select, func->mode, func->bits_per_word,
+               func->max_speed_hz);
+
+       if (cw1200_spi_on(plat_data)) {
+               pr_err("spi_on() failed!\n");
+               return -1;
+       }
+
+       if (spi_setup(func)) {
+               pr_err("spi_setup() failed!\n");
+               return -1;
+       }
+
+       self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL);
+       if (!self) {
+               pr_err("Can't allocate SPI hwbus_priv.");
+               return -ENOMEM;
+       }
+
+       self->pdata = plat_data;
+       self->func = func;
+       spin_lock_init(&self->lock);
+
+       spi_set_drvdata(func, self);
+
+       init_waitqueue_head(&self->wq);
+
+       status = cw1200_spi_irq_subscribe(self);
+
+       status = cw1200_core_probe(&cw1200_spi_hwbus_ops,
+                                  self, &func->dev, &self->core,
+                                  self->pdata->ref_clk,
+                                  self->pdata->macaddr,
+                                  self->pdata->sdd_file,
+                                  self->pdata->have_5ghz);
+
+       if (status) {
+               cw1200_spi_irq_unsubscribe(self);
+               cw1200_spi_off(plat_data);
+       }
+
+       return status;
+}
+
+/* Disconnect Function to be called by SPI stack when device is disconnected */
+static int cw1200_spi_disconnect(struct spi_device *func)
+{
+       struct hwbus_priv *self = spi_get_drvdata(func);
+
+       if (self) {
+               cw1200_spi_irq_unsubscribe(self);
+               if (self->core) {
+                       cw1200_core_release(self->core);
+                       self->core = NULL;
+               }
+       }
+       cw1200_spi_off(dev_get_platdata(&func->dev));
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int cw1200_spi_suspend(struct device *dev)
+{
+       struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
+
+       if (!cw1200_can_suspend(self->core))
+               return -EAGAIN;
+
+       /* XXX notify host that we have to keep CW1200 powered on? */
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL);
+
+#endif
+
+static struct spi_driver spi_driver = {
+       .probe          = cw1200_spi_probe,
+       .remove         = cw1200_spi_disconnect,
+       .driver = {
+               .name           = "cw1200_wlan_spi",
+#ifdef CONFIG_PM
+               .pm             = &cw1200_pm_ops,
+#endif
+       },
+};
+
+module_spi_driver(spi_driver);
diff --git a/drivers/net/wireless/st/cw1200/debug.c b/drivers/net/wireless/st/cw1200/debug.c
new file mode 100644 (file)
index 0000000..34f97c3
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ * DebugFS code
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include "cw1200.h"
+#include "debug.h"
+#include "fwio.h"
+
+/* join_status */
+static const char * const cw1200_debug_join_status[] = {
+       "passive",
+       "monitor",
+       "station (joining)",
+       "station (not authenticated yet)",
+       "station",
+       "adhoc",
+       "access point",
+};
+
+/* WSM_JOIN_PREAMBLE_... */
+static const char * const cw1200_debug_preamble[] = {
+       "long",
+       "short",
+       "long on 1 and 2 Mbps",
+};
+
+
+static const char * const cw1200_debug_link_id[] = {
+       "OFF",
+       "REQ",
+       "SOFT",
+       "HARD",
+       "RESET",
+       "RESET_REMAP",
+};
+
+static const char *cw1200_debug_mode(int mode)
+{
+       switch (mode) {
+       case NL80211_IFTYPE_UNSPECIFIED:
+               return "unspecified";
+       case NL80211_IFTYPE_MONITOR:
+               return "monitor";
+       case NL80211_IFTYPE_STATION:
+               return "station";
+       case NL80211_IFTYPE_ADHOC:
+               return "adhoc";
+       case NL80211_IFTYPE_MESH_POINT:
+               return "mesh point";
+       case NL80211_IFTYPE_AP:
+               return "access point";
+       case NL80211_IFTYPE_P2P_CLIENT:
+               return "p2p client";
+       case NL80211_IFTYPE_P2P_GO:
+               return "p2p go";
+       default:
+               return "unsupported";
+       }
+}
+
+static void cw1200_queue_status_show(struct seq_file *seq,
+                                    struct cw1200_queue *q)
+{
+       int i;
+       seq_printf(seq, "Queue       %d:\n", q->queue_id);
+       seq_printf(seq, "  capacity: %zu\n", q->capacity);
+       seq_printf(seq, "  queued:   %zu\n", q->num_queued);
+       seq_printf(seq, "  pending:  %zu\n", q->num_pending);
+       seq_printf(seq, "  sent:     %zu\n", q->num_sent);
+       seq_printf(seq, "  locked:   %s\n", q->tx_locked_cnt ? "yes" : "no");
+       seq_printf(seq, "  overfull: %s\n", q->overfull ? "yes" : "no");
+       seq_puts(seq,   "  link map: 0-> ");
+       for (i = 0; i < q->stats->map_capacity; ++i)
+               seq_printf(seq, "%.2d ", q->link_map_cache[i]);
+       seq_printf(seq, "<-%zu\n", q->stats->map_capacity);
+}
+
+static void cw1200_debug_print_map(struct seq_file *seq,
+                                  struct cw1200_common *priv,
+                                  const char *label,
+                                  u32 map)
+{
+       int i;
+       seq_printf(seq, "%s0-> ", label);
+       for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i)
+               seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
+       seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1);
+}
+
+static int cw1200_status_show(struct seq_file *seq, void *v)
+{
+       int i;
+       struct list_head *item;
+       struct cw1200_common *priv = seq->private;
+       struct cw1200_debug_priv *d = priv->debug;
+
+       seq_puts(seq,   "CW1200 Wireless LAN driver status\n");
+       seq_printf(seq, "Hardware:   %d.%d\n",
+                  priv->wsm_caps.hw_id,
+                  priv->wsm_caps.hw_subid);
+       seq_printf(seq, "Firmware:   %s %d.%d\n",
+                  cw1200_fw_types[priv->wsm_caps.fw_type],
+                  priv->wsm_caps.fw_ver,
+                  priv->wsm_caps.fw_build);
+       seq_printf(seq, "FW API:     %d\n",
+                  priv->wsm_caps.fw_api);
+       seq_printf(seq, "FW caps:    0x%.4X\n",
+                  priv->wsm_caps.fw_cap);
+       seq_printf(seq, "FW label:  '%s'\n",
+                  priv->wsm_caps.fw_label);
+       seq_printf(seq, "Mode:       %s%s\n",
+                  cw1200_debug_mode(priv->mode),
+                  priv->listening ? " (listening)" : "");
+       seq_printf(seq, "Join state: %s\n",
+                  cw1200_debug_join_status[priv->join_status]);
+       if (priv->channel)
+               seq_printf(seq, "Channel:    %d%s\n",
+                          priv->channel->hw_value,
+                          priv->channel_switch_in_progress ?
+                          " (switching)" : "");
+       if (priv->rx_filter.promiscuous)
+               seq_puts(seq,   "Filter:     promisc\n");
+       else if (priv->rx_filter.fcs)
+               seq_puts(seq,   "Filter:     fcs\n");
+       if (priv->rx_filter.bssid)
+               seq_puts(seq,   "Filter:     bssid\n");
+       if (!priv->disable_beacon_filter)
+               seq_puts(seq,   "Filter:     beacons\n");
+
+       if (priv->enable_beacon ||
+           priv->mode == NL80211_IFTYPE_AP ||
+           priv->mode == NL80211_IFTYPE_ADHOC ||
+           priv->mode == NL80211_IFTYPE_MESH_POINT ||
+           priv->mode == NL80211_IFTYPE_P2P_GO)
+               seq_printf(seq, "Beaconing:  %s\n",
+                          priv->enable_beacon ?
+                          "enabled" : "disabled");
+
+       for (i = 0; i < 4; ++i)
+               seq_printf(seq, "EDCA(%d):    %d, %d, %d, %d, %d\n", i,
+                          priv->edca.params[i].cwmin,
+                          priv->edca.params[i].cwmax,
+                          priv->edca.params[i].aifns,
+                          priv->edca.params[i].txop_limit,
+                          priv->edca.params[i].max_rx_lifetime);
+
+       if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+               static const char *pm_mode = "unknown";
+               switch (priv->powersave_mode.mode) {
+               case WSM_PSM_ACTIVE:
+                       pm_mode = "off";
+                       break;
+               case WSM_PSM_PS:
+                       pm_mode = "on";
+                       break;
+               case WSM_PSM_FAST_PS:
+                       pm_mode = "dynamic";
+                       break;
+               }
+               seq_printf(seq, "Preamble:   %s\n",
+                          cw1200_debug_preamble[priv->association_mode.preamble]);
+               seq_printf(seq, "AMPDU spcn: %d\n",
+                          priv->association_mode.mpdu_start_spacing);
+               seq_printf(seq, "Basic rate: 0x%.8X\n",
+                          le32_to_cpu(priv->association_mode.basic_rate_set));
+               seq_printf(seq, "Bss lost:   %d beacons\n",
+                          priv->bss_params.beacon_lost_count);
+               seq_printf(seq, "AID:        %d\n",
+                          priv->bss_params.aid);
+               seq_printf(seq, "Rates:      0x%.8X\n",
+                          priv->bss_params.operational_rate_set);
+               seq_printf(seq, "Powersave:  %s\n", pm_mode);
+       }
+       seq_printf(seq, "HT:         %s\n",
+                  cw1200_is_ht(&priv->ht_info) ? "on" : "off");
+       if (cw1200_is_ht(&priv->ht_info)) {
+               seq_printf(seq, "Greenfield: %s\n",
+                          cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no");
+               seq_printf(seq, "AMPDU dens: %d\n",
+                          cw1200_ht_ampdu_density(&priv->ht_info));
+       }
+       seq_printf(seq, "RSSI thold: %d\n",
+                  priv->cqm_rssi_thold);
+       seq_printf(seq, "RSSI hyst:  %d\n",
+                  priv->cqm_rssi_hyst);
+       seq_printf(seq, "Long retr:  %d\n",
+                  priv->long_frame_max_tx_count);
+       seq_printf(seq, "Short retr: %d\n",
+                  priv->short_frame_max_tx_count);
+       spin_lock_bh(&priv->tx_policy_cache.lock);
+       i = 0;
+       list_for_each(item, &priv->tx_policy_cache.used)
+               ++i;
+       spin_unlock_bh(&priv->tx_policy_cache.lock);
+       seq_printf(seq, "RC in use:  %d\n", i);
+
+       seq_puts(seq, "\n");
+       for (i = 0; i < 4; ++i) {
+               cw1200_queue_status_show(seq, &priv->tx_queue[i]);
+               seq_puts(seq, "\n");
+       }
+
+       cw1200_debug_print_map(seq, priv, "Link map:   ",
+                              priv->link_id_map);
+       cw1200_debug_print_map(seq, priv, "Asleep map: ",
+                              priv->sta_asleep_mask);
+       cw1200_debug_print_map(seq, priv, "PSPOLL map: ",
+                              priv->pspoll_mask);
+
+       seq_puts(seq, "\n");
+
+       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+               if (priv->link_id_db[i].status) {
+                       seq_printf(seq, "Link %d:     %s, %pM\n",
+                                  i + 1,
+                                  cw1200_debug_link_id[priv->link_id_db[i].status],
+                                  priv->link_id_db[i].mac);
+               }
+       }
+
+       seq_puts(seq, "\n");
+
+       seq_printf(seq, "BH status:  %s\n",
+                  atomic_read(&priv->bh_term) ? "terminated" : "alive");
+       seq_printf(seq, "Pending RX: %d\n",
+                  atomic_read(&priv->bh_rx));
+       seq_printf(seq, "Pending TX: %d\n",
+                  atomic_read(&priv->bh_tx));
+       if (priv->bh_error)
+               seq_printf(seq, "BH errcode: %d\n",
+                          priv->bh_error);
+       seq_printf(seq, "TX bufs:    %d x %d bytes\n",
+                  priv->wsm_caps.input_buffers,
+                  priv->wsm_caps.input_buffer_size);
+       seq_printf(seq, "Used bufs:  %d\n",
+                  priv->hw_bufs_used);
+       seq_printf(seq, "Powermgmt:  %s\n",
+                  priv->powersave_enabled ? "on" : "off");
+       seq_printf(seq, "Device:     %s\n",
+                  priv->device_can_sleep ? "asleep" : "awake");
+
+       spin_lock(&priv->wsm_cmd.lock);
+       seq_printf(seq, "WSM status: %s\n",
+                  priv->wsm_cmd.done ? "idle" : "active");
+       seq_printf(seq, "WSM cmd:    0x%.4X (%td bytes)\n",
+                  priv->wsm_cmd.cmd, priv->wsm_cmd.len);
+       seq_printf(seq, "WSM retval: %d\n",
+                  priv->wsm_cmd.ret);
+       spin_unlock(&priv->wsm_cmd.lock);
+
+       seq_printf(seq, "Datapath:   %s\n",
+                  atomic_read(&priv->tx_lock) ? "locked" : "unlocked");
+       if (atomic_read(&priv->tx_lock))
+               seq_printf(seq, "TXlock cnt: %d\n",
+                          atomic_read(&priv->tx_lock));
+
+       seq_printf(seq, "TXed:       %d\n",
+                  d->tx);
+       seq_printf(seq, "AGG TXed:   %d\n",
+                  d->tx_agg);
+       seq_printf(seq, "MULTI TXed: %d (%d)\n",
+                  d->tx_multi, d->tx_multi_frames);
+       seq_printf(seq, "RXed:       %d\n",
+                  d->rx);
+       seq_printf(seq, "AGG RXed:   %d\n",
+                  d->rx_agg);
+       seq_printf(seq, "TX miss:    %d\n",
+                  d->tx_cache_miss);
+       seq_printf(seq, "TX align:   %d\n",
+                  d->tx_align);
+       seq_printf(seq, "TX burst:   %d\n",
+                  d->tx_burst);
+       seq_printf(seq, "TX TTL:     %d\n",
+                  d->tx_ttl);
+       seq_printf(seq, "Scan:       %s\n",
+                  atomic_read(&priv->scan.in_progress) ? "active" : "idle");
+
+       return 0;
+}
+
+static int cw1200_status_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, &cw1200_status_show,
+               inode->i_private);
+}
+
+static const struct file_operations fops_status = {
+       .open = cw1200_status_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int cw1200_counters_show(struct seq_file *seq, void *v)
+{
+       int ret;
+       struct cw1200_common *priv = seq->private;
+       struct wsm_mib_counters_table counters;
+
+       ret = wsm_get_counters_table(priv, &counters);
+       if (ret)
+               return ret;
+
+#define PUT_COUNTER(tab, name) \
+       seq_printf(seq, "%s:" tab "%d\n", #name, \
+               __le32_to_cpu(counters.name))
+
+       PUT_COUNTER("\t\t", plcp_errors);
+       PUT_COUNTER("\t\t", fcs_errors);
+       PUT_COUNTER("\t\t", tx_packets);
+       PUT_COUNTER("\t\t", rx_packets);
+       PUT_COUNTER("\t\t", rx_packet_errors);
+       PUT_COUNTER("\t",   rx_decryption_failures);
+       PUT_COUNTER("\t\t", rx_mic_failures);
+       PUT_COUNTER("\t",   rx_no_key_failures);
+       PUT_COUNTER("\t",   tx_multicast_frames);
+       PUT_COUNTER("\t",   tx_frames_success);
+       PUT_COUNTER("\t",   tx_frame_failures);
+       PUT_COUNTER("\t",   tx_frames_retried);
+       PUT_COUNTER("\t",   tx_frames_multi_retried);
+       PUT_COUNTER("\t",   rx_frame_duplicates);
+       PUT_COUNTER("\t\t", rts_success);
+       PUT_COUNTER("\t\t", rts_failures);
+       PUT_COUNTER("\t\t", ack_failures);
+       PUT_COUNTER("\t",   rx_multicast_frames);
+       PUT_COUNTER("\t",   rx_frames_success);
+       PUT_COUNTER("\t",   rx_cmac_icv_errors);
+       PUT_COUNTER("\t\t", rx_cmac_replays);
+       PUT_COUNTER("\t",   rx_mgmt_ccmp_replays);
+
+#undef PUT_COUNTER
+
+       return 0;
+}
+
+static int cw1200_counters_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, &cw1200_counters_show,
+               inode->i_private);
+}
+
+static const struct file_operations fops_counters = {
+       .open = cw1200_counters_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static ssize_t cw1200_wsm_dumps(struct file *file,
+       const char __user *user_buf, size_t count, loff_t *ppos)
+{
+       struct cw1200_common *priv = file->private_data;
+       char buf[1];
+
+       if (!count)
+               return -EINVAL;
+       if (copy_from_user(buf, user_buf, 1))
+               return -EFAULT;
+
+       if (buf[0] == '1')
+               priv->wsm_enable_wsm_dumps = 1;
+       else
+               priv->wsm_enable_wsm_dumps = 0;
+
+       return count;
+}
+
+static const struct file_operations fops_wsm_dumps = {
+       .open = simple_open,
+       .write = cw1200_wsm_dumps,
+       .llseek = default_llseek,
+};
+
+int cw1200_debug_init(struct cw1200_common *priv)
+{
+       int ret = -ENOMEM;
+       struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv),
+                       GFP_KERNEL);
+       priv->debug = d;
+       if (!d)
+               return ret;
+
+       d->debugfs_phy = debugfs_create_dir("cw1200",
+                                           priv->hw->wiphy->debugfsdir);
+       if (!d->debugfs_phy)
+               goto err;
+
+       if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+                                priv, &fops_status))
+               goto err;
+
+       if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
+                                priv, &fops_counters))
+               goto err;
+
+       if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
+                                priv, &fops_wsm_dumps))
+               goto err;
+
+       return 0;
+
+err:
+       priv->debug = NULL;
+       debugfs_remove_recursive(d->debugfs_phy);
+       kfree(d);
+       return ret;
+}
+
+void cw1200_debug_release(struct cw1200_common *priv)
+{
+       struct cw1200_debug_priv *d = priv->debug;
+       if (d) {
+               debugfs_remove_recursive(d->debugfs_phy);
+               priv->debug = NULL;
+               kfree(d);
+       }
+}
diff --git a/drivers/net/wireless/st/cw1200/debug.h b/drivers/net/wireless/st/cw1200/debug.h
new file mode 100644 (file)
index 0000000..b525aba
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * DebugFS code for ST-Ericsson CW1200 mac80211 driver
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_DEBUG_H_INCLUDED
+#define CW1200_DEBUG_H_INCLUDED
+
+struct cw1200_debug_priv {
+       struct dentry *debugfs_phy;
+       int tx;
+       int tx_agg;
+       int rx;
+       int rx_agg;
+       int tx_multi;
+       int tx_multi_frames;
+       int tx_cache_miss;
+       int tx_align;
+       int tx_ttl;
+       int tx_burst;
+       int ba_cnt;
+       int ba_acc;
+       int ba_cnt_rx;
+       int ba_acc_rx;
+};
+
+int cw1200_debug_init(struct cw1200_common *priv);
+void cw1200_debug_release(struct cw1200_common *priv);
+
+static inline void cw1200_debug_txed(struct cw1200_common *priv)
+{
+       ++priv->debug->tx;
+}
+
+static inline void cw1200_debug_txed_agg(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_agg;
+}
+
+static inline void cw1200_debug_txed_multi(struct cw1200_common *priv,
+                                          int count)
+{
+       ++priv->debug->tx_multi;
+       priv->debug->tx_multi_frames += count;
+}
+
+static inline void cw1200_debug_rxed(struct cw1200_common *priv)
+{
+       ++priv->debug->rx;
+}
+
+static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv)
+{
+       ++priv->debug->rx_agg;
+}
+
+static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_cache_miss;
+}
+
+static inline void cw1200_debug_tx_align(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_align;
+}
+
+static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_ttl;
+}
+
+static inline void cw1200_debug_tx_burst(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_burst;
+}
+
+static inline void cw1200_debug_ba(struct cw1200_common *priv,
+                                  int ba_cnt, int ba_acc,
+                                  int ba_cnt_rx, int ba_acc_rx)
+{
+       priv->debug->ba_cnt = ba_cnt;
+       priv->debug->ba_acc = ba_acc;
+       priv->debug->ba_cnt_rx = ba_cnt_rx;
+       priv->debug->ba_acc_rx = ba_acc_rx;
+}
+
+#endif /* CW1200_DEBUG_H_INCLUDED */
diff --git a/drivers/net/wireless/st/cw1200/fwio.c b/drivers/net/wireless/st/cw1200/fwio.c
new file mode 100644 (file)
index 0000000..30e7646
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "cw1200.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "hwbus.h"
+#include "bh.h"
+
+static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision)
+{
+       int hw_type = -1;
+       u32 silicon_type = (config_reg_val >> 24) & 0x7;
+       u32 silicon_vers = (config_reg_val >> 31) & 0x1;
+
+       switch (silicon_type) {
+       case 0x00:
+               *major_revision = 1;
+               hw_type = HIF_9000_SILICON_VERSATILE;
+               break;
+       case 0x01:
+       case 0x02: /* CW1x00 */
+       case 0x04: /* CW1x60 */
+               *major_revision = silicon_type;
+               if (silicon_vers)
+                       hw_type = HIF_8601_VERSATILE;
+               else
+                       hw_type = HIF_8601_SILICON;
+               break;
+       default:
+               break;
+       }
+
+       return hw_type;
+}
+
+static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
+{
+       int ret, block, num_blocks;
+       unsigned i;
+       u32 val32;
+       u32 put = 0, get = 0;
+       u8 *buf = NULL;
+       const char *fw_path;
+       const struct firmware *firmware = NULL;
+
+       /* Macroses are local. */
+#define APB_WRITE(reg, val) \
+       do { \
+               ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
+               if (ret < 0) \
+                       goto exit; \
+       } while (0)
+#define APB_WRITE2(reg, val) \
+       do { \
+               ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
+               if (ret < 0) \
+                       goto free_buffer; \
+       } while (0)
+#define APB_READ(reg, val) \
+       do { \
+               ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
+               if (ret < 0) \
+                       goto free_buffer; \
+       } while (0)
+#define REG_WRITE(reg, val) \
+       do { \
+               ret = cw1200_reg_write_32(priv, (reg), (val)); \
+               if (ret < 0) \
+                       goto exit; \
+       } while (0)
+#define REG_READ(reg, val) \
+       do { \
+               ret = cw1200_reg_read_32(priv, (reg), &(val)); \
+               if (ret < 0) \
+                       goto exit; \
+       } while (0)
+
+       switch (priv->hw_revision) {
+       case CW1200_HW_REV_CUT10:
+               fw_path = FIRMWARE_CUT10;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_10;
+               break;
+       case CW1200_HW_REV_CUT11:
+               fw_path = FIRMWARE_CUT11;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_11;
+               break;
+       case CW1200_HW_REV_CUT20:
+               fw_path = FIRMWARE_CUT20;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_20;
+               break;
+       case CW1200_HW_REV_CUT22:
+               fw_path = FIRMWARE_CUT22;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_22;
+               break;
+       case CW1X60_HW_REV:
+               fw_path = FIRMWARE_CW1X60;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_CW1X60;
+               break;
+       default:
+               pr_err("Invalid silicon revision %d.\n", priv->hw_revision);
+               return -EINVAL;
+       }
+
+       /* Initialize common registers */
+       APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE);
+       APB_WRITE(DOWNLOAD_PUT_REG, 0);
+       APB_WRITE(DOWNLOAD_GET_REG, 0);
+       APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING);
+       APB_WRITE(DOWNLOAD_FLAGS_REG, 0);
+
+       /* Write the NOP Instruction */
+       REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000);
+       REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE);
+
+       /* Release CPU from RESET */
+       REG_READ(ST90TDS_CONFIG_REG_ID, val32);
+       val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT;
+       REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
+
+       /* Enable Clock */
+       val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT;
+       REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
+
+       /* Load a firmware file */
+       ret = request_firmware(&firmware, fw_path, priv->pdev);
+       if (ret) {
+               pr_err("Can't load firmware file %s.\n", fw_path);
+               goto exit;
+       }
+
+       buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+       if (!buf) {
+               pr_err("Can't allocate firmware load buffer.\n");
+               ret = -ENOMEM;
+               goto firmware_release;
+       }
+
+       /* Check if the bootloader is ready */
+       for (i = 0; i < 100; i += 1 + i / 2) {
+               APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32);
+               if (val32 == DOWNLOAD_I_AM_HERE)
+                       break;
+               mdelay(i);
+       } /* End of for loop */
+
+       if (val32 != DOWNLOAD_I_AM_HERE) {
+               pr_err("Bootloader is not ready.\n");
+               ret = -ETIMEDOUT;
+               goto free_buffer;
+       }
+
+       /* Calculcate number of download blocks */
+       num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1;
+
+       /* Updating the length in Download Ctrl Area */
+       val32 = firmware->size; /* Explicit cast from size_t to u32 */
+       APB_WRITE2(DOWNLOAD_IMAGE_SIZE_REG, val32);
+
+       /* Firmware downloading loop */
+       for (block = 0; block < num_blocks; block++) {
+               size_t tx_size;
+               size_t block_size;
+
+               /* check the download status */
+               APB_READ(DOWNLOAD_STATUS_REG, val32);
+               if (val32 != DOWNLOAD_PENDING) {
+                       pr_err("Bootloader reported error %d.\n", val32);
+                       ret = -EIO;
+                       goto free_buffer;
+               }
+
+               /* loop until put - get <= 24K */
+               for (i = 0; i < 100; i++) {
+                       APB_READ(DOWNLOAD_GET_REG, get);
+                       if ((put - get) <=
+                           (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE))
+                               break;
+                       mdelay(i);
+               }
+
+               if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
+                       pr_err("Timeout waiting for FIFO.\n");
+                       ret = -ETIMEDOUT;
+                       goto free_buffer;
+               }
+
+               /* calculate the block size */
+               tx_size = block_size = min_t(size_t, firmware->size - put,
+                                       DOWNLOAD_BLOCK_SIZE);
+
+               memcpy(buf, &firmware->data[put], block_size);
+               if (block_size < DOWNLOAD_BLOCK_SIZE) {
+                       memset(&buf[block_size], 0,
+                              DOWNLOAD_BLOCK_SIZE - block_size);
+                       tx_size = DOWNLOAD_BLOCK_SIZE;
+               }
+
+               /* send the block to sram */
+               ret = cw1200_apb_write(priv,
+                       CW1200_APB(DOWNLOAD_FIFO_OFFSET +
+                                  (put & (DOWNLOAD_FIFO_SIZE - 1))),
+                       buf, tx_size);
+               if (ret < 0) {
+                       pr_err("Can't write firmware block @ %d!\n",
+                              put & (DOWNLOAD_FIFO_SIZE - 1));
+                       goto free_buffer;
+               }
+
+               /* update the put register */
+               put += block_size;
+               APB_WRITE2(DOWNLOAD_PUT_REG, put);
+       } /* End of firmware download loop */
+
+       /* Wait for the download completion */
+       for (i = 0; i < 300; i += 1 + i / 2) {
+               APB_READ(DOWNLOAD_STATUS_REG, val32);
+               if (val32 != DOWNLOAD_PENDING)
+                       break;
+               mdelay(i);
+       }
+       if (val32 != DOWNLOAD_SUCCESS) {
+               pr_err("Wait for download completion failed: 0x%.8X\n", val32);
+               ret = -ETIMEDOUT;
+               goto free_buffer;
+       } else {
+               pr_info("Firmware download completed.\n");
+               ret = 0;
+       }
+
+free_buffer:
+       kfree(buf);
+firmware_release:
+       release_firmware(firmware);
+exit:
+       return ret;
+
+#undef APB_WRITE
+#undef APB_WRITE2
+#undef APB_READ
+#undef REG_WRITE
+#undef REG_READ
+}
+
+
+static int config_reg_read(struct cw1200_common *priv, u32 *val)
+{
+       switch (priv->hw_type) {
+       case HIF_9000_SILICON_VERSATILE: {
+               u16 val16;
+               int ret = cw1200_reg_read_16(priv,
+                                            ST90TDS_CONFIG_REG_ID,
+                                            &val16);
+               if (ret < 0)
+                       return ret;
+               *val = val16;
+               return 0;
+       }
+       case HIF_8601_VERSATILE:
+       case HIF_8601_SILICON:
+       default:
+               cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val);
+               break;
+       }
+       return 0;
+}
+
+static int config_reg_write(struct cw1200_common *priv, u32 val)
+{
+       switch (priv->hw_type) {
+       case HIF_9000_SILICON_VERSATILE:
+               return cw1200_reg_write_16(priv,
+                                          ST90TDS_CONFIG_REG_ID,
+                                          (u16)val);
+       case HIF_8601_VERSATILE:
+       case HIF_8601_SILICON:
+       default:
+               return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val);
+       }
+       return 0;
+}
+
+int cw1200_load_firmware(struct cw1200_common *priv)
+{
+       int ret;
+       int i;
+       u32 val32;
+       u16 val16;
+       int major_revision = -1;
+
+       /* Read CONFIG Register */
+       ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+       if (ret < 0) {
+               pr_err("Can't read config register.\n");
+               goto out;
+       }
+
+       if (val32 == 0 || val32 == 0xffffffff) {
+               pr_err("Bad config register value (0x%08x)\n", val32);
+               ret = -EIO;
+               goto out;
+       }
+
+       priv->hw_type = cw1200_get_hw_type(val32, &major_revision);
+       if (priv->hw_type < 0) {
+               pr_err("Can't deduce hardware type.\n");
+               ret = -ENOTSUPP;
+               goto out;
+       }
+
+       /* Set DPLL Reg value, and read back to confirm writes work */
+       ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
+                                 cw1200_dpll_from_clk(priv->hw_refclk));
+       if (ret < 0) {
+               pr_err("Can't write DPLL register.\n");
+               goto out;
+       }
+
+       msleep(20);
+
+       ret = cw1200_reg_read_32(priv,
+               ST90TDS_TSET_GEN_R_W_REG_ID, &val32);
+       if (ret < 0) {
+               pr_err("Can't read DPLL register.\n");
+               goto out;
+       }
+
+       if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) {
+               pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n",
+                      cw1200_dpll_from_clk(priv->hw_refclk), val32);
+               ret = -EIO;
+               goto out;
+       }
+
+       /* Set wakeup bit in device */
+       ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16);
+       if (ret < 0) {
+               pr_err("set_wakeup: can't read control register.\n");
+               goto out;
+       }
+
+       ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
+               val16 | ST90TDS_CONT_WUP_BIT);
+       if (ret < 0) {
+               pr_err("set_wakeup: can't write control register.\n");
+               goto out;
+       }
+
+       /* Wait for wakeup */
+       for (i = 0; i < 300; i += (1 + i / 2)) {
+               ret = cw1200_reg_read_16(priv,
+                       ST90TDS_CONTROL_REG_ID, &val16);
+               if (ret < 0) {
+                       pr_err("wait_for_wakeup: can't read control register.\n");
+                       goto out;
+               }
+
+               if (val16 & ST90TDS_CONT_RDY_BIT)
+                       break;
+
+               msleep(i);
+       }
+
+       if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) {
+               pr_err("wait_for_wakeup: device is not responding.\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       switch (major_revision) {
+       case 1:
+               /* CW1200 Hardware detection logic : Check for CUT1.1 */
+               ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32);
+               if (ret) {
+                       pr_err("HW detection: can't read CUT ID.\n");
+                       goto out;
+               }
+
+               switch (val32) {
+               case CW1200_CUT_11_ID_STR:
+                       pr_info("CW1x00 Cut 1.1 silicon detected.\n");
+                       priv->hw_revision = CW1200_HW_REV_CUT11;
+                       break;
+               default:
+                       pr_info("CW1x00 Cut 1.0 silicon detected.\n");
+                       priv->hw_revision = CW1200_HW_REV_CUT10;
+                       break;
+               }
+
+               /* According to ST-E, CUT<2.0 has busted BA TID0-3.
+                  Just disable it entirely...
+               */
+               priv->ba_rx_tid_mask = 0;
+               priv->ba_tx_tid_mask = 0;
+               break;
+       case 2: {
+               u32 ar1, ar2, ar3;
+               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1);
+               if (ret) {
+                       pr_err("(1) HW detection: can't read CUT ID\n");
+                       goto out;
+               }
+               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2);
+               if (ret) {
+                       pr_err("(2) HW detection: can't read CUT ID.\n");
+                       goto out;
+               }
+
+               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3);
+               if (ret) {
+                       pr_err("(3) HW detection: can't read CUT ID.\n");
+                       goto out;
+               }
+
+               if (ar1 == CW1200_CUT_22_ID_STR1 &&
+                   ar2 == CW1200_CUT_22_ID_STR2 &&
+                   ar3 == CW1200_CUT_22_ID_STR3) {
+                       pr_info("CW1x00 Cut 2.2 silicon detected.\n");
+                       priv->hw_revision = CW1200_HW_REV_CUT22;
+               } else {
+                       pr_info("CW1x00 Cut 2.0 silicon detected.\n");
+                       priv->hw_revision = CW1200_HW_REV_CUT20;
+               }
+               break;
+       }
+       case 4:
+               pr_info("CW1x60 silicon detected.\n");
+               priv->hw_revision = CW1X60_HW_REV;
+               break;
+       default:
+               pr_err("Unsupported silicon major revision %d.\n",
+                      major_revision);
+               ret = -ENOTSUPP;
+               goto out;
+       }
+
+       /* Checking for access mode */
+       ret = config_reg_read(priv, &val32);
+       if (ret < 0) {
+               pr_err("Can't read config register.\n");
+               goto out;
+       }
+
+       if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) {
+               pr_err("Device is already in QUEUE mode!\n");
+                       ret = -EINVAL;
+                       goto out;
+       }
+
+       switch (priv->hw_type)  {
+       case HIF_8601_SILICON:
+               if (priv->hw_revision == CW1X60_HW_REV) {
+                       pr_err("Can't handle CW1160/1260 firmware load yet.\n");
+                       ret = -ENOTSUPP;
+                       goto out;
+               }
+               ret = cw1200_load_firmware_cw1200(priv);
+               break;
+       default:
+               pr_err("Can't perform firmware load for hw type %d.\n",
+                      priv->hw_type);
+               ret = -ENOTSUPP;
+               goto out;
+       }
+       if (ret < 0) {
+               pr_err("Firmware load error.\n");
+               goto out;
+       }
+
+       /* Enable interrupt signalling */
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       ret = __cw1200_irq_enable(priv, 1);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       if (ret < 0)
+               goto unsubscribe;
+
+       /* Configure device for MESSSAGE MODE */
+       ret = config_reg_read(priv, &val32);
+       if (ret < 0) {
+               pr_err("Can't read config register.\n");
+               goto unsubscribe;
+       }
+       ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT);
+       if (ret < 0) {
+               pr_err("Can't write config register.\n");
+               goto unsubscribe;
+       }
+
+       /* Unless we read the CONFIG Register we are
+        * not able to get an interrupt
+        */
+       mdelay(10);
+       config_reg_read(priv, &val32);
+
+out:
+       return ret;
+
+unsubscribe:
+       /* Disable interrupt signalling */
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       ret = __cw1200_irq_enable(priv, 0);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
diff --git a/drivers/net/wireless/st/cw1200/fwio.h b/drivers/net/wireless/st/cw1200/fwio.h
new file mode 100644 (file)
index 0000000..ea30993
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Firmware API for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FWIO_H_INCLUDED
+#define FWIO_H_INCLUDED
+
+#define BOOTLOADER_CW1X60       "boot_cw1x60.bin"
+#define FIRMWARE_CW1X60                "wsm_cw1x60.bin"
+#define FIRMWARE_CUT22         "wsm_22.bin"
+#define FIRMWARE_CUT20         "wsm_20.bin"
+#define FIRMWARE_CUT11         "wsm_11.bin"
+#define FIRMWARE_CUT10         "wsm_10.bin"
+#define SDD_FILE_CW1X60                "sdd_cw1x60.bin"
+#define SDD_FILE_22            "sdd_22.bin"
+#define SDD_FILE_20            "sdd_20.bin"
+#define SDD_FILE_11            "sdd_11.bin"
+#define SDD_FILE_10            "sdd_10.bin"
+
+int cw1200_load_firmware(struct cw1200_common *priv);
+
+/* SDD definitions */
+#define SDD_PTA_CFG_ELT_ID 0xEB
+#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5
+u32 cw1200_dpll_from_clk(u16 clk);
+
+#endif
diff --git a/drivers/net/wireless/st/cw1200/hwbus.h b/drivers/net/wireless/st/cw1200/hwbus.h
new file mode 100644 (file)
index 0000000..8b2fc83
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Common hwbus abstraction layer interface for cw1200 wireless driver
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_HWBUS_H
+#define CW1200_HWBUS_H
+
+struct hwbus_priv;
+
+void cw1200_irq_handler(struct cw1200_common *priv);
+
+/* This MUST be wrapped with hwbus_ops->lock/unlock! */
+int __cw1200_irq_enable(struct cw1200_common *priv, int enable);
+
+struct hwbus_ops {
+       int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr,
+                                       void *dst, int count);
+       int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr,
+                                       const void *src, int count);
+       void (*lock)(struct hwbus_priv *self);
+       void (*unlock)(struct hwbus_priv *self);
+       size_t (*align_size)(struct hwbus_priv *self, size_t size);
+       int (*power_mgmt)(struct hwbus_priv *self, bool suspend);
+};
+
+#endif /* CW1200_HWBUS_H */
diff --git a/drivers/net/wireless/st/cw1200/hwio.c b/drivers/net/wireless/st/cw1200/hwio.c
new file mode 100644 (file)
index 0000000..ff230b7
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * Low-level device IO routines for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver, which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+
+#include "cw1200.h"
+#include "hwio.h"
+#include "hwbus.h"
+
+ /* Sdio addr is 4*spi_addr */
+#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
+#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
+                               ((((buf_id)    & 0x1F) << 7) \
+                               | (((mpf)        & 1) << 6) \
+                               | (((rfu)        & 1) << 5) \
+                               | (((reg_id_ofs) & 0x1F) << 0))
+#define MAX_RETRY              3
+
+
+static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr,
+                            void *buf, size_t buf_len, int buf_id)
+{
+       u16 addr_sdio;
+       u32 sdio_reg_addr_17bit;
+
+       /* Check if buffer is aligned to 4 byte boundary */
+       if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
+               pr_err("buffer is not aligned.\n");
+               return -EINVAL;
+       }
+
+       /* Convert to SDIO Register Address */
+       addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+       sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+       return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv,
+                                                 sdio_reg_addr_17bit,
+                                                 buf, buf_len);
+}
+
+static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr,
+                               const void *buf, size_t buf_len, int buf_id)
+{
+       u16 addr_sdio;
+       u32 sdio_reg_addr_17bit;
+
+       /* Convert to SDIO Register Address */
+       addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+       sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+       return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv,
+                                               sdio_reg_addr_17bit,
+                                               buf, buf_len);
+}
+
+static inline int __cw1200_reg_read_32(struct cw1200_common *priv,
+                                       u16 addr, u32 *val)
+{
+       __le32 tmp;
+       int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0);
+       *val = le32_to_cpu(tmp);
+       return i;
+}
+
+static inline int __cw1200_reg_write_32(struct cw1200_common *priv,
+                                       u16 addr, u32 val)
+{
+       __le32 tmp = cpu_to_le32(val);
+       return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0);
+}
+
+static inline int __cw1200_reg_read_16(struct cw1200_common *priv,
+                                       u16 addr, u16 *val)
+{
+       __le16 tmp;
+       int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0);
+       *val = le16_to_cpu(tmp);
+       return i;
+}
+
+static inline int __cw1200_reg_write_16(struct cw1200_common *priv,
+                                       u16 addr, u16 val)
+{
+       __le16 tmp = cpu_to_le16(val);
+       return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0);
+}
+
+int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf,
+                       size_t buf_len)
+{
+       int ret;
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
+                       size_t buf_len)
+{
+       int ret;
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len)
+{
+       int ret, retry = 1;
+       int buf_id_rx = priv->buf_id_rx;
+
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+
+       while (retry <= MAX_RETRY) {
+               ret = __cw1200_reg_read(priv,
+                                       ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+                                       buf_len, buf_id_rx + 1);
+               if (!ret) {
+                       buf_id_rx = (buf_id_rx + 1) & 3;
+                       priv->buf_id_rx = buf_id_rx;
+                       break;
+               } else {
+                       retry++;
+                       mdelay(1);
+                       pr_err("error :[%d]\n", ret);
+               }
+       }
+
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_data_write(struct cw1200_common *priv, const void *buf,
+                       size_t buf_len)
+{
+       int ret, retry = 1;
+       int buf_id_tx = priv->buf_id_tx;
+
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+
+       while (retry <= MAX_RETRY) {
+               ret = __cw1200_reg_write(priv,
+                                        ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+                                        buf_len, buf_id_tx);
+               if (!ret) {
+                       buf_id_tx = (buf_id_tx + 1) & 31;
+                       priv->buf_id_tx = buf_id_tx;
+                       break;
+               } else {
+                       retry++;
+                       mdelay(1);
+                       pr_err("error :[%d]\n", ret);
+               }
+       }
+
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
+                        size_t buf_len, u32 prefetch, u16 port_addr)
+{
+       u32 val32 = 0;
+       int i, ret;
+
+       if ((buf_len / 2) >= 0x1000) {
+               pr_err("Can't read more than 0xfff words.\n");
+               return -EINVAL;
+       }
+
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       /* Write address */
+       ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+       if (ret < 0) {
+               pr_err("Can't write address register.\n");
+               goto out;
+       }
+
+       /* Read CONFIG Register Value - We will read 32 bits */
+       ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+       if (ret < 0) {
+               pr_err("Can't read config register.\n");
+               goto out;
+       }
+
+       /* Set PREFETCH bit */
+       ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID,
+                                       val32 | prefetch);
+       if (ret < 0) {
+               pr_err("Can't write prefetch bit.\n");
+               goto out;
+       }
+
+       /* Check for PRE-FETCH bit to be cleared */
+       for (i = 0; i < 20; i++) {
+               ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+               if (ret < 0) {
+                       pr_err("Can't check prefetch bit.\n");
+                       goto out;
+               }
+               if (!(val32 & prefetch))
+                       break;
+
+               mdelay(i);
+       }
+
+       if (val32 & prefetch) {
+               pr_err("Prefetch bit is not cleared.\n");
+               goto out;
+       }
+
+       /* Read data port */
+       ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0);
+       if (ret < 0) {
+               pr_err("Can't read data port.\n");
+               goto out;
+       }
+
+out:
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
+                       size_t buf_len)
+{
+       int ret;
+
+       if ((buf_len / 2) >= 0x1000) {
+               pr_err("Can't write more than 0xfff words.\n");
+               return -EINVAL;
+       }
+
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+
+       /* Write address */
+       ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+       if (ret < 0) {
+               pr_err("Can't write address register.\n");
+               goto out;
+       }
+
+       /* Write data port */
+       ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID,
+                                       buf, buf_len, 0);
+       if (ret < 0) {
+               pr_err("Can't write data port.\n");
+               goto out;
+       }
+
+out:
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
+{
+       u32 val32;
+       u16 val16;
+       int ret;
+
+       if (HIF_8601_SILICON == priv->hw_type) {
+               ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+               if (ret < 0) {
+                       pr_err("Can't read config register.\n");
+                       return ret;
+               }
+
+               if (enable)
+                       val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE;
+               else
+                       val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE;
+
+               ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32);
+               if (ret < 0) {
+                       pr_err("Can't write config register.\n");
+                       return ret;
+               }
+       } else {
+               ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16);
+               if (ret < 0) {
+                       pr_err("Can't read control register.\n");
+                       return ret;
+               }
+
+               if (enable)
+                       val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE;
+               else
+                       val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE;
+
+               ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16);
+               if (ret < 0) {
+                       pr_err("Can't write control register.\n");
+                       return ret;
+               }
+       }
+       return 0;
+}
diff --git a/drivers/net/wireless/st/cw1200/hwio.h b/drivers/net/wireless/st/cw1200/hwio.h
new file mode 100644 (file)
index 0000000..ddf5266
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Low-level API for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_HWIO_H_INCLUDED
+#define CW1200_HWIO_H_INCLUDED
+
+/* extern */ struct cw1200_common;
+
+#define CW1200_CUT_11_ID_STR           (0x302E3830)
+#define CW1200_CUT_22_ID_STR1          (0x302e3132)
+#define CW1200_CUT_22_ID_STR2          (0x32302e30)
+#define CW1200_CUT_22_ID_STR3          (0x3335)
+#define CW1200_CUT_ID_ADDR             (0xFFF17F90)
+#define CW1200_CUT2_ID_ADDR            (0xFFF1FF90)
+
+/* Download control area */
+/* boot loader start address in SRAM */
+#define DOWNLOAD_BOOT_LOADER_OFFSET    (0x00000000)
+/* 32K, 0x4000 to 0xDFFF */
+#define DOWNLOAD_FIFO_OFFSET           (0x00004000)
+/* 32K */
+#define DOWNLOAD_FIFO_SIZE             (0x00008000)
+/* 128 bytes, 0xFF80 to 0xFFFF */
+#define DOWNLOAD_CTRL_OFFSET           (0x0000FF80)
+#define DOWNLOAD_CTRL_DATA_DWORDS      (32-6)
+
+struct download_cntl_t {
+       /* size of whole firmware file (including Cheksum), host init */
+       u32 image_size;
+       /* downloading flags */
+       u32 flags;
+       /* No. of bytes put into the download, init & updated by host */
+       u32 put;
+       /* last traced program counter, last ARM reg_pc */
+       u32 trace_pc;
+       /* No. of bytes read from the download, host init, device updates */
+       u32 get;
+       /* r0, boot losader status, host init to pending, device updates */
+       u32 status;
+       /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
+       u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS];
+};
+
+#define        DOWNLOAD_IMAGE_SIZE_REG         \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size))
+#define        DOWNLOAD_FLAGS_REG              \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags))
+#define DOWNLOAD_PUT_REG               \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put))
+#define DOWNLOAD_TRACE_PC_REG          \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc))
+#define        DOWNLOAD_GET_REG                \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get))
+#define        DOWNLOAD_STATUS_REG             \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status))
+#define DOWNLOAD_DEBUG_DATA_REG                \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data))
+#define DOWNLOAD_DEBUG_DATA_LEN                (108)
+
+#define DOWNLOAD_BLOCK_SIZE            (1024)
+
+/* For boot loader detection */
+#define DOWNLOAD_ARE_YOU_HERE          (0x87654321)
+#define DOWNLOAD_I_AM_HERE             (0x12345678)
+
+/* Download error code */
+#define DOWNLOAD_PENDING               (0xFFFFFFFF)
+#define DOWNLOAD_SUCCESS               (0)
+#define DOWNLOAD_EXCEPTION             (1)
+#define DOWNLOAD_ERR_MEM_1             (2)
+#define DOWNLOAD_ERR_MEM_2             (3)
+#define DOWNLOAD_ERR_SOFTWARE          (4)
+#define DOWNLOAD_ERR_FILE_SIZE         (5)
+#define DOWNLOAD_ERR_CHECKSUM          (6)
+#define DOWNLOAD_ERR_OVERFLOW          (7)
+#define DOWNLOAD_ERR_IMAGE             (8)
+#define DOWNLOAD_ERR_HOST              (9)
+#define DOWNLOAD_ERR_ABORT             (10)
+
+
+#define SYS_BASE_ADDR_SILICON          (0)
+#define PAC_BASE_ADDRESS_SILICON       (SYS_BASE_ADDR_SILICON + 0x09000000)
+#define PAC_SHARED_MEMORY_SILICON      (PAC_BASE_ADDRESS_SILICON)
+
+#define CW1200_APB(addr)               (PAC_SHARED_MEMORY_SILICON + (addr))
+
+/* Device register definitions */
+
+/* WBF - SPI Register Addresses */
+#define ST90TDS_ADDR_ID_BASE           (0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONFIG_REG_ID          (0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONTROL_REG_ID         (0x0001)
+/* 16 bits, Q mode W/R */
+#define ST90TDS_IN_OUT_QUEUE_REG_ID    (0x0002)
+/* 32 bits, AHB bus R/W */
+#define ST90TDS_AHB_DPORT_REG_ID       (0x0003)
+/* 16/32 bits */
+#define ST90TDS_SRAM_BASE_ADDR_REG_ID   (0x0004)
+/* 32 bits, APB bus R/W */
+#define ST90TDS_SRAM_DPORT_REG_ID      (0x0005)
+/* 32 bits, t_settle/general */
+#define ST90TDS_TSET_GEN_R_W_REG_ID    (0x0006)
+/* 16 bits, Q mode read, no length */
+#define ST90TDS_FRAME_OUT_REG_ID       (0x0007)
+#define ST90TDS_ADDR_ID_MAX            (ST90TDS_FRAME_OUT_REG_ID)
+
+/* WBF - Control register bit set */
+/* next o/p length, bit 11 to 0 */
+#define ST90TDS_CONT_NEXT_LEN_MASK     (0x0FFF)
+#define ST90TDS_CONT_WUP_BIT           (BIT(12))
+#define ST90TDS_CONT_RDY_BIT           (BIT(13))
+#define ST90TDS_CONT_IRQ_ENABLE                (BIT(14))
+#define ST90TDS_CONT_RDY_ENABLE                (BIT(15))
+#define ST90TDS_CONT_IRQ_RDY_ENABLE    (BIT(14)|BIT(15))
+
+/* SPI Config register bit set */
+#define ST90TDS_CONFIG_FRAME_BIT       (BIT(2))
+#define ST90TDS_CONFIG_WORD_MODE_BITS  (BIT(3)|BIT(4))
+#define ST90TDS_CONFIG_WORD_MODE_1     (BIT(3))
+#define ST90TDS_CONFIG_WORD_MODE_2     (BIT(4))
+#define ST90TDS_CONFIG_ERROR_0_BIT     (BIT(5))
+#define ST90TDS_CONFIG_ERROR_1_BIT     (BIT(6))
+#define ST90TDS_CONFIG_ERROR_2_BIT     (BIT(7))
+/* TBD: Sure??? */
+#define ST90TDS_CONFIG_CSN_FRAME_BIT   (BIT(7))
+#define ST90TDS_CONFIG_ERROR_3_BIT     (BIT(8))
+#define ST90TDS_CONFIG_ERROR_4_BIT     (BIT(9))
+/* QueueM */
+#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
+/* AHB bus */
+#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11))
+#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
+/* APB bus */
+#define ST90TDS_CONFIG_PRFETCH_BIT     (BIT(13))
+/* cpu reset */
+#define ST90TDS_CONFIG_CPU_RESET_BIT   (BIT(14))
+#define ST90TDS_CONFIG_CLEAR_INT_BIT   (BIT(15))
+
+/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */
+#define ST90TDS_CONF_IRQ_ENABLE                (BIT(16))
+#define ST90TDS_CONF_RDY_ENABLE                (BIT(17))
+#define ST90TDS_CONF_IRQ_RDY_ENABLE    (BIT(16)|BIT(17))
+
+int cw1200_data_read(struct cw1200_common *priv,
+                    void *buf, size_t buf_len);
+int cw1200_data_write(struct cw1200_common *priv,
+                     const void *buf, size_t buf_len);
+
+int cw1200_reg_read(struct cw1200_common *priv, u16 addr,
+                   void *buf, size_t buf_len);
+int cw1200_reg_write(struct cw1200_common *priv, u16 addr,
+                    const void *buf, size_t buf_len);
+
+static inline int cw1200_reg_read_16(struct cw1200_common *priv,
+                                    u16 addr, u16 *val)
+{
+       __le32 tmp;
+       int i;
+       i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
+       *val = le32_to_cpu(tmp) & 0xfffff;
+       return i;
+}
+
+static inline int cw1200_reg_write_16(struct cw1200_common *priv,
+                                     u16 addr, u16 val)
+{
+       __le32 tmp = cpu_to_le32((u32)val);
+       return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp));
+}
+
+static inline int cw1200_reg_read_32(struct cw1200_common *priv,
+                                    u16 addr, u32 *val)
+{
+       __le32 tmp;
+       int i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
+       *val = le32_to_cpu(tmp);
+       return i;
+}
+
+static inline int cw1200_reg_write_32(struct cw1200_common *priv,
+                                     u16 addr, u32 val)
+{
+       __le32 tmp = cpu_to_le32(val);
+       return cw1200_reg_write(priv, addr, &tmp, sizeof(val));
+}
+
+int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
+                        size_t buf_len, u32 prefetch, u16 port_addr);
+int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
+                    size_t buf_len);
+
+static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr,
+                                 void *buf, size_t buf_len)
+{
+       return cw1200_indirect_read(priv, addr, buf, buf_len,
+                                   ST90TDS_CONFIG_PRFETCH_BIT,
+                                   ST90TDS_SRAM_DPORT_REG_ID);
+}
+
+static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr,
+                                 void *buf, size_t buf_len)
+{
+       return cw1200_indirect_read(priv, addr, buf, buf_len,
+                                   ST90TDS_CONFIG_AHB_PRFETCH_BIT,
+                                   ST90TDS_AHB_DPORT_REG_ID);
+}
+
+static inline int cw1200_apb_read_32(struct cw1200_common *priv,
+                                    u32 addr, u32 *val)
+{
+       __le32 tmp;
+       int i = cw1200_apb_read(priv, addr, &tmp, sizeof(tmp));
+       *val = le32_to_cpu(tmp);
+       return i;
+}
+
+static inline int cw1200_apb_write_32(struct cw1200_common *priv,
+                                     u32 addr, u32 val)
+{
+       __le32 tmp = cpu_to_le32(val);
+       return cw1200_apb_write(priv, addr, &tmp, sizeof(val));
+}
+static inline int cw1200_ahb_read_32(struct cw1200_common *priv,
+                                    u32 addr, u32 *val)
+{
+       __le32 tmp;
+       int i = cw1200_ahb_read(priv, addr, &tmp, sizeof(tmp));
+       *val = le32_to_cpu(tmp);
+       return i;
+}
+
+#endif /* CW1200_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/st/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c
new file mode 100644 (file)
index 0000000..0e51e27
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+
+#include "cw1200.h"
+#include "txrx.h"
+#include "hwbus.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sta.h"
+#include "scan.h"
+#include "debug.h"
+#include "pm.h"
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
+MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("cw1200_core");
+
+/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
+static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
+module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
+
+static char *cw1200_sdd_path;
+module_param(cw1200_sdd_path, charp, 0644);
+MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
+static int cw1200_refclk;
+module_param(cw1200_refclk, int, 0644);
+MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
+
+int cw1200_power_mode = wsm_power_mode_quiescent;
+module_param(cw1200_power_mode, int, 0644);
+MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode.  0 == active, 1 == doze, 2 == quiescent (default)");
+
+#define RATETAB_ENT(_rate, _rateid, _flags)            \
+       {                                               \
+               .bitrate        = (_rate),              \
+               .hw_value       = (_rateid),            \
+               .flags          = (_flags),             \
+       }
+
+static struct ieee80211_rate cw1200_rates[] = {
+       RATETAB_ENT(10,  0,   0),
+       RATETAB_ENT(20,  1,   0),
+       RATETAB_ENT(55,  2,   0),
+       RATETAB_ENT(110, 3,   0),
+       RATETAB_ENT(60,  6,  0),
+       RATETAB_ENT(90,  7,  0),
+       RATETAB_ENT(120, 8,  0),
+       RATETAB_ENT(180, 9,  0),
+       RATETAB_ENT(240, 10, 0),
+       RATETAB_ENT(360, 11, 0),
+       RATETAB_ENT(480, 12, 0),
+       RATETAB_ENT(540, 13, 0),
+};
+
+static struct ieee80211_rate cw1200_mcs_rates[] = {
+       RATETAB_ENT(65,  14, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
+};
+
+#define cw1200_a_rates         (cw1200_rates + 4)
+#define cw1200_a_rates_size    (ARRAY_SIZE(cw1200_rates) - 4)
+#define cw1200_g_rates         (cw1200_rates + 0)
+#define cw1200_g_rates_size    (ARRAY_SIZE(cw1200_rates))
+#define cw1200_n_rates         (cw1200_mcs_rates)
+#define cw1200_n_rates_size    (ARRAY_SIZE(cw1200_mcs_rates))
+
+
+#define CHAN2G(_channel, _freq, _flags) {                      \
+       .band                   = IEEE80211_BAND_2GHZ,          \
+       .center_freq            = (_freq),                      \
+       .hw_value               = (_channel),                   \
+       .flags                  = (_flags),                     \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
+
+#define CHAN5G(_channel, _flags) {                             \
+       .band                   = IEEE80211_BAND_5GHZ,          \
+       .center_freq    = 5000 + (5 * (_channel)),              \
+       .hw_value               = (_channel),                   \
+       .flags                  = (_flags),                     \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
+
+static struct ieee80211_channel cw1200_2ghz_chantable[] = {
+       CHAN2G(1, 2412, 0),
+       CHAN2G(2, 2417, 0),
+       CHAN2G(3, 2422, 0),
+       CHAN2G(4, 2427, 0),
+       CHAN2G(5, 2432, 0),
+       CHAN2G(6, 2437, 0),
+       CHAN2G(7, 2442, 0),
+       CHAN2G(8, 2447, 0),
+       CHAN2G(9, 2452, 0),
+       CHAN2G(10, 2457, 0),
+       CHAN2G(11, 2462, 0),
+       CHAN2G(12, 2467, 0),
+       CHAN2G(13, 2472, 0),
+       CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel cw1200_5ghz_chantable[] = {
+       CHAN5G(34, 0),          CHAN5G(36, 0),
+       CHAN5G(38, 0),          CHAN5G(40, 0),
+       CHAN5G(42, 0),          CHAN5G(44, 0),
+       CHAN5G(46, 0),          CHAN5G(48, 0),
+       CHAN5G(52, 0),          CHAN5G(56, 0),
+       CHAN5G(60, 0),          CHAN5G(64, 0),
+       CHAN5G(100, 0),         CHAN5G(104, 0),
+       CHAN5G(108, 0),         CHAN5G(112, 0),
+       CHAN5G(116, 0),         CHAN5G(120, 0),
+       CHAN5G(124, 0),         CHAN5G(128, 0),
+       CHAN5G(132, 0),         CHAN5G(136, 0),
+       CHAN5G(140, 0),         CHAN5G(149, 0),
+       CHAN5G(153, 0),         CHAN5G(157, 0),
+       CHAN5G(161, 0),         CHAN5G(165, 0),
+       CHAN5G(184, 0),         CHAN5G(188, 0),
+       CHAN5G(192, 0),         CHAN5G(196, 0),
+       CHAN5G(200, 0),         CHAN5G(204, 0),
+       CHAN5G(208, 0),         CHAN5G(212, 0),
+       CHAN5G(216, 0),
+};
+
+static struct ieee80211_supported_band cw1200_band_2ghz = {
+       .channels = cw1200_2ghz_chantable,
+       .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
+       .bitrates = cw1200_g_rates,
+       .n_bitrates = cw1200_g_rates_size,
+       .ht_cap = {
+               .cap = IEEE80211_HT_CAP_GRN_FLD |
+                       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+                       IEEE80211_HT_CAP_MAX_AMSDU,
+               .ht_supported = 1,
+               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+               .mcs = {
+                       .rx_mask[0] = 0xFF,
+                       .rx_highest = __cpu_to_le16(0x41),
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+       },
+};
+
+static struct ieee80211_supported_band cw1200_band_5ghz = {
+       .channels = cw1200_5ghz_chantable,
+       .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
+       .bitrates = cw1200_a_rates,
+       .n_bitrates = cw1200_a_rates_size,
+       .ht_cap = {
+               .cap = IEEE80211_HT_CAP_GRN_FLD |
+                       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+                       IEEE80211_HT_CAP_MAX_AMSDU,
+               .ht_supported = 1,
+               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+               .mcs = {
+                       .rx_mask[0] = 0xFF,
+                       .rx_highest = __cpu_to_le16(0x41),
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+       },
+};
+
+static const unsigned long cw1200_ttl[] = {
+       1 * HZ, /* VO */
+       2 * HZ, /* VI */
+       5 * HZ, /* BE */
+       10 * HZ /* BK */
+};
+
+static const struct ieee80211_ops cw1200_ops = {
+       .start                  = cw1200_start,
+       .stop                   = cw1200_stop,
+       .add_interface          = cw1200_add_interface,
+       .remove_interface       = cw1200_remove_interface,
+       .change_interface       = cw1200_change_interface,
+       .tx                     = cw1200_tx,
+       .hw_scan                = cw1200_hw_scan,
+       .set_tim                = cw1200_set_tim,
+       .sta_notify             = cw1200_sta_notify,
+       .sta_add                = cw1200_sta_add,
+       .sta_remove             = cw1200_sta_remove,
+       .set_key                = cw1200_set_key,
+       .set_rts_threshold      = cw1200_set_rts_threshold,
+       .config                 = cw1200_config,
+       .bss_info_changed       = cw1200_bss_info_changed,
+       .prepare_multicast      = cw1200_prepare_multicast,
+       .configure_filter       = cw1200_configure_filter,
+       .conf_tx                = cw1200_conf_tx,
+       .get_stats              = cw1200_get_stats,
+       .ampdu_action           = cw1200_ampdu_action,
+       .flush                  = cw1200_flush,
+#ifdef CONFIG_PM
+       .suspend                = cw1200_wow_suspend,
+       .resume                 = cw1200_wow_resume,
+#endif
+       /* Intentionally not offloaded:                                 */
+       /*.channel_switch       = cw1200_channel_switch,                */
+       /*.remain_on_channel    = cw1200_remain_on_channel,             */
+       /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel,  */
+};
+
+static int cw1200_ba_rx_tids = -1;
+static int cw1200_ba_tx_tids = -1;
+module_param(cw1200_ba_rx_tids, int, 0644);
+module_param(cw1200_ba_tx_tids, int, 0644);
+MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
+MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support cw1200_wowlan_support = {
+       /* Support only for limited wowlan functionalities */
+       .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
+};
+#endif
+
+
+static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
+                                               const bool have_5ghz)
+{
+       int i, band;
+       struct ieee80211_hw *hw;
+       struct cw1200_common *priv;
+
+       hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
+       if (!hw)
+               return NULL;
+
+       priv = hw->priv;
+       priv->hw = hw;
+       priv->hw_type = -1;
+       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+       priv->rates = cw1200_rates; /* TODO: fetch from FW */
+       priv->mcs_rates = cw1200_n_rates;
+       if (cw1200_ba_rx_tids != -1)
+               priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
+       else
+               priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */
+       if (cw1200_ba_tx_tids != -1)
+               priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
+       else
+               priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */
+
+       ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
+       ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
+       ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+       ieee80211_hw_set(hw, CONNECTION_MONITOR);
+       ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+       ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+       ieee80211_hw_set(hw, SIGNAL_DBM);
+       ieee80211_hw_set(hw, SUPPORTS_PS);
+
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                         BIT(NL80211_IFTYPE_ADHOC) |
+                                         BIT(NL80211_IFTYPE_AP) |
+                                         BIT(NL80211_IFTYPE_MESH_POINT) |
+                                         BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                         BIT(NL80211_IFTYPE_P2P_GO);
+
+#ifdef CONFIG_PM
+       hw->wiphy->wowlan = &cw1200_wowlan_support;
+#endif
+
+       hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+       hw->queues = 4;
+
+       priv->rts_threshold = -1;
+
+       hw->max_rates = 8;
+       hw->max_rate_tries = 15;
+       hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
+               8;  /* TKIP IV */
+
+       hw->sta_data_size = sizeof(struct cw1200_sta_priv);
+
+       hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz;
+       if (have_5ghz)
+               hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz;
+
+       /* Channel params have to be cleared before registering wiphy again */
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
+               if (!sband)
+                       continue;
+               for (i = 0; i < sband->n_channels; i++) {
+                       sband->channels[i].flags = 0;
+                       sband->channels[i].max_antenna_gain = 0;
+                       sband->channels[i].max_power = 30;
+               }
+       }
+
+       hw->wiphy->max_scan_ssids = 2;
+       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+
+       if (macaddr)
+               SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
+       else
+               SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
+
+       /* Fix up mac address if necessary */
+       if (hw->wiphy->perm_addr[3] == 0 &&
+           hw->wiphy->perm_addr[4] == 0 &&
+           hw->wiphy->perm_addr[5] == 0) {
+               get_random_bytes(&hw->wiphy->perm_addr[3], 3);
+       }
+
+       mutex_init(&priv->wsm_cmd_mux);
+       mutex_init(&priv->conf_mutex);
+       priv->workqueue = create_singlethread_workqueue("cw1200_wq");
+       sema_init(&priv->scan.lock, 1);
+       INIT_WORK(&priv->scan.work, cw1200_scan_work);
+       INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
+       INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
+       INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
+                         cw1200_clear_recent_scan_work);
+       INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
+       INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
+       INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
+       INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
+       INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
+       spin_lock_init(&priv->event_queue_lock);
+       INIT_LIST_HEAD(&priv->event_queue);
+       INIT_WORK(&priv->event_handler, cw1200_event_handler);
+       INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
+       INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
+       spin_lock_init(&priv->bss_loss_lock);
+       spin_lock_init(&priv->ps_state_lock);
+       INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
+       INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
+       INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
+       INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
+       INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
+       INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
+       INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
+       INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
+       INIT_WORK(&priv->set_beacon_wakeup_period_work,
+                 cw1200_set_beacon_wakeup_period_work);
+       setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout,
+                   (unsigned long)priv);
+
+       if (cw1200_queue_stats_init(&priv->tx_queue_stats,
+                                   CW1200_LINK_ID_MAX,
+                                   cw1200_skb_dtor,
+                                   priv)) {
+               ieee80211_free_hw(hw);
+               return NULL;
+       }
+
+       for (i = 0; i < 4; ++i) {
+               if (cw1200_queue_init(&priv->tx_queue[i],
+                                     &priv->tx_queue_stats, i, 16,
+                                     cw1200_ttl[i])) {
+                       for (; i > 0; i--)
+                               cw1200_queue_deinit(&priv->tx_queue[i - 1]);
+                       cw1200_queue_stats_deinit(&priv->tx_queue_stats);
+                       ieee80211_free_hw(hw);
+                       return NULL;
+               }
+       }
+
+       init_waitqueue_head(&priv->channel_switch_done);
+       init_waitqueue_head(&priv->wsm_cmd_wq);
+       init_waitqueue_head(&priv->wsm_startup_done);
+       init_waitqueue_head(&priv->ps_mode_switch_done);
+       wsm_buf_init(&priv->wsm_cmd_buf);
+       spin_lock_init(&priv->wsm_cmd.lock);
+       priv->wsm_cmd.done = 1;
+       tx_policy_init(priv);
+
+       return hw;
+}
+
+static int cw1200_register_common(struct ieee80211_hw *dev)
+{
+       struct cw1200_common *priv = dev->priv;
+       int err;
+
+#ifdef CONFIG_PM
+       err = cw1200_pm_init(&priv->pm_state, priv);
+       if (err) {
+               pr_err("Cannot init PM. (%d).\n",
+                      err);
+               return err;
+       }
+#endif
+
+       err = ieee80211_register_hw(dev);
+       if (err) {
+               pr_err("Cannot register device (%d).\n",
+                      err);
+#ifdef CONFIG_PM
+               cw1200_pm_deinit(&priv->pm_state);
+#endif
+               return err;
+       }
+
+       cw1200_debug_init(priv);
+
+       pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
+       return 0;
+}
+
+static void cw1200_free_common(struct ieee80211_hw *dev)
+{
+       ieee80211_free_hw(dev);
+}
+
+static void cw1200_unregister_common(struct ieee80211_hw *dev)
+{
+       struct cw1200_common *priv = dev->priv;
+       int i;
+
+       ieee80211_unregister_hw(dev);
+
+       del_timer_sync(&priv->mcast_timeout);
+       cw1200_unregister_bh(priv);
+
+       cw1200_debug_release(priv);
+
+       mutex_destroy(&priv->conf_mutex);
+
+       wsm_buf_deinit(&priv->wsm_cmd_buf);
+
+       destroy_workqueue(priv->workqueue);
+       priv->workqueue = NULL;
+
+       if (priv->sdd) {
+               release_firmware(priv->sdd);
+               priv->sdd = NULL;
+       }
+
+       for (i = 0; i < 4; ++i)
+               cw1200_queue_deinit(&priv->tx_queue[i]);
+
+       cw1200_queue_stats_deinit(&priv->tx_queue_stats);
+#ifdef CONFIG_PM
+       cw1200_pm_deinit(&priv->pm_state);
+#endif
+}
+
+/* Clock is in KHz */
+u32 cw1200_dpll_from_clk(u16 clk_khz)
+{
+       switch (clk_khz) {
+       case 0x32C8: /* 13000 KHz */
+               return 0x1D89D241;
+       case 0x3E80: /* 16000 KHz */
+               return 0x000001E1;
+       case 0x41A0: /* 16800 KHz */
+               return 0x124931C1;
+       case 0x4B00: /* 19200 KHz */
+               return 0x00000191;
+       case 0x5DC0: /* 24000 KHz */
+               return 0x00000141;
+       case 0x6590: /* 26000 KHz */
+               return 0x0EC4F121;
+       case 0x8340: /* 33600 KHz */
+               return 0x092490E1;
+       case 0x9600: /* 38400 KHz */
+               return 0x100010C1;
+       case 0x9C40: /* 40000 KHz */
+               return 0x000000C1;
+       case 0xBB80: /* 48000 KHz */
+               return 0x000000A1;
+       case 0xCB20: /* 52000 KHz */
+               return 0x07627091;
+       default:
+               pr_err("Unknown Refclk freq (0x%04x), using 26000KHz\n",
+                      clk_khz);
+               return 0x0EC4F121;
+       }
+}
+
+int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
+                     struct hwbus_priv *hwbus,
+                     struct device *pdev,
+                     struct cw1200_common **core,
+                     int ref_clk, const u8 *macaddr,
+                     const char *sdd_path, bool have_5ghz)
+{
+       int err = -EINVAL;
+       struct ieee80211_hw *dev;
+       struct cw1200_common *priv;
+       struct wsm_operational_mode mode = {
+               .power_mode = cw1200_power_mode,
+               .disable_more_flag_usage = true,
+       };
+
+       dev = cw1200_init_common(macaddr, have_5ghz);
+       if (!dev)
+               goto err;
+
+       priv = dev->priv;
+       priv->hw_refclk = ref_clk;
+       if (cw1200_refclk)
+               priv->hw_refclk = cw1200_refclk;
+
+       priv->sdd_path = (char *)sdd_path;
+       if (cw1200_sdd_path)
+               priv->sdd_path = cw1200_sdd_path;
+
+       priv->hwbus_ops = hwbus_ops;
+       priv->hwbus_priv = hwbus;
+       priv->pdev = pdev;
+       SET_IEEE80211_DEV(priv->hw, pdev);
+
+       /* Pass struct cw1200_common back up */
+       *core = priv;
+
+       err = cw1200_register_bh(priv);
+       if (err)
+               goto err1;
+
+       err = cw1200_load_firmware(priv);
+       if (err)
+               goto err2;
+
+       if (wait_event_interruptible_timeout(priv->wsm_startup_done,
+                                            priv->firmware_ready,
+                                            3*HZ) <= 0) {
+               /* TODO: Need to find how to reset device
+                  in QUEUE mode properly.
+               */
+               pr_err("Timeout waiting on device startup\n");
+               err = -ETIMEDOUT;
+               goto err2;
+       }
+
+       /* Set low-power mode. */
+       wsm_set_operational_mode(priv, &mode);
+
+       /* Enable multi-TX confirmation */
+       wsm_use_multi_tx_conf(priv, true);
+
+       err = cw1200_register_common(dev);
+       if (err)
+               goto err2;
+
+       return err;
+
+err2:
+       cw1200_unregister_bh(priv);
+err1:
+       cw1200_free_common(dev);
+err:
+       *core = NULL;
+       return err;
+}
+EXPORT_SYMBOL_GPL(cw1200_core_probe);
+
+void cw1200_core_release(struct cw1200_common *self)
+{
+       /* Disable device interrupts */
+       self->hwbus_ops->lock(self->hwbus_priv);
+       __cw1200_irq_enable(self, 0);
+       self->hwbus_ops->unlock(self->hwbus_priv);
+
+       /* And then clean up */
+       cw1200_unregister_common(self->hw);
+       cw1200_free_common(self->hw);
+       return;
+}
+EXPORT_SYMBOL_GPL(cw1200_core_release);
diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c
new file mode 100644 (file)
index 0000000..d2202ae
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Mac80211 power management API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/if_ether.h>
+#include "cw1200.h"
+#include "pm.h"
+#include "sta.h"
+#include "bh.h"
+#include "hwbus.h"
+
+#define CW1200_BEACON_SKIPPING_MULTIPLIER 3
+
+struct cw1200_udp_port_filter {
+       struct wsm_udp_port_filter_hdr hdr;
+       /* Up to 4 filters are allowed. */
+       struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS];
+} __packed;
+
+struct cw1200_ether_type_filter {
+       struct wsm_ether_type_filter_hdr hdr;
+       /* Up to 4 filters are allowed. */
+       struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS];
+} __packed;
+
+static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = {
+       .hdr.num = 2,
+       .filters = {
+               [0] = {
+                       .action = WSM_FILTER_ACTION_FILTER_OUT,
+                       .type = WSM_FILTER_PORT_TYPE_DST,
+                       .port = __cpu_to_le16(67), /* DHCP Bootps */
+               },
+               [1] = {
+                       .action = WSM_FILTER_ACTION_FILTER_OUT,
+                       .type = WSM_FILTER_PORT_TYPE_DST,
+                       .port = __cpu_to_le16(68), /* DHCP Bootpc */
+               },
+       }
+};
+
+static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = {
+       .num = 0,
+};
+
+#ifndef ETH_P_WAPI
+#define ETH_P_WAPI     0x88B4
+#endif
+
+static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = {
+       .hdr.num = 4,
+       .filters = {
+               [0] = {
+                       .action = WSM_FILTER_ACTION_FILTER_IN,
+                       .type = __cpu_to_le16(ETH_P_IP),
+               },
+               [1] = {
+                       .action = WSM_FILTER_ACTION_FILTER_IN,
+                       .type = __cpu_to_le16(ETH_P_PAE),
+               },
+               [2] = {
+                       .action = WSM_FILTER_ACTION_FILTER_IN,
+                       .type = __cpu_to_le16(ETH_P_WAPI),
+               },
+               [3] = {
+                       .action = WSM_FILTER_ACTION_FILTER_IN,
+                       .type = __cpu_to_le16(ETH_P_ARP),
+               },
+       },
+};
+
+static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = {
+       .num = 0,
+};
+
+/* private */
+struct cw1200_suspend_state {
+       unsigned long bss_loss_tmo;
+       unsigned long join_tmo;
+       unsigned long direct_probe;
+       unsigned long link_id_gc;
+       bool beacon_skipping;
+       u8 prev_ps_mode;
+};
+
+static void cw1200_pm_stay_awake_tmo(unsigned long arg)
+{
+       /* XXX what's the point of this ? */
+}
+
+int cw1200_pm_init(struct cw1200_pm_state *pm,
+                  struct cw1200_common *priv)
+{
+       spin_lock_init(&pm->lock);
+
+       setup_timer(&pm->stay_awake, cw1200_pm_stay_awake_tmo,
+                   (unsigned long)pm);
+
+       return 0;
+}
+
+void cw1200_pm_deinit(struct cw1200_pm_state *pm)
+{
+       del_timer_sync(&pm->stay_awake);
+}
+
+void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+                         unsigned long tmo)
+{
+       long cur_tmo;
+       spin_lock_bh(&pm->lock);
+       cur_tmo = pm->stay_awake.expires - jiffies;
+       if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
+               mod_timer(&pm->stay_awake, jiffies + tmo);
+       spin_unlock_bh(&pm->lock);
+}
+
+static long cw1200_suspend_work(struct delayed_work *work)
+{
+       int ret = cancel_delayed_work(work);
+       long tmo;
+       if (ret > 0) {
+               /* Timer is pending */
+               tmo = work->timer.expires - jiffies;
+               if (tmo < 0)
+                       tmo = 0;
+       } else {
+               tmo = -1;
+       }
+       return tmo;
+}
+
+static int cw1200_resume_work(struct cw1200_common *priv,
+                              struct delayed_work *work,
+                              unsigned long tmo)
+{
+       if ((long)tmo < 0)
+               return 1;
+
+       return queue_delayed_work(priv->workqueue, work, tmo);
+}
+
+int cw1200_can_suspend(struct cw1200_common *priv)
+{
+       if (atomic_read(&priv->bh_rx)) {
+               wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
+               return 0;
+       }
+       return 1;
+}
+EXPORT_SYMBOL_GPL(cw1200_can_suspend);
+
+int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cw1200_pm_state *pm_state = &priv->pm_state;
+       struct cw1200_suspend_state *state;
+       int ret;
+
+       spin_lock_bh(&pm_state->lock);
+       ret = timer_pending(&pm_state->stay_awake);
+       spin_unlock_bh(&pm_state->lock);
+       if (ret)
+               return -EAGAIN;
+
+       /* Do not suspend when datapath is not idle */
+       if (priv->tx_queue_stats.num_queued)
+               return -EBUSY;
+
+       /* Make sure there is no configuration requests in progress. */
+       if (!mutex_trylock(&priv->conf_mutex))
+               return -EBUSY;
+
+       /* Ensure pending operations are done.
+        * Note also that wow_suspend must return in ~2.5sec, before
+        * watchdog is triggered.
+        */
+       if (priv->channel_switch_in_progress)
+               goto revert1;
+
+       /* Do not suspend when join is pending */
+       if (priv->join_pending)
+               goto revert1;
+
+       /* Do not suspend when scanning */
+       if (down_trylock(&priv->scan.lock))
+               goto revert1;
+
+       /* Lock TX. */
+       wsm_lock_tx_async(priv);
+
+       /* Wait to avoid possible race with bh code.
+        * But do not wait too long...
+        */
+       if (wait_event_timeout(priv->bh_evt_wq,
+                              !priv->hw_bufs_used, HZ / 10) <= 0)
+               goto revert2;
+
+       /* Set UDP filter */
+       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr);
+
+       /* Set ethernet frame type filter */
+       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr);
+
+       /* Allocate state */
+       state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
+       if (!state)
+               goto revert3;
+
+       /* Change to legacy PS while going to suspend */
+       if (!priv->vif->p2p &&
+           priv->join_status == CW1200_JOIN_STATUS_STA &&
+           priv->powersave_mode.mode != WSM_PSM_PS) {
+               state->prev_ps_mode = priv->powersave_mode.mode;
+               priv->powersave_mode.mode = WSM_PSM_PS;
+               cw1200_set_pm(priv, &priv->powersave_mode);
+               if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
+                                                    !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
+                       goto revert4;
+               }
+       }
+
+       /* Store delayed work states. */
+       state->bss_loss_tmo =
+               cw1200_suspend_work(&priv->bss_loss_work);
+       state->join_tmo =
+               cw1200_suspend_work(&priv->join_timeout);
+       state->direct_probe =
+               cw1200_suspend_work(&priv->scan.probe_work);
+       state->link_id_gc =
+               cw1200_suspend_work(&priv->link_id_gc_work);
+
+       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+       atomic_set(&priv->recent_scan, 0);
+
+       /* Enable beacon skipping */
+       if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+           priv->join_dtim_period &&
+           !priv->has_multicast_subscription) {
+               state->beacon_skipping = true;
+               wsm_set_beacon_wakeup_period(priv,
+                                            priv->join_dtim_period,
+                                            CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period);
+       }
+
+       /* Stop serving thread */
+       if (cw1200_bh_suspend(priv))
+               goto revert5;
+
+       ret = timer_pending(&priv->mcast_timeout);
+       if (ret)
+               goto revert6;
+
+       /* Store suspend state */
+       pm_state->suspend_state = state;
+
+       /* Enable IRQ wake */
+       ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true);
+       if (ret) {
+               wiphy_err(priv->hw->wiphy,
+                         "PM request failed: %d. WoW is disabled.\n", ret);
+               cw1200_wow_resume(hw);
+               return -EBUSY;
+       }
+
+       /* Force resume if event is coming from the device. */
+       if (atomic_read(&priv->bh_rx)) {
+               cw1200_wow_resume(hw);
+               return -EAGAIN;
+       }
+
+       return 0;
+
+revert6:
+       WARN_ON(cw1200_bh_resume(priv));
+revert5:
+       cw1200_resume_work(priv, &priv->bss_loss_work,
+                          state->bss_loss_tmo);
+       cw1200_resume_work(priv, &priv->join_timeout,
+                          state->join_tmo);
+       cw1200_resume_work(priv, &priv->scan.probe_work,
+                          state->direct_probe);
+       cw1200_resume_work(priv, &priv->link_id_gc_work,
+                          state->link_id_gc);
+revert4:
+       kfree(state);
+revert3:
+       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
+       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
+revert2:
+       wsm_unlock_tx(priv);
+       up(&priv->scan.lock);
+revert1:
+       mutex_unlock(&priv->conf_mutex);
+       return -EBUSY;
+}
+
+int cw1200_wow_resume(struct ieee80211_hw *hw)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cw1200_pm_state *pm_state = &priv->pm_state;
+       struct cw1200_suspend_state *state;
+
+       state = pm_state->suspend_state;
+       pm_state->suspend_state = NULL;
+
+       /* Disable IRQ wake */
+       priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false);
+
+       /* Scan.lock must be released before BH is resumed other way
+        * in case when BSS_LOST command arrived the processing of the
+        * command will be delayed.
+        */
+       up(&priv->scan.lock);
+
+       /* Resume BH thread */
+       WARN_ON(cw1200_bh_resume(priv));
+
+       /* Restores previous PS mode */
+       if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) {
+               priv->powersave_mode.mode = state->prev_ps_mode;
+               cw1200_set_pm(priv, &priv->powersave_mode);
+       }
+
+       if (state->beacon_skipping) {
+               wsm_set_beacon_wakeup_period(priv, priv->beacon_int *
+                                            priv->join_dtim_period >
+                                            MAX_BEACON_SKIP_TIME_MS ? 1 :
+                                            priv->join_dtim_period, 0);
+               state->beacon_skipping = false;
+       }
+
+       /* Resume delayed work */
+       cw1200_resume_work(priv, &priv->bss_loss_work,
+                          state->bss_loss_tmo);
+       cw1200_resume_work(priv, &priv->join_timeout,
+                          state->join_tmo);
+       cw1200_resume_work(priv, &priv->scan.probe_work,
+                          state->direct_probe);
+       cw1200_resume_work(priv, &priv->link_id_gc_work,
+                          state->link_id_gc);
+
+       /* Remove UDP port filter */
+       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
+
+       /* Remove ethernet frame type filter */
+       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
+
+       /* Unlock datapath */
+       wsm_unlock_tx(priv);
+
+       /* Unlock configuration mutex */
+       mutex_unlock(&priv->conf_mutex);
+
+       /* Free memory */
+       kfree(state);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/st/cw1200/pm.h b/drivers/net/wireless/st/cw1200/pm.h
new file mode 100644 (file)
index 0000000..3ed90ff
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef PM_H_INCLUDED
+#define PM_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API                                                                */
+
+/* extern */  struct cw1200_common;
+/* private */ struct cw1200_suspend_state;
+
+struct cw1200_pm_state {
+       struct cw1200_suspend_state *suspend_state;
+       struct timer_list stay_awake;
+       struct platform_device *pm_dev;
+       spinlock_t lock; /* Protect access */
+};
+
+#ifdef CONFIG_PM
+int cw1200_pm_init(struct cw1200_pm_state *pm,
+                   struct cw1200_common *priv);
+void cw1200_pm_deinit(struct cw1200_pm_state *pm);
+int cw1200_wow_suspend(struct ieee80211_hw *hw,
+                      struct cfg80211_wowlan *wowlan);
+int cw1200_wow_resume(struct ieee80211_hw *hw);
+int cw1200_can_suspend(struct cw1200_common *priv);
+void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+                         unsigned long tmo);
+#else
+static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+                                       unsigned long tmo) {
+}
+#endif
+#endif
diff --git a/drivers/net/wireless/st/cw1200/queue.c b/drivers/net/wireless/st/cw1200/queue.c
new file mode 100644 (file)
index 0000000..0ba5ef9
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include <linux/sched.h>
+#include "queue.h"
+#include "cw1200.h"
+#include "debug.h"
+
+/* private */ struct cw1200_queue_item
+{
+       struct list_head        head;
+       struct sk_buff          *skb;
+       u32                     packet_id;
+       unsigned long           queue_timestamp;
+       unsigned long           xmit_timestamp;
+       struct cw1200_txpriv    txpriv;
+       u8                      generation;
+};
+
+static inline void __cw1200_queue_lock(struct cw1200_queue *queue)
+{
+       struct cw1200_queue_stats *stats = queue->stats;
+       if (queue->tx_locked_cnt++ == 0) {
+               pr_debug("[TX] Queue %d is locked.\n",
+                        queue->queue_id);
+               ieee80211_stop_queue(stats->priv->hw, queue->queue_id);
+       }
+}
+
+static inline void __cw1200_queue_unlock(struct cw1200_queue *queue)
+{
+       struct cw1200_queue_stats *stats = queue->stats;
+       BUG_ON(!queue->tx_locked_cnt);
+       if (--queue->tx_locked_cnt == 0) {
+               pr_debug("[TX] Queue %d is unlocked.\n",
+                        queue->queue_id);
+               ieee80211_wake_queue(stats->priv->hw, queue->queue_id);
+       }
+}
+
+static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation,
+                                        u8 *queue_id, u8 *item_generation,
+                                        u8 *item_id)
+{
+       *item_id                = (packet_id >>  0) & 0xFF;
+       *item_generation        = (packet_id >>  8) & 0xFF;
+       *queue_id               = (packet_id >> 16) & 0xFF;
+       *queue_generation       = (packet_id >> 24) & 0xFF;
+}
+
+static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id,
+                                           u8 item_generation, u8 item_id)
+{
+       return ((u32)item_id << 0) |
+               ((u32)item_generation << 8) |
+               ((u32)queue_id << 16) |
+               ((u32)queue_generation << 24);
+}
+
+static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats,
+                                struct list_head *gc_list)
+{
+       struct cw1200_queue_item *item, *tmp;
+
+       list_for_each_entry_safe(item, tmp, gc_list, head) {
+               list_del(&item->head);
+               stats->skb_dtor(stats->priv, item->skb, &item->txpriv);
+               kfree(item);
+       }
+}
+
+static void cw1200_queue_register_post_gc(struct list_head *gc_list,
+                                         struct cw1200_queue_item *item)
+{
+       struct cw1200_queue_item *gc_item;
+       gc_item = kmalloc(sizeof(struct cw1200_queue_item),
+                       GFP_ATOMIC);
+       BUG_ON(!gc_item);
+       memcpy(gc_item, item, sizeof(struct cw1200_queue_item));
+       list_add_tail(&gc_item->head, gc_list);
+}
+
+static void __cw1200_queue_gc(struct cw1200_queue *queue,
+                             struct list_head *head,
+                             bool unlock)
+{
+       struct cw1200_queue_stats *stats = queue->stats;
+       struct cw1200_queue_item *item = NULL, *tmp;
+       bool wakeup_stats = false;
+
+       list_for_each_entry_safe(item, tmp, &queue->queue, head) {
+               if (jiffies - item->queue_timestamp < queue->ttl)
+                       break;
+               --queue->num_queued;
+               --queue->link_map_cache[item->txpriv.link_id];
+               spin_lock_bh(&stats->lock);
+               --stats->num_queued;
+               if (!--stats->link_map_cache[item->txpriv.link_id])
+                       wakeup_stats = true;
+               spin_unlock_bh(&stats->lock);
+               cw1200_debug_tx_ttl(stats->priv);
+               cw1200_queue_register_post_gc(head, item);
+               item->skb = NULL;
+               list_move_tail(&item->head, &queue->free_pool);
+       }
+
+       if (wakeup_stats)
+               wake_up(&stats->wait_link_id_empty);
+
+       if (queue->overfull) {
+               if (queue->num_queued <= (queue->capacity >> 1)) {
+                       queue->overfull = false;
+                       if (unlock)
+                               __cw1200_queue_unlock(queue);
+               } else if (item) {
+                       unsigned long tmo = item->queue_timestamp + queue->ttl;
+                       mod_timer(&queue->gc, tmo);
+                       cw1200_pm_stay_awake(&stats->priv->pm_state,
+                                            tmo - jiffies);
+               }
+       }
+}
+
+static void cw1200_queue_gc(unsigned long arg)
+{
+       LIST_HEAD(list);
+       struct cw1200_queue *queue =
+               (struct cw1200_queue *)arg;
+
+       spin_lock_bh(&queue->lock);
+       __cw1200_queue_gc(queue, &list, true);
+       spin_unlock_bh(&queue->lock);
+       cw1200_queue_post_gc(queue->stats, &list);
+}
+
+int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
+                           size_t map_capacity,
+                           cw1200_queue_skb_dtor_t skb_dtor,
+                           struct cw1200_common *priv)
+{
+       memset(stats, 0, sizeof(*stats));
+       stats->map_capacity = map_capacity;
+       stats->skb_dtor = skb_dtor;
+       stats->priv = priv;
+       spin_lock_init(&stats->lock);
+       init_waitqueue_head(&stats->wait_link_id_empty);
+
+       stats->link_map_cache = kzalloc(sizeof(int) * map_capacity,
+                                       GFP_KERNEL);
+       if (!stats->link_map_cache)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int cw1200_queue_init(struct cw1200_queue *queue,
+                     struct cw1200_queue_stats *stats,
+                     u8 queue_id,
+                     size_t capacity,
+                     unsigned long ttl)
+{
+       size_t i;
+
+       memset(queue, 0, sizeof(*queue));
+       queue->stats = stats;
+       queue->capacity = capacity;
+       queue->queue_id = queue_id;
+       queue->ttl = ttl;
+       INIT_LIST_HEAD(&queue->queue);
+       INIT_LIST_HEAD(&queue->pending);
+       INIT_LIST_HEAD(&queue->free_pool);
+       spin_lock_init(&queue->lock);
+       setup_timer(&queue->gc, cw1200_queue_gc, (unsigned long)queue);
+
+       queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
+                       GFP_KERNEL);
+       if (!queue->pool)
+               return -ENOMEM;
+
+       queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity,
+                       GFP_KERNEL);
+       if (!queue->link_map_cache) {
+               kfree(queue->pool);
+               queue->pool = NULL;
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < capacity; ++i)
+               list_add_tail(&queue->pool[i].head, &queue->free_pool);
+
+       return 0;
+}
+
+int cw1200_queue_clear(struct cw1200_queue *queue)
+{
+       int i;
+       LIST_HEAD(gc_list);
+       struct cw1200_queue_stats *stats = queue->stats;
+       struct cw1200_queue_item *item, *tmp;
+
+       spin_lock_bh(&queue->lock);
+       queue->generation++;
+       list_splice_tail_init(&queue->queue, &queue->pending);
+       list_for_each_entry_safe(item, tmp, &queue->pending, head) {
+               WARN_ON(!item->skb);
+               cw1200_queue_register_post_gc(&gc_list, item);
+               item->skb = NULL;
+               list_move_tail(&item->head, &queue->free_pool);
+       }
+       queue->num_queued = 0;
+       queue->num_pending = 0;
+
+       spin_lock_bh(&stats->lock);
+       for (i = 0; i < stats->map_capacity; ++i) {
+               stats->num_queued -= queue->link_map_cache[i];
+               stats->link_map_cache[i] -= queue->link_map_cache[i];
+               queue->link_map_cache[i] = 0;
+       }
+       spin_unlock_bh(&stats->lock);
+       if (queue->overfull) {
+               queue->overfull = false;
+               __cw1200_queue_unlock(queue);
+       }
+       spin_unlock_bh(&queue->lock);
+       wake_up(&stats->wait_link_id_empty);
+       cw1200_queue_post_gc(stats, &gc_list);
+       return 0;
+}
+
+void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
+{
+       kfree(stats->link_map_cache);
+       stats->link_map_cache = NULL;
+}
+
+void cw1200_queue_deinit(struct cw1200_queue *queue)
+{
+       cw1200_queue_clear(queue);
+       del_timer_sync(&queue->gc);
+       INIT_LIST_HEAD(&queue->free_pool);
+       kfree(queue->pool);
+       kfree(queue->link_map_cache);
+       queue->pool = NULL;
+       queue->link_map_cache = NULL;
+       queue->capacity = 0;
+}
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+                                  u32 link_id_map)
+{
+       size_t ret;
+       int i, bit;
+       size_t map_capacity = queue->stats->map_capacity;
+
+       if (!link_id_map)
+               return 0;
+
+       spin_lock_bh(&queue->lock);
+       if (link_id_map == (u32)-1) {
+               ret = queue->num_queued - queue->num_pending;
+       } else {
+               ret = 0;
+               for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
+                       if (link_id_map & bit)
+                               ret += queue->link_map_cache[i];
+               }
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+int cw1200_queue_put(struct cw1200_queue *queue,
+                    struct sk_buff *skb,
+                    struct cw1200_txpriv *txpriv)
+{
+       int ret = 0;
+       LIST_HEAD(gc_list);
+       struct cw1200_queue_stats *stats = queue->stats;
+
+       if (txpriv->link_id >= queue->stats->map_capacity)
+               return -EINVAL;
+
+       spin_lock_bh(&queue->lock);
+       if (!WARN_ON(list_empty(&queue->free_pool))) {
+               struct cw1200_queue_item *item = list_first_entry(
+                       &queue->free_pool, struct cw1200_queue_item, head);
+               BUG_ON(item->skb);
+
+               list_move_tail(&item->head, &queue->queue);
+               item->skb = skb;
+               item->txpriv = *txpriv;
+               item->generation = 0;
+               item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
+                                                           queue->queue_id,
+                                                           item->generation,
+                                                           item - queue->pool);
+               item->queue_timestamp = jiffies;
+
+               ++queue->num_queued;
+               ++queue->link_map_cache[txpriv->link_id];
+
+               spin_lock_bh(&stats->lock);
+               ++stats->num_queued;
+               ++stats->link_map_cache[txpriv->link_id];
+               spin_unlock_bh(&stats->lock);
+
+               /* TX may happen in parallel sometimes.
+                * Leave extra queue slots so we don't overflow.
+                */
+               if (queue->overfull == false &&
+                   queue->num_queued >=
+                   (queue->capacity - (num_present_cpus() - 1))) {
+                       queue->overfull = true;
+                       __cw1200_queue_lock(queue);
+                       mod_timer(&queue->gc, jiffies);
+               }
+       } else {
+               ret = -ENOENT;
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+int cw1200_queue_get(struct cw1200_queue *queue,
+                    u32 link_id_map,
+                    struct wsm_tx **tx,
+                    struct ieee80211_tx_info **tx_info,
+                    const struct cw1200_txpriv **txpriv)
+{
+       int ret = -ENOENT;
+       struct cw1200_queue_item *item;
+       struct cw1200_queue_stats *stats = queue->stats;
+       bool wakeup_stats = false;
+
+       spin_lock_bh(&queue->lock);
+       list_for_each_entry(item, &queue->queue, head) {
+               if (link_id_map & BIT(item->txpriv.link_id)) {
+                       ret = 0;
+                       break;
+               }
+       }
+
+       if (!WARN_ON(ret)) {
+               *tx = (struct wsm_tx *)item->skb->data;
+               *tx_info = IEEE80211_SKB_CB(item->skb);
+               *txpriv = &item->txpriv;
+               (*tx)->packet_id = item->packet_id;
+               list_move_tail(&item->head, &queue->pending);
+               ++queue->num_pending;
+               --queue->link_map_cache[item->txpriv.link_id];
+               item->xmit_timestamp = jiffies;
+
+               spin_lock_bh(&stats->lock);
+               --stats->num_queued;
+               if (!--stats->link_map_cache[item->txpriv.link_id])
+                       wakeup_stats = true;
+               spin_unlock_bh(&stats->lock);
+       }
+       spin_unlock_bh(&queue->lock);
+       if (wakeup_stats)
+               wake_up(&stats->wait_link_id_empty);
+       return ret;
+}
+
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id)
+{
+       int ret = 0;
+       u8 queue_generation, queue_id, item_generation, item_id;
+       struct cw1200_queue_item *item;
+       struct cw1200_queue_stats *stats = queue->stats;
+
+       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+                             &item_generation, &item_id);
+
+       item = &queue->pool[item_id];
+
+       spin_lock_bh(&queue->lock);
+       BUG_ON(queue_id != queue->queue_id);
+       if (queue_generation != queue->generation) {
+               ret = -ENOENT;
+       } else if (item_id >= (unsigned) queue->capacity) {
+               WARN_ON(1);
+               ret = -EINVAL;
+       } else if (item->generation != item_generation) {
+               WARN_ON(1);
+               ret = -ENOENT;
+       } else {
+               --queue->num_pending;
+               ++queue->link_map_cache[item->txpriv.link_id];
+
+               spin_lock_bh(&stats->lock);
+               ++stats->num_queued;
+               ++stats->link_map_cache[item->txpriv.link_id];
+               spin_unlock_bh(&stats->lock);
+
+               item->generation = ++item_generation;
+               item->packet_id = cw1200_queue_mk_packet_id(queue_generation,
+                                                           queue_id,
+                                                           item_generation,
+                                                           item_id);
+               list_move(&item->head, &queue->queue);
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+int cw1200_queue_requeue_all(struct cw1200_queue *queue)
+{
+       struct cw1200_queue_item *item, *tmp;
+       struct cw1200_queue_stats *stats = queue->stats;
+       spin_lock_bh(&queue->lock);
+
+       list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) {
+               --queue->num_pending;
+               ++queue->link_map_cache[item->txpriv.link_id];
+
+               spin_lock_bh(&stats->lock);
+               ++stats->num_queued;
+               ++stats->link_map_cache[item->txpriv.link_id];
+               spin_unlock_bh(&stats->lock);
+
+               ++item->generation;
+               item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
+                                                           queue->queue_id,
+                                                           item->generation,
+                                                           item - queue->pool);
+               list_move(&item->head, &queue->queue);
+       }
+       spin_unlock_bh(&queue->lock);
+
+       return 0;
+}
+
+int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id)
+{
+       int ret = 0;
+       u8 queue_generation, queue_id, item_generation, item_id;
+       struct cw1200_queue_item *item;
+       struct cw1200_queue_stats *stats = queue->stats;
+       struct sk_buff *gc_skb = NULL;
+       struct cw1200_txpriv gc_txpriv;
+
+       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+                             &item_generation, &item_id);
+
+       item = &queue->pool[item_id];
+
+       spin_lock_bh(&queue->lock);
+       BUG_ON(queue_id != queue->queue_id);
+       if (queue_generation != queue->generation) {
+               ret = -ENOENT;
+       } else if (item_id >= (unsigned) queue->capacity) {
+               WARN_ON(1);
+               ret = -EINVAL;
+       } else if (item->generation != item_generation) {
+               WARN_ON(1);
+               ret = -ENOENT;
+       } else {
+               gc_txpriv = item->txpriv;
+               gc_skb = item->skb;
+               item->skb = NULL;
+               --queue->num_pending;
+               --queue->num_queued;
+               ++queue->num_sent;
+               ++item->generation;
+               /* Do not use list_move_tail here, but list_move:
+                * try to utilize cache row.
+                */
+               list_move(&item->head, &queue->free_pool);
+
+               if (queue->overfull &&
+                   (queue->num_queued <= (queue->capacity >> 1))) {
+                       queue->overfull = false;
+                       __cw1200_queue_unlock(queue);
+               }
+       }
+       spin_unlock_bh(&queue->lock);
+
+       if (gc_skb)
+               stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv);
+
+       return ret;
+}
+
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
+                        struct sk_buff **skb,
+                        const struct cw1200_txpriv **txpriv)
+{
+       int ret = 0;
+       u8 queue_generation, queue_id, item_generation, item_id;
+       struct cw1200_queue_item *item;
+       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+                             &item_generation, &item_id);
+
+       item = &queue->pool[item_id];
+
+       spin_lock_bh(&queue->lock);
+       BUG_ON(queue_id != queue->queue_id);
+       if (queue_generation != queue->generation) {
+               ret = -ENOENT;
+       } else if (item_id >= (unsigned) queue->capacity) {
+               WARN_ON(1);
+               ret = -EINVAL;
+       } else if (item->generation != item_generation) {
+               WARN_ON(1);
+               ret = -ENOENT;
+       } else {
+               *skb = item->skb;
+               *txpriv = &item->txpriv;
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+void cw1200_queue_lock(struct cw1200_queue *queue)
+{
+       spin_lock_bh(&queue->lock);
+       __cw1200_queue_lock(queue);
+       spin_unlock_bh(&queue->lock);
+}
+
+void cw1200_queue_unlock(struct cw1200_queue *queue)
+{
+       spin_lock_bh(&queue->lock);
+       __cw1200_queue_unlock(queue);
+       spin_unlock_bh(&queue->lock);
+}
+
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+                                    unsigned long *timestamp,
+                                    u32 pending_frame_id)
+{
+       struct cw1200_queue_item *item;
+       bool ret;
+
+       spin_lock_bh(&queue->lock);
+       ret = !list_empty(&queue->pending);
+       if (ret) {
+               list_for_each_entry(item, &queue->pending, head) {
+                       if (item->packet_id != pending_frame_id)
+                               if (time_before(item->xmit_timestamp,
+                                               *timestamp))
+                                       *timestamp = item->xmit_timestamp;
+               }
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
+                                u32 link_id_map)
+{
+       bool empty = true;
+
+       spin_lock_bh(&stats->lock);
+       if (link_id_map == (u32)-1) {
+               empty = stats->num_queued == 0;
+       } else {
+               int i;
+               for (i = 0; i < stats->map_capacity; ++i) {
+                       if (link_id_map & BIT(i)) {
+                               if (stats->link_map_cache[i]) {
+                                       empty = false;
+                                       break;
+                               }
+                       }
+               }
+       }
+       spin_unlock_bh(&stats->lock);
+
+       return empty;
+}
diff --git a/drivers/net/wireless/st/cw1200/queue.h b/drivers/net/wireless/st/cw1200/queue.h
new file mode 100644 (file)
index 0000000..119f9c7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_QUEUE_H_INCLUDED
+#define CW1200_QUEUE_H_INCLUDED
+
+/* private */ struct cw1200_queue_item;
+
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct cw1200_common;
+/* extern */ struct ieee80211_tx_queue_stats;
+/* extern */ struct cw1200_txpriv;
+
+/* forward */ struct cw1200_queue_stats;
+
+typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv,
+                                       struct sk_buff *skb,
+                                       const struct cw1200_txpriv *txpriv);
+
+struct cw1200_queue {
+       struct cw1200_queue_stats *stats;
+       size_t                  capacity;
+       size_t                  num_queued;
+       size_t                  num_pending;
+       size_t                  num_sent;
+       struct cw1200_queue_item *pool;
+       struct list_head        queue;
+       struct list_head        free_pool;
+       struct list_head        pending;
+       int                     tx_locked_cnt;
+       int                     *link_map_cache;
+       bool                    overfull;
+       spinlock_t              lock; /* Protect queue entry */
+       u8                      queue_id;
+       u8                      generation;
+       struct timer_list       gc;
+       unsigned long           ttl;
+};
+
+struct cw1200_queue_stats {
+       spinlock_t              lock; /* Protect stats entry */
+       int                     *link_map_cache;
+       int                     num_queued;
+       size_t                  map_capacity;
+       wait_queue_head_t       wait_link_id_empty;
+       cw1200_queue_skb_dtor_t skb_dtor;
+       struct cw1200_common    *priv;
+};
+
+struct cw1200_txpriv {
+       u8 link_id;
+       u8 raw_link_id;
+       u8 tid;
+       u8 rate_id;
+       u8 offset;
+};
+
+int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
+                           size_t map_capacity,
+                           cw1200_queue_skb_dtor_t skb_dtor,
+                           struct cw1200_common *priv);
+int cw1200_queue_init(struct cw1200_queue *queue,
+                     struct cw1200_queue_stats *stats,
+                     u8 queue_id,
+                     size_t capacity,
+                     unsigned long ttl);
+int cw1200_queue_clear(struct cw1200_queue *queue);
+void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats);
+void cw1200_queue_deinit(struct cw1200_queue *queue);
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+                                  u32 link_id_map);
+int cw1200_queue_put(struct cw1200_queue *queue,
+                    struct sk_buff *skb,
+                    struct cw1200_txpriv *txpriv);
+int cw1200_queue_get(struct cw1200_queue *queue,
+                    u32 link_id_map,
+                    struct wsm_tx **tx,
+                    struct ieee80211_tx_info **tx_info,
+                    const struct cw1200_txpriv **txpriv);
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id);
+int cw1200_queue_requeue_all(struct cw1200_queue *queue);
+int cw1200_queue_remove(struct cw1200_queue *queue,
+                       u32 packet_id);
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
+                        struct sk_buff **skb,
+                        const struct cw1200_txpriv **txpriv);
+void cw1200_queue_lock(struct cw1200_queue *queue);
+void cw1200_queue_unlock(struct cw1200_queue *queue);
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+                                    unsigned long *timestamp,
+                                    u32 pending_frame_id);
+
+bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
+                                u32 link_id_map);
+
+static inline u8 cw1200_queue_get_queue_id(u32 packet_id)
+{
+       return (packet_id >> 16) & 0xFF;
+}
+
+static inline u8 cw1200_queue_get_generation(u32 packet_id)
+{
+       return (packet_id >>  8) & 0xFF;
+}
+
+#endif /* CW1200_QUEUE_H_INCLUDED */
diff --git a/drivers/net/wireless/st/cw1200/scan.c b/drivers/net/wireless/st/cw1200/scan.c
new file mode 100644 (file)
index 0000000..bff81b8
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * Scan implementation for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/sched.h>
+#include "cw1200.h"
+#include "scan.h"
+#include "sta.h"
+#include "pm.h"
+
+static void cw1200_scan_restart_delayed(struct cw1200_common *priv);
+
+static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
+{
+       int ret, i;
+       int tmo = 2000;
+
+       switch (priv->join_status) {
+       case CW1200_JOIN_STATUS_PRE_STA:
+       case CW1200_JOIN_STATUS_JOINING:
+               return -EBUSY;
+       default:
+               break;
+       }
+
+       wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
+                 scan->type, scan->num_channels, scan->flags);
+
+       for (i = 0; i < scan->num_channels; ++i)
+               tmo += scan->ch[i].max_chan_time + 10;
+
+       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+       atomic_set(&priv->scan.in_progress, 1);
+       atomic_set(&priv->recent_scan, 1);
+       cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo));
+       queue_delayed_work(priv->workqueue, &priv->scan.timeout,
+                          msecs_to_jiffies(tmo));
+       ret = wsm_scan(priv, scan);
+       if (ret) {
+               atomic_set(&priv->scan.in_progress, 0);
+               cancel_delayed_work_sync(&priv->scan.timeout);
+               cw1200_scan_restart_delayed(priv);
+       }
+       return ret;
+}
+
+int cw1200_hw_scan(struct ieee80211_hw *hw,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_scan_request *hw_req)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cfg80211_scan_request *req = &hw_req->req;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+       };
+       int i, ret;
+
+       if (!priv->vif)
+               return -EINVAL;
+
+       /* Scan when P2P_GO corrupt firmware MiniAP mode */
+       if (priv->join_status == CW1200_JOIN_STATUS_AP)
+               return -EOPNOTSUPP;
+
+       if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
+               req->n_ssids = 0;
+
+       wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
+                 req->n_ssids);
+
+       if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
+               return -EINVAL;
+
+       frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
+               req->ie_len);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       if (req->ie_len)
+               memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len);
+
+       /* will be unlocked in cw1200_scan_work() */
+       down(&priv->scan.lock);
+       mutex_lock(&priv->conf_mutex);
+
+       ret = wsm_set_template_frame(priv, &frame);
+       if (!ret) {
+               /* Host want to be the probe responder. */
+               ret = wsm_set_probe_responder(priv, true);
+       }
+       if (ret) {
+               mutex_unlock(&priv->conf_mutex);
+               up(&priv->scan.lock);
+               dev_kfree_skb(frame.skb);
+               return ret;
+       }
+
+       wsm_lock_tx(priv);
+
+       BUG_ON(priv->scan.req);
+       priv->scan.req = req;
+       priv->scan.n_ssids = 0;
+       priv->scan.status = 0;
+       priv->scan.begin = &req->channels[0];
+       priv->scan.curr = priv->scan.begin;
+       priv->scan.end = &req->channels[req->n_channels];
+       priv->scan.output_power = priv->output_power;
+
+       for (i = 0; i < req->n_ssids; ++i) {
+               struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
+               memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
+               dst->length = req->ssids[i].ssid_len;
+               ++priv->scan.n_ssids;
+       }
+
+       mutex_unlock(&priv->conf_mutex);
+
+       if (frame.skb)
+               dev_kfree_skb(frame.skb);
+       queue_work(priv->workqueue, &priv->scan.work);
+       return 0;
+}
+
+void cw1200_scan_work(struct work_struct *work)
+{
+       struct cw1200_common *priv = container_of(work, struct cw1200_common,
+                                                       scan.work);
+       struct ieee80211_channel **it;
+       struct wsm_scan scan = {
+               .type = WSM_SCAN_TYPE_FOREGROUND,
+               .flags = WSM_SCAN_FLAG_SPLIT_METHOD,
+       };
+       bool first_run = (priv->scan.begin == priv->scan.curr &&
+                         priv->scan.begin != priv->scan.end);
+       int i;
+
+       if (first_run) {
+               /* Firmware gets crazy if scan request is sent
+                * when STA is joined but not yet associated.
+                * Force unjoin in this case.
+                */
+               if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
+                       cw1200_join_timeout(&priv->join_timeout.work);
+       }
+
+       mutex_lock(&priv->conf_mutex);
+
+       if (first_run) {
+               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+                   !(priv->powersave_mode.mode & WSM_PSM_PS)) {
+                       struct wsm_set_pm pm = priv->powersave_mode;
+                       pm.mode = WSM_PSM_PS;
+                       cw1200_set_pm(priv, &pm);
+               } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+                       /* FW bug: driver has to restart p2p-dev mode
+                        * after scan
+                        */
+                       cw1200_disable_listening(priv);
+               }
+       }
+
+       if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
+               if (priv->scan.output_power != priv->output_power)
+                       wsm_set_output_power(priv, priv->output_power * 10);
+               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+                   !(priv->powersave_mode.mode & WSM_PSM_PS))
+                       cw1200_set_pm(priv, &priv->powersave_mode);
+
+               if (priv->scan.status < 0)
+                       wiphy_warn(priv->hw->wiphy,
+                                  "[SCAN] Scan failed (%d).\n",
+                                  priv->scan.status);
+               else if (priv->scan.req)
+                       wiphy_dbg(priv->hw->wiphy,
+                                 "[SCAN] Scan completed.\n");
+               else
+                       wiphy_dbg(priv->hw->wiphy,
+                                 "[SCAN] Scan canceled.\n");
+
+               priv->scan.req = NULL;
+               cw1200_scan_restart_delayed(priv);
+               wsm_unlock_tx(priv);
+               mutex_unlock(&priv->conf_mutex);
+               ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0);
+               up(&priv->scan.lock);
+               return;
+       } else {
+               struct ieee80211_channel *first = *priv->scan.curr;
+               for (it = priv->scan.curr + 1, i = 1;
+                    it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
+                    ++it, ++i) {
+                       if ((*it)->band != first->band)
+                               break;
+                       if (((*it)->flags ^ first->flags) &
+                                       IEEE80211_CHAN_NO_IR)
+                               break;
+                       if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
+                           (*it)->max_power != first->max_power)
+                               break;
+               }
+               scan.band = first->band;
+
+               if (priv->scan.req->no_cck)
+                       scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
+               else
+                       scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
+               scan.num_probes =
+                       (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
+               scan.num_ssids = priv->scan.n_ssids;
+               scan.ssids = &priv->scan.ssids[0];
+               scan.num_channels = it - priv->scan.curr;
+               /* TODO: Is it optimal? */
+               scan.probe_delay = 100;
+               /* It is not stated in WSM specification, however
+                * FW team says that driver may not use FG scan
+                * when joined.
+                */
+               if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+                       scan.type = WSM_SCAN_TYPE_BACKGROUND;
+                       scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+               }
+               scan.ch = kzalloc(
+                       sizeof(struct wsm_scan_ch) * (it - priv->scan.curr),
+                       GFP_KERNEL);
+               if (!scan.ch) {
+                       priv->scan.status = -ENOMEM;
+                       goto fail;
+               }
+               for (i = 0; i < scan.num_channels; ++i) {
+                       scan.ch[i].number = priv->scan.curr[i]->hw_value;
+                       if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
+                               scan.ch[i].min_chan_time = 50;
+                               scan.ch[i].max_chan_time = 100;
+                       } else {
+                               scan.ch[i].min_chan_time = 10;
+                               scan.ch[i].max_chan_time = 25;
+                       }
+               }
+               if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
+                   priv->scan.output_power != first->max_power) {
+                       priv->scan.output_power = first->max_power;
+                       wsm_set_output_power(priv,
+                                            priv->scan.output_power * 10);
+               }
+               priv->scan.status = cw1200_scan_start(priv, &scan);
+               kfree(scan.ch);
+               if (priv->scan.status)
+                       goto fail;
+               priv->scan.curr = it;
+       }
+       mutex_unlock(&priv->conf_mutex);
+       return;
+
+fail:
+       priv->scan.curr = priv->scan.end;
+       mutex_unlock(&priv->conf_mutex);
+       queue_work(priv->workqueue, &priv->scan.work);
+       return;
+}
+
+static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
+{
+       /* FW bug: driver has to restart p2p-dev mode after scan. */
+       if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+               cw1200_enable_listening(priv);
+               cw1200_update_filtering(priv);
+       }
+
+       if (priv->delayed_unjoin) {
+               priv->delayed_unjoin = false;
+               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+                       wsm_unlock_tx(priv);
+       } else if (priv->delayed_link_loss) {
+                       wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
+                       priv->delayed_link_loss = 0;
+                       cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+       }
+}
+
+static void cw1200_scan_complete(struct cw1200_common *priv)
+{
+       queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
+       if (priv->scan.direct_probe) {
+               wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
+               cw1200_scan_restart_delayed(priv);
+               priv->scan.direct_probe = 0;
+               up(&priv->scan.lock);
+               wsm_unlock_tx(priv);
+       } else {
+               cw1200_scan_work(&priv->scan.work);
+       }
+}
+
+void cw1200_scan_failed_cb(struct cw1200_common *priv)
+{
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+               /* STA is stopped. */
+               return;
+
+       if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
+               priv->scan.status = -EIO;
+               queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
+       }
+}
+
+
+void cw1200_scan_complete_cb(struct cw1200_common *priv,
+                               struct wsm_scan_complete *arg)
+{
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+               /* STA is stopped. */
+               return;
+
+       if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
+               priv->scan.status = 1;
+               queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
+       }
+}
+
+void cw1200_clear_recent_scan_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common,
+                            clear_recent_scan_work.work);
+       atomic_xchg(&priv->recent_scan, 0);
+}
+
+void cw1200_scan_timeout(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, scan.timeout.work);
+       if (atomic_xchg(&priv->scan.in_progress, 0)) {
+               if (priv->scan.status > 0) {
+                       priv->scan.status = 0;
+               } else if (!priv->scan.status) {
+                       wiphy_warn(priv->hw->wiphy,
+                                  "Timeout waiting for scan complete notification.\n");
+                       priv->scan.status = -ETIMEDOUT;
+                       priv->scan.curr = priv->scan.end;
+                       wsm_stop_scan(priv);
+               }
+               cw1200_scan_complete(priv);
+       }
+}
+
+void cw1200_probe_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, scan.probe_work.work);
+       u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+       const struct cw1200_txpriv *txpriv;
+       struct wsm_tx *wsm;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+       };
+       struct wsm_ssid ssids[1] = {{
+               .length = 0,
+       } };
+       struct wsm_scan_ch ch[1] = {{
+               .min_chan_time = 0,
+               .max_chan_time = 10,
+       } };
+       struct wsm_scan scan = {
+               .type = WSM_SCAN_TYPE_FOREGROUND,
+               .num_probes = 1,
+               .probe_delay = 0,
+               .num_channels = 1,
+               .ssids = ssids,
+               .ch = ch,
+       };
+       u8 *ies;
+       size_t ies_len;
+       int ret;
+
+       wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
+
+       mutex_lock(&priv->conf_mutex);
+       if (down_trylock(&priv->scan.lock)) {
+               /* Scan is already in progress. Requeue self. */
+               schedule();
+               queue_delayed_work(priv->workqueue, &priv->scan.probe_work,
+                                  msecs_to_jiffies(100));
+               mutex_unlock(&priv->conf_mutex);
+               return;
+       }
+
+       /* Make sure we still have a pending probe req */
+       if (cw1200_queue_get_skb(queue, priv->pending_frame_id,
+                                &frame.skb, &txpriv)) {
+               up(&priv->scan.lock);
+               mutex_unlock(&priv->conf_mutex);
+               wsm_unlock_tx(priv);
+               return;
+       }
+       wsm = (struct wsm_tx *)frame.skb->data;
+       scan.max_tx_rate = wsm->max_tx_rate;
+       scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+       if (priv->join_status == CW1200_JOIN_STATUS_STA ||
+           priv->join_status == CW1200_JOIN_STATUS_IBSS) {
+               scan.type = WSM_SCAN_TYPE_BACKGROUND;
+               scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+       }
+       ch[0].number = priv->channel->hw_value;
+
+       skb_pull(frame.skb, txpriv->offset);
+
+       ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
+       ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
+
+       if (ies_len) {
+               u8 *ssidie =
+                       (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
+               if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
+                       u8 *nextie = &ssidie[2 + ssidie[1]];
+                       /* Remove SSID from the IE list. It has to be provided
+                        * as a separate argument in cw1200_scan_start call
+                        */
+
+                       /* Store SSID localy */
+                       ssids[0].length = ssidie[1];
+                       memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
+                       scan.num_ssids = 1;
+
+                       /* Remove SSID from IE list */
+                       ssidie[1] = 0;
+                       memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
+                       skb_trim(frame.skb, frame.skb->len - ssids[0].length);
+               }
+       }
+
+       /* FW bug: driver has to restart p2p-dev mode after scan */
+       if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+               cw1200_disable_listening(priv);
+       ret = wsm_set_template_frame(priv, &frame);
+       priv->scan.direct_probe = 1;
+       if (!ret) {
+               wsm_flush_tx(priv);
+               ret = cw1200_scan_start(priv, &scan);
+       }
+       mutex_unlock(&priv->conf_mutex);
+
+       skb_push(frame.skb, txpriv->offset);
+       if (!ret)
+               IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
+       BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
+
+       if (ret) {
+               priv->scan.direct_probe = 0;
+               up(&priv->scan.lock);
+               wsm_unlock_tx(priv);
+       }
+
+       return;
+}
diff --git a/drivers/net/wireless/st/cw1200/scan.h b/drivers/net/wireless/st/cw1200/scan.h
new file mode 100644 (file)
index 0000000..cc75459
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Scan interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SCAN_H_INCLUDED
+#define SCAN_H_INCLUDED
+
+#include <linux/semaphore.h>
+#include "wsm.h"
+
+/* external */ struct sk_buff;
+/* external */ struct cfg80211_scan_request;
+/* external */ struct ieee80211_channel;
+/* external */ struct ieee80211_hw;
+/* external */ struct work_struct;
+
+struct cw1200_scan {
+       struct semaphore lock;
+       struct work_struct work;
+       struct delayed_work timeout;
+       struct cfg80211_scan_request *req;
+       struct ieee80211_channel **begin;
+       struct ieee80211_channel **curr;
+       struct ieee80211_channel **end;
+       struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
+       int output_power;
+       int n_ssids;
+       int status;
+       atomic_t in_progress;
+       /* Direct probe requests workaround */
+       struct delayed_work probe_work;
+       int direct_probe;
+};
+
+int cw1200_hw_scan(struct ieee80211_hw *hw,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_scan_request *hw_req);
+void cw1200_scan_work(struct work_struct *work);
+void cw1200_scan_timeout(struct work_struct *work);
+void cw1200_clear_recent_scan_work(struct work_struct *work);
+void cw1200_scan_complete_cb(struct cw1200_common *priv,
+                            struct wsm_scan_complete *arg);
+void cw1200_scan_failed_cb(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* Raw probe requests TX workaround                                    */
+void cw1200_probe_work(struct work_struct *work);
+
+#endif
diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c
new file mode 100644 (file)
index 0000000..95a7fdb
--- /dev/null
@@ -0,0 +1,2399 @@
+/*
+ * Mac80211 STA API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+
+#include "cw1200.h"
+#include "sta.h"
+#include "fwio.h"
+#include "bh.h"
+#include "debug.h"
+
+#ifndef ERP_INFO_BYTE_OFFSET
+#define ERP_INFO_BYTE_OFFSET 2
+#endif
+
+static void cw1200_do_join(struct cw1200_common *priv);
+static void cw1200_do_unjoin(struct cw1200_common *priv);
+
+static int cw1200_upload_beacon(struct cw1200_common *priv);
+static int cw1200_upload_pspoll(struct cw1200_common *priv);
+static int cw1200_upload_null(struct cw1200_common *priv);
+static int cw1200_upload_qosnull(struct cw1200_common *priv);
+static int cw1200_start_ap(struct cw1200_common *priv);
+static int cw1200_update_beaconing(struct cw1200_common *priv);
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+                                  bool enable);
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+                               struct ieee80211_vif *vif,
+                               enum sta_notify_cmd notify_cmd,
+                               int link_id);
+static int __cw1200_flush(struct cw1200_common *priv, bool drop);
+
+static inline void __cw1200_free_event_queue(struct list_head *list)
+{
+       struct cw1200_wsm_event *event, *tmp;
+       list_for_each_entry_safe(event, tmp, list, link) {
+               list_del(&event->link);
+               kfree(event);
+       }
+}
+
+/* ******************************************************************** */
+/* STA API                                                             */
+
+int cw1200_start(struct ieee80211_hw *dev)
+{
+       struct cw1200_common *priv = dev->priv;
+       int ret = 0;
+
+       cw1200_pm_stay_awake(&priv->pm_state, HZ);
+
+       mutex_lock(&priv->conf_mutex);
+
+       /* default EDCA */
+       WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false);
+       WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false);
+       WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false);
+       WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false);
+       ret = wsm_set_edca_params(priv, &priv->edca);
+       if (ret)
+               goto out;
+
+       ret = cw1200_set_uapsd_param(priv, &priv->edca);
+       if (ret)
+               goto out;
+
+       priv->setbssparams_done = false;
+
+       memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
+       priv->mode = NL80211_IFTYPE_MONITOR;
+       priv->wep_default_key_id = -1;
+
+       priv->cqm_beacon_loss_count = 10;
+
+       ret = cw1200_setup_mac(priv);
+       if (ret)
+               goto out;
+
+out:
+       mutex_unlock(&priv->conf_mutex);
+       return ret;
+}
+
+void cw1200_stop(struct ieee80211_hw *dev)
+{
+       struct cw1200_common *priv = dev->priv;
+       LIST_HEAD(list);
+       int i;
+
+       wsm_lock_tx(priv);
+
+       while (down_trylock(&priv->scan.lock)) {
+               /* Scan is in progress. Force it to stop. */
+               priv->scan.req = NULL;
+               schedule();
+       }
+       up(&priv->scan.lock);
+
+       cancel_delayed_work_sync(&priv->scan.probe_work);
+       cancel_delayed_work_sync(&priv->scan.timeout);
+       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+       cancel_delayed_work_sync(&priv->join_timeout);
+       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+       cancel_work_sync(&priv->unjoin_work);
+       cancel_delayed_work_sync(&priv->link_id_gc_work);
+       flush_workqueue(priv->workqueue);
+       del_timer_sync(&priv->mcast_timeout);
+       mutex_lock(&priv->conf_mutex);
+       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+       priv->listening = false;
+
+       spin_lock(&priv->event_queue_lock);
+       list_splice_init(&priv->event_queue, &list);
+       spin_unlock(&priv->event_queue_lock);
+       __cw1200_free_event_queue(&list);
+
+
+       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+       priv->join_pending = false;
+
+       for (i = 0; i < 4; i++)
+               cw1200_queue_clear(&priv->tx_queue[i]);
+       mutex_unlock(&priv->conf_mutex);
+       tx_policy_clean(priv);
+
+       /* HACK! */
+       if (atomic_xchg(&priv->tx_lock, 1) != 1)
+               pr_debug("[STA] TX is force-unlocked due to stop request.\n");
+
+       wsm_unlock_tx(priv);
+       atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */
+}
+
+static int cw1200_bssloss_mitigation = 1;
+module_param(cw1200_bssloss_mitigation, int, 0644);
+MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)");
+
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+                            int init, int good, int bad)
+{
+       int tx = 0;
+
+       priv->delayed_link_loss = 0;
+       cancel_work_sync(&priv->bss_params_work);
+
+       pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n",
+                priv->bss_loss_state,
+                init, good, bad,
+                atomic_read(&priv->tx_lock),
+                priv->delayed_unjoin);
+
+       /* If we have a pending unjoin */
+       if (priv->delayed_unjoin)
+               return;
+
+       if (init) {
+               queue_delayed_work(priv->workqueue,
+                                  &priv->bss_loss_work,
+                                  HZ);
+               priv->bss_loss_state = 0;
+
+               /* Skip the confimration procedure in P2P case */
+               if (!priv->vif->p2p && !atomic_read(&priv->tx_lock))
+                       tx = 1;
+       } else if (good) {
+               cancel_delayed_work_sync(&priv->bss_loss_work);
+               priv->bss_loss_state = 0;
+               queue_work(priv->workqueue, &priv->bss_params_work);
+       } else if (bad) {
+               /* XXX Should we just keep going until we time out? */
+               if (priv->bss_loss_state < 3)
+                       tx = 1;
+       } else {
+               cancel_delayed_work_sync(&priv->bss_loss_work);
+               priv->bss_loss_state = 0;
+       }
+
+       /* Bypass mitigation if it's disabled */
+       if (!cw1200_bssloss_mitigation)
+               tx = 0;
+
+       /* Spit out a NULL packet to our AP if necessary */
+       if (tx) {
+               struct sk_buff *skb;
+
+               priv->bss_loss_state++;
+
+               skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+               WARN_ON(!skb);
+               if (skb)
+                       cw1200_tx(priv->hw, NULL, skb);
+       }
+}
+
+int cw1200_add_interface(struct ieee80211_hw *dev,
+                        struct ieee80211_vif *vif)
+{
+       int ret;
+       struct cw1200_common *priv = dev->priv;
+       /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
+
+       vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+                            IEEE80211_VIF_SUPPORTS_UAPSD |
+                            IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
+       mutex_lock(&priv->conf_mutex);
+
+       if (priv->mode != NL80211_IFTYPE_MONITOR) {
+               mutex_unlock(&priv->conf_mutex);
+               return -EOPNOTSUPP;
+       }
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+       case NL80211_IFTYPE_AP:
+               priv->mode = vif->type;
+               break;
+       default:
+               mutex_unlock(&priv->conf_mutex);
+               return -EOPNOTSUPP;
+       }
+
+       priv->vif = vif;
+       memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
+       ret = cw1200_setup_mac(priv);
+       /* Enable auto-calibration */
+       /* Exception in subsequent channel switch; disabled.
+        *  wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
+        *      &auto_calibration_mode, sizeof(auto_calibration_mode));
+       */
+
+       mutex_unlock(&priv->conf_mutex);
+       return ret;
+}
+
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+                            struct ieee80211_vif *vif)
+{
+       struct cw1200_common *priv = dev->priv;
+       struct wsm_reset reset = {
+               .reset_statistics = true,
+       };
+       int i;
+
+       mutex_lock(&priv->conf_mutex);
+       switch (priv->join_status) {
+       case CW1200_JOIN_STATUS_JOINING:
+       case CW1200_JOIN_STATUS_PRE_STA:
+       case CW1200_JOIN_STATUS_STA:
+       case CW1200_JOIN_STATUS_IBSS:
+               wsm_lock_tx(priv);
+               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+                       wsm_unlock_tx(priv);
+               break;
+       case CW1200_JOIN_STATUS_AP:
+               for (i = 0; priv->link_id_map; ++i) {
+                       if (priv->link_id_map & BIT(i)) {
+                               reset.link_id = i;
+                               wsm_reset(priv, &reset);
+                               priv->link_id_map &= ~BIT(i);
+                       }
+               }
+               memset(priv->link_id_db, 0, sizeof(priv->link_id_db));
+               priv->sta_asleep_mask = 0;
+               priv->enable_beacon = false;
+               priv->tx_multicast = false;
+               priv->aid0_bit_set = false;
+               priv->buffered_multicasts = false;
+               priv->pspoll_mask = 0;
+               reset.link_id = 0;
+               wsm_reset(priv, &reset);
+               break;
+       case CW1200_JOIN_STATUS_MONITOR:
+               cw1200_update_listening(priv, false);
+               break;
+       default:
+               break;
+       }
+       priv->vif = NULL;
+       priv->mode = NL80211_IFTYPE_MONITOR;
+       eth_zero_addr(priv->mac_addr);
+       memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
+       cw1200_free_keys(priv);
+       cw1200_setup_mac(priv);
+       priv->listening = false;
+       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+       if (!__cw1200_flush(priv, true))
+               wsm_unlock_tx(priv);
+
+       mutex_unlock(&priv->conf_mutex);
+}
+
+int cw1200_change_interface(struct ieee80211_hw *dev,
+                           struct ieee80211_vif *vif,
+                           enum nl80211_iftype new_type,
+                           bool p2p)
+{
+       int ret = 0;
+       pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type,
+                p2p, vif->type, vif->p2p);
+
+       if (new_type != vif->type || vif->p2p != p2p) {
+               cw1200_remove_interface(dev, vif);
+               vif->type = new_type;
+               vif->p2p = p2p;
+               ret = cw1200_add_interface(dev, vif);
+       }
+
+       return ret;
+}
+
+int cw1200_config(struct ieee80211_hw *dev, u32 changed)
+{
+       int ret = 0;
+       struct cw1200_common *priv = dev->priv;
+       struct ieee80211_conf *conf = &dev->conf;
+
+       pr_debug("CONFIG CHANGED:  %08x\n", changed);
+
+       down(&priv->scan.lock);
+       mutex_lock(&priv->conf_mutex);
+       /* TODO: IEEE80211_CONF_CHANGE_QOS */
+       /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */
+
+       if (changed & IEEE80211_CONF_CHANGE_POWER) {
+               priv->output_power = conf->power_level;
+               pr_debug("[STA] TX power: %d\n", priv->output_power);
+               wsm_set_output_power(priv, priv->output_power * 10);
+       }
+
+       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
+           (priv->channel != conf->chandef.chan)) {
+               struct ieee80211_channel *ch = conf->chandef.chan;
+               struct wsm_switch_channel channel = {
+                       .channel_number = ch->hw_value,
+               };
+               pr_debug("[STA] Freq %d (wsm ch: %d).\n",
+                        ch->center_freq, ch->hw_value);
+
+               /* __cw1200_flush() implicitly locks tx, if successful */
+               if (!__cw1200_flush(priv, false)) {
+                       if (!wsm_switch_channel(priv, &channel)) {
+                               ret = wait_event_timeout(priv->channel_switch_done,
+                                                        !priv->channel_switch_in_progress,
+                                                        3 * HZ);
+                               if (ret) {
+                                       /* Already unlocks if successful */
+                                       priv->channel = ch;
+                                       ret = 0;
+                               } else {
+                                       ret = -ETIMEDOUT;
+                               }
+                       } else {
+                               /* Unlock if switch channel fails */
+                               wsm_unlock_tx(priv);
+                       }
+               }
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_PS) {
+               if (!(conf->flags & IEEE80211_CONF_PS))
+                       priv->powersave_mode.mode = WSM_PSM_ACTIVE;
+               else if (conf->dynamic_ps_timeout <= 0)
+                       priv->powersave_mode.mode = WSM_PSM_PS;
+               else
+                       priv->powersave_mode.mode = WSM_PSM_FAST_PS;
+
+               /* Firmware requires that value for this 1-byte field must
+                * be specified in units of 500us. Values above the 128ms
+                * threshold are not supported.
+                */
+               if (conf->dynamic_ps_timeout >= 0x80)
+                       priv->powersave_mode.fast_psm_idle_period = 0xFF;
+               else
+                       priv->powersave_mode.fast_psm_idle_period =
+                                       conf->dynamic_ps_timeout << 1;
+
+               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+                   priv->bss_params.aid)
+                       cw1200_set_pm(priv, &priv->powersave_mode);
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+               /* TBD: It looks like it's transparent
+                * there's a monitor interface present -- use this
+                * to determine for example whether to calculate
+                * timestamps for packets or not, do not use instead
+                * of filter flags!
+                */
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+               struct wsm_operational_mode mode = {
+                       .power_mode = cw1200_power_mode,
+                       .disable_more_flag_usage = true,
+               };
+
+               wsm_lock_tx(priv);
+               /* Disable p2p-dev mode forced by TX request */
+               if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) &&
+                   (conf->flags & IEEE80211_CONF_IDLE) &&
+                   !priv->listening) {
+                       cw1200_disable_listening(priv);
+                       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+               }
+               wsm_set_operational_mode(priv, &mode);
+               wsm_unlock_tx(priv);
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+               pr_debug("[STA] Retry limits: %d (long), %d (short).\n",
+                        conf->long_frame_max_tx_count,
+                        conf->short_frame_max_tx_count);
+               spin_lock_bh(&priv->tx_policy_cache.lock);
+               priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
+               priv->short_frame_max_tx_count =
+                       (conf->short_frame_max_tx_count < 0x0F) ?
+                       conf->short_frame_max_tx_count : 0x0F;
+               priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
+               spin_unlock_bh(&priv->tx_policy_cache.lock);
+       }
+       mutex_unlock(&priv->conf_mutex);
+       up(&priv->scan.lock);
+       return ret;
+}
+
+void cw1200_update_filtering(struct cw1200_common *priv)
+{
+       int ret;
+       bool bssid_filtering = !priv->rx_filter.bssid;
+       bool is_p2p = priv->vif && priv->vif->p2p;
+       bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type;
+
+       static struct wsm_beacon_filter_control bf_ctrl;
+       static struct wsm_mib_beacon_filter_table bf_tbl = {
+               .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC,
+               .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
+               .entry[0].oui[0] = 0x50,
+               .entry[0].oui[1] = 0x6F,
+               .entry[0].oui[2] = 0x9A,
+               .entry[1].ie_id = WLAN_EID_HT_OPERATION,
+               .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
+               .entry[2].ie_id = WLAN_EID_ERP_INFO,
+               .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
+       };
+
+       if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE)
+               return;
+       else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+               bssid_filtering = false;
+
+       if (priv->disable_beacon_filter) {
+               bf_ctrl.enabled = 0;
+               bf_ctrl.bcn_count = 1;
+               bf_tbl.num = __cpu_to_le32(0);
+       } else if (is_p2p || !is_sta) {
+               bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE |
+                       WSM_BEACON_FILTER_AUTO_ERP;
+               bf_ctrl.bcn_count = 0;
+               bf_tbl.num = __cpu_to_le32(2);
+       } else {
+               bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE;
+               bf_ctrl.bcn_count = 0;
+               bf_tbl.num = __cpu_to_le32(3);
+       }
+
+       /* When acting as p2p client being connected to p2p GO, in order to
+        * receive frames from a different p2p device, turn off bssid filter.
+        *
+        * WARNING: FW dependency!
+        * This can only be used with FW WSM371 and its successors.
+        * In that FW version even with bssid filter turned off,
+        * device will block most of the unwanted frames.
+        */
+       if (is_p2p)
+               bssid_filtering = false;
+
+       ret = wsm_set_rx_filter(priv, &priv->rx_filter);
+       if (!ret)
+               ret = wsm_set_beacon_filter_table(priv, &bf_tbl);
+       if (!ret)
+               ret = wsm_beacon_filter_control(priv, &bf_ctrl);
+       if (!ret)
+               ret = wsm_set_bssid_filtering(priv, bssid_filtering);
+       if (!ret)
+               ret = wsm_set_multicast_filter(priv, &priv->multicast_filter);
+       if (ret)
+               wiphy_err(priv->hw->wiphy,
+                         "Update filtering failed: %d.\n", ret);
+       return;
+}
+
+void cw1200_update_filtering_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common,
+                            update_filtering_work);
+
+       cw1200_update_filtering(priv);
+}
+
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common,
+                            set_beacon_wakeup_period_work);
+
+       wsm_set_beacon_wakeup_period(priv,
+                                    priv->beacon_int * priv->join_dtim_period >
+                                    MAX_BEACON_SKIP_TIME_MS ? 1 :
+                                    priv->join_dtim_period, 0);
+}
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+                            struct netdev_hw_addr_list *mc_list)
+{
+       static u8 broadcast_ipv6[ETH_ALEN] = {
+               0x33, 0x33, 0x00, 0x00, 0x00, 0x01
+       };
+       static u8 broadcast_ipv4[ETH_ALEN] = {
+               0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
+       };
+       struct cw1200_common *priv = hw->priv;
+       struct netdev_hw_addr *ha;
+       int count = 0;
+
+       /* Disable multicast filtering */
+       priv->has_multicast_subscription = false;
+       memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
+
+       if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
+               return 0;
+
+       /* Enable if requested */
+       netdev_hw_addr_list_for_each(ha, mc_list) {
+               pr_debug("[STA] multicast: %pM\n", ha->addr);
+               memcpy(&priv->multicast_filter.macaddrs[count],
+                      ha->addr, ETH_ALEN);
+               if (!ether_addr_equal(ha->addr, broadcast_ipv4) &&
+                   !ether_addr_equal(ha->addr, broadcast_ipv6))
+                       priv->has_multicast_subscription = true;
+               count++;
+       }
+
+       if (count) {
+               priv->multicast_filter.enable = __cpu_to_le32(1);
+               priv->multicast_filter.num_addrs = __cpu_to_le32(count);
+       }
+
+       return netdev_hw_addr_list_count(mc_list);
+}
+
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+                            unsigned int changed_flags,
+                            unsigned int *total_flags,
+                            u64 multicast)
+{
+       struct cw1200_common *priv = dev->priv;
+       bool listening = !!(*total_flags &
+                           (FIF_OTHER_BSS |
+                            FIF_BCN_PRBRESP_PROMISC |
+                            FIF_PROBE_REQ));
+
+       *total_flags &= FIF_OTHER_BSS |
+                       FIF_FCSFAIL |
+                       FIF_BCN_PRBRESP_PROMISC |
+                       FIF_PROBE_REQ;
+
+       down(&priv->scan.lock);
+       mutex_lock(&priv->conf_mutex);
+
+       priv->rx_filter.promiscuous = 0;
+       priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
+                       FIF_PROBE_REQ)) ? 1 : 0;
+       priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
+       priv->disable_beacon_filter = !(*total_flags &
+                                       (FIF_BCN_PRBRESP_PROMISC |
+                                        FIF_PROBE_REQ));
+       if (priv->listening != listening) {
+               priv->listening = listening;
+               wsm_lock_tx(priv);
+               cw1200_update_listening(priv, listening);
+               wsm_unlock_tx(priv);
+       }
+       cw1200_update_filtering(priv);
+       mutex_unlock(&priv->conf_mutex);
+       up(&priv->scan.lock);
+}
+
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+                  u16 queue, const struct ieee80211_tx_queue_params *params)
+{
+       struct cw1200_common *priv = dev->priv;
+       int ret = 0;
+       /* To prevent re-applying PM request OID again and again*/
+       bool old_uapsd_flags;
+
+       mutex_lock(&priv->conf_mutex);
+
+       if (queue < dev->queues) {
+               old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags);
+
+               WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
+               ret = wsm_set_tx_queue_params(priv,
+                                             &priv->tx_queue_params.params[queue], queue);
+               if (ret) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               WSM_EDCA_SET(&priv->edca, queue, params->aifs,
+                            params->cw_min, params->cw_max,
+                            params->txop, 0xc8,
+                            params->uapsd);
+               ret = wsm_set_edca_params(priv, &priv->edca);
+               if (ret) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (priv->mode == NL80211_IFTYPE_STATION) {
+                       ret = cw1200_set_uapsd_param(priv, &priv->edca);
+                       if (!ret && priv->setbssparams_done &&
+                           (priv->join_status == CW1200_JOIN_STATUS_STA) &&
+                           (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags)))
+                               ret = cw1200_set_pm(priv, &priv->powersave_mode);
+               }
+       } else {
+               ret = -EINVAL;
+       }
+
+out:
+       mutex_unlock(&priv->conf_mutex);
+       return ret;
+}
+
+int cw1200_get_stats(struct ieee80211_hw *dev,
+                    struct ieee80211_low_level_stats *stats)
+{
+       struct cw1200_common *priv = dev->priv;
+
+       memcpy(stats, &priv->stats, sizeof(*stats));
+       return 0;
+}
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+       struct wsm_set_pm pm = *arg;
+
+       if (priv->uapsd_info.uapsd_flags != 0)
+               pm.mode &= ~WSM_PSM_FAST_PS_FLAG;
+
+       if (memcmp(&pm, &priv->firmware_ps_mode,
+                  sizeof(struct wsm_set_pm))) {
+               priv->firmware_ps_mode = pm;
+               return wsm_set_pm(priv, &pm);
+       } else {
+               return 0;
+       }
+}
+
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+                  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                  struct ieee80211_key_conf *key)
+{
+       int ret = -EOPNOTSUPP;
+       struct cw1200_common *priv = dev->priv;
+       struct ieee80211_key_seq seq;
+
+       mutex_lock(&priv->conf_mutex);
+
+       if (cmd == SET_KEY) {
+               u8 *peer_addr = NULL;
+               int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+                       1 : 0;
+               int idx = cw1200_alloc_key(priv);
+               struct wsm_add_key *wsm_key = &priv->keys[idx];
+
+               if (idx < 0) {
+                       ret = -EINVAL;
+                       goto finally;
+               }
+
+               if (sta)
+                       peer_addr = sta->addr;
+
+               key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE |
+                             IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
+
+               switch (key->cipher) {
+               case WLAN_CIPHER_SUITE_WEP40:
+               case WLAN_CIPHER_SUITE_WEP104:
+                       if (key->keylen > 16) {
+                               cw1200_free_key(priv, idx);
+                               ret = -EINVAL;
+                               goto finally;
+                       }
+
+                       if (pairwise) {
+                               wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
+                               memcpy(wsm_key->wep_pairwise.peer,
+                                      peer_addr, ETH_ALEN);
+                               memcpy(wsm_key->wep_pairwise.keydata,
+                                      &key->key[0], key->keylen);
+                               wsm_key->wep_pairwise.keylen = key->keylen;
+                       } else {
+                               wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
+                               memcpy(wsm_key->wep_group.keydata,
+                                      &key->key[0], key->keylen);
+                               wsm_key->wep_group.keylen = key->keylen;
+                               wsm_key->wep_group.keyid = key->keyidx;
+                       }
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       ieee80211_get_key_rx_seq(key, 0, &seq);
+                       if (pairwise) {
+                               wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
+                               memcpy(wsm_key->tkip_pairwise.peer,
+                                      peer_addr, ETH_ALEN);
+                               memcpy(wsm_key->tkip_pairwise.keydata,
+                                      &key->key[0], 16);
+                               memcpy(wsm_key->tkip_pairwise.tx_mic_key,
+                                      &key->key[16], 8);
+                               memcpy(wsm_key->tkip_pairwise.rx_mic_key,
+                                      &key->key[24], 8);
+                       } else {
+                               size_t mic_offset =
+                                       (priv->mode == NL80211_IFTYPE_AP) ?
+                                       16 : 24;
+                               wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
+                               memcpy(wsm_key->tkip_group.keydata,
+                                      &key->key[0], 16);
+                               memcpy(wsm_key->tkip_group.rx_mic_key,
+                                      &key->key[mic_offset], 8);
+
+                               wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[6] = 0;
+                               wsm_key->tkip_group.rx_seqnum[7] = 0;
+
+                               wsm_key->tkip_group.keyid = key->keyidx;
+                       }
+                       break;
+               case WLAN_CIPHER_SUITE_CCMP:
+                       ieee80211_get_key_rx_seq(key, 0, &seq);
+                       if (pairwise) {
+                               wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
+                               memcpy(wsm_key->aes_pairwise.peer,
+                                      peer_addr, ETH_ALEN);
+                               memcpy(wsm_key->aes_pairwise.keydata,
+                                      &key->key[0], 16);
+                       } else {
+                               wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
+                               memcpy(wsm_key->aes_group.keydata,
+                                      &key->key[0], 16);
+
+                               wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5];
+                               wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4];
+                               wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3];
+                               wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2];
+                               wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1];
+                               wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0];
+                               wsm_key->aes_group.rx_seqnum[6] = 0;
+                               wsm_key->aes_group.rx_seqnum[7] = 0;
+                               wsm_key->aes_group.keyid = key->keyidx;
+                       }
+                       break;
+               case WLAN_CIPHER_SUITE_SMS4:
+                       if (pairwise) {
+                               wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
+                               memcpy(wsm_key->wapi_pairwise.peer,
+                                      peer_addr, ETH_ALEN);
+                               memcpy(wsm_key->wapi_pairwise.keydata,
+                                      &key->key[0], 16);
+                               memcpy(wsm_key->wapi_pairwise.mic_key,
+                                      &key->key[16], 16);
+                               wsm_key->wapi_pairwise.keyid = key->keyidx;
+                       } else {
+                               wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
+                               memcpy(wsm_key->wapi_group.keydata,
+                                      &key->key[0],  16);
+                               memcpy(wsm_key->wapi_group.mic_key,
+                                      &key->key[16], 16);
+                               wsm_key->wapi_group.keyid = key->keyidx;
+                       }
+                       break;
+               default:
+                       pr_warn("Unhandled key type %d\n", key->cipher);
+                       cw1200_free_key(priv, idx);
+                       ret = -EOPNOTSUPP;
+                       goto finally;
+               }
+               ret = wsm_add_key(priv, wsm_key);
+               if (!ret)
+                       key->hw_key_idx = idx;
+               else
+                       cw1200_free_key(priv, idx);
+       } else if (cmd == DISABLE_KEY) {
+               struct wsm_remove_key wsm_key = {
+                       .index = key->hw_key_idx,
+               };
+
+               if (wsm_key.index > WSM_KEY_MAX_INDEX) {
+                       ret = -EINVAL;
+                       goto finally;
+               }
+
+               cw1200_free_key(priv, wsm_key.index);
+               ret = wsm_remove_key(priv, &wsm_key);
+       } else {
+               pr_warn("Unhandled key command %d\n", cmd);
+       }
+
+finally:
+       mutex_unlock(&priv->conf_mutex);
+       return ret;
+}
+
+void cw1200_wep_key_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, wep_key_work);
+       u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+       __le32 wep_default_key_id = __cpu_to_le32(
+               priv->wep_default_key_id);
+
+       pr_debug("[STA] Setting default WEP key: %d\n",
+                priv->wep_default_key_id);
+       wsm_flush_tx(priv);
+       wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
+                     &wep_default_key_id, sizeof(wep_default_key_id));
+       cw1200_queue_requeue(queue, priv->pending_frame_id);
+       wsm_unlock_tx(priv);
+}
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       int ret = 0;
+       __le32 val32;
+       struct cw1200_common *priv = hw->priv;
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+               return 0;
+
+       if (value != (u32) -1)
+               val32 = __cpu_to_le32(value);
+       else
+               val32 = 0; /* disabled */
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+               /* device is down, can _not_ set threshold */
+               ret = -ENODEV;
+               goto out;
+       }
+
+       if (priv->rts_threshold == value)
+               goto out;
+
+       pr_debug("[STA] Setting RTS threshold: %d\n",
+                priv->rts_threshold);
+
+       /* mutex_lock(&priv->conf_mutex); */
+       ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
+                           &val32, sizeof(val32));
+       if (!ret)
+               priv->rts_threshold = value;
+       /* mutex_unlock(&priv->conf_mutex); */
+
+out:
+       return ret;
+}
+
+/* If successful, LOCKS the TX queue! */
+static int __cw1200_flush(struct cw1200_common *priv, bool drop)
+{
+       int i, ret;
+
+       for (;;) {
+               /* TODO: correct flush handling is required when dev_stop.
+                * Temporary workaround: 2s
+                */
+               if (drop) {
+                       for (i = 0; i < 4; ++i)
+                               cw1200_queue_clear(&priv->tx_queue[i]);
+               } else {
+                       ret = wait_event_timeout(
+                               priv->tx_queue_stats.wait_link_id_empty,
+                               cw1200_queue_stats_is_empty(
+                                       &priv->tx_queue_stats, -1),
+                               2 * HZ);
+               }
+
+               if (!drop && ret <= 0) {
+                       ret = -ETIMEDOUT;
+                       break;
+               } else {
+                       ret = 0;
+               }
+
+               wsm_lock_tx(priv);
+               if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) {
+                       /* Highly unlikely: WSM requeued frames. */
+                       wsm_unlock_tx(priv);
+                       continue;
+               }
+               break;
+       }
+       return ret;
+}
+
+void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop)
+{
+       struct cw1200_common *priv = hw->priv;
+
+       switch (priv->mode) {
+       case NL80211_IFTYPE_MONITOR:
+               drop = true;
+               break;
+       case NL80211_IFTYPE_AP:
+               if (!priv->enable_beacon)
+                       drop = true;
+               break;
+       }
+
+       if (!__cw1200_flush(priv, drop))
+               wsm_unlock_tx(priv);
+
+       return;
+}
+
+/* ******************************************************************** */
+/* WSM callbacks                                                       */
+
+void cw1200_free_event_queue(struct cw1200_common *priv)
+{
+       LIST_HEAD(list);
+
+       spin_lock(&priv->event_queue_lock);
+       list_splice_init(&priv->event_queue, &list);
+       spin_unlock(&priv->event_queue_lock);
+
+       __cw1200_free_event_queue(&list);
+}
+
+void cw1200_event_handler(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, event_handler);
+       struct cw1200_wsm_event *event;
+       LIST_HEAD(list);
+
+       spin_lock(&priv->event_queue_lock);
+       list_splice_init(&priv->event_queue, &list);
+       spin_unlock(&priv->event_queue_lock);
+
+       list_for_each_entry(event, &list, link) {
+               switch (event->evt.id) {
+               case WSM_EVENT_ERROR:
+                       pr_err("Unhandled WSM Error from LMAC\n");
+                       break;
+               case WSM_EVENT_BSS_LOST:
+                       pr_debug("[CQM] BSS lost.\n");
+                       cancel_work_sync(&priv->unjoin_work);
+                       if (!down_trylock(&priv->scan.lock)) {
+                               cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+                               up(&priv->scan.lock);
+                       } else {
+                               /* Scan is in progress. Delay reporting.
+                                * Scan complete will trigger bss_loss_work
+                                */
+                               priv->delayed_link_loss = 1;
+                               /* Also start a watchdog. */
+                               queue_delayed_work(priv->workqueue,
+                                                  &priv->bss_loss_work, 5*HZ);
+                       }
+                       break;
+               case WSM_EVENT_BSS_REGAINED:
+                       pr_debug("[CQM] BSS regained.\n");
+                       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+                       cancel_work_sync(&priv->unjoin_work);
+                       break;
+               case WSM_EVENT_RADAR_DETECTED:
+                       wiphy_info(priv->hw->wiphy, "radar pulse detected\n");
+                       break;
+               case WSM_EVENT_RCPI_RSSI:
+               {
+                       /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+                        * RSSI = RCPI / 2 - 110
+                        */
+                       int rcpi_rssi = (int)(event->evt.data & 0xFF);
+                       int cqm_evt;
+                       if (priv->cqm_use_rssi)
+                               rcpi_rssi = (s8)rcpi_rssi;
+                       else
+                               rcpi_rssi =  rcpi_rssi / 2 - 110;
+
+                       cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+                       pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi);
+                       ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
+                                                 GFP_KERNEL);
+                       break;
+               }
+               case WSM_EVENT_BT_INACTIVE:
+                       pr_warn("Unhandled BT INACTIVE from LMAC\n");
+                       break;
+               case WSM_EVENT_BT_ACTIVE:
+                       pr_warn("Unhandled BT ACTIVE from LMAC\n");
+                       break;
+               }
+       }
+       __cw1200_free_event_queue(&list);
+}
+
+void cw1200_bss_loss_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, bss_loss_work.work);
+
+       pr_debug("[CQM] Reporting connection loss.\n");
+       wsm_lock_tx(priv);
+       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+               wsm_unlock_tx(priv);
+}
+
+void cw1200_bss_params_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, bss_params_work);
+       mutex_lock(&priv->conf_mutex);
+
+       priv->bss_params.reset_beacon_loss = 1;
+       wsm_set_bss_params(priv, &priv->bss_params);
+       priv->bss_params.reset_beacon_loss = 0;
+
+       mutex_unlock(&priv->conf_mutex);
+}
+
+/* ******************************************************************** */
+/* Internal API                                                                */
+
+/* This function is called to Parse the SDD file
+ * to extract listen_interval and PTA related information
+ * sdd is a TLV: u8 id, u8 len, u8 data[]
+ */
+static int cw1200_parse_sdd_file(struct cw1200_common *priv)
+{
+       const u8 *p = priv->sdd->data;
+       int ret = 0;
+
+       while (p + 2 <= priv->sdd->data + priv->sdd->size) {
+               if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) {
+                       pr_warn("Malformed sdd structure\n");
+                       return -1;
+               }
+               switch (p[0]) {
+               case SDD_PTA_CFG_ELT_ID: {
+                       u16 v;
+                       if (p[1] < 4) {
+                               pr_warn("SDD_PTA_CFG_ELT_ID malformed\n");
+                               ret = -1;
+                               break;
+                       }
+                       v = le16_to_cpu(*((__le16 *)(p + 2)));
+                       if (!v)  /* non-zero means this is enabled */
+                               break;
+
+                       v = le16_to_cpu(*((__le16 *)(p + 4)));
+                       priv->conf_listen_interval = (v >> 7) & 0x1F;
+                       pr_debug("PTA found; Listen Interval %d\n",
+                                priv->conf_listen_interval);
+                       break;
+               }
+               case SDD_REFERENCE_FREQUENCY_ELT_ID: {
+                       u16 clk = le16_to_cpu(*((__le16 *)(p + 2)));
+                       if (clk != priv->hw_refclk)
+                               pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n",
+                                       clk, priv->hw_refclk);
+                       break;
+               }
+               default:
+                       break;
+               }
+               p += p[1] + 2;
+       }
+
+       if (!priv->bt_present) {
+               pr_debug("PTA element NOT found.\n");
+               priv->conf_listen_interval = 0;
+       }
+       return ret;
+}
+
+int cw1200_setup_mac(struct cw1200_common *priv)
+{
+       int ret = 0;
+
+       /* NOTE: There is a bug in FW: it reports signal
+        * as RSSI if RSSI subscription is enabled.
+        * It's not enough to set WSM_RCPI_RSSI_USE_RSSI.
+        *
+        * NOTE2: RSSI based reports have been switched to RCPI, since
+        * FW has a bug and RSSI reported values are not stable,
+        * what can leads to signal level oscilations in user-end applications
+        */
+       struct wsm_rcpi_rssi_threshold threshold = {
+               .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+               WSM_RCPI_RSSI_DONT_USE_UPPER |
+               WSM_RCPI_RSSI_DONT_USE_LOWER,
+               .rollingAverageCount = 16,
+       };
+
+       struct wsm_configuration cfg = {
+               .dot11StationId = &priv->mac_addr[0],
+       };
+
+       /* Remember the decission here to make sure, we will handle
+        * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS
+        */
+       if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
+               priv->cqm_use_rssi = true;
+
+       if (!priv->sdd) {
+               ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev);
+               if (ret) {
+                       pr_err("Can't load sdd file %s.\n", priv->sdd_path);
+                       return ret;
+               }
+               cw1200_parse_sdd_file(priv);
+       }
+
+       cfg.dpdData = priv->sdd->data;
+       cfg.dpdData_size = priv->sdd->size;
+       ret = wsm_configuration(priv, &cfg);
+       if (ret)
+               return ret;
+
+       /* Configure RSSI/SCPI reporting as RSSI. */
+       wsm_set_rcpi_rssi_threshold(priv, &threshold);
+
+       return 0;
+}
+
+static void cw1200_join_complete(struct cw1200_common *priv)
+{
+       pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status);
+
+       priv->join_pending = false;
+       if (priv->join_complete_status) {
+               priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+               cw1200_update_listening(priv, priv->listening);
+               cw1200_do_unjoin(priv);
+               ieee80211_connection_loss(priv->vif);
+       } else {
+               if (priv->mode == NL80211_IFTYPE_ADHOC)
+                       priv->join_status = CW1200_JOIN_STATUS_IBSS;
+               else
+                       priv->join_status = CW1200_JOIN_STATUS_PRE_STA;
+       }
+       wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */
+}
+
+void cw1200_join_complete_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, join_complete_work);
+       mutex_lock(&priv->conf_mutex);
+       cw1200_join_complete(priv);
+       mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+                            struct wsm_join_complete *arg)
+{
+       pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n",
+                arg->status);
+
+       if (cancel_delayed_work(&priv->join_timeout)) {
+               priv->join_complete_status = arg->status;
+               queue_work(priv->workqueue, &priv->join_complete_work);
+       }
+}
+
+/* MUST be called with tx_lock held!  It will be unlocked for us. */
+static void cw1200_do_join(struct cw1200_common *priv)
+{
+       const u8 *bssid;
+       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+       struct cfg80211_bss *bss = NULL;
+       struct wsm_protected_mgmt_policy mgmt_policy;
+       struct wsm_join join = {
+               .mode = conf->ibss_joined ?
+                               WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
+               .preamble_type = WSM_JOIN_PREAMBLE_LONG,
+               .probe_for_join = 1,
+               .atim_window = 0,
+               .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+                                                         conf->basic_rates),
+       };
+       if (delayed_work_pending(&priv->join_timeout)) {
+               pr_warn("[STA] - Join request already pending, skipping..\n");
+               wsm_unlock_tx(priv);
+               return;
+       }
+
+       if (priv->join_status)
+               cw1200_do_unjoin(priv);
+
+       bssid = priv->vif->bss_conf.bssid;
+
+       bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0,
+                              IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
+
+       if (!bss && !conf->ibss_joined) {
+               wsm_unlock_tx(priv);
+               return;
+       }
+
+       mutex_lock(&priv->conf_mutex);
+
+       /* Under the conf lock: check scan status and
+        * bail out if it is in progress.
+        */
+       if (atomic_read(&priv->scan.in_progress)) {
+               wsm_unlock_tx(priv);
+               goto done_put;
+       }
+
+       priv->join_pending = true;
+
+       /* Sanity check basic rates */
+       if (!join.basic_rate_set)
+               join.basic_rate_set = 7;
+
+       /* Sanity check beacon interval */
+       if (!priv->beacon_int)
+               priv->beacon_int = 1;
+
+       join.beacon_interval = priv->beacon_int;
+
+       /* BT Coex related changes */
+       if (priv->bt_present) {
+               if (((priv->conf_listen_interval * 100) %
+                    priv->beacon_int) == 0)
+                       priv->listen_interval =
+                               ((priv->conf_listen_interval * 100) /
+                                priv->beacon_int);
+               else
+                       priv->listen_interval =
+                               ((priv->conf_listen_interval * 100) /
+                                priv->beacon_int + 1);
+       }
+
+       if (priv->hw->conf.ps_dtim_period)
+               priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
+       join.dtim_period = priv->join_dtim_period;
+
+       join.channel_number = priv->channel->hw_value;
+       join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+
+       memcpy(join.bssid, bssid, sizeof(join.bssid));
+
+       pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n",
+                join.bssid,
+                join.dtim_period, priv->beacon_int);
+
+       if (!conf->ibss_joined) {
+               const u8 *ssidie;
+               rcu_read_lock();
+               ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+               if (ssidie) {
+                       join.ssid_len = ssidie[1];
+                       memcpy(join.ssid, &ssidie[2], join.ssid_len);
+               }
+               rcu_read_unlock();
+       }
+
+       if (priv->vif->p2p) {
+               join.flags |= WSM_JOIN_FLAGS_P2P_GO;
+               join.basic_rate_set =
+                       cw1200_rate_mask_to_wsm(priv, 0xFF0);
+       }
+
+       /* Enable asynchronous join calls */
+       if (!conf->ibss_joined) {
+               join.flags |= WSM_JOIN_FLAGS_FORCE;
+               join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND;
+       }
+
+       wsm_flush_tx(priv);
+
+       /* Stay Awake for Join and Auth Timeouts and a bit more */
+       cw1200_pm_stay_awake(&priv->pm_state,
+                            CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT);
+
+       cw1200_update_listening(priv, false);
+
+       /* Turn on Block ACKs */
+       wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask,
+                                priv->ba_rx_tid_mask);
+
+       /* Set up timeout */
+       if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) {
+               priv->join_status = CW1200_JOIN_STATUS_JOINING;
+               queue_delayed_work(priv->workqueue,
+                                  &priv->join_timeout,
+                                  CW1200_JOIN_TIMEOUT);
+       }
+
+       /* 802.11w protected mgmt frames */
+       mgmt_policy.protectedMgmtEnable = 0;
+       mgmt_policy.unprotectedMgmtFramesAllowed = 1;
+       mgmt_policy.encryptionForAuthFrame = 1;
+       wsm_set_protected_mgmt_policy(priv, &mgmt_policy);
+
+       /* Perform actual join */
+       if (wsm_join(priv, &join)) {
+               pr_err("[STA] cw1200_join_work: wsm_join failed!\n");
+               cancel_delayed_work_sync(&priv->join_timeout);
+               cw1200_update_listening(priv, priv->listening);
+               /* Tx lock still held, unjoin will clear it. */
+               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+                       wsm_unlock_tx(priv);
+       } else {
+               if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND))
+                       cw1200_join_complete(priv); /* Will clear tx_lock */
+
+               /* Upload keys */
+               cw1200_upload_keys(priv);
+
+               /* Due to beacon filtering it is possible that the
+                * AP's beacon is not known for the mac80211 stack.
+                * Disable filtering temporary to make sure the stack
+                * receives at least one
+                */
+               priv->disable_beacon_filter = true;
+       }
+       cw1200_update_filtering(priv);
+
+done_put:
+       mutex_unlock(&priv->conf_mutex);
+       if (bss)
+               cfg80211_put_bss(priv->hw->wiphy, bss);
+}
+
+void cw1200_join_timeout(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, join_timeout.work);
+       pr_debug("[WSM] Join timed out.\n");
+       wsm_lock_tx(priv);
+       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+               wsm_unlock_tx(priv);
+}
+
+static void cw1200_do_unjoin(struct cw1200_common *priv)
+{
+       struct wsm_reset reset = {
+               .reset_statistics = true,
+       };
+
+       cancel_delayed_work_sync(&priv->join_timeout);
+
+       mutex_lock(&priv->conf_mutex);
+       priv->join_pending = false;
+
+       if (atomic_read(&priv->scan.in_progress)) {
+               if (priv->delayed_unjoin)
+                       wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n");
+               else
+                       priv->delayed_unjoin = true;
+               goto done;
+       }
+
+       priv->delayed_link_loss = false;
+
+       if (!priv->join_status)
+               goto done;
+
+       if (priv->join_status == CW1200_JOIN_STATUS_AP)
+               goto done;
+
+       cancel_work_sync(&priv->update_filtering_work);
+       cancel_work_sync(&priv->set_beacon_wakeup_period_work);
+       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+
+       /* Unjoin is a reset. */
+       wsm_flush_tx(priv);
+       wsm_keep_alive_period(priv, 0);
+       wsm_reset(priv, &reset);
+       wsm_set_output_power(priv, priv->output_power * 10);
+       priv->join_dtim_period = 0;
+       cw1200_setup_mac(priv);
+       cw1200_free_event_queue(priv);
+       cancel_work_sync(&priv->event_handler);
+       cw1200_update_listening(priv, priv->listening);
+       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+
+       /* Disable Block ACKs */
+       wsm_set_block_ack_policy(priv, 0, 0);
+
+       priv->disable_beacon_filter = false;
+       cw1200_update_filtering(priv);
+       memset(&priv->association_mode, 0,
+              sizeof(priv->association_mode));
+       memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+       priv->setbssparams_done = false;
+       memset(&priv->firmware_ps_mode, 0,
+              sizeof(priv->firmware_ps_mode));
+
+       pr_debug("[STA] Unjoin completed.\n");
+
+done:
+       mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_unjoin_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, unjoin_work);
+
+       cw1200_do_unjoin(priv);
+
+       /* Tell the stack we're dead */
+       ieee80211_connection_loss(priv->vif);
+
+       wsm_unlock_tx(priv);
+}
+
+int cw1200_enable_listening(struct cw1200_common *priv)
+{
+       struct wsm_start start = {
+               .mode = WSM_START_MODE_P2P_DEV,
+               .band = WSM_PHY_BAND_2_4G,
+               .beacon_interval = 100,
+               .dtim_period = 1,
+               .probe_delay = 0,
+               .basic_rate_set = 0x0F,
+       };
+
+       if (priv->channel) {
+               start.band = priv->channel->band == IEEE80211_BAND_5GHZ ?
+                            WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+               start.channel_number = priv->channel->hw_value;
+       } else {
+               start.band = WSM_PHY_BAND_2_4G;
+               start.channel_number = 1;
+       }
+
+       return wsm_start(priv, &start);
+}
+
+int cw1200_disable_listening(struct cw1200_common *priv)
+{
+       int ret;
+       struct wsm_reset reset = {
+               .reset_statistics = true,
+       };
+       ret = wsm_reset(priv, &reset);
+       return ret;
+}
+
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled)
+{
+       if (enabled) {
+               if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) {
+                       if (!cw1200_enable_listening(priv))
+                               priv->join_status = CW1200_JOIN_STATUS_MONITOR;
+                       wsm_set_probe_responder(priv, true);
+               }
+       } else {
+               if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+                       if (!cw1200_disable_listening(priv))
+                               priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+                       wsm_set_probe_responder(priv, false);
+               }
+       }
+}
+
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+                          const struct wsm_edca_params *arg)
+{
+       int ret;
+       u16 uapsd_flags = 0;
+
+       /* Here's the mapping AC [queue, bit]
+        *  VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]
+        */
+
+       if (arg->uapsd_enable[0])
+               uapsd_flags |= 1 << 3;
+
+       if (arg->uapsd_enable[1])
+               uapsd_flags |= 1 << 2;
+
+       if (arg->uapsd_enable[2])
+               uapsd_flags |= 1 << 1;
+
+       if (arg->uapsd_enable[3])
+               uapsd_flags |= 1;
+
+       /* Currently pseudo U-APSD operation is not supported, so setting
+        * MinAutoTriggerInterval, MaxAutoTriggerInterval and
+        * AutoTriggerStep to 0
+        */
+
+       priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags);
+       priv->uapsd_info.min_auto_trigger_interval = 0;
+       priv->uapsd_info.max_auto_trigger_interval = 0;
+       priv->uapsd_info.auto_trigger_step = 0;
+
+       ret = wsm_set_uapsd_info(priv, &priv->uapsd_info);
+       return ret;
+}
+
+/* ******************************************************************** */
+/* AP API                                                              */
+
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cw1200_sta_priv *sta_priv =
+                       (struct cw1200_sta_priv *)&sta->drv_priv;
+       struct cw1200_link_entry *entry;
+       struct sk_buff *skb;
+
+       if (priv->mode != NL80211_IFTYPE_AP)
+               return 0;
+
+       sta_priv->link_id = cw1200_find_link_id(priv, sta->addr);
+       if (WARN_ON(!sta_priv->link_id)) {
+               wiphy_info(priv->hw->wiphy,
+                          "[AP] No more link IDs available.\n");
+               return -ENOENT;
+       }
+
+       entry = &priv->link_id_db[sta_priv->link_id - 1];
+       spin_lock_bh(&priv->ps_state_lock);
+       if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
+                                       IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+               priv->sta_asleep_mask |= BIT(sta_priv->link_id);
+       entry->status = CW1200_LINK_HARD;
+       while ((skb = skb_dequeue(&entry->rx_queue)))
+               ieee80211_rx_irqsafe(priv->hw, skb);
+       spin_unlock_bh(&priv->ps_state_lock);
+       return 0;
+}
+
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     struct ieee80211_sta *sta)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cw1200_sta_priv *sta_priv =
+                       (struct cw1200_sta_priv *)&sta->drv_priv;
+       struct cw1200_link_entry *entry;
+
+       if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
+               return 0;
+
+       entry = &priv->link_id_db[sta_priv->link_id - 1];
+       spin_lock_bh(&priv->ps_state_lock);
+       entry->status = CW1200_LINK_RESERVE;
+       entry->timestamp = jiffies;
+       wsm_lock_tx_async(priv);
+       if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+               wsm_unlock_tx(priv);
+       spin_unlock_bh(&priv->ps_state_lock);
+       flush_workqueue(priv->workqueue);
+       return 0;
+}
+
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+                               struct ieee80211_vif *vif,
+                               enum sta_notify_cmd notify_cmd,
+                               int link_id)
+{
+       struct cw1200_common *priv = dev->priv;
+       u32 bit, prev;
+
+       /* Zero link id means "for all link IDs" */
+       if (link_id)
+               bit = BIT(link_id);
+       else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
+               bit = 0;
+       else
+               bit = priv->link_id_map;
+       prev = priv->sta_asleep_mask & bit;
+
+       switch (notify_cmd) {
+       case STA_NOTIFY_SLEEP:
+               if (!prev) {
+                       if (priv->buffered_multicasts &&
+                           !priv->sta_asleep_mask)
+                               queue_work(priv->workqueue,
+                                          &priv->multicast_start_work);
+                       priv->sta_asleep_mask |= bit;
+               }
+               break;
+       case STA_NOTIFY_AWAKE:
+               if (prev) {
+                       priv->sta_asleep_mask &= ~bit;
+                       priv->pspoll_mask &= ~bit;
+                       if (priv->tx_multicast && link_id &&
+                           !priv->sta_asleep_mask)
+                               queue_work(priv->workqueue,
+                                          &priv->multicast_stop_work);
+                       cw1200_bh_wakeup(priv);
+               }
+               break;
+       }
+}
+
+void cw1200_sta_notify(struct ieee80211_hw *dev,
+                      struct ieee80211_vif *vif,
+                      enum sta_notify_cmd notify_cmd,
+                      struct ieee80211_sta *sta)
+{
+       struct cw1200_common *priv = dev->priv;
+       struct cw1200_sta_priv *sta_priv =
+               (struct cw1200_sta_priv *)&sta->drv_priv;
+
+       spin_lock_bh(&priv->ps_state_lock);
+       __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id);
+       spin_unlock_bh(&priv->ps_state_lock);
+}
+
+static void cw1200_ps_notify(struct cw1200_common *priv,
+                     int link_id, bool ps)
+{
+       if (link_id > CW1200_MAX_STA_IN_AP_MODE)
+               return;
+
+       pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n",
+                ps ? "Stop" : "Start",
+                link_id, priv->sta_asleep_mask);
+
+       __cw1200_sta_notify(priv->hw, priv->vif,
+                           ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
+}
+
+static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set)
+{
+       struct sk_buff *skb;
+       struct wsm_update_ie update_ie = {
+               .what = WSM_UPDATE_IE_BEACON,
+               .count = 1,
+       };
+       u16 tim_offset, tim_length;
+
+       pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis");
+
+       skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+                       &tim_offset, &tim_length);
+       if (!skb) {
+               if (!__cw1200_flush(priv, true))
+                       wsm_unlock_tx(priv);
+               return -ENOENT;
+       }
+
+       if (tim_offset && tim_length >= 6) {
+               /* Ignore DTIM count from mac80211:
+                * firmware handles DTIM internally.
+                */
+               skb->data[tim_offset + 2] = 0;
+
+               /* Set/reset aid0 bit */
+               if (aid0_bit_set)
+                       skb->data[tim_offset + 4] |= 1;
+               else
+                       skb->data[tim_offset + 4] &= ~1;
+       }
+
+       update_ie.ies = &skb->data[tim_offset];
+       update_ie.length = tim_length;
+       wsm_update_ie(priv, &update_ie);
+
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+void cw1200_set_tim_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, set_tim_work);
+       (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set);
+}
+
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+                  bool set)
+{
+       struct cw1200_common *priv = dev->priv;
+       queue_work(priv->workqueue, &priv->set_tim_work);
+       return 0;
+}
+
+void cw1200_set_cts_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, set_cts_work);
+
+       u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
+       struct wsm_update_ie update_ie = {
+               .what = WSM_UPDATE_IE_BEACON,
+               .count = 1,
+               .ies = erp_ie,
+               .length = 3,
+       };
+       u32 erp_info;
+       __le32 use_cts_prot;
+       mutex_lock(&priv->conf_mutex);
+       erp_info = priv->erp_info;
+       mutex_unlock(&priv->conf_mutex);
+       use_cts_prot =
+               erp_info & WLAN_ERP_USE_PROTECTION ?
+               __cpu_to_le32(1) : 0;
+
+       erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
+
+       pr_debug("[STA] ERP information 0x%x\n", erp_info);
+
+       wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION,
+                     &use_cts_prot, sizeof(use_cts_prot));
+       wsm_update_ie(priv, &update_ie);
+
+       return;
+}
+
+static int cw1200_set_btcoexinfo(struct cw1200_common *priv)
+{
+       struct wsm_override_internal_txrate arg;
+       int ret = 0;
+
+       if (priv->mode == NL80211_IFTYPE_STATION) {
+               /* Plumb PSPOLL and NULL template */
+               cw1200_upload_pspoll(priv);
+               cw1200_upload_null(priv);
+               cw1200_upload_qosnull(priv);
+       } else {
+               return 0;
+       }
+
+       memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
+
+       if (!priv->vif->p2p) {
+               /* STATION mode */
+               if (priv->bss_params.operational_rate_set & ~0xF) {
+                       pr_debug("[STA] STA has ERP rates\n");
+                       /* G or BG mode */
+                       arg.internalTxRate = (__ffs(
+                       priv->bss_params.operational_rate_set & ~0xF));
+               } else {
+                       pr_debug("[STA] STA has non ERP rates\n");
+                       /* B only mode */
+                       arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
+               }
+               arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
+       } else {
+               /* P2P mode */
+               arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+               arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+       }
+
+       pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
+                priv->mode,
+                arg.internalTxRate,
+                arg.nonErpInternalTxRate);
+
+       ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+                           &arg, sizeof(arg));
+
+       return ret;
+}
+
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_bss_conf *info,
+                            u32 changed)
+{
+       struct cw1200_common *priv = dev->priv;
+       bool do_join = false;
+
+       mutex_lock(&priv->conf_mutex);
+
+       pr_debug("BSS CHANGED:  %08x\n", changed);
+
+       /* TODO: BSS_CHANGED_QOS */
+       /* TODO: BSS_CHANGED_TXPOWER */
+
+       if (changed & BSS_CHANGED_ARP_FILTER) {
+               struct wsm_mib_arp_ipv4_filter filter = {0};
+               int i;
+
+               pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
+                        info->arp_addr_cnt);
+
+               /* Currently only one IP address is supported by firmware.
+                * In case of more IPs arp filtering will be disabled.
+                */
+               if (info->arp_addr_cnt > 0 &&
+                   info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
+                       for (i = 0; i < info->arp_addr_cnt; i++) {
+                               filter.ipv4addrs[i] = info->arp_addr_list[i];
+                               pr_debug("[STA] addr[%d]: 0x%X\n",
+                                        i, filter.ipv4addrs[i]);
+                       }
+                       filter.enable = __cpu_to_le32(1);
+               }
+
+               pr_debug("[STA] arp ip filter enable: %d\n",
+                        __le32_to_cpu(filter.enable));
+
+               wsm_set_arp_ipv4_filter(priv, &filter);
+       }
+
+       if (changed &
+           (BSS_CHANGED_BEACON |
+            BSS_CHANGED_AP_PROBE_RESP |
+            BSS_CHANGED_BSSID |
+            BSS_CHANGED_SSID |
+            BSS_CHANGED_IBSS)) {
+               pr_debug("BSS_CHANGED_BEACON\n");
+               priv->beacon_int = info->beacon_int;
+               cw1200_update_beaconing(priv);
+               cw1200_upload_beacon(priv);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
+               pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon);
+
+               if (priv->enable_beacon != info->enable_beacon) {
+                       cw1200_enable_beaconing(priv, info->enable_beacon);
+                       priv->enable_beacon = info->enable_beacon;
+               }
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               pr_debug("CHANGED_BEACON_INT\n");
+               if (info->ibss_joined)
+                       do_join = true;
+               else if (priv->join_status == CW1200_JOIN_STATUS_AP)
+                       cw1200_update_beaconing(priv);
+       }
+
+       /* assoc/disassoc, or maybe AID changed */
+       if (changed & BSS_CHANGED_ASSOC) {
+               wsm_lock_tx(priv);
+               priv->wep_default_key_id = -1;
+               wsm_unlock_tx(priv);
+       }
+
+       if (changed & BSS_CHANGED_BSSID) {
+               pr_debug("BSS_CHANGED_BSSID\n");
+               do_join = true;
+       }
+
+       if (changed &
+           (BSS_CHANGED_ASSOC |
+            BSS_CHANGED_BSSID |
+            BSS_CHANGED_IBSS |
+            BSS_CHANGED_BASIC_RATES |
+            BSS_CHANGED_HT)) {
+               pr_debug("BSS_CHANGED_ASSOC\n");
+               if (info->assoc) {
+                       if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) {
+                               ieee80211_connection_loss(vif);
+                               mutex_unlock(&priv->conf_mutex);
+                               return;
+                       } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) {
+                               priv->join_status = CW1200_JOIN_STATUS_STA;
+                       }
+               } else {
+                       do_join = true;
+               }
+
+               if (info->assoc || info->ibss_joined) {
+                       struct ieee80211_sta *sta = NULL;
+                       __le32 htprot = 0;
+
+                       if (info->dtim_period)
+                               priv->join_dtim_period = info->dtim_period;
+                       priv->beacon_int = info->beacon_int;
+
+                       rcu_read_lock();
+
+                       if (info->bssid && !info->ibss_joined)
+                               sta = ieee80211_find_sta(vif, info->bssid);
+                       if (sta) {
+                               priv->ht_info.ht_cap = sta->ht_cap;
+                               priv->bss_params.operational_rate_set =
+                                       cw1200_rate_mask_to_wsm(priv,
+                                                               sta->supp_rates[priv->channel->band]);
+                               priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef);
+                               priv->ht_info.operation_mode = info->ht_operation_mode;
+                       } else {
+                               memset(&priv->ht_info, 0,
+                                      sizeof(priv->ht_info));
+                               priv->bss_params.operational_rate_set = -1;
+                       }
+                       rcu_read_unlock();
+
+                       /* Non Greenfield stations present */
+                       if (priv->ht_info.operation_mode &
+                           IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+                               htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT);
+
+                       /* Set HT protection method */
+                       htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2);
+
+                       /* TODO:
+                        * STBC_param.dual_cts
+                        *  STBC_param.LSIG_TXOP_FILL
+                        */
+
+                       wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION,
+                                     &htprot, sizeof(htprot));
+
+                       priv->association_mode.greenfield =
+                               cw1200_ht_greenfield(&priv->ht_info);
+                       priv->association_mode.flags =
+                               WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
+                               WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
+                               WSM_ASSOCIATION_MODE_USE_HT_MODE |
+                               WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
+                               WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
+                       priv->association_mode.preamble =
+                               info->use_short_preamble ?
+                               WSM_JOIN_PREAMBLE_SHORT :
+                               WSM_JOIN_PREAMBLE_LONG;
+                       priv->association_mode.basic_rate_set = __cpu_to_le32(
+                               cw1200_rate_mask_to_wsm(priv,
+                                                       info->basic_rates));
+                       priv->association_mode.mpdu_start_spacing =
+                               cw1200_ht_ampdu_density(&priv->ht_info);
+
+                       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+                       cancel_work_sync(&priv->unjoin_work);
+
+                       priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count;
+                       priv->bss_params.aid = info->aid;
+
+                       if (priv->join_dtim_period < 1)
+                               priv->join_dtim_period = 1;
+
+                       pr_debug("[STA] DTIM %d, interval: %d\n",
+                                priv->join_dtim_period, priv->beacon_int);
+                       pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
+                                priv->association_mode.preamble,
+                                priv->association_mode.greenfield,
+                                priv->bss_params.aid,
+                                priv->bss_params.operational_rate_set,
+                                priv->association_mode.basic_rate_set);
+                       wsm_set_association_mode(priv, &priv->association_mode);
+
+                       if (!info->ibss_joined) {
+                               wsm_keep_alive_period(priv, 30 /* sec */);
+                               wsm_set_bss_params(priv, &priv->bss_params);
+                               priv->setbssparams_done = true;
+                               cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work);
+                               cw1200_set_pm(priv, &priv->powersave_mode);
+                       }
+                       if (priv->vif->p2p) {
+                               pr_debug("[STA] Setting p2p powersave configuration.\n");
+                               wsm_set_p2p_ps_modeinfo(priv,
+                                                       &priv->p2p_ps_modeinfo);
+                       }
+                       if (priv->bt_present)
+                               cw1200_set_btcoexinfo(priv);
+               } else {
+                       memset(&priv->association_mode, 0,
+                              sizeof(priv->association_mode));
+                       memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+               }
+       }
+
+       /* ERP Protection */
+       if (changed & (BSS_CHANGED_ASSOC |
+                      BSS_CHANGED_ERP_CTS_PROT |
+                      BSS_CHANGED_ERP_PREAMBLE)) {
+               u32 prev_erp_info = priv->erp_info;
+               if (info->use_cts_prot)
+                       priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+               else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
+                       priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+
+               if (info->use_short_preamble)
+                       priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
+               else
+                       priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
+
+               pr_debug("[STA] ERP Protection: %x\n", priv->erp_info);
+
+               if (prev_erp_info != priv->erp_info)
+                       queue_work(priv->workqueue, &priv->set_cts_work);
+       }
+
+       /* ERP Slottime */
+       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
+               __le32 slot_time = info->use_short_slot ?
+                       __cpu_to_le32(9) : __cpu_to_le32(20);
+               pr_debug("[STA] Slot time: %d us.\n",
+                        __le32_to_cpu(slot_time));
+               wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME,
+                             &slot_time, sizeof(slot_time));
+       }
+
+       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
+               struct wsm_rcpi_rssi_threshold threshold = {
+                       .rollingAverageCount = 8,
+               };
+               pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n",
+                        info->cqm_rssi_thold, info->cqm_rssi_hyst);
+               priv->cqm_rssi_thold = info->cqm_rssi_thold;
+               priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
+
+               if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
+                       /* RSSI subscription enabled */
+                       /* TODO: It's not a correct way of setting threshold.
+                        * Upper and lower must be set equal here and adjusted
+                        * in callback. However current implementation is much
+                        * more relaible and stable.
+                        */
+
+                       /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+                        * RSSI = RCPI / 2 - 110
+                        */
+                       if (priv->cqm_use_rssi) {
+                               threshold.upperThreshold =
+                                       info->cqm_rssi_thold + info->cqm_rssi_hyst;
+                               threshold.lowerThreshold =
+                                       info->cqm_rssi_thold;
+                               threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+                       } else {
+                               threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2;
+                               threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2;
+                       }
+                       threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
+               } else {
+                       /* There is a bug in FW, see sta.c. We have to enable
+                        * dummy subscription to get correct RSSI values.
+                        */
+                       threshold.rssiRcpiMode |=
+                               WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+                               WSM_RCPI_RSSI_DONT_USE_UPPER |
+                               WSM_RCPI_RSSI_DONT_USE_LOWER;
+                       if (priv->cqm_use_rssi)
+                               threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+               }
+               wsm_set_rcpi_rssi_threshold(priv, &threshold);
+       }
+       mutex_unlock(&priv->conf_mutex);
+
+       if (do_join) {
+               wsm_lock_tx(priv);
+               cw1200_do_join(priv); /* Will unlock it for us */
+       }
+}
+
+void cw1200_multicast_start_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, multicast_start_work);
+       long tmo = priv->join_dtim_period *
+                       (priv->beacon_int + 20) * HZ / 1024;
+
+       cancel_work_sync(&priv->multicast_stop_work);
+
+       if (!priv->aid0_bit_set) {
+               wsm_lock_tx(priv);
+               cw1200_set_tim_impl(priv, true);
+               priv->aid0_bit_set = true;
+               mod_timer(&priv->mcast_timeout, jiffies + tmo);
+               wsm_unlock_tx(priv);
+       }
+}
+
+void cw1200_multicast_stop_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, multicast_stop_work);
+
+       if (priv->aid0_bit_set) {
+               del_timer_sync(&priv->mcast_timeout);
+               wsm_lock_tx(priv);
+               priv->aid0_bit_set = false;
+               cw1200_set_tim_impl(priv, false);
+               wsm_unlock_tx(priv);
+       }
+}
+
+void cw1200_mcast_timeout(unsigned long arg)
+{
+       struct cw1200_common *priv =
+               (struct cw1200_common *)arg;
+
+       wiphy_warn(priv->hw->wiphy,
+                  "Multicast delivery timeout.\n");
+       spin_lock_bh(&priv->ps_state_lock);
+       priv->tx_multicast = priv->aid0_bit_set &&
+                       priv->buffered_multicasts;
+       if (priv->tx_multicast)
+               cw1200_bh_wakeup(priv);
+       spin_unlock_bh(&priv->ps_state_lock);
+}
+
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+                       struct ieee80211_vif *vif,
+                       enum ieee80211_ampdu_mlme_action action,
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                       u8 buf_size, bool amsdu)
+{
+       /* Aggregation is implemented fully in firmware,
+        * including block ack negotiation. Do not allow
+        * mac80211 stack to do anything: it interferes with
+        * the firmware.
+        */
+
+       /* Note that we still need this function stubbed. */
+       return -ENOTSUPP;
+}
+
+/* ******************************************************************** */
+/* WSM callback                                                                */
+void cw1200_suspend_resume(struct cw1200_common *priv,
+                         struct wsm_suspend_resume *arg)
+{
+       pr_debug("[AP] %s: %s\n",
+                arg->stop ? "stop" : "start",
+                arg->multicast ? "broadcast" : "unicast");
+
+       if (arg->multicast) {
+               bool cancel_tmo = false;
+               spin_lock_bh(&priv->ps_state_lock);
+               if (arg->stop) {
+                       priv->tx_multicast = false;
+               } else {
+                       /* Firmware sends this indication every DTIM if there
+                        * is a STA in powersave connected. There is no reason
+                        * to suspend, following wakeup will consume much more
+                        * power than it could be saved.
+                        */
+                       cw1200_pm_stay_awake(&priv->pm_state,
+                                            priv->join_dtim_period *
+                                            (priv->beacon_int + 20) * HZ / 1024);
+                       priv->tx_multicast = (priv->aid0_bit_set &&
+                                             priv->buffered_multicasts);
+                       if (priv->tx_multicast) {
+                               cancel_tmo = true;
+                               cw1200_bh_wakeup(priv);
+                       }
+               }
+               spin_unlock_bh(&priv->ps_state_lock);
+               if (cancel_tmo)
+                       del_timer_sync(&priv->mcast_timeout);
+       } else {
+               spin_lock_bh(&priv->ps_state_lock);
+               cw1200_ps_notify(priv, arg->link_id, arg->stop);
+               spin_unlock_bh(&priv->ps_state_lock);
+               if (!arg->stop)
+                       cw1200_bh_wakeup(priv);
+       }
+       return;
+}
+
+/* ******************************************************************** */
+/* AP privates                                                         */
+
+static int cw1200_upload_beacon(struct cw1200_common *priv)
+{
+       int ret = 0;
+       struct ieee80211_mgmt *mgmt;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_BEACON,
+       };
+
+       u16 tim_offset;
+       u16 tim_len;
+
+       if (priv->mode == NL80211_IFTYPE_STATION ||
+           priv->mode == NL80211_IFTYPE_MONITOR ||
+           priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+               goto done;
+
+       if (priv->vif->p2p)
+               frame.rate = WSM_TRANSMIT_RATE_6;
+
+       frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+                                            &tim_offset, &tim_len);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       ret = wsm_set_template_frame(priv, &frame);
+
+       if (ret)
+               goto done;
+
+       /* TODO: Distill probe resp; remove TIM
+        * and any other beacon-specific IEs
+        */
+       mgmt = (void *)frame.skb->data;
+       mgmt->frame_control =
+               __cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                             IEEE80211_STYPE_PROBE_RESP);
+
+       frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
+       if (priv->vif->p2p) {
+               ret = wsm_set_probe_responder(priv, true);
+       } else {
+               ret = wsm_set_template_frame(priv, &frame);
+               wsm_set_probe_responder(priv, false);
+       }
+
+done:
+       dev_kfree_skb(frame.skb);
+
+       return ret;
+}
+
+static int cw1200_upload_pspoll(struct cw1200_common *priv)
+{
+       int ret = 0;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_PS_POLL,
+               .rate = 0xFF,
+       };
+
+
+       frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       ret = wsm_set_template_frame(priv, &frame);
+
+       dev_kfree_skb(frame.skb);
+
+       return ret;
+}
+
+static int cw1200_upload_null(struct cw1200_common *priv)
+{
+       int ret = 0;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_NULL,
+               .rate = 0xFF,
+       };
+
+       frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       ret = wsm_set_template_frame(priv, &frame);
+
+       dev_kfree_skb(frame.skb);
+
+       return ret;
+}
+
+static int cw1200_upload_qosnull(struct cw1200_common *priv)
+{
+       /* TODO:  This needs to be implemented
+
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_QOS_NULL,
+               .rate = 0xFF,
+       };
+
+       frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       ret = wsm_set_template_frame(priv, &frame);
+
+       dev_kfree_skb(frame.skb);
+
+       */
+       return 0;
+}
+
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+                                  bool enable)
+{
+       struct wsm_beacon_transmit transmit = {
+               .enable_beaconing = enable,
+       };
+
+       return wsm_beacon_transmit(priv, &transmit);
+}
+
+static int cw1200_start_ap(struct cw1200_common *priv)
+{
+       int ret;
+       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+       struct wsm_start start = {
+               .mode = priv->vif->p2p ?
+                               WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
+               .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+                               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+               .channel_number = priv->channel->hw_value,
+               .beacon_interval = conf->beacon_int,
+               .dtim_period = conf->dtim_period,
+               .preamble = conf->use_short_preamble ?
+                               WSM_JOIN_PREAMBLE_SHORT :
+                               WSM_JOIN_PREAMBLE_LONG,
+               .probe_delay = 100,
+               .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+                               conf->basic_rates),
+       };
+       struct wsm_operational_mode mode = {
+               .power_mode = cw1200_power_mode,
+               .disable_more_flag_usage = true,
+       };
+
+       memset(start.ssid, 0, sizeof(start.ssid));
+       if (!conf->hidden_ssid) {
+               start.ssid_len = conf->ssid_len;
+               memcpy(start.ssid, conf->ssid, start.ssid_len);
+       }
+
+       priv->beacon_int = conf->beacon_int;
+       priv->join_dtim_period = conf->dtim_period;
+
+       memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
+
+       pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n",
+                start.channel_number, start.band,
+                start.beacon_interval, start.dtim_period,
+                start.basic_rate_set,
+                start.ssid_len, start.ssid);
+       ret = wsm_start(priv, &start);
+       if (!ret)
+               ret = cw1200_upload_keys(priv);
+       if (!ret && priv->vif->p2p) {
+               pr_debug("[AP] Setting p2p powersave configuration.\n");
+               wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo);
+       }
+       if (!ret) {
+               wsm_set_block_ack_policy(priv, 0, 0);
+               priv->join_status = CW1200_JOIN_STATUS_AP;
+               cw1200_update_filtering(priv);
+       }
+       wsm_set_operational_mode(priv, &mode);
+       return ret;
+}
+
+static int cw1200_update_beaconing(struct cw1200_common *priv)
+{
+       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+       struct wsm_reset reset = {
+               .link_id = 0,
+               .reset_statistics = true,
+       };
+
+       if (priv->mode == NL80211_IFTYPE_AP) {
+               /* TODO: check if changed channel, band */
+               if (priv->join_status != CW1200_JOIN_STATUS_AP ||
+                   priv->beacon_int != conf->beacon_int) {
+                       pr_debug("ap restarting\n");
+                       wsm_lock_tx(priv);
+                       if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE)
+                               wsm_reset(priv, &reset);
+                       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+                       cw1200_start_ap(priv);
+                       wsm_unlock_tx(priv);
+               } else
+                       pr_debug("ap started join_status: %d\n",
+                                priv->join_status);
+       }
+       return 0;
+}
diff --git a/drivers/net/wireless/st/cw1200/sta.h b/drivers/net/wireless/st/cw1200/sta.h
new file mode 100644 (file)
index 0000000..bebb337
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef STA_H_INCLUDED
+#define STA_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API                                                                */
+
+int cw1200_start(struct ieee80211_hw *dev);
+void cw1200_stop(struct ieee80211_hw *dev);
+int cw1200_add_interface(struct ieee80211_hw *dev,
+                        struct ieee80211_vif *vif);
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+                            struct ieee80211_vif *vif);
+int cw1200_change_interface(struct ieee80211_hw *dev,
+                           struct ieee80211_vif *vif,
+                           enum nl80211_iftype new_type,
+                           bool p2p);
+int cw1200_config(struct ieee80211_hw *dev, u32 changed);
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+                            unsigned int changed_flags,
+                            unsigned int *total_flags,
+                            u64 multicast);
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+                  u16 queue, const struct ieee80211_tx_queue_params *params);
+int cw1200_get_stats(struct ieee80211_hw *dev,
+                    struct ieee80211_low_level_stats *stats);
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+                  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                  struct ieee80211_key_conf *key);
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
+
+void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop);
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+                            struct netdev_hw_addr_list *mc_list);
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
+
+/* ******************************************************************** */
+/* WSM callbacks                                                       */
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+                               struct wsm_join_complete *arg);
+
+/* ******************************************************************** */
+/* WSM events                                                          */
+
+void cw1200_free_event_queue(struct cw1200_common *priv);
+void cw1200_event_handler(struct work_struct *work);
+void cw1200_bss_loss_work(struct work_struct *work);
+void cw1200_bss_params_work(struct work_struct *work);
+void cw1200_keep_alive_work(struct work_struct *work);
+void cw1200_tx_failure_work(struct work_struct *work);
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good,
+                            int bad);
+static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+                                        int init, int good, int bad)
+{
+       spin_lock(&priv->bss_loss_lock);
+       __cw1200_cqm_bssloss_sm(priv, init, good, bad);
+       spin_unlock(&priv->bss_loss_lock);
+}
+
+/* ******************************************************************** */
+/* Internal API                                                                */
+
+int cw1200_setup_mac(struct cw1200_common *priv);
+void cw1200_join_timeout(struct work_struct *work);
+void cw1200_unjoin_work(struct work_struct *work);
+void cw1200_join_complete_work(struct work_struct *work);
+void cw1200_wep_key_work(struct work_struct *work);
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled);
+void cw1200_update_filtering(struct cw1200_common *priv);
+void cw1200_update_filtering_work(struct work_struct *work);
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work);
+int cw1200_enable_listening(struct cw1200_common *priv);
+int cw1200_disable_listening(struct cw1200_common *priv);
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+                               const struct wsm_edca_params *arg);
+void cw1200_ba_work(struct work_struct *work);
+void cw1200_ba_timer(unsigned long arg);
+
+/* AP stuffs */
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+                  bool set);
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta);
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     struct ieee80211_sta *sta);
+void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+                      enum sta_notify_cmd notify_cmd,
+                      struct ieee80211_sta *sta);
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_bss_conf *info,
+                            u32 changed);
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+                       struct ieee80211_vif *vif,
+                       enum ieee80211_ampdu_mlme_action action,
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                       u8 buf_size, bool amsdu);
+
+void cw1200_suspend_resume(struct cw1200_common *priv,
+                         struct wsm_suspend_resume *arg);
+void cw1200_set_tim_work(struct work_struct *work);
+void cw1200_set_cts_work(struct work_struct *work);
+void cw1200_multicast_start_work(struct work_struct *work);
+void cw1200_multicast_stop_work(struct work_struct *work);
+void cw1200_mcast_timeout(unsigned long arg);
+
+#endif
diff --git a/drivers/net/wireless/st/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c
new file mode 100644 (file)
index 0000000..d28bd49
--- /dev/null
@@ -0,0 +1,1472 @@
+/*
+ * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "cw1200.h"
+#include "wsm.h"
+#include "bh.h"
+#include "sta.h"
+#include "debug.h"
+
+#define CW1200_INVALID_RATE_ID (0xFF)
+
+static int cw1200_handle_action_rx(struct cw1200_common *priv,
+                                  struct sk_buff *skb);
+static const struct ieee80211_rate *
+cw1200_get_tx_rate(const struct cw1200_common *priv,
+                  const struct ieee80211_tx_rate *rate);
+
+/* ******************************************************************** */
+/* TX queue lock / unlock                                              */
+
+static inline void cw1200_tx_queues_lock(struct cw1200_common *priv)
+{
+       int i;
+       for (i = 0; i < 4; ++i)
+               cw1200_queue_lock(&priv->tx_queue[i]);
+}
+
+static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv)
+{
+       int i;
+       for (i = 0; i < 4; ++i)
+               cw1200_queue_unlock(&priv->tx_queue[i]);
+}
+
+/* ******************************************************************** */
+/* TX policy cache implementation                                      */
+
+static void tx_policy_dump(struct tx_policy *policy)
+{
+       pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n",
+                policy->raw[0] & 0x0F,  policy->raw[0] >> 4,
+                policy->raw[1] & 0x0F,  policy->raw[1] >> 4,
+                policy->raw[2] & 0x0F,  policy->raw[2] >> 4,
+                policy->raw[3] & 0x0F,  policy->raw[3] >> 4,
+                policy->raw[4] & 0x0F,  policy->raw[4] >> 4,
+                policy->raw[5] & 0x0F,  policy->raw[5] >> 4,
+                policy->raw[6] & 0x0F,  policy->raw[6] >> 4,
+                policy->raw[7] & 0x0F,  policy->raw[7] >> 4,
+                policy->raw[8] & 0x0F,  policy->raw[8] >> 4,
+                policy->raw[9] & 0x0F,  policy->raw[9] >> 4,
+                policy->raw[10] & 0x0F,  policy->raw[10] >> 4,
+                policy->raw[11] & 0x0F,  policy->raw[11] >> 4,
+                policy->defined);
+}
+
+static void tx_policy_build(const struct cw1200_common *priv,
+       /* [out] */ struct tx_policy *policy,
+       struct ieee80211_tx_rate *rates, size_t count)
+{
+       int i, j;
+       unsigned limit = priv->short_frame_max_tx_count;
+       unsigned total = 0;
+       BUG_ON(rates[0].idx < 0);
+       memset(policy, 0, sizeof(*policy));
+
+       /* Sort rates in descending order. */
+       for (i = 1; i < count; ++i) {
+               if (rates[i].idx < 0) {
+                       count = i;
+                       break;
+               }
+               if (rates[i].idx > rates[i - 1].idx) {
+                       struct ieee80211_tx_rate tmp = rates[i - 1];
+                       rates[i - 1] = rates[i];
+                       rates[i] = tmp;
+               }
+       }
+
+       /* Eliminate duplicates. */
+       total = rates[0].count;
+       for (i = 0, j = 1; j < count; ++j) {
+               if (rates[j].idx == rates[i].idx) {
+                       rates[i].count += rates[j].count;
+               } else if (rates[j].idx > rates[i].idx) {
+                       break;
+               } else {
+                       ++i;
+                       if (i != j)
+                               rates[i] = rates[j];
+               }
+               total += rates[j].count;
+       }
+       count = i + 1;
+
+       /* Re-fill policy trying to keep every requested rate and with
+        * respect to the global max tx retransmission count.
+        */
+       if (limit < count)
+               limit = count;
+       if (total > limit) {
+               for (i = 0; i < count; ++i) {
+                       int left = count - i - 1;
+                       if (rates[i].count > limit - left)
+                               rates[i].count = limit - left;
+                       limit -= rates[i].count;
+               }
+       }
+
+       /* HACK!!! Device has problems (at least) switching from
+        * 54Mbps CTS to 1Mbps. This switch takes enormous amount
+        * of time (100-200 ms), leading to valuable throughput drop.
+        * As a workaround, additional g-rates are injected to the
+        * policy.
+        */
+       if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
+           rates[0].idx > 4 && rates[0].count > 2 &&
+           rates[1].idx < 2) {
+               int mid_rate = (rates[0].idx + 4) >> 1;
+
+               /* Decrease number of retries for the initial rate */
+               rates[0].count -= 2;
+
+               if (mid_rate != 4) {
+                       /* Keep fallback rate at 1Mbps. */
+                       rates[3] = rates[1];
+
+                       /* Inject 1 transmission on lowest g-rate */
+                       rates[2].idx = 4;
+                       rates[2].count = 1;
+                       rates[2].flags = rates[1].flags;
+
+                       /* Inject 1 transmission on mid-rate */
+                       rates[1].idx = mid_rate;
+                       rates[1].count = 1;
+
+                       /* Fallback to 1 Mbps is a really bad thing,
+                        * so let's try to increase probability of
+                        * successful transmission on the lowest g rate
+                        * even more
+                        */
+                       if (rates[0].count >= 3) {
+                               --rates[0].count;
+                               ++rates[2].count;
+                       }
+
+                       /* Adjust amount of rates defined */
+                       count += 2;
+               } else {
+                       /* Keep fallback rate at 1Mbps. */
+                       rates[2] = rates[1];
+
+                       /* Inject 2 transmissions on lowest g-rate */
+                       rates[1].idx = 4;
+                       rates[1].count = 2;
+
+                       /* Adjust amount of rates defined */
+                       count += 1;
+               }
+       }
+
+       policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1;
+
+       for (i = 0; i < count; ++i) {
+               register unsigned rateid, off, shift, retries;
+
+               rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value;
+               off = rateid >> 3;              /* eq. rateid / 8 */
+               shift = (rateid & 0x07) << 2;   /* eq. (rateid % 8) * 4 */
+
+               retries = rates[i].count;
+               if (retries > 0x0F) {
+                       rates[i].count = 0x0f;
+                       retries = 0x0F;
+               }
+               policy->tbl[off] |= __cpu_to_le32(retries << shift);
+               policy->retry_count += retries;
+       }
+
+       pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d\n",
+                count,
+                rates[0].idx, rates[0].count,
+                rates[1].idx, rates[1].count,
+                rates[2].idx, rates[2].count,
+                rates[3].idx, rates[3].count);
+}
+
+static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
+                                       const struct tx_policy *cached)
+{
+       size_t count = wanted->defined >> 1;
+       if (wanted->defined > cached->defined)
+               return false;
+       if (count) {
+               if (memcmp(wanted->raw, cached->raw, count))
+                       return false;
+       }
+       if (wanted->defined & 1) {
+               if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F))
+                       return false;
+       }
+       return true;
+}
+
+static int tx_policy_find(struct tx_policy_cache *cache,
+                               const struct tx_policy *wanted)
+{
+       /* O(n) complexity. Not so good, but there's only 8 entries in
+        * the cache.
+        * Also lru helps to reduce search time.
+        */
+       struct tx_policy_cache_entry *it;
+       /* First search for policy in "used" list */
+       list_for_each_entry(it, &cache->used, link) {
+               if (tx_policy_is_equal(wanted, &it->policy))
+                       return it - cache->cache;
+       }
+       /* Then - in "free list" */
+       list_for_each_entry(it, &cache->free, link) {
+               if (tx_policy_is_equal(wanted, &it->policy))
+                       return it - cache->cache;
+       }
+       return -1;
+}
+
+static inline void tx_policy_use(struct tx_policy_cache *cache,
+                                struct tx_policy_cache_entry *entry)
+{
+       ++entry->policy.usage_count;
+       list_move(&entry->link, &cache->used);
+}
+
+static inline int tx_policy_release(struct tx_policy_cache *cache,
+                                   struct tx_policy_cache_entry *entry)
+{
+       int ret = --entry->policy.usage_count;
+       if (!ret)
+               list_move(&entry->link, &cache->free);
+       return ret;
+}
+
+void tx_policy_clean(struct cw1200_common *priv)
+{
+       int idx, locked;
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+       struct tx_policy_cache_entry *entry;
+
+       cw1200_tx_queues_lock(priv);
+       spin_lock_bh(&cache->lock);
+       locked = list_empty(&cache->free);
+
+       for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) {
+               entry = &cache->cache[idx];
+               /* Policy usage count should be 0 at this time as all queues
+                  should be empty
+                */
+               if (WARN_ON(entry->policy.usage_count)) {
+                       entry->policy.usage_count = 0;
+                       list_move(&entry->link, &cache->free);
+               }
+               memset(&entry->policy, 0, sizeof(entry->policy));
+       }
+       if (locked)
+               cw1200_tx_queues_unlock(priv);
+
+       cw1200_tx_queues_unlock(priv);
+       spin_unlock_bh(&cache->lock);
+}
+
+/* ******************************************************************** */
+/* External TX policy cache API                                                */
+
+void tx_policy_init(struct cw1200_common *priv)
+{
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+       int i;
+
+       memset(cache, 0, sizeof(*cache));
+
+       spin_lock_init(&cache->lock);
+       INIT_LIST_HEAD(&cache->used);
+       INIT_LIST_HEAD(&cache->free);
+
+       for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i)
+               list_add(&cache->cache[i].link, &cache->free);
+}
+
+static int tx_policy_get(struct cw1200_common *priv,
+                 struct ieee80211_tx_rate *rates,
+                 size_t count, bool *renew)
+{
+       int idx;
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+       struct tx_policy wanted;
+
+       tx_policy_build(priv, &wanted, rates, count);
+
+       spin_lock_bh(&cache->lock);
+       if (WARN_ON_ONCE(list_empty(&cache->free))) {
+               spin_unlock_bh(&cache->lock);
+               return CW1200_INVALID_RATE_ID;
+       }
+       idx = tx_policy_find(cache, &wanted);
+       if (idx >= 0) {
+               pr_debug("[TX policy] Used TX policy: %d\n", idx);
+               *renew = false;
+       } else {
+               struct tx_policy_cache_entry *entry;
+               *renew = true;
+               /* If policy is not found create a new one
+                * using the oldest entry in "free" list
+                */
+               entry = list_entry(cache->free.prev,
+                       struct tx_policy_cache_entry, link);
+               entry->policy = wanted;
+               idx = entry - cache->cache;
+               pr_debug("[TX policy] New TX policy: %d\n", idx);
+               tx_policy_dump(&entry->policy);
+       }
+       tx_policy_use(cache, &cache->cache[idx]);
+       if (list_empty(&cache->free)) {
+               /* Lock TX queues. */
+               cw1200_tx_queues_lock(priv);
+       }
+       spin_unlock_bh(&cache->lock);
+       return idx;
+}
+
+static void tx_policy_put(struct cw1200_common *priv, int idx)
+{
+       int usage, locked;
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+
+       spin_lock_bh(&cache->lock);
+       locked = list_empty(&cache->free);
+       usage = tx_policy_release(cache, &cache->cache[idx]);
+       if (locked && !usage) {
+               /* Unlock TX queues. */
+               cw1200_tx_queues_unlock(priv);
+       }
+       spin_unlock_bh(&cache->lock);
+}
+
+static int tx_policy_upload(struct cw1200_common *priv)
+{
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+       int i;
+       struct wsm_set_tx_rate_retry_policy arg = {
+               .num = 0,
+       };
+       spin_lock_bh(&cache->lock);
+
+       /* Upload only modified entries. */
+       for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) {
+               struct tx_policy *src = &cache->cache[i].policy;
+               if (src->retry_count && !src->uploaded) {
+                       struct wsm_tx_rate_retry_policy *dst =
+                               &arg.tbl[arg.num];
+                       dst->index = i;
+                       dst->short_retries = priv->short_frame_max_tx_count;
+                       dst->long_retries = priv->long_frame_max_tx_count;
+
+                       dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED |
+                               WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT;
+                       memcpy(dst->rate_count_indices, src->tbl,
+                              sizeof(dst->rate_count_indices));
+                       src->uploaded = 1;
+                       ++arg.num;
+               }
+       }
+       spin_unlock_bh(&cache->lock);
+       cw1200_debug_tx_cache_miss(priv);
+       pr_debug("[TX policy] Upload %d policies\n", arg.num);
+       return wsm_set_tx_rate_retry_policy(priv, &arg);
+}
+
+void tx_policy_upload_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, tx_policy_upload_work);
+
+       pr_debug("[TX] TX policy upload.\n");
+       tx_policy_upload(priv);
+
+       wsm_unlock_tx(priv);
+       cw1200_tx_queues_unlock(priv);
+}
+
+/* ******************************************************************** */
+/* cw1200 TX implementation                                            */
+
+struct cw1200_txinfo {
+       struct sk_buff *skb;
+       unsigned queue;
+       struct ieee80211_tx_info *tx_info;
+       const struct ieee80211_rate *rate;
+       struct ieee80211_hdr *hdr;
+       size_t hdrlen;
+       const u8 *da;
+       struct cw1200_sta_priv *sta_priv;
+       struct ieee80211_sta *sta;
+       struct cw1200_txpriv txpriv;
+};
+
+u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates)
+{
+       u32 ret = 0;
+       int i;
+       for (i = 0; i < 32; ++i) {
+               if (rates & BIT(i))
+                       ret |= BIT(priv->rates[i].hw_value);
+       }
+       return ret;
+}
+
+static const struct ieee80211_rate *
+cw1200_get_tx_rate(const struct cw1200_common *priv,
+                  const struct ieee80211_tx_rate *rate)
+{
+       if (rate->idx < 0)
+               return NULL;
+       if (rate->flags & IEEE80211_TX_RC_MCS)
+               return &priv->mcs_rates[rate->idx];
+       return &priv->hw->wiphy->bands[priv->channel->band]->
+               bitrates[rate->idx];
+}
+
+static int
+cw1200_tx_h_calc_link_ids(struct cw1200_common *priv,
+                         struct cw1200_txinfo *t)
+{
+       if (t->sta && t->sta_priv->link_id)
+               t->txpriv.raw_link_id =
+                               t->txpriv.link_id =
+                               t->sta_priv->link_id;
+       else if (priv->mode != NL80211_IFTYPE_AP)
+               t->txpriv.raw_link_id =
+                               t->txpriv.link_id = 0;
+       else if (is_multicast_ether_addr(t->da)) {
+               if (priv->enable_beacon) {
+                       t->txpriv.raw_link_id = 0;
+                       t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM;
+               } else {
+                       t->txpriv.raw_link_id = 0;
+                       t->txpriv.link_id = 0;
+               }
+       } else {
+               t->txpriv.link_id = cw1200_find_link_id(priv, t->da);
+               if (!t->txpriv.link_id)
+                       t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da);
+               if (!t->txpriv.link_id) {
+                       wiphy_err(priv->hw->wiphy,
+                                 "No more link IDs available.\n");
+                       return -ENOENT;
+               }
+               t->txpriv.raw_link_id = t->txpriv.link_id;
+       }
+       if (t->txpriv.raw_link_id)
+               priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp =
+                               jiffies;
+       if (t->sta && (t->sta->uapsd_queues & BIT(t->queue)))
+               t->txpriv.link_id = CW1200_LINK_ID_UAPSD;
+       return 0;
+}
+
+static void
+cw1200_tx_h_pm(struct cw1200_common *priv,
+              struct cw1200_txinfo *t)
+{
+       if (ieee80211_is_auth(t->hdr->frame_control)) {
+               u32 mask = ~BIT(t->txpriv.raw_link_id);
+               spin_lock_bh(&priv->ps_state_lock);
+               priv->sta_asleep_mask &= mask;
+               priv->pspoll_mask &= mask;
+               spin_unlock_bh(&priv->ps_state_lock);
+       }
+}
+
+static void
+cw1200_tx_h_calc_tid(struct cw1200_common *priv,
+                    struct cw1200_txinfo *t)
+{
+       if (ieee80211_is_data_qos(t->hdr->frame_control)) {
+               u8 *qos = ieee80211_get_qos_ctl(t->hdr);
+               t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK;
+       } else if (ieee80211_is_data(t->hdr->frame_control)) {
+               t->txpriv.tid = 0;
+       }
+}
+
+static int
+cw1200_tx_h_crypt(struct cw1200_common *priv,
+                 struct cw1200_txinfo *t)
+{
+       if (!t->tx_info->control.hw_key ||
+           !ieee80211_has_protected(t->hdr->frame_control))
+               return 0;
+
+       t->hdrlen += t->tx_info->control.hw_key->iv_len;
+       skb_put(t->skb, t->tx_info->control.hw_key->icv_len);
+
+       if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+               skb_put(t->skb, 8); /* MIC space */
+
+       return 0;
+}
+
+static int
+cw1200_tx_h_align(struct cw1200_common *priv,
+                 struct cw1200_txinfo *t,
+                 u8 *flags)
+{
+       size_t offset = (size_t)t->skb->data & 3;
+
+       if (!offset)
+               return 0;
+
+       if (offset & 1) {
+               wiphy_err(priv->hw->wiphy,
+                         "Bug: attempt to transmit a frame with wrong alignment: %zu\n",
+                         offset);
+               return -EINVAL;
+       }
+
+       if (skb_headroom(t->skb) < offset) {
+               wiphy_err(priv->hw->wiphy,
+                         "Bug: no space allocated for DMA alignment. headroom: %d\n",
+                         skb_headroom(t->skb));
+               return -ENOMEM;
+       }
+       skb_push(t->skb, offset);
+       t->hdrlen += offset;
+       t->txpriv.offset += offset;
+       *flags |= WSM_TX_2BYTES_SHIFT;
+       cw1200_debug_tx_align(priv);
+       return 0;
+}
+
+static int
+cw1200_tx_h_action(struct cw1200_common *priv,
+                  struct cw1200_txinfo *t)
+{
+       struct ieee80211_mgmt *mgmt =
+               (struct ieee80211_mgmt *)t->hdr;
+       if (ieee80211_is_action(t->hdr->frame_control) &&
+           mgmt->u.action.category == WLAN_CATEGORY_BACK)
+               return 1;
+       else
+               return 0;
+}
+
+/* Add WSM header */
+static struct wsm_tx *
+cw1200_tx_h_wsm(struct cw1200_common *priv,
+               struct cw1200_txinfo *t)
+{
+       struct wsm_tx *wsm;
+
+       if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) {
+               wiphy_err(priv->hw->wiphy,
+                         "Bug: no space allocated for WSM header. headroom: %d\n",
+                         skb_headroom(t->skb));
+               return NULL;
+       }
+
+       wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx));
+       t->txpriv.offset += sizeof(struct wsm_tx);
+       memset(wsm, 0, sizeof(*wsm));
+       wsm->hdr.len = __cpu_to_le16(t->skb->len);
+       wsm->hdr.id = __cpu_to_le16(0x0004);
+       wsm->queue_id = wsm_queue_id_to_wsm(t->queue);
+       return wsm;
+}
+
+/* BT Coex specific handling */
+static void
+cw1200_tx_h_bt(struct cw1200_common *priv,
+              struct cw1200_txinfo *t,
+              struct wsm_tx *wsm)
+{
+       u8 priority = 0;
+
+       if (!priv->bt_present)
+               return;
+
+       if (ieee80211_is_nullfunc(t->hdr->frame_control)) {
+               priority = WSM_EPTA_PRIORITY_MGT;
+       } else if (ieee80211_is_data(t->hdr->frame_control)) {
+               /* Skip LLC SNAP header (+6) */
+               u8 *payload = &t->skb->data[t->hdrlen];
+               __be16 *ethertype = (__be16 *)&payload[6];
+               if (be16_to_cpu(*ethertype) == ETH_P_PAE)
+                       priority = WSM_EPTA_PRIORITY_EAPOL;
+       } else if (ieee80211_is_assoc_req(t->hdr->frame_control) ||
+               ieee80211_is_reassoc_req(t->hdr->frame_control)) {
+               struct ieee80211_mgmt *mgt_frame =
+                               (struct ieee80211_mgmt *)t->hdr;
+
+               if (le16_to_cpu(mgt_frame->u.assoc_req.listen_interval) <
+                                               priv->listen_interval) {
+                       pr_debug("Modified Listen Interval to %d from %d\n",
+                                priv->listen_interval,
+                                mgt_frame->u.assoc_req.listen_interval);
+                       /* Replace listen interval derieved from
+                        * the one read from SDD
+                        */
+                       mgt_frame->u.assoc_req.listen_interval = cpu_to_le16(priv->listen_interval);
+               }
+       }
+
+       if (!priority) {
+               if (ieee80211_is_action(t->hdr->frame_control))
+                       priority = WSM_EPTA_PRIORITY_ACTION;
+               else if (ieee80211_is_mgmt(t->hdr->frame_control))
+                       priority = WSM_EPTA_PRIORITY_MGT;
+               else if ((wsm->queue_id == WSM_QUEUE_VOICE))
+                       priority = WSM_EPTA_PRIORITY_VOICE;
+               else if ((wsm->queue_id == WSM_QUEUE_VIDEO))
+                       priority = WSM_EPTA_PRIORITY_VIDEO;
+               else
+                       priority = WSM_EPTA_PRIORITY_DATA;
+       }
+
+       pr_debug("[TX] EPTA priority %d.\n", priority);
+
+       wsm->flags |= priority << 1;
+}
+
+static int
+cw1200_tx_h_rate_policy(struct cw1200_common *priv,
+                       struct cw1200_txinfo *t,
+                       struct wsm_tx *wsm)
+{
+       bool tx_policy_renew = false;
+
+       t->txpriv.rate_id = tx_policy_get(priv,
+               t->tx_info->control.rates, IEEE80211_TX_MAX_RATES,
+               &tx_policy_renew);
+       if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID)
+               return -EFAULT;
+
+       wsm->flags |= t->txpriv.rate_id << 4;
+
+       t->rate = cw1200_get_tx_rate(priv,
+               &t->tx_info->control.rates[0]),
+       wsm->max_tx_rate = t->rate->hw_value;
+       if (t->rate->flags & IEEE80211_TX_RC_MCS) {
+               if (cw1200_ht_greenfield(&priv->ht_info))
+                       wsm->ht_tx_parameters |=
+                               __cpu_to_le32(WSM_HT_TX_GREENFIELD);
+               else
+                       wsm->ht_tx_parameters |=
+                               __cpu_to_le32(WSM_HT_TX_MIXED);
+       }
+
+       if (tx_policy_renew) {
+               pr_debug("[TX] TX policy renew.\n");
+               /* It's not so optimal to stop TX queues every now and then.
+                * Better to reimplement task scheduling with
+                * a counter. TODO.
+                */
+               wsm_lock_tx_async(priv);
+               cw1200_tx_queues_lock(priv);
+               if (queue_work(priv->workqueue,
+                              &priv->tx_policy_upload_work) <= 0) {
+                       cw1200_tx_queues_unlock(priv);
+                       wsm_unlock_tx(priv);
+               }
+       }
+       return 0;
+}
+
+static bool
+cw1200_tx_h_pm_state(struct cw1200_common *priv,
+                    struct cw1200_txinfo *t)
+{
+       int was_buffered = 1;
+
+       if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM &&
+           !priv->buffered_multicasts) {
+               priv->buffered_multicasts = true;
+               if (priv->sta_asleep_mask)
+                       queue_work(priv->workqueue,
+                                  &priv->multicast_start_work);
+       }
+
+       if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID)
+               was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++;
+
+       return !was_buffered;
+}
+
+/* ******************************************************************** */
+
+void cw1200_tx(struct ieee80211_hw *dev,
+              struct ieee80211_tx_control *control,
+              struct sk_buff *skb)
+{
+       struct cw1200_common *priv = dev->priv;
+       struct cw1200_txinfo t = {
+               .skb = skb,
+               .queue = skb_get_queue_mapping(skb),
+               .tx_info = IEEE80211_SKB_CB(skb),
+               .hdr = (struct ieee80211_hdr *)skb->data,
+               .txpriv.tid = CW1200_MAX_TID,
+               .txpriv.rate_id = CW1200_INVALID_RATE_ID,
+       };
+       struct ieee80211_sta *sta;
+       struct wsm_tx *wsm;
+       bool tid_update = 0;
+       u8 flags = 0;
+       int ret;
+
+       if (priv->bh_error)
+               goto drop;
+
+       t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control);
+       t.da = ieee80211_get_DA(t.hdr);
+       if (control) {
+               t.sta = control->sta;
+               t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv;
+       }
+
+       if (WARN_ON(t.queue >= 4))
+               goto drop;
+
+       ret = cw1200_tx_h_calc_link_ids(priv, &t);
+       if (ret)
+               goto drop;
+
+       pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n",
+                skb->len, t.queue, t.txpriv.link_id,
+                t.txpriv.raw_link_id);
+
+       cw1200_tx_h_pm(priv, &t);
+       cw1200_tx_h_calc_tid(priv, &t);
+       ret = cw1200_tx_h_crypt(priv, &t);
+       if (ret)
+               goto drop;
+       ret = cw1200_tx_h_align(priv, &t, &flags);
+       if (ret)
+               goto drop;
+       ret = cw1200_tx_h_action(priv, &t);
+       if (ret)
+               goto drop;
+       wsm = cw1200_tx_h_wsm(priv, &t);
+       if (!wsm) {
+               ret = -ENOMEM;
+               goto drop;
+       }
+       wsm->flags |= flags;
+       cw1200_tx_h_bt(priv, &t, wsm);
+       ret = cw1200_tx_h_rate_policy(priv, &t, wsm);
+       if (ret)
+               goto drop;
+
+       rcu_read_lock();
+       sta = rcu_dereference(t.sta);
+
+       spin_lock_bh(&priv->ps_state_lock);
+       {
+               tid_update = cw1200_tx_h_pm_state(priv, &t);
+               BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue],
+                                       t.skb, &t.txpriv));
+       }
+       spin_unlock_bh(&priv->ps_state_lock);
+
+       if (tid_update && sta)
+               ieee80211_sta_set_buffered(sta, t.txpriv.tid, true);
+
+       rcu_read_unlock();
+
+       cw1200_bh_wakeup(priv);
+
+       return;
+
+drop:
+       cw1200_skb_dtor(priv, skb, &t.txpriv);
+       return;
+}
+
+/* ******************************************************************** */
+
+static int cw1200_handle_action_rx(struct cw1200_common *priv,
+                                  struct sk_buff *skb)
+{
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
+
+       /* Filter block ACK negotiation: fully controlled by firmware */
+       if (mgmt->u.action.category == WLAN_CATEGORY_BACK)
+               return 1;
+
+       return 0;
+}
+
+static int cw1200_handle_pspoll(struct cw1200_common *priv,
+                               struct sk_buff *skb)
+{
+       struct ieee80211_sta *sta;
+       struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data;
+       int link_id = 0;
+       u32 pspoll_mask = 0;
+       int drop = 1;
+       int i;
+
+       if (priv->join_status != CW1200_JOIN_STATUS_AP)
+               goto done;
+       if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN))
+               goto done;
+
+       rcu_read_lock();
+       sta = ieee80211_find_sta(priv->vif, pspoll->ta);
+       if (sta) {
+               struct cw1200_sta_priv *sta_priv;
+               sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv;
+               link_id = sta_priv->link_id;
+               pspoll_mask = BIT(sta_priv->link_id);
+       }
+       rcu_read_unlock();
+       if (!link_id)
+               goto done;
+
+       priv->pspoll_mask |= pspoll_mask;
+       drop = 0;
+
+       /* Do not report pspols if data for given link id is queued already. */
+       for (i = 0; i < 4; ++i) {
+               if (cw1200_queue_get_num_queued(&priv->tx_queue[i],
+                                               pspoll_mask)) {
+                       cw1200_bh_wakeup(priv);
+                       drop = 1;
+                       break;
+               }
+       }
+       pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd");
+done:
+       return drop;
+}
+
+/* ******************************************************************** */
+
+void cw1200_tx_confirm_cb(struct cw1200_common *priv,
+                         int link_id,
+                         struct wsm_tx_confirm *arg)
+{
+       u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id);
+       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+       struct sk_buff *skb;
+       const struct cw1200_txpriv *txpriv;
+
+       pr_debug("[TX] TX confirm: %d, %d.\n",
+                arg->status, arg->ack_failures);
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+               /* STA is stopped. */
+               return;
+       }
+
+       if (WARN_ON(queue_id >= 4))
+               return;
+
+       if (arg->status)
+               pr_debug("TX failed: %d.\n", arg->status);
+
+       if ((arg->status == WSM_REQUEUE) &&
+           (arg->flags & WSM_TX_STATUS_REQUEUE)) {
+               /* "Requeue" means "implicit suspend" */
+               struct wsm_suspend_resume suspend = {
+                       .link_id = link_id,
+                       .stop = 1,
+                       .multicast = !link_id,
+               };
+               cw1200_suspend_resume(priv, &suspend);
+               wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n",
+                          link_id,
+                          cw1200_queue_get_generation(arg->packet_id) + 1,
+                          priv->sta_asleep_mask);
+               cw1200_queue_requeue(queue, arg->packet_id);
+               spin_lock_bh(&priv->ps_state_lock);
+               if (!link_id) {
+                       priv->buffered_multicasts = true;
+                       if (priv->sta_asleep_mask) {
+                               queue_work(priv->workqueue,
+                                          &priv->multicast_start_work);
+                       }
+               }
+               spin_unlock_bh(&priv->ps_state_lock);
+       } else if (!cw1200_queue_get_skb(queue, arg->packet_id,
+                                        &skb, &txpriv)) {
+               struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb);
+               int tx_count = arg->ack_failures;
+               u8 ht_flags = 0;
+               int i;
+
+               if (cw1200_ht_greenfield(&priv->ht_info))
+                       ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
+
+               spin_lock(&priv->bss_loss_lock);
+               if (priv->bss_loss_state &&
+                   arg->packet_id == priv->bss_loss_confirm_id) {
+                       if (arg->status) {
+                               /* Recovery failed */
+                               __cw1200_cqm_bssloss_sm(priv, 0, 0, 1);
+                       } else {
+                               /* Recovery succeeded */
+                               __cw1200_cqm_bssloss_sm(priv, 0, 1, 0);
+                       }
+               }
+               spin_unlock(&priv->bss_loss_lock);
+
+               if (!arg->status) {
+                       tx->flags |= IEEE80211_TX_STAT_ACK;
+                       ++tx_count;
+                       cw1200_debug_txed(priv);
+                       if (arg->flags & WSM_TX_STATUS_AGGREGATION) {
+                               /* Do not report aggregation to mac80211:
+                                * it confuses minstrel a lot.
+                                */
+                               /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */
+                               cw1200_debug_txed_agg(priv);
+                       }
+               } else {
+                       if (tx_count)
+                               ++tx_count;
+               }
+
+               for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
+                       if (tx->status.rates[i].count >= tx_count) {
+                               tx->status.rates[i].count = tx_count;
+                               break;
+                       }
+                       tx_count -= tx->status.rates[i].count;
+                       if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS)
+                               tx->status.rates[i].flags |= ht_flags;
+               }
+
+               for (++i; i < IEEE80211_TX_MAX_RATES; ++i) {
+                       tx->status.rates[i].count = 0;
+                       tx->status.rates[i].idx = -1;
+               }
+
+               /* Pull off any crypto trailers that we added on */
+               if (tx->control.hw_key) {
+                       skb_trim(skb, skb->len - tx->control.hw_key->icv_len);
+                       if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+                               skb_trim(skb, skb->len - 8); /* MIC space */
+               }
+               cw1200_queue_remove(queue, arg->packet_id);
+       }
+       /* XXX TODO:  Only wake if there are pending transmits.. */
+       cw1200_bh_wakeup(priv);
+}
+
+static void cw1200_notify_buffered_tx(struct cw1200_common *priv,
+                              struct sk_buff *skb, int link_id, int tid)
+{
+       struct ieee80211_sta *sta;
+       struct ieee80211_hdr *hdr;
+       u8 *buffered;
+       u8 still_buffered = 0;
+
+       if (link_id && tid < CW1200_MAX_TID) {
+               buffered = priv->link_id_db
+                               [link_id - 1].buffered;
+
+               spin_lock_bh(&priv->ps_state_lock);
+               if (!WARN_ON(!buffered[tid]))
+                       still_buffered = --buffered[tid];
+               spin_unlock_bh(&priv->ps_state_lock);
+
+               if (!still_buffered && tid < CW1200_MAX_TID) {
+                       hdr = (struct ieee80211_hdr *)skb->data;
+                       rcu_read_lock();
+                       sta = ieee80211_find_sta(priv->vif, hdr->addr1);
+                       if (sta)
+                               ieee80211_sta_set_buffered(sta, tid, false);
+                       rcu_read_unlock();
+               }
+       }
+}
+
+void cw1200_skb_dtor(struct cw1200_common *priv,
+                    struct sk_buff *skb,
+                    const struct cw1200_txpriv *txpriv)
+{
+       skb_pull(skb, txpriv->offset);
+       if (txpriv->rate_id != CW1200_INVALID_RATE_ID) {
+               cw1200_notify_buffered_tx(priv, skb,
+                                         txpriv->raw_link_id, txpriv->tid);
+               tx_policy_put(priv, txpriv->rate_id);
+       }
+       ieee80211_tx_status(priv->hw, skb);
+}
+
+void cw1200_rx_cb(struct cw1200_common *priv,
+                 struct wsm_rx *arg,
+                 int link_id,
+                 struct sk_buff **skb_p)
+{
+       struct sk_buff *skb = *skb_p;
+       struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       struct cw1200_link_entry *entry = NULL;
+       unsigned long grace_period;
+
+       bool early_data = false;
+       bool p2p = priv->vif && priv->vif->p2p;
+       size_t hdrlen;
+       hdr->flag = 0;
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+               /* STA is stopped. */
+               goto drop;
+       }
+
+       if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) {
+               entry = &priv->link_id_db[link_id - 1];
+               if (entry->status == CW1200_LINK_SOFT &&
+                   ieee80211_is_data(frame->frame_control))
+                       early_data = true;
+               entry->timestamp = jiffies;
+       } else if (p2p &&
+                  ieee80211_is_action(frame->frame_control) &&
+                  (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+               pr_debug("[RX] Going to MAP&RESET link ID\n");
+               WARN_ON(work_pending(&priv->linkid_reset_work));
+               memcpy(&priv->action_frame_sa[0],
+                      ieee80211_get_SA(frame), ETH_ALEN);
+               priv->action_linkid = 0;
+               schedule_work(&priv->linkid_reset_work);
+       }
+
+       if (link_id && p2p &&
+           ieee80211_is_action(frame->frame_control) &&
+           (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+               /* Link ID already exists for the ACTION frame.
+                * Reset and Remap
+                */
+               WARN_ON(work_pending(&priv->linkid_reset_work));
+               memcpy(&priv->action_frame_sa[0],
+                      ieee80211_get_SA(frame), ETH_ALEN);
+               priv->action_linkid = link_id;
+               schedule_work(&priv->linkid_reset_work);
+       }
+       if (arg->status) {
+               if (arg->status == WSM_STATUS_MICFAILURE) {
+                       pr_debug("[RX] MIC failure.\n");
+                       hdr->flag |= RX_FLAG_MMIC_ERROR;
+               } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
+                       pr_debug("[RX] No key found.\n");
+                       goto drop;
+               } else {
+                       pr_debug("[RX] Receive failure: %d.\n",
+                                arg->status);
+                       goto drop;
+               }
+       }
+
+       if (skb->len < sizeof(struct ieee80211_pspoll)) {
+               wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n");
+               goto drop;
+       }
+
+       if (ieee80211_is_pspoll(frame->frame_control))
+               if (cw1200_handle_pspoll(priv, skb))
+                       goto drop;
+
+       hdr->band = ((arg->channel_number & 0xff00) ||
+                    (arg->channel_number > 14)) ?
+                       IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
+       hdr->freq = ieee80211_channel_to_frequency(
+                       arg->channel_number,
+                       hdr->band);
+
+       if (arg->rx_rate >= 14) {
+               hdr->flag |= RX_FLAG_HT;
+               hdr->rate_idx = arg->rx_rate - 14;
+       } else if (arg->rx_rate >= 4) {
+               hdr->rate_idx = arg->rx_rate - 2;
+       } else {
+               hdr->rate_idx = arg->rx_rate;
+       }
+
+       hdr->signal = (s8)arg->rcpi_rssi;
+       hdr->antenna = 0;
+
+       hdrlen = ieee80211_hdrlen(frame->frame_control);
+
+       if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+               size_t iv_len = 0, icv_len = 0;
+
+               hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED;
+
+               /* Oops... There is no fast way to ask mac80211 about
+                * IV/ICV lengths. Even defineas are not exposed.
+                */
+               switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+               case WSM_RX_STATUS_WEP:
+                       iv_len = 4 /* WEP_IV_LEN */;
+                       icv_len = 4 /* WEP_ICV_LEN */;
+                       break;
+               case WSM_RX_STATUS_TKIP:
+                       iv_len = 8 /* TKIP_IV_LEN */;
+                       icv_len = 4 /* TKIP_ICV_LEN */
+                               + 8 /*MICHAEL_MIC_LEN*/;
+                       hdr->flag |= RX_FLAG_MMIC_STRIPPED;
+                       break;
+               case WSM_RX_STATUS_AES:
+                       iv_len = 8 /* CCMP_HDR_LEN */;
+                       icv_len = 8 /* CCMP_MIC_LEN */;
+                       break;
+               case WSM_RX_STATUS_WAPI:
+                       iv_len = 18 /* WAPI_HDR_LEN */;
+                       icv_len = 16 /* WAPI_MIC_LEN */;
+                       break;
+               default:
+                       pr_warn("Unknown encryption type %d\n",
+                               WSM_RX_STATUS_ENCRYPTION(arg->flags));
+                       goto drop;
+               }
+
+               /* Firmware strips ICV in case of MIC failure. */
+               if (arg->status == WSM_STATUS_MICFAILURE)
+                       icv_len = 0;
+
+               if (skb->len < hdrlen + iv_len + icv_len) {
+                       wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n");
+                       goto drop;
+               }
+
+               /* Remove IV, ICV and MIC */
+               skb_trim(skb, skb->len - icv_len);
+               memmove(skb->data + iv_len, skb->data, hdrlen);
+               skb_pull(skb, iv_len);
+       }
+
+       /* Remove TSF from the end of frame */
+       if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) {
+               memcpy(&hdr->mactime, skb->data + skb->len - 8, 8);
+               hdr->mactime = le64_to_cpu(hdr->mactime);
+               if (skb->len >= 8)
+                       skb_trim(skb, skb->len - 8);
+       } else {
+               hdr->mactime = 0;
+       }
+
+       cw1200_debug_rxed(priv);
+       if (arg->flags & WSM_RX_STATUS_AGGREGATE)
+               cw1200_debug_rxed_agg(priv);
+
+       if (ieee80211_is_action(frame->frame_control) &&
+           (arg->flags & WSM_RX_STATUS_ADDRESS1)) {
+               if (cw1200_handle_action_rx(priv, skb))
+                       return;
+       } else if (ieee80211_is_beacon(frame->frame_control) &&
+                  !arg->status && priv->vif &&
+                  ether_addr_equal(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid)) {
+               const u8 *tim_ie;
+               u8 *ies = ((struct ieee80211_mgmt *)
+                         (skb->data))->u.beacon.variable;
+               size_t ies_len = skb->len - (ies - (u8 *)(skb->data));
+
+               tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len);
+               if (tim_ie) {
+                       struct ieee80211_tim_ie *tim =
+                               (struct ieee80211_tim_ie *)&tim_ie[2];
+
+                       if (priv->join_dtim_period != tim->dtim_period) {
+                               priv->join_dtim_period = tim->dtim_period;
+                               queue_work(priv->workqueue,
+                                          &priv->set_beacon_wakeup_period_work);
+                       }
+               }
+
+               /* Disable beacon filter once we're associated... */
+               if (priv->disable_beacon_filter &&
+                   (priv->vif->bss_conf.assoc ||
+                    priv->vif->bss_conf.ibss_joined)) {
+                       priv->disable_beacon_filter = false;
+                       queue_work(priv->workqueue,
+                                  &priv->update_filtering_work);
+               }
+       }
+
+       /* Stay awake after frame is received to give
+        * userspace chance to react and acquire appropriate
+        * wakelock.
+        */
+       if (ieee80211_is_auth(frame->frame_control))
+               grace_period = 5 * HZ;
+       else if (ieee80211_is_deauth(frame->frame_control))
+               grace_period = 5 * HZ;
+       else
+               grace_period = 1 * HZ;
+       cw1200_pm_stay_awake(&priv->pm_state, grace_period);
+
+       if (early_data) {
+               spin_lock_bh(&priv->ps_state_lock);
+               /* Double-check status with lock held */
+               if (entry->status == CW1200_LINK_SOFT)
+                       skb_queue_tail(&entry->rx_queue, skb);
+               else
+                       ieee80211_rx_irqsafe(priv->hw, skb);
+               spin_unlock_bh(&priv->ps_state_lock);
+       } else {
+               ieee80211_rx_irqsafe(priv->hw, skb);
+       }
+       *skb_p = NULL;
+
+       return;
+
+drop:
+       /* TODO: update failure counters */
+       return;
+}
+
+/* ******************************************************************** */
+/* Security                                                            */
+
+int cw1200_alloc_key(struct cw1200_common *priv)
+{
+       int idx;
+
+       idx = ffs(~priv->key_map) - 1;
+       if (idx < 0 || idx > WSM_KEY_MAX_INDEX)
+               return -1;
+
+       priv->key_map |= BIT(idx);
+       priv->keys[idx].index = idx;
+       return idx;
+}
+
+void cw1200_free_key(struct cw1200_common *priv, int idx)
+{
+       BUG_ON(!(priv->key_map & BIT(idx)));
+       memset(&priv->keys[idx], 0, sizeof(priv->keys[idx]));
+       priv->key_map &= ~BIT(idx);
+}
+
+void cw1200_free_keys(struct cw1200_common *priv)
+{
+       memset(&priv->keys, 0, sizeof(priv->keys));
+       priv->key_map = 0;
+}
+
+int cw1200_upload_keys(struct cw1200_common *priv)
+{
+       int idx, ret = 0;
+       for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx)
+               if (priv->key_map & BIT(idx)) {
+                       ret = wsm_add_key(priv, &priv->keys[idx]);
+                       if (ret < 0)
+                               break;
+               }
+       return ret;
+}
+
+/* Workaround for WFD test case 6.1.10 */
+void cw1200_link_id_reset(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, linkid_reset_work);
+       int temp_linkid;
+
+       if (!priv->action_linkid) {
+               /* In GO mode we can receive ACTION frames without a linkID */
+               temp_linkid = cw1200_alloc_link_id(priv,
+                               &priv->action_frame_sa[0]);
+               WARN_ON(!temp_linkid);
+               if (temp_linkid) {
+                       /* Make sure we execute the WQ */
+                       flush_workqueue(priv->workqueue);
+                       /* Release the link ID */
+                       spin_lock_bh(&priv->ps_state_lock);
+                       priv->link_id_db[temp_linkid - 1].prev_status =
+                               priv->link_id_db[temp_linkid - 1].status;
+                       priv->link_id_db[temp_linkid - 1].status =
+                               CW1200_LINK_RESET;
+                       spin_unlock_bh(&priv->ps_state_lock);
+                       wsm_lock_tx_async(priv);
+                       if (queue_work(priv->workqueue,
+                                      &priv->link_id_work) <= 0)
+                               wsm_unlock_tx(priv);
+               }
+       } else {
+               spin_lock_bh(&priv->ps_state_lock);
+               priv->link_id_db[priv->action_linkid - 1].prev_status =
+                       priv->link_id_db[priv->action_linkid - 1].status;
+               priv->link_id_db[priv->action_linkid - 1].status =
+                       CW1200_LINK_RESET_REMAP;
+               spin_unlock_bh(&priv->ps_state_lock);
+               wsm_lock_tx_async(priv);
+               if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+                       wsm_unlock_tx(priv);
+               flush_workqueue(priv->workqueue);
+       }
+}
+
+int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac)
+{
+       int i, ret = 0;
+       spin_lock_bh(&priv->ps_state_lock);
+       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+               if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) &&
+                   priv->link_id_db[i].status) {
+                       priv->link_id_db[i].timestamp = jiffies;
+                       ret = i + 1;
+                       break;
+               }
+       }
+       spin_unlock_bh(&priv->ps_state_lock);
+       return ret;
+}
+
+int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac)
+{
+       int i, ret = 0;
+       unsigned long max_inactivity = 0;
+       unsigned long now = jiffies;
+
+       spin_lock_bh(&priv->ps_state_lock);
+       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+               if (!priv->link_id_db[i].status) {
+                       ret = i + 1;
+                       break;
+               } else if (priv->link_id_db[i].status != CW1200_LINK_HARD &&
+                          !priv->tx_queue_stats.link_map_cache[i + 1]) {
+                       unsigned long inactivity =
+                               now - priv->link_id_db[i].timestamp;
+                       if (inactivity < max_inactivity)
+                               continue;
+                       max_inactivity = inactivity;
+                       ret = i + 1;
+               }
+       }
+       if (ret) {
+               struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1];
+               pr_debug("[AP] STA added, link_id: %d\n", ret);
+               entry->status = CW1200_LINK_RESERVE;
+               memcpy(&entry->mac, mac, ETH_ALEN);
+               memset(&entry->buffered, 0, CW1200_MAX_TID);
+               skb_queue_head_init(&entry->rx_queue);
+               wsm_lock_tx_async(priv);
+               if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+                       wsm_unlock_tx(priv);
+       } else {
+               wiphy_info(priv->hw->wiphy,
+                          "[AP] Early: no more link IDs available.\n");
+       }
+
+       spin_unlock_bh(&priv->ps_state_lock);
+       return ret;
+}
+
+void cw1200_link_id_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, link_id_work);
+       wsm_flush_tx(priv);
+       cw1200_link_id_gc_work(&priv->link_id_gc_work.work);
+       wsm_unlock_tx(priv);
+}
+
+void cw1200_link_id_gc_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, link_id_gc_work.work);
+       struct wsm_reset reset = {
+               .reset_statistics = false,
+       };
+       struct wsm_map_link map_link = {
+               .link_id = 0,
+       };
+       unsigned long now = jiffies;
+       unsigned long next_gc = -1;
+       long ttl;
+       bool need_reset;
+       u32 mask;
+       int i;
+
+       if (priv->join_status != CW1200_JOIN_STATUS_AP)
+               return;
+
+       wsm_lock_tx(priv);
+       spin_lock_bh(&priv->ps_state_lock);
+       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+               need_reset = false;
+               mask = BIT(i + 1);
+               if (priv->link_id_db[i].status == CW1200_LINK_RESERVE ||
+                   (priv->link_id_db[i].status == CW1200_LINK_HARD &&
+                    !(priv->link_id_map & mask))) {
+                       if (priv->link_id_map & mask) {
+                               priv->sta_asleep_mask &= ~mask;
+                               priv->pspoll_mask &= ~mask;
+                               need_reset = true;
+                       }
+                       priv->link_id_map |= mask;
+                       if (priv->link_id_db[i].status != CW1200_LINK_HARD)
+                               priv->link_id_db[i].status = CW1200_LINK_SOFT;
+                       memcpy(map_link.mac_addr, priv->link_id_db[i].mac,
+                              ETH_ALEN);
+                       spin_unlock_bh(&priv->ps_state_lock);
+                       if (need_reset) {
+                               reset.link_id = i + 1;
+                               wsm_reset(priv, &reset);
+                       }
+                       map_link.link_id = i + 1;
+                       wsm_map_link(priv, &map_link);
+                       next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT);
+                       spin_lock_bh(&priv->ps_state_lock);
+               } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) {
+                       ttl = priv->link_id_db[i].timestamp - now +
+                                       CW1200_LINK_ID_GC_TIMEOUT;
+                       if (ttl <= 0) {
+                               need_reset = true;
+                               priv->link_id_db[i].status = CW1200_LINK_OFF;
+                               priv->link_id_map &= ~mask;
+                               priv->sta_asleep_mask &= ~mask;
+                               priv->pspoll_mask &= ~mask;
+                               eth_zero_addr(map_link.mac_addr);
+                               spin_unlock_bh(&priv->ps_state_lock);
+                               reset.link_id = i + 1;
+                               wsm_reset(priv, &reset);
+                               spin_lock_bh(&priv->ps_state_lock);
+                       } else {
+                               next_gc = min_t(unsigned long, next_gc, ttl);
+                       }
+               } else if (priv->link_id_db[i].status == CW1200_LINK_RESET ||
+                               priv->link_id_db[i].status ==
+                               CW1200_LINK_RESET_REMAP) {
+                       int status = priv->link_id_db[i].status;
+                       priv->link_id_db[i].status =
+                                       priv->link_id_db[i].prev_status;
+                       priv->link_id_db[i].timestamp = now;
+                       reset.link_id = i + 1;
+                       spin_unlock_bh(&priv->ps_state_lock);
+                       wsm_reset(priv, &reset);
+                       if (status == CW1200_LINK_RESET_REMAP) {
+                               memcpy(map_link.mac_addr,
+                                      priv->link_id_db[i].mac,
+                                      ETH_ALEN);
+                               map_link.link_id = i + 1;
+                               wsm_map_link(priv, &map_link);
+                               next_gc = min(next_gc,
+                                               CW1200_LINK_ID_GC_TIMEOUT);
+                       }
+                       spin_lock_bh(&priv->ps_state_lock);
+               }
+               if (need_reset) {
+                       skb_queue_purge(&priv->link_id_db[i].rx_queue);
+                       pr_debug("[AP] STA removed, link_id: %d\n",
+                                reset.link_id);
+               }
+       }
+       spin_unlock_bh(&priv->ps_state_lock);
+       if (next_gc != -1)
+               queue_delayed_work(priv->workqueue,
+                                  &priv->link_id_gc_work, next_gc);
+       wsm_unlock_tx(priv);
+}
diff --git a/drivers/net/wireless/st/cw1200/txrx.h b/drivers/net/wireless/st/cw1200/txrx.h
new file mode 100644 (file)
index 0000000..492a4e1
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Datapath interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_TXRX_H
+#define CW1200_TXRX_H
+
+#include <linux/list.h>
+
+/* extern */ struct ieee80211_hw;
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct wsm_rx;
+/* extern */ struct wsm_tx_confirm;
+/* extern */ struct cw1200_txpriv;
+
+struct tx_policy {
+       union {
+               __le32 tbl[3];
+               u8 raw[12];
+       };
+       u8  defined;
+       u8  usage_count;
+       u8  retry_count;
+       u8  uploaded;
+};
+
+struct tx_policy_cache_entry {
+       struct tx_policy policy;
+       struct list_head link;
+};
+
+#define TX_POLICY_CACHE_SIZE   (8)
+struct tx_policy_cache {
+       struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
+       struct list_head used;
+       struct list_head free;
+       spinlock_t lock; /* Protect policy cache */
+};
+
+/* ******************************************************************** */
+/* TX policy cache                                                     */
+/* Intention of TX policy cache is an overcomplicated WSM API.
+ * Device does not accept per-PDU tx retry sequence.
+ * It uses "tx retry policy id" instead, so driver code has to sync
+ * linux tx retry sequences with a retry policy table in the device.
+ */
+void tx_policy_init(struct cw1200_common *priv);
+void tx_policy_upload_work(struct work_struct *work);
+void tx_policy_clean(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* TX implementation                                                   */
+
+u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv,
+                              u32 rates);
+void cw1200_tx(struct ieee80211_hw *dev,
+              struct ieee80211_tx_control *control,
+              struct sk_buff *skb);
+void cw1200_skb_dtor(struct cw1200_common *priv,
+                    struct sk_buff *skb,
+                    const struct cw1200_txpriv *txpriv);
+
+/* ******************************************************************** */
+/* WSM callbacks                                                       */
+
+void cw1200_tx_confirm_cb(struct cw1200_common *priv,
+                         int link_id,
+                         struct wsm_tx_confirm *arg);
+void cw1200_rx_cb(struct cw1200_common *priv,
+                 struct wsm_rx *arg,
+                 int link_id,
+                 struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* Timeout                                                             */
+
+void cw1200_tx_timeout(struct work_struct *work);
+
+/* ******************************************************************** */
+/* Security                                                            */
+int cw1200_alloc_key(struct cw1200_common *priv);
+void cw1200_free_key(struct cw1200_common *priv, int idx);
+void cw1200_free_keys(struct cw1200_common *priv);
+int cw1200_upload_keys(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* Workaround for WFD test case 6.1.10                                 */
+void cw1200_link_id_reset(struct work_struct *work);
+
+#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
+
+int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac);
+int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac);
+void cw1200_link_id_work(struct work_struct *work);
+void cw1200_link_id_gc_work(struct work_struct *work);
+
+
+#endif /* CW1200_TXRX_H */
diff --git a/drivers/net/wireless/st/cw1200/wsm.c b/drivers/net/wireless/st/cw1200/wsm.c
new file mode 100644 (file)
index 0000000..9e0ca30
--- /dev/null
@@ -0,0 +1,1822 @@
+/*
+ * WSM host interface (HI) implementation for
+ * ST-Ericsson CW1200 mac80211 drivers.
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+#include "cw1200.h"
+#include "wsm.h"
+#include "bh.h"
+#include "sta.h"
+#include "debug.h"
+
+#define WSM_CMD_TIMEOUT                (2 * HZ) /* With respect to interrupt loss */
+#define WSM_CMD_START_TIMEOUT  (7 * HZ)
+#define WSM_CMD_RESET_TIMEOUT  (3 * HZ) /* 2 sec. timeout was observed.   */
+#define WSM_CMD_MAX_TIMEOUT    (3 * HZ)
+
+#define WSM_SKIP(buf, size)                                            \
+       do {                                                            \
+               if ((buf)->data + size > (buf)->end)                    \
+                       goto underflow;                                 \
+               (buf)->data += size;                                    \
+       } while (0)
+
+#define WSM_GET(buf, ptr, size)                                                \
+       do {                                                            \
+               if ((buf)->data + size > (buf)->end)                    \
+                       goto underflow;                                 \
+               memcpy(ptr, (buf)->data, size);                         \
+               (buf)->data += size;                                    \
+       } while (0)
+
+#define __WSM_GET(buf, type, type2, cvt)                               \
+       ({                                                              \
+               type val;                                               \
+               if ((buf)->data + sizeof(type) > (buf)->end)            \
+                       goto underflow;                                 \
+               val = cvt(*(type2 *)(buf)->data);                       \
+               (buf)->data += sizeof(type);                            \
+               val;                                                    \
+       })
+
+#define WSM_GET8(buf)  __WSM_GET(buf, u8, u8, (u8))
+#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16, __le16_to_cpu)
+#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32, __le32_to_cpu)
+
+#define WSM_PUT(buf, ptr, size)                                                \
+       do {                                                            \
+               if ((buf)->data + size > (buf)->end)            \
+                       if (wsm_buf_reserve((buf), size))       \
+                               goto nomem;                             \
+               memcpy((buf)->data, ptr, size);                         \
+               (buf)->data += size;                                    \
+       } while (0)
+
+#define __WSM_PUT(buf, val, type, type2, cvt)                          \
+       do {                                                            \
+               if ((buf)->data + sizeof(type) > (buf)->end)            \
+                       if (wsm_buf_reserve((buf), sizeof(type))) \
+                               goto nomem;                             \
+               *(type2 *)(buf)->data = cvt(val);                       \
+               (buf)->data += sizeof(type);                            \
+       } while (0)
+
+#define WSM_PUT8(buf, val)  __WSM_PUT(buf, val, u8, u8, (u8))
+#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __le16, __cpu_to_le16)
+#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __le32, __cpu_to_le32)
+
+static void wsm_buf_reset(struct wsm_buf *buf);
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size);
+
+static int wsm_cmd_send(struct cw1200_common *priv,
+                       struct wsm_buf *buf,
+                       void *arg, u16 cmd, long tmo);
+
+#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux))
+#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux))
+
+/* ******************************************************************** */
+/* WSM API implementation                                              */
+
+static int wsm_generic_confirm(struct cw1200_common *priv,
+                            void *arg,
+                            struct wsm_buf *buf)
+{
+       u32 status = WSM_GET32(buf);
+       if (status != WSM_STATUS_SUCCESS)
+               return -EINVAL;
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime);
+       WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime);
+       WSM_PUT32(buf, arg->dot11RtsThreshold);
+
+       /* DPD block. */
+       WSM_PUT16(buf, arg->dpdData_size + 12);
+       WSM_PUT16(buf, 1); /* DPD version */
+       WSM_PUT(buf, arg->dot11StationId, ETH_ALEN);
+       WSM_PUT16(buf, 5); /* DPD flags */
+       WSM_PUT(buf, arg->dpdData, arg->dpdData_size);
+
+       ret = wsm_cmd_send(priv, buf, arg,
+                          WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+static int wsm_configuration_confirm(struct cw1200_common *priv,
+                                    struct wsm_configuration *arg,
+                                    struct wsm_buf *buf)
+{
+       int i;
+       int status;
+
+       status = WSM_GET32(buf);
+       if (WARN_ON(status != WSM_STATUS_SUCCESS))
+               return -EINVAL;
+
+       WSM_GET(buf, arg->dot11StationId, ETH_ALEN);
+       arg->dot11FrequencyBandsSupported = WSM_GET8(buf);
+       WSM_SKIP(buf, 1);
+       arg->supportedRateMask = WSM_GET32(buf);
+       for (i = 0; i < 2; ++i) {
+               arg->txPowerRange[i].min_power_level = WSM_GET32(buf);
+               arg->txPowerRange[i].max_power_level = WSM_GET32(buf);
+               arg->txPowerRange[i].stepping = WSM_GET32(buf);
+       }
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id);
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT32(buf, arg->reset_statistics ? 0 : 1);
+       ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+struct wsm_mib {
+       u16 mib_id;
+       void *buf;
+       size_t buf_size;
+};
+
+int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
+                       size_t buf_size)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       struct wsm_mib mib_buf = {
+               .mib_id = mib_id,
+               .buf = _buf,
+               .buf_size = buf_size,
+       };
+       wsm_cmd_lock(priv);
+
+       WSM_PUT16(buf, mib_id);
+       WSM_PUT16(buf, 0);
+
+       ret = wsm_cmd_send(priv, buf, &mib_buf,
+                          WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+static int wsm_read_mib_confirm(struct cw1200_common *priv,
+                               struct wsm_mib *arg,
+                               struct wsm_buf *buf)
+{
+       u16 size;
+       if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS))
+               return -EINVAL;
+
+       if (WARN_ON(WSM_GET16(buf) != arg->mib_id))
+               return -EINVAL;
+
+       size = WSM_GET16(buf);
+       if (size > arg->buf_size)
+               size = arg->buf_size;
+
+       WSM_GET(buf, arg->buf, size);
+       arg->buf_size = size;
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
+                       size_t buf_size)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       struct wsm_mib mib_buf = {
+               .mib_id = mib_id,
+               .buf = _buf,
+               .buf_size = buf_size,
+       };
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT16(buf, mib_id);
+       WSM_PUT16(buf, buf_size);
+       WSM_PUT(buf, _buf, buf_size);
+
+       ret = wsm_cmd_send(priv, buf, &mib_buf,
+                          WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+static int wsm_write_mib_confirm(struct cw1200_common *priv,
+                               struct wsm_mib *arg,
+                               struct wsm_buf *buf)
+{
+       int ret;
+
+       ret = wsm_generic_confirm(priv, arg, buf);
+       if (ret)
+               return ret;
+
+       if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) {
+               /* OperationalMode: update PM status. */
+               const char *p = arg->buf;
+               cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false);
+       }
+       return 0;
+}
+
+/* ******************************************************************** */
+
+int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg)
+{
+       int i;
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       if (arg->num_channels > 48)
+               return -EINVAL;
+
+       if (arg->num_ssids > 2)
+               return -EINVAL;
+
+       if (arg->band > 1)
+               return -EINVAL;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->band);
+       WSM_PUT8(buf, arg->type);
+       WSM_PUT8(buf, arg->flags);
+       WSM_PUT8(buf, arg->max_tx_rate);
+       WSM_PUT32(buf, arg->auto_scan_interval);
+       WSM_PUT8(buf, arg->num_probes);
+       WSM_PUT8(buf, arg->num_channels);
+       WSM_PUT8(buf, arg->num_ssids);
+       WSM_PUT8(buf, arg->probe_delay);
+
+       for (i = 0; i < arg->num_channels; ++i) {
+               WSM_PUT16(buf, arg->ch[i].number);
+               WSM_PUT16(buf, 0);
+               WSM_PUT32(buf, arg->ch[i].min_chan_time);
+               WSM_PUT32(buf, arg->ch[i].max_chan_time);
+               WSM_PUT32(buf, 0);
+       }
+
+       for (i = 0; i < arg->num_ssids; ++i) {
+               WSM_PUT32(buf, arg->ssids[i].length);
+               WSM_PUT(buf, &arg->ssids[i].ssid[0],
+                       sizeof(arg->ssids[i].ssid));
+       }
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_scan(struct cw1200_common *priv)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       wsm_cmd_lock(priv);
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+}
+
+
+static int wsm_tx_confirm(struct cw1200_common *priv,
+                         struct wsm_buf *buf,
+                         int link_id)
+{
+       struct wsm_tx_confirm tx_confirm;
+
+       tx_confirm.packet_id = WSM_GET32(buf);
+       tx_confirm.status = WSM_GET32(buf);
+       tx_confirm.tx_rate = WSM_GET8(buf);
+       tx_confirm.ack_failures = WSM_GET8(buf);
+       tx_confirm.flags = WSM_GET16(buf);
+       tx_confirm.media_delay = WSM_GET32(buf);
+       tx_confirm.tx_queue_delay = WSM_GET32(buf);
+
+       cw1200_tx_confirm_cb(priv, link_id, &tx_confirm);
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+static int wsm_multi_tx_confirm(struct cw1200_common *priv,
+                               struct wsm_buf *buf, int link_id)
+{
+       int ret;
+       int count;
+       int i;
+
+       count = WSM_GET32(buf);
+       if (WARN_ON(count <= 0))
+               return -EINVAL;
+
+       if (count > 1) {
+               /* We already released one buffer, now for the rest */
+               ret = wsm_release_tx_buffer(priv, count - 1);
+               if (ret < 0)
+                       return ret;
+               else if (ret > 0)
+                       cw1200_bh_wakeup(priv);
+       }
+
+       cw1200_debug_txed_multi(priv, count);
+       for (i = 0; i < count; ++i) {
+               ret = wsm_tx_confirm(priv, buf, link_id);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+static int wsm_join_confirm(struct cw1200_common *priv,
+                           struct wsm_join_cnf *arg,
+                           struct wsm_buf *buf)
+{
+       arg->status = WSM_GET32(buf);
+       if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS)
+               return -EINVAL;
+
+       arg->min_power_level = WSM_GET32(buf);
+       arg->max_power_level = WSM_GET32(buf);
+
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+int wsm_join(struct cw1200_common *priv, struct wsm_join *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       struct wsm_join_cnf resp;
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->mode);
+       WSM_PUT8(buf, arg->band);
+       WSM_PUT16(buf, arg->channel_number);
+       WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid));
+       WSM_PUT16(buf, arg->atim_window);
+       WSM_PUT8(buf, arg->preamble_type);
+       WSM_PUT8(buf, arg->probe_for_join);
+       WSM_PUT8(buf, arg->dtim_period);
+       WSM_PUT8(buf, arg->flags);
+       WSM_PUT32(buf, arg->ssid_len);
+       WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid));
+       WSM_PUT32(buf, arg->beacon_interval);
+       WSM_PUT32(buf, arg->basic_rate_set);
+
+       priv->tx_burst_idx = -1;
+       ret = wsm_cmd_send(priv, buf, &resp,
+                          WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT);
+       /* TODO:  Update state based on resp.min|max_power_level */
+
+       priv->join_complete_status = resp.status;
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_bss_params(struct cw1200_common *priv,
+                      const struct wsm_set_bss_params *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, (arg->reset_beacon_loss ?  0x1 : 0));
+       WSM_PUT8(buf, arg->beacon_lost_count);
+       WSM_PUT16(buf, arg->aid);
+       WSM_PUT32(buf, arg->operational_rate_set);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT(buf, arg, sizeof(*arg));
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->index);
+       WSM_PUT8(buf, 0);
+       WSM_PUT16(buf, 0);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_tx_queue_params(struct cw1200_common *priv,
+               const struct wsm_set_tx_queue_params *arg, u8 id)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1};
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, queue_id_to_wmm_aci[id]);
+       WSM_PUT8(buf, 0);
+       WSM_PUT8(buf, arg->ackPolicy);
+       WSM_PUT8(buf, 0);
+       WSM_PUT32(buf, arg->maxTransmitLifetime);
+       WSM_PUT16(buf, arg->allowedMediumTime);
+       WSM_PUT16(buf, 0);
+
+       ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_edca_params(struct cw1200_common *priv,
+                               const struct wsm_edca_params *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       /* Implemented according to specification. */
+
+       WSM_PUT16(buf, arg->params[3].cwmin);
+       WSM_PUT16(buf, arg->params[2].cwmin);
+       WSM_PUT16(buf, arg->params[1].cwmin);
+       WSM_PUT16(buf, arg->params[0].cwmin);
+
+       WSM_PUT16(buf, arg->params[3].cwmax);
+       WSM_PUT16(buf, arg->params[2].cwmax);
+       WSM_PUT16(buf, arg->params[1].cwmax);
+       WSM_PUT16(buf, arg->params[0].cwmax);
+
+       WSM_PUT8(buf, arg->params[3].aifns);
+       WSM_PUT8(buf, arg->params[2].aifns);
+       WSM_PUT8(buf, arg->params[1].aifns);
+       WSM_PUT8(buf, arg->params[0].aifns);
+
+       WSM_PUT16(buf, arg->params[3].txop_limit);
+       WSM_PUT16(buf, arg->params[2].txop_limit);
+       WSM_PUT16(buf, arg->params[1].txop_limit);
+       WSM_PUT16(buf, arg->params[0].txop_limit);
+
+       WSM_PUT32(buf, arg->params[3].max_rx_lifetime);
+       WSM_PUT32(buf, arg->params[2].max_rx_lifetime);
+       WSM_PUT32(buf, arg->params[1].max_rx_lifetime);
+       WSM_PUT32(buf, arg->params[0].max_rx_lifetime);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_switch_channel(struct cw1200_common *priv,
+                       const struct wsm_switch_channel *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->mode);
+       WSM_PUT8(buf, arg->switch_count);
+       WSM_PUT16(buf, arg->channel_number);
+
+       priv->channel_switch_in_progress = 1;
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT);
+       if (ret)
+               priv->channel_switch_in_progress = 0;
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       priv->ps_mode_switch_in_progress = 1;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->mode);
+       WSM_PUT8(buf, arg->fast_psm_idle_period);
+       WSM_PUT8(buf, arg->ap_psm_change_period);
+       WSM_PUT8(buf, arg->min_auto_pspoll_period);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->mode);
+       WSM_PUT8(buf, arg->band);
+       WSM_PUT16(buf, arg->channel_number);
+       WSM_PUT32(buf, arg->ct_window);
+       WSM_PUT32(buf, arg->beacon_interval);
+       WSM_PUT8(buf, arg->dtim_period);
+       WSM_PUT8(buf, arg->preamble);
+       WSM_PUT8(buf, arg->probe_delay);
+       WSM_PUT8(buf, arg->ssid_len);
+       WSM_PUT(buf, arg->ssid, sizeof(arg->ssid));
+       WSM_PUT32(buf, arg->basic_rate_set);
+
+       priv->tx_burst_idx = -1;
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_beacon_transmit(struct cw1200_common *priv,
+                       const struct wsm_beacon_transmit *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start_find(struct cw1200_common *priv)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+       ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_find(struct cw1200_common *priv)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+       ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id);
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr));
+       WSM_PUT16(buf, 0);
+
+       ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_update_ie(struct cw1200_common *priv,
+                 const struct wsm_update_ie *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT16(buf, arg->what);
+       WSM_PUT16(buf, arg->count);
+       WSM_PUT(buf, arg->ies, arg->length);
+
+       ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+int wsm_set_probe_responder(struct cw1200_common *priv, bool enable)
+{
+       priv->rx_filter.probeResponder = enable;
+       return wsm_set_rx_filter(priv, &priv->rx_filter);
+}
+
+/* ******************************************************************** */
+/* WSM indication events implementation                                        */
+const char * const cw1200_fw_types[] = {
+       "ETF",
+       "WFM",
+       "WSM",
+       "HI test",
+       "Platform test"
+};
+
+static int wsm_startup_indication(struct cw1200_common *priv,
+                                       struct wsm_buf *buf)
+{
+       priv->wsm_caps.input_buffers     = WSM_GET16(buf);
+       priv->wsm_caps.input_buffer_size = WSM_GET16(buf);
+       priv->wsm_caps.hw_id      = WSM_GET16(buf);
+       priv->wsm_caps.hw_subid   = WSM_GET16(buf);
+       priv->wsm_caps.status     = WSM_GET16(buf);
+       priv->wsm_caps.fw_cap     = WSM_GET16(buf);
+       priv->wsm_caps.fw_type    = WSM_GET16(buf);
+       priv->wsm_caps.fw_api     = WSM_GET16(buf);
+       priv->wsm_caps.fw_build   = WSM_GET16(buf);
+       priv->wsm_caps.fw_ver     = WSM_GET16(buf);
+       WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label));
+       priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */
+
+       if (WARN_ON(priv->wsm_caps.status))
+               return -EINVAL;
+
+       if (WARN_ON(priv->wsm_caps.fw_type > 4))
+               return -EINVAL;
+
+       pr_info("CW1200 WSM init done.\n"
+               "   Input buffers: %d x %d bytes\n"
+               "   Hardware: %d.%d\n"
+               "   %s firmware [%s], ver: %d, build: %d,"
+               "   api: %d, cap: 0x%.4X\n",
+               priv->wsm_caps.input_buffers,
+               priv->wsm_caps.input_buffer_size,
+               priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid,
+               cw1200_fw_types[priv->wsm_caps.fw_type],
+               priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver,
+               priv->wsm_caps.fw_build,
+               priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap);
+
+       /* Disable unsupported frequency bands */
+       if (!(priv->wsm_caps.fw_cap & 0x1))
+               priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+       if (!(priv->wsm_caps.fw_cap & 0x2))
+               priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+       priv->firmware_ready = 1;
+       wake_up(&priv->wsm_startup_done);
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+static int wsm_receive_indication(struct cw1200_common *priv,
+                                 int link_id,
+                                 struct wsm_buf *buf,
+                                 struct sk_buff **skb_p)
+{
+       struct wsm_rx rx;
+       struct ieee80211_hdr *hdr;
+       size_t hdr_len;
+       __le16 fctl;
+
+       rx.status = WSM_GET32(buf);
+       rx.channel_number = WSM_GET16(buf);
+       rx.rx_rate = WSM_GET8(buf);
+       rx.rcpi_rssi = WSM_GET8(buf);
+       rx.flags = WSM_GET32(buf);
+
+       /* FW Workaround: Drop probe resp or
+          beacon when RSSI is 0
+       */
+       hdr = (struct ieee80211_hdr *)(*skb_p)->data;
+
+       if (!rx.rcpi_rssi &&
+           (ieee80211_is_probe_resp(hdr->frame_control) ||
+            ieee80211_is_beacon(hdr->frame_control)))
+               return 0;
+
+       /* If no RSSI subscription has been made,
+        * convert RCPI to RSSI here
+        */
+       if (!priv->cqm_use_rssi)
+               rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110;
+
+       fctl = *(__le16 *)buf->data;
+       hdr_len = buf->data - buf->begin;
+       skb_pull(*skb_p, hdr_len);
+       if (!rx.status && ieee80211_is_deauth(fctl)) {
+               if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+                       /* Shedule unjoin work */
+                       pr_debug("[WSM] Issue unjoin command (RX).\n");
+                       wsm_lock_tx_async(priv);
+                       if (queue_work(priv->workqueue,
+                                      &priv->unjoin_work) <= 0)
+                               wsm_unlock_tx(priv);
+               }
+       }
+       cw1200_rx_cb(priv, &rx, link_id, skb_p);
+       if (*skb_p)
+               skb_push(*skb_p, hdr_len);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf)
+{
+       int first;
+       struct cw1200_wsm_event *event;
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+               /* STA is stopped. */
+               return 0;
+       }
+
+       event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL);
+       if (!event)
+               return -ENOMEM;
+
+       event->evt.id = WSM_GET32(buf);
+       event->evt.data = WSM_GET32(buf);
+
+       pr_debug("[WSM] Event: %d(%d)\n",
+                event->evt.id, event->evt.data);
+
+       spin_lock(&priv->event_queue_lock);
+       first = list_empty(&priv->event_queue);
+       list_add_tail(&event->link, &priv->event_queue);
+       spin_unlock(&priv->event_queue_lock);
+
+       if (first)
+               queue_work(priv->workqueue, &priv->event_handler);
+
+       return 0;
+
+underflow:
+       kfree(event);
+       return -EINVAL;
+}
+
+static int wsm_channel_switch_indication(struct cw1200_common *priv,
+                                        struct wsm_buf *buf)
+{
+       WARN_ON(WSM_GET32(buf));
+
+       priv->channel_switch_in_progress = 0;
+       wake_up(&priv->channel_switch_done);
+
+       wsm_unlock_tx(priv);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_set_pm_indication(struct cw1200_common *priv,
+                                struct wsm_buf *buf)
+{
+       /* TODO:  Check buf (struct wsm_set_pm_complete) for validity */
+       if (priv->ps_mode_switch_in_progress) {
+               priv->ps_mode_switch_in_progress = 0;
+               wake_up(&priv->ps_mode_switch_done);
+       }
+       return 0;
+}
+
+static int wsm_scan_started(struct cw1200_common *priv, void *arg,
+                           struct wsm_buf *buf)
+{
+       u32 status = WSM_GET32(buf);
+       if (status != WSM_STATUS_SUCCESS) {
+               cw1200_scan_failed_cb(priv);
+               return -EINVAL;
+       }
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+static int wsm_scan_complete_indication(struct cw1200_common *priv,
+                                       struct wsm_buf *buf)
+{
+       struct wsm_scan_complete arg;
+       arg.status = WSM_GET32(buf);
+       arg.psm = WSM_GET8(buf);
+       arg.num_channels = WSM_GET8(buf);
+       cw1200_scan_complete_cb(priv, &arg);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_join_complete_indication(struct cw1200_common *priv,
+                                       struct wsm_buf *buf)
+{
+       struct wsm_join_complete arg;
+       arg.status = WSM_GET32(buf);
+       pr_debug("[WSM] Join complete indication, status: %d\n", arg.status);
+       cw1200_join_complete_cb(priv, &arg);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_find_complete_indication(struct cw1200_common *priv,
+                                       struct wsm_buf *buf)
+{
+       pr_warn("Implement find_complete_indication\n");
+       return 0;
+}
+
+static int wsm_ba_timeout_indication(struct cw1200_common *priv,
+                                    struct wsm_buf *buf)
+{
+       u32 dummy;
+       u8 tid;
+       u8 dummy2;
+       u8 addr[ETH_ALEN];
+
+       dummy = WSM_GET32(buf);
+       tid = WSM_GET8(buf);
+       dummy2 = WSM_GET8(buf);
+       WSM_GET(buf, addr, ETH_ALEN);
+
+       pr_info("BlockACK timeout, tid %d, addr %pM\n",
+               tid, addr);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_suspend_resume_indication(struct cw1200_common *priv,
+                                        int link_id, struct wsm_buf *buf)
+{
+       u32 flags;
+       struct wsm_suspend_resume arg;
+
+       flags = WSM_GET32(buf);
+       arg.link_id = link_id;
+       arg.stop = !(flags & 1);
+       arg.multicast = !!(flags & 8);
+       arg.queue = (flags >> 1) & 3;
+
+       cw1200_suspend_resume(priv, &arg);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+
+/* ******************************************************************** */
+/* WSM TX                                                              */
+
+static int wsm_cmd_send(struct cw1200_common *priv,
+                       struct wsm_buf *buf,
+                       void *arg, u16 cmd, long tmo)
+{
+       size_t buf_len = buf->data - buf->begin;
+       int ret;
+
+       /* Don't bother if we're dead. */
+       if (priv->bh_error) {
+               ret = 0;
+               goto done;
+       }
+
+       /* Block until the cmd buffer is completed.  Tortuous. */
+       spin_lock(&priv->wsm_cmd.lock);
+       while (!priv->wsm_cmd.done) {
+               spin_unlock(&priv->wsm_cmd.lock);
+               spin_lock(&priv->wsm_cmd.lock);
+       }
+       priv->wsm_cmd.done = 0;
+       spin_unlock(&priv->wsm_cmd.lock);
+
+       if (cmd == WSM_WRITE_MIB_REQ_ID ||
+           cmd == WSM_READ_MIB_REQ_ID)
+               pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n",
+                        cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]),
+                        buf_len);
+       else
+               pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len);
+
+       /* Due to buggy SPI on CW1200, we need to
+        * pad the message by a few bytes to ensure
+        * that it's completely received.
+        */
+       buf_len += 4;
+
+       /* Fill HI message header */
+       /* BH will add sequence number */
+       ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
+       ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
+
+       spin_lock(&priv->wsm_cmd.lock);
+       BUG_ON(priv->wsm_cmd.ptr);
+       priv->wsm_cmd.ptr = buf->begin;
+       priv->wsm_cmd.len = buf_len;
+       priv->wsm_cmd.arg = arg;
+       priv->wsm_cmd.cmd = cmd;
+       spin_unlock(&priv->wsm_cmd.lock);
+
+       cw1200_bh_wakeup(priv);
+
+       /* Wait for command completion */
+       ret = wait_event_timeout(priv->wsm_cmd_wq,
+                                priv->wsm_cmd.done, tmo);
+
+       if (!ret && !priv->wsm_cmd.done) {
+               spin_lock(&priv->wsm_cmd.lock);
+               priv->wsm_cmd.done = 1;
+               priv->wsm_cmd.ptr = NULL;
+               spin_unlock(&priv->wsm_cmd.lock);
+               if (priv->bh_error) {
+                       /* Return ok to help system cleanup */
+                       ret = 0;
+               } else {
+                       pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd);
+                       print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE,
+                                            buf->begin, buf_len);
+                       pr_err("Outstanding outgoing frames:  %d\n", priv->hw_bufs_used);
+
+                       /* Kill BH thread to report the error to the top layer. */
+                       atomic_add(1, &priv->bh_term);
+                       wake_up(&priv->bh_wq);
+                       ret = -ETIMEDOUT;
+               }
+       } else {
+               spin_lock(&priv->wsm_cmd.lock);
+               BUG_ON(!priv->wsm_cmd.done);
+               ret = priv->wsm_cmd.ret;
+               spin_unlock(&priv->wsm_cmd.lock);
+       }
+done:
+       wsm_buf_reset(buf);
+       return ret;
+}
+
+/* ******************************************************************** */
+/* WSM TX port control                                                 */
+
+void wsm_lock_tx(struct cw1200_common *priv)
+{
+       wsm_cmd_lock(priv);
+       if (atomic_add_return(1, &priv->tx_lock) == 1) {
+               if (wsm_flush_tx(priv))
+                       pr_debug("[WSM] TX is locked.\n");
+       }
+       wsm_cmd_unlock(priv);
+}
+
+void wsm_lock_tx_async(struct cw1200_common *priv)
+{
+       if (atomic_add_return(1, &priv->tx_lock) == 1)
+               pr_debug("[WSM] TX is locked (async).\n");
+}
+
+bool wsm_flush_tx(struct cw1200_common *priv)
+{
+       unsigned long timestamp = jiffies;
+       bool pending = false;
+       long timeout;
+       int i;
+
+       /* Flush must be called with TX lock held. */
+       BUG_ON(!atomic_read(&priv->tx_lock));
+
+       /* First check if we really need to do something.
+        * It is safe to use unprotected access, as hw_bufs_used
+        * can only decrements.
+        */
+       if (!priv->hw_bufs_used)
+               return true;
+
+       if (priv->bh_error) {
+               /* In case of failure do not wait for magic. */
+               pr_err("[WSM] Fatal error occurred, will not flush TX.\n");
+               return false;
+       } else {
+               /* Get a timestamp of "oldest" frame */
+               for (i = 0; i < 4; ++i)
+                       pending |= cw1200_queue_get_xmit_timestamp(
+                                       &priv->tx_queue[i],
+                                       &timestamp, 0xffffffff);
+               /* If there's nothing pending, we're good */
+               if (!pending)
+                       return true;
+
+               timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
+               if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq,
+                                                     !priv->hw_bufs_used,
+                                                     timeout) <= 0) {
+                       /* Hmmm... Not good. Frame had stuck in firmware. */
+                       priv->bh_error = 1;
+                       wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used);
+                       wake_up(&priv->bh_wq);
+                       return false;
+               }
+
+               /* Ok, everything is flushed. */
+               return true;
+       }
+}
+
+void wsm_unlock_tx(struct cw1200_common *priv)
+{
+       int tx_lock;
+       tx_lock = atomic_sub_return(1, &priv->tx_lock);
+       BUG_ON(tx_lock < 0);
+
+       if (tx_lock == 0) {
+               if (!priv->bh_error)
+                       cw1200_bh_wakeup(priv);
+               pr_debug("[WSM] TX is unlocked.\n");
+       }
+}
+
+/* ******************************************************************** */
+/* WSM RX                                                              */
+
+int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len)
+{
+       struct wsm_buf buf;
+       u32 reason;
+       u32 reg[18];
+       char fname[48];
+       unsigned int i;
+
+       static const char * const reason_str[] = {
+               "undefined instruction",
+               "prefetch abort",
+               "data abort",
+               "unknown error",
+       };
+
+       buf.begin = buf.data = data;
+       buf.end = &buf.begin[len];
+
+       reason = WSM_GET32(&buf);
+       for (i = 0; i < ARRAY_SIZE(reg); ++i)
+               reg[i] = WSM_GET32(&buf);
+       WSM_GET(&buf, fname, sizeof(fname));
+
+       if (reason < 4)
+               wiphy_err(priv->hw->wiphy,
+                         "Firmware exception: %s.\n",
+                         reason_str[reason]);
+       else
+               wiphy_err(priv->hw->wiphy,
+                         "Firmware assert at %.*s, line %d\n",
+                         (int) sizeof(fname), fname, reg[1]);
+
+       for (i = 0; i < 12; i += 4)
+               wiphy_err(priv->hw->wiphy,
+                         "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n",
+                         i + 0, reg[i + 0], i + 1, reg[i + 1],
+                         i + 2, reg[i + 2], i + 3, reg[i + 3]);
+       wiphy_err(priv->hw->wiphy,
+                 "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n",
+                 reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]);
+       i += 4;
+       wiphy_err(priv->hw->wiphy,
+                 "CPSR: 0x%.8X, SPSR: 0x%.8X\n",
+                 reg[i + 0], reg[i + 1]);
+
+       print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE,
+                            fname, sizeof(fname));
+       return 0;
+
+underflow:
+       wiphy_err(priv->hw->wiphy, "Firmware exception.\n");
+       print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE,
+                            data, len);
+       return -EINVAL;
+}
+
+int wsm_handle_rx(struct cw1200_common *priv, u16 id,
+                 struct wsm_hdr *wsm, struct sk_buff **skb_p)
+{
+       int ret = 0;
+       struct wsm_buf wsm_buf;
+       int link_id = (id >> 6) & 0x0F;
+
+       /* Strip link id. */
+       id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+
+       wsm_buf.begin = (u8 *)&wsm[0];
+       wsm_buf.data = (u8 *)&wsm[1];
+       wsm_buf.end = &wsm_buf.begin[__le16_to_cpu(wsm->len)];
+
+       pr_debug("[WSM] <<< 0x%.4X (%td)\n", id,
+                wsm_buf.end - wsm_buf.begin);
+
+       if (id == WSM_TX_CONFIRM_IND_ID) {
+               ret = wsm_tx_confirm(priv, &wsm_buf, link_id);
+       } else if (id == WSM_MULTI_TX_CONFIRM_ID) {
+               ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id);
+       } else if (id & 0x0400) {
+               void *wsm_arg;
+               u16 wsm_cmd;
+
+               /* Do not trust FW too much. Protection against repeated
+                * response and race condition removal (see above).
+                */
+               spin_lock(&priv->wsm_cmd.lock);
+               wsm_arg = priv->wsm_cmd.arg;
+               wsm_cmd = priv->wsm_cmd.cmd &
+                               ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+               priv->wsm_cmd.cmd = 0xFFFF;
+               spin_unlock(&priv->wsm_cmd.lock);
+
+               if (WARN_ON((id & ~0x0400) != wsm_cmd)) {
+                       /* Note that any non-zero is a fatal retcode. */
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               /* Note that wsm_arg can be NULL in case of timeout in
+                * wsm_cmd_send().
+                */
+
+               switch (id) {
+               case WSM_READ_MIB_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_read_mib_confirm(priv, wsm_arg,
+                                                               &wsm_buf);
+                       break;
+               case WSM_WRITE_MIB_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_write_mib_confirm(priv, wsm_arg,
+                                                           &wsm_buf);
+                       break;
+               case WSM_START_SCAN_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_scan_started(priv, wsm_arg, &wsm_buf);
+                       break;
+               case WSM_CONFIGURATION_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_configuration_confirm(priv, wsm_arg,
+                                                               &wsm_buf);
+                       break;
+               case WSM_JOIN_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf);
+                       break;
+               case WSM_STOP_SCAN_RESP_ID:
+               case WSM_RESET_RESP_ID:
+               case WSM_ADD_KEY_RESP_ID:
+               case WSM_REMOVE_KEY_RESP_ID:
+               case WSM_SET_PM_RESP_ID:
+               case WSM_SET_BSS_PARAMS_RESP_ID:
+               case 0x0412: /* set_tx_queue_params */
+               case WSM_EDCA_PARAMS_RESP_ID:
+               case WSM_SWITCH_CHANNEL_RESP_ID:
+               case WSM_START_RESP_ID:
+               case WSM_BEACON_TRANSMIT_RESP_ID:
+               case 0x0419: /* start_find */
+               case 0x041A: /* stop_find */
+               case 0x041B: /* update_ie */
+               case 0x041C: /* map_link */
+                       WARN_ON(wsm_arg != NULL);
+                       ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf);
+                       if (ret) {
+                               wiphy_warn(priv->hw->wiphy,
+                                          "wsm_generic_confirm failed for request 0x%04x.\n",
+                                          id & ~0x0400);
+
+                               /* often 0x407 and 0x410 occur, this means we're dead.. */
+                               if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) {
+                                       wsm_lock_tx(priv);
+                                       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+                                               wsm_unlock_tx(priv);
+                               }
+                       }
+                       break;
+               default:
+                       wiphy_warn(priv->hw->wiphy,
+                                  "Unrecognized confirmation 0x%04x\n",
+                                  id & ~0x0400);
+               }
+
+               spin_lock(&priv->wsm_cmd.lock);
+               priv->wsm_cmd.ret = ret;
+               priv->wsm_cmd.done = 1;
+               spin_unlock(&priv->wsm_cmd.lock);
+
+               ret = 0; /* Error response from device should ne stop BH. */
+
+               wake_up(&priv->wsm_cmd_wq);
+       } else if (id & 0x0800) {
+               switch (id) {
+               case WSM_STARTUP_IND_ID:
+                       ret = wsm_startup_indication(priv, &wsm_buf);
+                       break;
+               case WSM_RECEIVE_IND_ID:
+                       ret = wsm_receive_indication(priv, link_id,
+                                                    &wsm_buf, skb_p);
+                       break;
+               case 0x0805:
+                       ret = wsm_event_indication(priv, &wsm_buf);
+                       break;
+               case WSM_SCAN_COMPLETE_IND_ID:
+                       ret = wsm_scan_complete_indication(priv, &wsm_buf);
+                       break;
+               case 0x0808:
+                       ret = wsm_ba_timeout_indication(priv, &wsm_buf);
+                       break;
+               case 0x0809:
+                       ret = wsm_set_pm_indication(priv, &wsm_buf);
+                       break;
+               case 0x080A:
+                       ret = wsm_channel_switch_indication(priv, &wsm_buf);
+                       break;
+               case 0x080B:
+                       ret = wsm_find_complete_indication(priv, &wsm_buf);
+                       break;
+               case 0x080C:
+                       ret = wsm_suspend_resume_indication(priv,
+                                       link_id, &wsm_buf);
+                       break;
+               case 0x080F:
+                       ret = wsm_join_complete_indication(priv, &wsm_buf);
+                       break;
+               default:
+                       pr_warn("Unrecognised WSM ID %04x\n", id);
+               }
+       } else {
+               WARN_ON(1);
+               ret = -EINVAL;
+       }
+out:
+       return ret;
+}
+
+static bool wsm_handle_tx_data(struct cw1200_common *priv,
+                              struct wsm_tx *wsm,
+                              const struct ieee80211_tx_info *tx_info,
+                              const struct cw1200_txpriv *txpriv,
+                              struct cw1200_queue *queue)
+{
+       bool handled = false;
+       const struct ieee80211_hdr *frame =
+               (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset];
+       __le16 fctl = frame->frame_control;
+       enum {
+               do_probe,
+               do_drop,
+               do_wep,
+               do_tx,
+       } action = do_tx;
+
+       switch (priv->mode) {
+       case NL80211_IFTYPE_STATION:
+               if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+                       action = do_tx;
+               else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA)
+                       action = do_drop;
+               break;
+       case NL80211_IFTYPE_AP:
+               if (!priv->join_status) {
+                       action = do_drop;
+               } else if (!(BIT(txpriv->raw_link_id) &
+                            (BIT(0) | priv->link_id_map))) {
+                       wiphy_warn(priv->hw->wiphy,
+                                  "A frame with expired link id is dropped.\n");
+                       action = do_drop;
+               }
+               if (cw1200_queue_get_generation(wsm->packet_id) >
+                               CW1200_MAX_REQUEUE_ATTEMPTS) {
+                       /* HACK!!! WSM324 firmware has tendency to requeue
+                        * multicast frames in a loop, causing performance
+                        * drop and high power consumption of the driver.
+                        * In this situation it is better just to drop
+                        * the problematic frame.
+                        */
+                       wiphy_warn(priv->hw->wiphy,
+                                  "Too many attempts to requeue a frame; dropped.\n");
+                       action = do_drop;
+               }
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (priv->join_status != CW1200_JOIN_STATUS_IBSS)
+                       action = do_drop;
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               action = do_tx; /* TODO:  Test me! */
+               break;
+       case NL80211_IFTYPE_MONITOR:
+       default:
+               action = do_drop;
+               break;
+       }
+
+       if (action == do_tx) {
+               if (ieee80211_is_nullfunc(fctl)) {
+                       spin_lock(&priv->bss_loss_lock);
+                       if (priv->bss_loss_state) {
+                               priv->bss_loss_confirm_id = wsm->packet_id;
+                               wsm->queue_id = WSM_QUEUE_VOICE;
+                       }
+                       spin_unlock(&priv->bss_loss_lock);
+               } else if (ieee80211_is_probe_req(fctl)) {
+                       action = do_probe;
+               } else if (ieee80211_is_deauth(fctl) &&
+                          priv->mode != NL80211_IFTYPE_AP) {
+                       pr_debug("[WSM] Issue unjoin command due to tx deauth.\n");
+                       wsm_lock_tx_async(priv);
+                       if (queue_work(priv->workqueue,
+                                      &priv->unjoin_work) <= 0)
+                               wsm_unlock_tx(priv);
+               } else if (ieee80211_has_protected(fctl) &&
+                          tx_info->control.hw_key &&
+                          tx_info->control.hw_key->keyidx != priv->wep_default_key_id &&
+                          (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+                           tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+                       action = do_wep;
+               }
+       }
+
+       switch (action) {
+       case do_probe:
+               /* An interesting FW "feature". Device filters probe responses.
+                * The easiest way to get it back is to convert
+                * probe request into WSM start_scan command.
+                */
+               pr_debug("[WSM] Convert probe request to scan.\n");
+               wsm_lock_tx_async(priv);
+               priv->pending_frame_id = wsm->packet_id;
+               if (queue_delayed_work(priv->workqueue,
+                                      &priv->scan.probe_work, 0) <= 0)
+                       wsm_unlock_tx(priv);
+               handled = true;
+               break;
+       case do_drop:
+               pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl);
+               BUG_ON(cw1200_queue_remove(queue, wsm->packet_id));
+               handled = true;
+               break;
+       case do_wep:
+               pr_debug("[WSM] Issue set_default_wep_key.\n");
+               wsm_lock_tx_async(priv);
+               priv->wep_default_key_id = tx_info->control.hw_key->keyidx;
+               priv->pending_frame_id = wsm->packet_id;
+               if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0)
+                       wsm_unlock_tx(priv);
+               handled = true;
+               break;
+       case do_tx:
+               pr_debug("[WSM] Transmit frame.\n");
+               break;
+       default:
+               /* Do nothing */
+               break;
+       }
+       return handled;
+}
+
+static int cw1200_get_prio_queue(struct cw1200_common *priv,
+                                u32 link_id_map, int *total)
+{
+       static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) |
+               BIT(CW1200_LINK_ID_UAPSD);
+       struct wsm_edca_queue_params *edca;
+       unsigned score, best = -1;
+       int winner = -1;
+       int queued;
+       int i;
+
+       /* search for a winner using edca params */
+       for (i = 0; i < 4; ++i) {
+               queued = cw1200_queue_get_num_queued(&priv->tx_queue[i],
+                               link_id_map);
+               if (!queued)
+                       continue;
+               *total += queued;
+               edca = &priv->edca.params[i];
+               score = ((edca->aifns + edca->cwmin) << 16) +
+                       ((edca->cwmax - edca->cwmin) *
+                        (get_random_int() & 0xFFFF));
+               if (score < best && (winner < 0 || i != 3)) {
+                       best = score;
+                       winner = i;
+               }
+       }
+
+       /* override winner if bursting */
+       if (winner >= 0 && priv->tx_burst_idx >= 0 &&
+           winner != priv->tx_burst_idx &&
+           !cw1200_queue_get_num_queued(
+                   &priv->tx_queue[winner],
+                   link_id_map & urgent) &&
+           cw1200_queue_get_num_queued(
+                   &priv->tx_queue[priv->tx_burst_idx],
+                   link_id_map))
+               winner = priv->tx_burst_idx;
+
+       return winner;
+}
+
+static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
+                                    struct cw1200_queue **queue_p,
+                                    u32 *tx_allowed_mask_p,
+                                    bool *more)
+{
+       int idx;
+       u32 tx_allowed_mask;
+       int total = 0;
+
+       /* Search for a queue with multicast frames buffered */
+       if (priv->tx_multicast) {
+               tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM);
+               idx = cw1200_get_prio_queue(priv,
+                               tx_allowed_mask, &total);
+               if (idx >= 0) {
+                       *more = total > 1;
+                       goto found;
+               }
+       }
+
+       /* Search for unicast traffic */
+       tx_allowed_mask = ~priv->sta_asleep_mask;
+       tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD);
+       if (priv->sta_asleep_mask) {
+               tx_allowed_mask |= priv->pspoll_mask;
+               tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM);
+       } else {
+               tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM);
+       }
+       idx = cw1200_get_prio_queue(priv,
+                       tx_allowed_mask, &total);
+       if (idx < 0)
+               return -ENOENT;
+
+found:
+       *queue_p = &priv->tx_queue[idx];
+       *tx_allowed_mask_p = tx_allowed_mask;
+       return 0;
+}
+
+int wsm_get_tx(struct cw1200_common *priv, u8 **data,
+              size_t *tx_len, int *burst)
+{
+       struct wsm_tx *wsm = NULL;
+       struct ieee80211_tx_info *tx_info;
+       struct cw1200_queue *queue = NULL;
+       int queue_num;
+       u32 tx_allowed_mask = 0;
+       const struct cw1200_txpriv *txpriv = NULL;
+       int count = 0;
+
+       /* More is used only for broadcasts. */
+       bool more = false;
+
+       if (priv->wsm_cmd.ptr) { /* CMD request */
+               ++count;
+               spin_lock(&priv->wsm_cmd.lock);
+               BUG_ON(!priv->wsm_cmd.ptr);
+               *data = priv->wsm_cmd.ptr;
+               *tx_len = priv->wsm_cmd.len;
+               *burst = 1;
+               spin_unlock(&priv->wsm_cmd.lock);
+       } else {
+               for (;;) {
+                       int ret;
+
+                       if (atomic_add_return(0, &priv->tx_lock))
+                               break;
+
+                       spin_lock_bh(&priv->ps_state_lock);
+
+                       ret = wsm_get_tx_queue_and_mask(priv, &queue,
+                                                       &tx_allowed_mask, &more);
+                       queue_num = queue - priv->tx_queue;
+
+                       if (priv->buffered_multicasts &&
+                           (ret || !more) &&
+                           (priv->tx_multicast || !priv->sta_asleep_mask)) {
+                               priv->buffered_multicasts = false;
+                               if (priv->tx_multicast) {
+                                       priv->tx_multicast = false;
+                                       queue_work(priv->workqueue,
+                                                  &priv->multicast_stop_work);
+                               }
+                       }
+
+                       spin_unlock_bh(&priv->ps_state_lock);
+
+                       if (ret)
+                               break;
+
+                       if (cw1200_queue_get(queue,
+                                            tx_allowed_mask,
+                                            &wsm, &tx_info, &txpriv))
+                               continue;
+
+                       if (wsm_handle_tx_data(priv, wsm,
+                                              tx_info, txpriv, queue))
+                               continue;  /* Handled by WSM */
+
+                       wsm->hdr.id &= __cpu_to_le16(
+                               ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX));
+                       wsm->hdr.id |= cpu_to_le16(
+                               WSM_TX_LINK_ID(txpriv->raw_link_id));
+                       priv->pspoll_mask &= ~BIT(txpriv->raw_link_id);
+
+                       *data = (u8 *)wsm;
+                       *tx_len = __le16_to_cpu(wsm->hdr.len);
+
+                       /* allow bursting if txop is set */
+                       if (priv->edca.params[queue_num].txop_limit)
+                               *burst = min(*burst,
+                                            (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1);
+                       else
+                               *burst = 1;
+
+                       /* store index of bursting queue */
+                       if (*burst > 1)
+                               priv->tx_burst_idx = queue_num;
+                       else
+                               priv->tx_burst_idx = -1;
+
+                       if (more) {
+                               struct ieee80211_hdr *hdr =
+                                       (struct ieee80211_hdr *)
+                                       &((u8 *)wsm)[txpriv->offset];
+                               /* more buffered multicast/broadcast frames
+                                *  ==> set MoreData flag in IEEE 802.11 header
+                                *  to inform PS STAs
+                                */
+                               hdr->frame_control |=
+                                       cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+                       }
+
+                       pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n",
+                                0x0004, *tx_len, *data,
+                                wsm->more ? 'M' : ' ');
+                       ++count;
+                       break;
+               }
+       }
+
+       return count;
+}
+
+void wsm_txed(struct cw1200_common *priv, u8 *data)
+{
+       if (data == priv->wsm_cmd.ptr) {
+               spin_lock(&priv->wsm_cmd.lock);
+               priv->wsm_cmd.ptr = NULL;
+               spin_unlock(&priv->wsm_cmd.lock);
+       }
+}
+
+/* ******************************************************************** */
+/* WSM buffer                                                          */
+
+void wsm_buf_init(struct wsm_buf *buf)
+{
+       BUG_ON(buf->begin);
+       buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+       buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin;
+       wsm_buf_reset(buf);
+}
+
+void wsm_buf_deinit(struct wsm_buf *buf)
+{
+       kfree(buf->begin);
+       buf->begin = buf->data = buf->end = NULL;
+}
+
+static void wsm_buf_reset(struct wsm_buf *buf)
+{
+       if (buf->begin) {
+               buf->data = &buf->begin[4];
+               *(u32 *)buf->begin = 0;
+       } else {
+               buf->data = buf->begin;
+       }
+}
+
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size)
+{
+       size_t pos = buf->data - buf->begin;
+       size_t size = pos + extra_size;
+
+       size = round_up(size, FWLOAD_BLOCK_SIZE);
+
+       buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA);
+       if (buf->begin) {
+               buf->data = &buf->begin[pos];
+               buf->end = &buf->begin[size];
+               return 0;
+       } else {
+               buf->end = buf->data = buf->begin;
+               return -ENOMEM;
+       }
+}
diff --git a/drivers/net/wireless/st/cw1200/wsm.h b/drivers/net/wireless/st/cw1200/wsm.h
new file mode 100644 (file)
index 0000000..48086e8
--- /dev/null
@@ -0,0 +1,1870 @@
+/*
+ * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on CW1200 UMAC WSM API, which is
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Stewart Mathers <stewart.mathers@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_WSM_H_INCLUDED
+#define CW1200_WSM_H_INCLUDED
+
+#include <linux/spinlock.h>
+
+struct cw1200_common;
+
+/* Bands */
+/* Radio band 2.412 -2.484 GHz. */
+#define WSM_PHY_BAND_2_4G              (0)
+
+/* Radio band 4.9375-5.8250 GHz. */
+#define WSM_PHY_BAND_5G                        (1)
+
+/* Transmit rates */
+/* 1   Mbps            ERP-DSSS */
+#define WSM_TRANSMIT_RATE_1            (0)
+
+/* 2   Mbps            ERP-DSSS */
+#define WSM_TRANSMIT_RATE_2            (1)
+
+/* 5.5 Mbps            ERP-CCK */
+#define WSM_TRANSMIT_RATE_5            (2)
+
+/* 11  Mbps            ERP-CCK */
+#define WSM_TRANSMIT_RATE_11           (3)
+
+/* 22  Mbps            ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_22                (4) */
+
+/* 33  Mbps            ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_33                (5) */
+
+/* 6   Mbps   (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_6            (6)
+
+/* 9   Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_9            (7)
+
+/* 12  Mbps  (6 Mbps)  ERP-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_12           (8)
+
+/* 18  Mbps  (9 Mbps)  ERP-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_18           (9)
+
+/* 24  Mbps (12 Mbps)  ERP-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_24           (10)
+
+/* 36  Mbps (18 Mbps)  ERP-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_36           (11)
+
+/* 48  Mbps (24 Mbps)  ERP-OFDM, 64QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_48           (12)
+
+/* 54  Mbps (27 Mbps)  ERP-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_54           (13)
+
+/* 6.5 Mbps            HT-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_6         (14)
+
+/* 13  Mbps            HT-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_13                (15)
+
+/* 19.5 Mbps           HT-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_19                (16)
+
+/* 26  Mbps            HT-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_26                (17)
+
+/* 39  Mbps            HT-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_39                (18)
+
+/* 52  Mbps            HT-OFDM, 64QAM coding rate 2/3 */
+#define WSM_TRANSMIT_RATE_HT_52                (19)
+
+/* 58.5 Mbps           HT-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_58                (20)
+
+/* 65  Mbps            HT-OFDM, 64QAM coding rate 5/6 */
+#define WSM_TRANSMIT_RATE_HT_65                (21)
+
+/* Scan types */
+/* Foreground scan */
+#define WSM_SCAN_TYPE_FOREGROUND       (0)
+
+/* Background scan */
+#define WSM_SCAN_TYPE_BACKGROUND       (1)
+
+/* Auto scan */
+#define WSM_SCAN_TYPE_AUTO             (2)
+
+/* Scan flags */
+/* Forced background scan means if the station cannot */
+/* enter the power-save mode, it shall force to perform a */
+/* background scan. Only valid when ScanType is */
+/* background scan. */
+#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0))
+
+/* The WLAN device scans one channel at a time so */
+/* that disturbance to the data traffic is minimized. */
+#define WSM_SCAN_FLAG_SPLIT_METHOD     (BIT(1))
+
+/* Preamble Type. Long if not set. */
+#define WSM_SCAN_FLAG_SHORT_PREAMBLE   (BIT(2))
+
+/* 11n Tx Mode. Mixed if not set. */
+#define WSM_SCAN_FLAG_11N_GREENFIELD   (BIT(3))
+
+/* Scan constraints */
+/* Maximum number of channels to be scanned. */
+#define WSM_SCAN_MAX_NUM_OF_CHANNELS   (48)
+
+/* The maximum number of SSIDs that the device can scan for. */
+#define WSM_SCAN_MAX_NUM_OF_SSIDS      (2)
+
+/* Power management modes */
+/* 802.11 Active mode */
+#define WSM_PSM_ACTIVE                 (0)
+
+/* 802.11 PS mode */
+#define WSM_PSM_PS                     BIT(0)
+
+/* Fast Power Save bit */
+#define WSM_PSM_FAST_PS_FLAG           BIT(7)
+
+/* Dynamic aka Fast power save */
+#define WSM_PSM_FAST_PS                        (BIT(0) | BIT(7))
+
+/* Undetermined */
+/* Note : Undetermined status is reported when the */
+/* NULL data frame used to advertise the PM mode to */
+/* the AP at Pre or Post Background Scan is not Acknowledged */
+#define WSM_PSM_UNKNOWN                        BIT(1)
+
+/* Queue IDs */
+/* best effort/legacy */
+#define WSM_QUEUE_BEST_EFFORT          (0)
+
+/* background */
+#define WSM_QUEUE_BACKGROUND           (1)
+
+/* video */
+#define WSM_QUEUE_VIDEO                        (2)
+
+/* voice */
+#define WSM_QUEUE_VOICE                        (3)
+
+/* HT TX parameters */
+/* Non-HT */
+#define WSM_HT_TX_NON_HT               (0)
+
+/* Mixed format */
+#define WSM_HT_TX_MIXED                        (1)
+
+/* Greenfield format */
+#define WSM_HT_TX_GREENFIELD           (2)
+
+/* STBC allowed */
+#define WSM_HT_TX_STBC                 (BIT(7))
+
+/* EPTA prioirty flags for BT Coex */
+/* default epta priority */
+#define WSM_EPTA_PRIORITY_DEFAULT      4
+/* use for normal data */
+#define WSM_EPTA_PRIORITY_DATA         4
+/* use for connect/disconnect/roaming*/
+#define WSM_EPTA_PRIORITY_MGT          5
+/* use for action frames */
+#define WSM_EPTA_PRIORITY_ACTION       5
+/* use for AC_VI data */
+#define WSM_EPTA_PRIORITY_VIDEO                5
+/* use for AC_VO data */
+#define WSM_EPTA_PRIORITY_VOICE                6
+/* use for EAPOL exchange */
+#define WSM_EPTA_PRIORITY_EAPOL                7
+
+/* TX status */
+/* Frame was sent aggregated */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_AGGREGATION      (BIT(0))
+
+/* Host should requeue this frame later. */
+/* Valid only when status is WSM_REQUEUE. */
+#define WSM_TX_STATUS_REQUEUE          (BIT(1))
+
+/* Normal Ack */
+#define WSM_TX_STATUS_NORMAL_ACK       (0<<2)
+
+/* No Ack */
+#define WSM_TX_STATUS_NO_ACK           (1<<2)
+
+/* No explicit acknowledgement */
+#define WSM_TX_STATUS_NO_EXPLICIT_ACK  (2<<2)
+
+/* Block Ack */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_BLOCK_ACK                (3<<2)
+
+/* RX status */
+/* Unencrypted */
+#define WSM_RX_STATUS_UNENCRYPTED      (0<<0)
+
+/* WEP */
+#define WSM_RX_STATUS_WEP              (1<<0)
+
+/* TKIP */
+#define WSM_RX_STATUS_TKIP             (2<<0)
+
+/* AES */
+#define WSM_RX_STATUS_AES              (3<<0)
+
+/* WAPI */
+#define WSM_RX_STATUS_WAPI             (4<<0)
+
+/* Macro to fetch encryption subfield. */
+#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07)
+
+/* Frame was part of an aggregation */
+#define WSM_RX_STATUS_AGGREGATE                (BIT(3))
+
+/* Frame was first in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_FIRST  (BIT(4))
+
+/* Frame was last in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_LAST   (BIT(5))
+
+/* Indicates a defragmented frame */
+#define WSM_RX_STATUS_DEFRAGMENTED     (BIT(6))
+
+/* Indicates a Beacon frame */
+#define WSM_RX_STATUS_BEACON           (BIT(7))
+
+/* Indicates STA bit beacon TIM field */
+#define WSM_RX_STATUS_TIM              (BIT(8))
+
+/* Indicates Beacon frame's virtual bitmap contains multicast bit */
+#define WSM_RX_STATUS_MULTICAST                (BIT(9))
+
+/* Indicates frame contains a matching SSID */
+#define WSM_RX_STATUS_MATCHING_SSID    (BIT(10))
+
+/* Indicates frame contains a matching BSSI */
+#define WSM_RX_STATUS_MATCHING_BSSI    (BIT(11))
+
+/* Indicates More bit set in Framectl field */
+#define WSM_RX_STATUS_MORE_DATA                (BIT(12))
+
+/* Indicates frame received during a measurement process */
+#define WSM_RX_STATUS_MEASUREMENT      (BIT(13))
+
+/* Indicates frame received as an HT packet */
+#define WSM_RX_STATUS_HT               (BIT(14))
+
+/* Indicates frame received with STBC */
+#define WSM_RX_STATUS_STBC             (BIT(15))
+
+/* Indicates Address 1 field matches dot11StationId */
+#define WSM_RX_STATUS_ADDRESS1         (BIT(16))
+
+/* Indicates Group address present in the Address 1 field */
+#define WSM_RX_STATUS_GROUP            (BIT(17))
+
+/* Indicates Broadcast address present in the Address 1 field */
+#define WSM_RX_STATUS_BROADCAST                (BIT(18))
+
+/* Indicates group key used with encrypted frames */
+#define WSM_RX_STATUS_GROUP_KEY                (BIT(19))
+
+/* Macro to fetch encryption key index. */
+#define WSM_RX_STATUS_KEY_IDX(status)  (((status >> 20)) & 0x0F)
+
+/* Indicates TSF inclusion after 802.11 frame body */
+#define WSM_RX_STATUS_TSF_INCLUDED     (BIT(24))
+
+/* Frame Control field starts at Frame offset + 2 */
+#define WSM_TX_2BYTES_SHIFT            (BIT(7))
+
+/* Join mode */
+/* IBSS */
+#define WSM_JOIN_MODE_IBSS             (0)
+
+/* BSS */
+#define WSM_JOIN_MODE_BSS              (1)
+
+/* PLCP preamble type */
+/* For long preamble */
+#define WSM_JOIN_PREAMBLE_LONG         (0)
+
+/* For short preamble (Long for 1Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT                (1)
+
+/* For short preamble (Long for 1 and 2Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT_2      (2)
+
+/* Join flags */
+/* Unsynchronized */
+#define WSM_JOIN_FLAGS_UNSYNCRONIZED   BIT(0)
+/* The BSS owner is a P2P GO */
+#define WSM_JOIN_FLAGS_P2P_GO          BIT(1)
+/* Force to join BSS with the BSSID and the
+ * SSID specified without waiting for beacons. The
+ * ProbeForJoin parameter is ignored.
+ */
+#define WSM_JOIN_FLAGS_FORCE           BIT(2)
+/* Give probe request/response higher
+ * priority over the BT traffic
+ */
+#define WSM_JOIN_FLAGS_PRIO            BIT(3)
+/* Issue immediate join confirmation and use
+ * join complete to notify about completion
+ */
+#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5)
+
+/* Key types */
+#define WSM_KEY_TYPE_WEP_DEFAULT       (0)
+#define WSM_KEY_TYPE_WEP_PAIRWISE      (1)
+#define WSM_KEY_TYPE_TKIP_GROUP                (2)
+#define WSM_KEY_TYPE_TKIP_PAIRWISE     (3)
+#define WSM_KEY_TYPE_AES_GROUP         (4)
+#define WSM_KEY_TYPE_AES_PAIRWISE      (5)
+#define WSM_KEY_TYPE_WAPI_GROUP                (6)
+#define WSM_KEY_TYPE_WAPI_PAIRWISE     (7)
+
+/* Key indexes */
+#define WSM_KEY_MAX_INDEX              (10)
+
+/* ACK policy */
+#define WSM_ACK_POLICY_NORMAL          (0)
+#define WSM_ACK_POLICY_NO_ACK          (1)
+
+/* Start modes */
+#define WSM_START_MODE_AP              (0)     /* Mini AP */
+#define WSM_START_MODE_P2P_GO          (1)     /* P2P GO */
+#define WSM_START_MODE_P2P_DEV         (2)     /* P2P device */
+
+/* SetAssociationMode MIB flags */
+#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE         (BIT(0))
+#define WSM_ASSOCIATION_MODE_USE_HT_MODE               (BIT(1))
+#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET                (BIT(2))
+#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING    (BIT(3))
+#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES                (BIT(4))
+
+/* RcpiRssiThreshold MIB flags */
+#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0))
+#define WSM_RCPI_RSSI_USE_RSSI         (BIT(1))
+#define WSM_RCPI_RSSI_DONT_USE_UPPER   (BIT(2))
+#define WSM_RCPI_RSSI_DONT_USE_LOWER   (BIT(3))
+
+/* Update-ie constants */
+#define WSM_UPDATE_IE_BEACON           (BIT(0))
+#define WSM_UPDATE_IE_PROBE_RESP       (BIT(1))
+#define WSM_UPDATE_IE_PROBE_REQ                (BIT(2))
+
+/* WSM events */
+/* Error */
+#define WSM_EVENT_ERROR                        (0)
+
+/* BSS lost */
+#define WSM_EVENT_BSS_LOST             (1)
+
+/* BSS regained */
+#define WSM_EVENT_BSS_REGAINED         (2)
+
+/* Radar detected */
+#define WSM_EVENT_RADAR_DETECTED       (3)
+
+/* RCPI or RSSI threshold triggered */
+#define WSM_EVENT_RCPI_RSSI            (4)
+
+/* BT inactive */
+#define WSM_EVENT_BT_INACTIVE          (5)
+
+/* BT active */
+#define WSM_EVENT_BT_ACTIVE            (6)
+
+/* MIB IDs */
+/* 4.1  dot11StationId */
+#define WSM_MIB_ID_DOT11_STATION_ID            0x0000
+
+/* 4.2  dot11MaxtransmitMsduLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME  0x0001
+
+/* 4.3  dot11MaxReceiveLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME  0x0002
+
+/* 4.4  dot11SlotTime */
+#define WSM_MIB_ID_DOT11_SLOT_TIME             0x0003
+
+/* 4.5  dot11GroupAddressesTable */
+#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004
+#define WSM_MAX_GRP_ADDRTABLE_ENTRIES          8
+
+/* 4.6  dot11WepDefaultKeyId */
+#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID    0x0005
+
+/* 4.7  dot11CurrentTxPowerLevel */
+#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL        0x0006
+
+/* 4.8  dot11RTSThreshold */
+#define WSM_MIB_ID_DOT11_RTS_THRESHOLD         0x0007
+
+/* 4.9  NonErpProtection */
+#define WSM_MIB_ID_NON_ERP_PROTECTION          0x1000
+
+/* 4.10 ArpIpAddressesTable */
+#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE      0x1001
+#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES       1
+
+/* 4.11 TemplateFrame */
+#define WSM_MIB_ID_TEMPLATE_FRAME              0x1002
+
+/* 4.12 RxFilter */
+#define WSM_MIB_ID_RX_FILTER                   0x1003
+
+/* 4.13 BeaconFilterTable */
+#define WSM_MIB_ID_BEACON_FILTER_TABLE         0x1004
+
+/* 4.14 BeaconFilterEnable */
+#define WSM_MIB_ID_BEACON_FILTER_ENABLE                0x1005
+
+/* 4.15 OperationalPowerMode */
+#define WSM_MIB_ID_OPERATIONAL_POWER_MODE      0x1006
+
+/* 4.16 BeaconWakeUpPeriod */
+#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD                0x1007
+
+/* 4.17 RcpiRssiThreshold */
+#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD         0x1009
+
+/* 4.18 StatisticsTable */
+#define WSM_MIB_ID_STATISTICS_TABLE            0x100A
+
+/* 4.19 IbssPsConfig */
+#define WSM_MIB_ID_IBSS_PS_CONFIG              0x100B
+
+/* 4.20 CountersTable */
+#define WSM_MIB_ID_COUNTERS_TABLE              0x100C
+
+/* 4.21 BlockAckPolicy */
+#define WSM_MIB_ID_BLOCK_ACK_POLICY            0x100E
+
+/* 4.22 OverrideInternalTxRate */
+#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE   0x100F
+
+/* 4.23 SetAssociationMode */
+#define WSM_MIB_ID_SET_ASSOCIATION_MODE                0x1010
+
+/* 4.24 UpdateEptaConfigData */
+#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA     0x1011
+
+/* 4.25 SelectCcaMethod */
+#define WSM_MIB_ID_SELECT_CCA_METHOD           0x1012
+
+/* 4.26 SetUpasdInformation */
+#define WSM_MIB_ID_SET_UAPSD_INFORMATION       0x1013
+
+/* 4.27 SetAutoCalibrationMode  WBF00004073 */
+#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE   0x1015
+
+/* 4.28 SetTxRateRetryPolicy */
+#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY    0x1016
+
+/* 4.29 SetHostMessageTypeFilter */
+#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER    0x1017
+
+/* 4.30 P2PFindInfo */
+#define WSM_MIB_ID_P2P_FIND_INFO               0x1018
+
+/* 4.31 P2PPsModeInfo */
+#define WSM_MIB_ID_P2P_PS_MODE_INFO            0x1019
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A
+
+/* 4.33 SetUDPPortDataFrameFilter */
+#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER        0x101B
+
+/* 4.34 SetMagicDataFrameFilter */
+#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER  0x101C
+
+/* 4.35 P2PDeviceInfo */
+#define WSM_MIB_ID_P2P_DEVICE_INFO             0x101D
+
+/* 4.36 SetWCDMABand */
+#define WSM_MIB_ID_SET_WCDMA_BAND              0x101E
+
+/* 4.37 GroupTxSequenceCounter */
+#define WSM_MIB_ID_GRP_SEQ_COUNTER             0x101F
+
+/* 4.38 ProtectedMgmtPolicy */
+#define WSM_MIB_ID_PROTECTED_MGMT_POLICY       0x1020
+
+/* 4.39 SetHtProtection */
+#define WSM_MIB_ID_SET_HT_PROTECTION           0x1021
+
+/* 4.40 GPIO Command */
+#define WSM_MIB_ID_GPIO_COMMAND                        0x1022
+
+/* 4.41 TSF Counter Value */
+#define WSM_MIB_ID_TSF_COUNTER                 0x1023
+
+/* Test Purposes Only */
+#define WSM_MIB_ID_BLOCK_ACK_INFO              0x100D
+
+/* 4.42 UseMultiTxConfMessage */
+#define WSM_MIB_USE_MULTI_TX_CONF              0x1024
+
+/* 4.43 Keep-alive period */
+#define WSM_MIB_ID_KEEP_ALIVE_PERIOD           0x1025
+
+/* 4.44 Disable BSSID filter */
+#define WSM_MIB_ID_DISABLE_BSSID_FILTER                0x1026
+
+/* Frame template types */
+#define WSM_FRAME_TYPE_PROBE_REQUEST   (0)
+#define WSM_FRAME_TYPE_BEACON          (1)
+#define WSM_FRAME_TYPE_NULL            (2)
+#define WSM_FRAME_TYPE_QOS_NULL                (3)
+#define WSM_FRAME_TYPE_PS_POLL         (4)
+#define WSM_FRAME_TYPE_PROBE_RESPONSE  (5)
+
+#define WSM_FRAME_GREENFIELD           (0x80)  /* See 4.11 */
+
+/* Status */
+/* The WSM firmware has completed a request */
+/* successfully. */
+#define WSM_STATUS_SUCCESS              (0)
+
+/* This is a generic failure code if other error codes do */
+/* not apply. */
+#define WSM_STATUS_FAILURE              (1)
+
+/* A request contains one or more invalid parameters. */
+#define WSM_INVALID_PARAMETER           (2)
+
+/* The request cannot perform because the device is in */
+/* an inappropriate mode. */
+#define WSM_ACCESS_DENIED               (3)
+
+/* The frame received includes a decryption error. */
+#define WSM_STATUS_DECRYPTFAILURE       (4)
+
+/* A MIC failure is detected in the received packets. */
+#define WSM_STATUS_MICFAILURE           (5)
+
+/* The transmit request failed due to retry limit being */
+/* exceeded. */
+#define WSM_STATUS_RETRY_EXCEEDED       (6)
+
+/* The transmit request failed due to MSDU life time */
+/* being exceeded. */
+#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7)
+
+/* The link to the AP is lost. */
+#define WSM_STATUS_LINK_LOST            (8)
+
+/* No key was found for the encrypted frame */
+#define WSM_STATUS_NO_KEY_FOUND         (9)
+
+/* Jammer was detected when transmitting this frame */
+#define WSM_STATUS_JAMMER_DETECTED      (10)
+
+/* The message should be requeued later. */
+/* This is applicable only to Transmit */
+#define WSM_REQUEUE                     (11)
+
+/* Advanced filtering options */
+#define WSM_MAX_FILTER_ELEMENTS                (4)
+
+#define WSM_FILTER_ACTION_IGNORE       (0)
+#define WSM_FILTER_ACTION_FILTER_IN    (1)
+#define WSM_FILTER_ACTION_FILTER_OUT   (2)
+
+#define WSM_FILTER_PORT_TYPE_DST       (0)
+#define WSM_FILTER_PORT_TYPE_SRC       (1)
+
+/* Actual header of WSM messages */
+struct wsm_hdr {
+       __le16 len;
+       __le16 id;
+};
+
+#define WSM_TX_SEQ_MAX                 (7)
+#define WSM_TX_SEQ(seq)                        \
+               ((seq & WSM_TX_SEQ_MAX) << 13)
+#define WSM_TX_LINK_ID_MAX             (0x0F)
+#define WSM_TX_LINK_ID(link_id)                \
+               ((link_id & WSM_TX_LINK_ID_MAX) << 6)
+
+#define MAX_BEACON_SKIP_TIME_MS 1000
+
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2)
+
+/* ******************************************************************** */
+/* WSM capability                                                      */
+
+#define WSM_STARTUP_IND_ID 0x0801
+
+struct wsm_startup_ind {
+       u16 input_buffers;
+       u16 input_buffer_size;
+       u16 status;
+       u16 hw_id;
+       u16 hw_subid;
+       u16 fw_cap;
+       u16 fw_type;
+       u16 fw_api;
+       u16 fw_build;
+       u16 fw_ver;
+       char fw_label[128];
+       u32 config[4];
+};
+
+/* ******************************************************************** */
+/* WSM commands                                                                */
+
+/* 3.1 */
+#define WSM_CONFIGURATION_REQ_ID 0x0009
+#define WSM_CONFIGURATION_RESP_ID 0x0409
+
+struct wsm_tx_power_range {
+       int min_power_level;
+       int max_power_level;
+       u32 stepping;
+};
+
+struct wsm_configuration {
+       /* [in] */ u32 dot11MaxTransmitMsduLifeTime;
+       /* [in] */ u32 dot11MaxReceiveLifeTime;
+       /* [in] */ u32 dot11RtsThreshold;
+       /* [in, out] */ u8 *dot11StationId;
+       /* [in] */ const void *dpdData;
+       /* [in] */ size_t dpdData_size;
+       /* [out] */ u8 dot11FrequencyBandsSupported;
+       /* [out] */ u32 supportedRateMask;
+       /* [out] */ struct wsm_tx_power_range txPowerRange[2];
+};
+
+int wsm_configuration(struct cw1200_common *priv,
+                     struct wsm_configuration *arg);
+
+/* 3.3 */
+#define WSM_RESET_REQ_ID 0x000A
+#define WSM_RESET_RESP_ID 0x040A
+struct wsm_reset {
+       /* [in] */ int link_id;
+       /* [in] */ bool reset_statistics;
+};
+
+int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg);
+
+/* 3.5 */
+#define WSM_READ_MIB_REQ_ID 0x0005
+#define WSM_READ_MIB_RESP_ID 0x0405
+int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
+                size_t buf_size);
+
+/* 3.7 */
+#define WSM_WRITE_MIB_REQ_ID 0x0006
+#define WSM_WRITE_MIB_RESP_ID 0x0406
+int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
+                 size_t buf_size);
+
+/* 3.9 */
+#define WSM_START_SCAN_REQ_ID 0x0007
+#define WSM_START_SCAN_RESP_ID 0x0407
+
+struct wsm_ssid {
+       u8 ssid[32];
+       u32 length;
+};
+
+struct wsm_scan_ch {
+       u16 number;
+       u32 min_chan_time;
+       u32 max_chan_time;
+       u32 tx_power_level;
+};
+
+struct wsm_scan {
+       /* WSM_PHY_BAND_... */
+       u8 band;
+
+       /* WSM_SCAN_TYPE_... */
+       u8 type;
+
+       /* WSM_SCAN_FLAG_... */
+       u8 flags;
+
+       /* WSM_TRANSMIT_RATE_... */
+       u8 max_tx_rate;
+
+       /* Interval period in TUs that the device shall the re- */
+       /* execute the requested scan. Max value supported by the device */
+       /* is 256s. */
+       u32 auto_scan_interval;
+
+       /* Number of probe requests (per SSID) sent to one (1) */
+       /* channel. Zero (0) means that none is send, which */
+       /* means that a passive scan is to be done. Value */
+       /* greater than zero (0) means that an active scan is to */
+       /* be done. */
+       u32 num_probes;
+
+       /* Number of channels to be scanned. */
+       /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */
+       u8 num_channels;
+
+       /* Number of SSID provided in the scan command (this */
+       /* is zero (0) in broadcast scan) */
+       /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */
+       u8 num_ssids;
+
+       /* The delay time (in microseconds) period */
+       /* before sending a probe-request. */
+       u8 probe_delay;
+
+       /* SSIDs to be scanned [numOfSSIDs]; */
+       struct wsm_ssid *ssids;
+
+       /* Channels to be scanned [numOfChannels]; */
+       struct wsm_scan_ch *ch;
+};
+
+int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg);
+
+/* 3.11 */
+#define WSM_STOP_SCAN_REQ_ID 0x0008
+#define WSM_STOP_SCAN_RESP_ID 0x0408
+int wsm_stop_scan(struct cw1200_common *priv);
+
+/* 3.13 */
+#define WSM_SCAN_COMPLETE_IND_ID 0x0806
+struct wsm_scan_complete {
+       /* WSM_STATUS_... */
+       u32 status;
+
+       /* WSM_PSM_... */
+       u8 psm;
+
+       /* Number of channels that the scan operation completed. */
+       u8 num_channels;
+};
+
+/* 3.14 */
+#define WSM_TX_CONFIRM_IND_ID 0x0404
+#define WSM_MULTI_TX_CONFIRM_ID 0x041E
+
+struct wsm_tx_confirm {
+       /* Packet identifier used in wsm_tx. */
+       u32 packet_id;
+
+       /* WSM_STATUS_... */
+       u32 status;
+
+       /* WSM_TRANSMIT_RATE_... */
+       u8 tx_rate;
+
+       /* The number of times the frame was transmitted */
+       /* without receiving an acknowledgement. */
+       u8 ack_failures;
+
+       /* WSM_TX_STATUS_... */
+       u16 flags;
+
+       /* The total time in microseconds that the frame spent in */
+       /* the WLAN device before transmission as completed. */
+       u32 media_delay;
+
+       /* The total time in microseconds that the frame spent in */
+       /* the WLAN device before transmission was started. */
+       u32 tx_queue_delay;
+};
+
+/* 3.15 */
+typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv,
+                                  struct wsm_tx_confirm *arg);
+
+/* Note that ideology of wsm_tx struct is different against the rest of
+ * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input
+ * argument for WSM call, but a prepared bytestream to be sent to firmware.
+ * It is filled partly in cw1200_tx, partly in low-level WSM code.
+ * Please pay attention once again: ideology is different.
+ *
+ * Legend:
+ * - [in]: cw1200_tx must fill this field.
+ * - [wsm]: the field is filled by low-level WSM.
+ */
+struct wsm_tx {
+       /* common WSM header */
+       struct wsm_hdr hdr;
+
+       /* Packet identifier that meant to be used in completion. */
+       u32 packet_id;  /* Note this is actually a cookie */
+
+       /* WSM_TRANSMIT_RATE_... */
+       u8 max_tx_rate;
+
+       /* WSM_QUEUE_... */
+       u8 queue_id;
+
+       /* True: another packet is pending on the host for transmission. */
+       u8 more;
+
+       /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */
+       /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */
+       /* Bits 3:1  - PTA Priority */
+       /* Bits 6:4  - Tx Rate Retry Policy */
+       /* Bit 7 - Reserved */
+       u8 flags;
+
+       /* Should be 0. */
+       u32 reserved;
+
+       /* The elapsed time in TUs, after the initial transmission */
+       /* of an MSDU, after which further attempts to transmit */
+       /* the MSDU shall be terminated. Overrides the global */
+       /* dot11MaxTransmitMsduLifeTime setting [optional] */
+       /* Device will set the default value if this is 0. */
+       __le32 expire_time;
+
+       /* WSM_HT_TX_... */
+       __le32 ht_tx_parameters;
+} __packed;
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */
+#define WSM_TX_EXTRA_HEADROOM (28)
+
+/* 3.16 */
+#define WSM_RECEIVE_IND_ID 0x0804
+
+struct wsm_rx {
+       /* WSM_STATUS_... */
+       u32 status;
+
+       /* Specifies the channel of the received packet. */
+       u16 channel_number;
+
+       /* WSM_TRANSMIT_RATE_... */
+       u8 rx_rate;
+
+       /* This value is expressed in signed Q8.0 format for */
+       /* RSSI and unsigned Q7.1 format for RCPI. */
+       u8 rcpi_rssi;
+
+       /* WSM_RX_STATUS_... */
+       u32 flags;
+};
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */
+#define WSM_RX_EXTRA_HEADROOM (16)
+
+typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg,
+                          struct sk_buff **skb_p);
+
+/* 3.17 */
+struct wsm_event {
+       /* WSM_STATUS_... */
+       /* [out] */ u32 id;
+
+       /* Indication parameters. */
+       /* For error indication, this shall be a 32-bit WSM status. */
+       /* For RCPI or RSSI indication, this should be an 8-bit */
+       /* RCPI or RSSI value. */
+       /* [out] */ u32 data;
+};
+
+struct cw1200_wsm_event {
+       struct list_head link;
+       struct wsm_event evt;
+};
+
+/* 3.18 - 3.22 */
+/* Measurement. Skipped for now. Irrelevent. */
+
+typedef void (*wsm_event_cb) (struct cw1200_common *priv,
+                             struct wsm_event *arg);
+
+/* 3.23 */
+#define WSM_JOIN_REQ_ID 0x000B
+#define WSM_JOIN_RESP_ID 0x040B
+
+struct wsm_join {
+       /* WSM_JOIN_MODE_... */
+       u8 mode;
+
+       /* WSM_PHY_BAND_... */
+       u8 band;
+
+       /* Specifies the channel number to join. The channel */
+       /* number will be mapped to an actual frequency */
+       /* according to the band */
+       u16 channel_number;
+
+       /* Specifies the BSSID of the BSS or IBSS to be joined */
+       /* or the IBSS to be started. */
+       u8 bssid[6];
+
+       /* ATIM window of IBSS */
+       /* When ATIM window is zero the initiated IBSS does */
+       /* not support power saving. */
+       u16 atim_window;
+
+       /* WSM_JOIN_PREAMBLE_... */
+       u8 preamble_type;
+
+       /* Specifies if a probe request should be send with the */
+       /* specified SSID when joining to the network. */
+       u8 probe_for_join;
+
+       /* DTIM Period (In multiples of beacon interval) */
+       u8 dtim_period;
+
+       /* WSM_JOIN_FLAGS_... */
+       u8 flags;
+
+       /* Length of the SSID */
+       u32 ssid_len;
+
+       /* Specifies the SSID of the IBSS to join or start */
+       u8 ssid[32];
+
+       /* Specifies the time between TBTTs in TUs */
+       u32 beacon_interval;
+
+       /* A bit mask that defines the BSS basic rate set. */
+       u32 basic_rate_set;
+};
+
+struct wsm_join_cnf {
+       u32 status;
+
+       /* Minimum transmission power level in units of 0.1dBm */
+       u32 min_power_level;
+
+       /* Maximum transmission power level in units of 0.1dBm */
+       u32 max_power_level;
+};
+
+int wsm_join(struct cw1200_common *priv, struct wsm_join *arg);
+
+/* 3.24 */
+struct wsm_join_complete {
+       /* WSM_STATUS_... */
+       u32 status;
+};
+
+/* 3.25 */
+#define WSM_SET_PM_REQ_ID 0x0010
+#define WSM_SET_PM_RESP_ID 0x0410
+struct wsm_set_pm {
+       /* WSM_PSM_... */
+       u8 mode;
+
+       /* in unit of 500us; 0 to use default */
+       u8 fast_psm_idle_period;
+
+       /* in unit of 500us; 0 to use default */
+       u8 ap_psm_change_period;
+
+       /* in unit of 500us; 0 to disable auto-pspoll */
+       u8 min_auto_pspoll_period;
+};
+
+int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
+
+/* 3.27 */
+struct wsm_set_pm_complete {
+       u8 psm;                 /* WSM_PSM_... */
+};
+
+/* 3.28 */
+#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011
+#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411
+struct wsm_set_bss_params {
+       /* This resets the beacon loss counters only */
+       u8 reset_beacon_loss;
+
+       /* The number of lost consecutive beacons after which */
+       /* the WLAN device should indicate the BSS-Lost event */
+       /* to the WLAN host driver. */
+       u8 beacon_lost_count;
+
+       /* The AID received during the association process. */
+       u16 aid;
+
+       /* The operational rate set mask */
+       u32 operational_rate_set;
+};
+
+int wsm_set_bss_params(struct cw1200_common *priv,
+                      const struct wsm_set_bss_params *arg);
+
+/* 3.30 */
+#define WSM_ADD_KEY_REQ_ID         0x000C
+#define WSM_ADD_KEY_RESP_ID        0x040C
+struct wsm_add_key {
+       u8 type;                /* WSM_KEY_TYPE_... */
+       u8 index;               /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */
+       u16 reserved;
+       union {
+               struct {
+                       u8 peer[6];     /* MAC address of the peer station */
+                       u8 reserved;
+                       u8 keylen;              /* Key length in bytes */
+                       u8 keydata[16];         /* Key data */
+               } __packed wep_pairwise;
+               struct {
+                       u8 keyid;       /* Unique per key identifier (0..3) */
+                       u8 keylen;              /* Key length in bytes */
+                       u16 reserved;
+                       u8 keydata[16];         /* Key data */
+               } __packed wep_group;
+               struct {
+                       u8 peer[6];     /* MAC address of the peer station */
+                       u16 reserved;
+                       u8 keydata[16]; /* TKIP key data */
+                       u8 rx_mic_key[8];               /* Rx MIC key */
+                       u8 tx_mic_key[8];               /* Tx MIC key */
+               } __packed tkip_pairwise;
+               struct {
+                       u8 keydata[16]; /* TKIP key data */
+                       u8 rx_mic_key[8];               /* Rx MIC key */
+                       u8 keyid;               /* Key ID */
+                       u8 reserved[3];
+                       u8 rx_seqnum[8];        /* Receive Sequence Counter */
+               } __packed tkip_group;
+               struct {
+                       u8 peer[6];     /* MAC address of the peer station */
+                       u16 reserved;
+                       u8 keydata[16]; /* AES key data */
+               } __packed aes_pairwise;
+               struct {
+                       u8 keydata[16]; /* AES key data */
+                       u8 keyid;               /* Key ID */
+                       u8 reserved[3];
+                       u8 rx_seqnum[8];        /* Receive Sequence Counter */
+               } __packed aes_group;
+               struct {
+                       u8 peer[6];     /* MAC address of the peer station */
+                       u8 keyid;               /* Key ID */
+                       u8 reserved;
+                       u8 keydata[16]; /* WAPI key data */
+                       u8 mic_key[16]; /* MIC key data */
+               } __packed wapi_pairwise;
+               struct {
+                       u8 keydata[16]; /* WAPI key data */
+                       u8 mic_key[16]; /* MIC key data */
+                       u8 keyid;               /* Key ID */
+                       u8 reserved[3];
+               } __packed wapi_group;
+       } __packed;
+} __packed;
+
+int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg);
+
+/* 3.32 */
+#define WSM_REMOVE_KEY_REQ_ID         0x000D
+#define WSM_REMOVE_KEY_RESP_ID        0x040D
+struct wsm_remove_key {
+       u8 index; /* Key entry index : 0-10 */
+};
+
+int wsm_remove_key(struct cw1200_common *priv,
+                  const struct wsm_remove_key *arg);
+
+/* 3.34 */
+struct wsm_set_tx_queue_params {
+       /* WSM_ACK_POLICY_... */
+       u8 ackPolicy;
+
+       /* Medium Time of TSPEC (in 32us units) allowed per */
+       /* One Second Averaging Period for this queue. */
+       u16 allowedMediumTime;
+
+       /* dot11MaxTransmitMsduLifetime to be used for the */
+       /* specified queue. */
+       u32 maxTransmitLifetime;
+};
+
+struct wsm_tx_queue_params {
+       /* NOTE: index is a linux queue id. */
+       struct wsm_set_tx_queue_params params[4];
+};
+
+
+#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\
+               max_life_time)  \
+do {                                                   \
+       struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \
+       p->ackPolicy = (ack_policy);                            \
+       p->allowedMediumTime = (allowed_time);                          \
+       p->maxTransmitLifetime = (max_life_time);                       \
+} while (0)
+
+int wsm_set_tx_queue_params(struct cw1200_common *priv,
+                           const struct wsm_set_tx_queue_params *arg, u8 id);
+
+/* 3.36 */
+#define WSM_EDCA_PARAMS_REQ_ID 0x0013
+#define WSM_EDCA_PARAMS_RESP_ID 0x0413
+struct wsm_edca_queue_params {
+       /* CWmin (in slots) for the access class. */
+       u16 cwmin;
+
+       /* CWmax (in slots) for the access class. */
+       u16 cwmax;
+
+       /* AIFS (in slots) for the access class. */
+       u16 aifns;
+
+       /* TX OP Limit (in microseconds) for the access class. */
+       u16 txop_limit;
+
+       /* dot11MaxReceiveLifetime to be used for the specified */
+       /* the access class. Overrides the global */
+       /* dot11MaxReceiveLifetime value */
+       u32 max_rx_lifetime;
+};
+
+struct wsm_edca_params {
+       /* NOTE: index is a linux queue id. */
+       struct wsm_edca_queue_params params[4];
+       bool uapsd_enable[4];
+};
+
+#define TXOP_UNIT 32
+#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\
+                    __uapsd) \
+       do {                                                    \
+               struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \
+               p->cwmin = __cw_min;                                    \
+               p->cwmax = __cw_max;                                    \
+               p->aifns = __aifs;                                      \
+               p->txop_limit = ((__txop) * TXOP_UNIT);                 \
+               p->max_rx_lifetime = __lifetime;                        \
+               (__edca)->uapsd_enable[__queue] = (__uapsd);            \
+       } while (0)
+
+int wsm_set_edca_params(struct cw1200_common *priv,
+                       const struct wsm_edca_params *arg);
+
+int wsm_set_uapsd_param(struct cw1200_common *priv,
+                       const struct wsm_edca_params *arg);
+
+/* 3.38 */
+/* Set-System info. Skipped for now. Irrelevent. */
+
+/* 3.40 */
+#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016
+#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416
+
+struct wsm_switch_channel {
+       /* 1 - means the STA shall not transmit any further */
+       /* frames until the channel switch has completed */
+       u8 mode;
+
+       /* Number of TBTTs until channel switch occurs. */
+       /* 0 - indicates switch shall occur at any time */
+       /* 1 - occurs immediately before the next TBTT */
+       u8 switch_count;
+
+       /* The new channel number to switch to. */
+       /* Note this is defined as per section 2.7. */
+       u16 channel_number;
+};
+
+int wsm_switch_channel(struct cw1200_common *priv,
+                      const struct wsm_switch_channel *arg);
+
+typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv);
+
+#define WSM_START_REQ_ID 0x0017
+#define WSM_START_RESP_ID 0x0417
+
+struct wsm_start {
+       /* WSM_START_MODE_... */
+       /* [in] */ u8 mode;
+
+       /* WSM_PHY_BAND_... */
+       /* [in] */ u8 band;
+
+       /* Channel number */
+       /* [in] */ u16 channel_number;
+
+       /* Client Traffic window in units of TU */
+       /* Valid only when mode == ..._P2P */
+       /* [in] */ u32 ct_window;
+
+       /* Interval between two consecutive */
+       /* beacon transmissions in TU. */
+       /* [in] */ u32 beacon_interval;
+
+       /* DTIM period in terms of beacon intervals */
+       /* [in] */ u8 dtim_period;
+
+       /* WSM_JOIN_PREAMBLE_... */
+       /* [in] */ u8 preamble;
+
+       /* The delay time (in microseconds) period */
+       /* before sending a probe-request. */
+       /* [in] */ u8 probe_delay;
+
+       /* Length of the SSID */
+       /* [in] */ u8 ssid_len;
+
+       /* SSID of the BSS or P2P_GO to be started now. */
+       /* [in] */ u8 ssid[32];
+
+       /* The basic supported rates for the MiniAP. */
+       /* [in] */ u32 basic_rate_set;
+};
+
+int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg);
+
+#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018
+#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418
+
+struct wsm_beacon_transmit {
+       /* 1: enable; 0: disable */
+       /* [in] */ u8 enable_beaconing;
+};
+
+int wsm_beacon_transmit(struct cw1200_common *priv,
+                       const struct wsm_beacon_transmit *arg);
+
+int wsm_start_find(struct cw1200_common *priv);
+
+int wsm_stop_find(struct cw1200_common *priv);
+
+typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status);
+
+struct wsm_suspend_resume {
+       /* See 3.52 */
+       /* Link ID */
+       /* [out] */ int link_id;
+       /* Stop sending further Tx requests down to device for this link */
+       /* [out] */ bool stop;
+       /* Transmit multicast Frames */
+       /* [out] */ bool multicast;
+       /* The AC on which Tx to be suspended /resumed. */
+       /* This is applicable only for U-APSD */
+       /* WSM_QUEUE_... */
+       /* [out] */ int queue;
+};
+
+typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv,
+                                      struct wsm_suspend_resume *arg);
+
+/* 3.54 Update-IE request. */
+struct wsm_update_ie {
+       /* WSM_UPDATE_IE_... */
+       /* [in] */ u16 what;
+       /* [in] */ u16 count;
+       /* [in] */ u8 *ies;
+       /* [in] */ size_t length;
+};
+
+int wsm_update_ie(struct cw1200_common *priv,
+                 const struct wsm_update_ie *arg);
+
+/* 3.56 */
+struct wsm_map_link {
+       /* MAC address of the remote device */
+       /* [in] */ u8 mac_addr[6];
+       /* [in] */ u8 link_id;
+};
+
+int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg);
+
+/* ******************************************************************** */
+/* MIB shortcats                                                       */
+
+static inline int wsm_set_output_power(struct cw1200_common *priv,
+                                      int power_level)
+{
+       __le32 val = __cpu_to_le32(power_level);
+       return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL,
+                            &val, sizeof(val));
+}
+
+static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv,
+                                              unsigned dtim_interval,
+                                              unsigned listen_interval)
+{
+       struct {
+               u8 numBeaconPeriods;
+               u8 reserved;
+               __le16 listenInterval;
+       } val = {
+               dtim_interval, 0, __cpu_to_le16(listen_interval)
+       };
+
+       if (dtim_interval > 0xFF || listen_interval > 0xFFFF)
+               return -EINVAL;
+       else
+               return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD,
+                                    &val, sizeof(val));
+}
+
+struct wsm_rcpi_rssi_threshold {
+       u8 rssiRcpiMode;        /* WSM_RCPI_RSSI_... */
+       u8 lowerThreshold;
+       u8 upperThreshold;
+       u8 rollingAverageCount;
+};
+
+static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv,
+                                       struct wsm_rcpi_rssi_threshold *arg)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg,
+                            sizeof(*arg));
+}
+
+struct wsm_mib_counters_table {
+       __le32 plcp_errors;
+       __le32 fcs_errors;
+       __le32 tx_packets;
+       __le32 rx_packets;
+       __le32 rx_packet_errors;
+       __le32 rx_decryption_failures;
+       __le32 rx_mic_failures;
+       __le32 rx_no_key_failures;
+       __le32 tx_multicast_frames;
+       __le32 tx_frames_success;
+       __le32 tx_frame_failures;
+       __le32 tx_frames_retried;
+       __le32 tx_frames_multi_retried;
+       __le32 rx_frame_duplicates;
+       __le32 rts_success;
+       __le32 rts_failures;
+       __le32 ack_failures;
+       __le32 rx_multicast_frames;
+       __le32 rx_frames_success;
+       __le32 rx_cmac_icv_errors;
+       __le32 rx_cmac_replays;
+       __le32 rx_mgmt_ccmp_replays;
+} __packed;
+
+static inline int wsm_get_counters_table(struct cw1200_common *priv,
+                                        struct wsm_mib_counters_table *arg)
+{
+       return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE,
+                           arg, sizeof(*arg));
+}
+
+static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac)
+{
+       return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN);
+}
+
+struct wsm_rx_filter {
+       bool promiscuous;
+       bool bssid;
+       bool fcs;
+       bool probeResponder;
+};
+
+static inline int wsm_set_rx_filter(struct cw1200_common *priv,
+                                   const struct wsm_rx_filter *arg)
+{
+       __le32 val = 0;
+       if (arg->promiscuous)
+               val |= __cpu_to_le32(BIT(0));
+       if (arg->bssid)
+               val |= __cpu_to_le32(BIT(1));
+       if (arg->fcs)
+               val |= __cpu_to_le32(BIT(2));
+       if (arg->probeResponder)
+               val |= __cpu_to_le32(BIT(3));
+       return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val));
+}
+
+int wsm_set_probe_responder(struct cw1200_common *priv, bool enable);
+
+#define WSM_BEACON_FILTER_IE_HAS_CHANGED       BIT(0)
+#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1)
+#define WSM_BEACON_FILTER_IE_HAS_APPEARED      BIT(2)
+
+struct wsm_beacon_filter_table_entry {
+       u8      ie_id;
+       u8      flags;
+       u8      oui[3];
+       u8      match_data[3];
+} __packed;
+
+struct wsm_mib_beacon_filter_table {
+       __le32 num;
+       struct wsm_beacon_filter_table_entry entry[10];
+} __packed;
+
+static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv,
+                                             struct wsm_mib_beacon_filter_table *ft)
+{
+       size_t size = __le32_to_cpu(ft->num) *
+                    sizeof(struct wsm_beacon_filter_table_entry) +
+                    sizeof(__le32);
+
+       return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size);
+}
+
+#define WSM_BEACON_FILTER_ENABLE       BIT(0) /* Enable/disable beacon filtering */
+#define WSM_BEACON_FILTER_AUTO_ERP     BIT(1) /* If 1 FW will handle ERP IE changes internally */
+
+struct wsm_beacon_filter_control {
+       int enabled;
+       int bcn_count;
+};
+
+static inline int wsm_beacon_filter_control(struct cw1200_common *priv,
+                                       struct wsm_beacon_filter_control *arg)
+{
+       struct {
+               __le32 enabled;
+               __le32 bcn_count;
+       } val;
+       val.enabled = __cpu_to_le32(arg->enabled);
+       val.bcn_count = __cpu_to_le32(arg->bcn_count);
+       return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val,
+                            sizeof(val));
+}
+
+enum wsm_power_mode {
+       wsm_power_mode_active = 0,
+       wsm_power_mode_doze = 1,
+       wsm_power_mode_quiescent = 2,
+};
+
+struct wsm_operational_mode {
+       enum wsm_power_mode power_mode;
+       int disable_more_flag_usage;
+       int perform_ant_diversity;
+};
+
+static inline int wsm_set_operational_mode(struct cw1200_common *priv,
+                                       const struct wsm_operational_mode *arg)
+{
+       u8 val = arg->power_mode;
+       if (arg->disable_more_flag_usage)
+               val |= BIT(4);
+       if (arg->perform_ant_diversity)
+               val |= BIT(5);
+       return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val,
+                            sizeof(val));
+}
+
+struct wsm_template_frame {
+       u8 frame_type;
+       u8 rate;
+       struct sk_buff *skb;
+};
+
+static inline int wsm_set_template_frame(struct cw1200_common *priv,
+                                        struct wsm_template_frame *arg)
+{
+       int ret;
+       u8 *p = skb_push(arg->skb, 4);
+       p[0] = arg->frame_type;
+       p[1] = arg->rate;
+       ((__le16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4);
+       ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len);
+       skb_pull(arg->skb, 4);
+       return ret;
+}
+
+
+struct wsm_protected_mgmt_policy {
+       bool protectedMgmtEnable;
+       bool unprotectedMgmtFramesAllowed;
+       bool encryptionForAuthFrame;
+};
+
+static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv,
+               struct wsm_protected_mgmt_policy *arg)
+{
+       __le32 val = 0;
+       int ret;
+       if (arg->protectedMgmtEnable)
+               val |= __cpu_to_le32(BIT(0));
+       if (arg->unprotectedMgmtFramesAllowed)
+               val |= __cpu_to_le32(BIT(1));
+       if (arg->encryptionForAuthFrame)
+               val |= __cpu_to_le32(BIT(2));
+       ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY,
+                       &val, sizeof(val));
+       return ret;
+}
+
+struct wsm_mib_block_ack_policy {
+       u8 tx_tid;
+       u8 reserved1;
+       u8 rx_tid;
+       u8 reserved2;
+} __packed;
+
+static inline int wsm_set_block_ack_policy(struct cw1200_common *priv,
+                                          u8 tx_tid_policy,
+                                          u8 rx_tid_policy)
+{
+       struct wsm_mib_block_ack_policy val = {
+               .tx_tid = tx_tid_policy,
+               .rx_tid = rx_tid_policy,
+       };
+       return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val,
+                            sizeof(val));
+}
+
+struct wsm_mib_association_mode {
+       u8 flags;               /* WSM_ASSOCIATION_MODE_... */
+       u8 preamble;    /* WSM_JOIN_PREAMBLE_... */
+       u8 greenfield;  /* 1 for greenfield */
+       u8 mpdu_start_spacing;
+       __le32 basic_rate_set;
+} __packed;
+
+static inline int wsm_set_association_mode(struct cw1200_common *priv,
+                                          struct wsm_mib_association_mode *arg)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg,
+                            sizeof(*arg));
+}
+
+#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2)
+#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3)
+struct wsm_tx_rate_retry_policy {
+       u8 index;
+       u8 short_retries;
+       u8 long_retries;
+       /* BIT(2) - Terminate retries when Tx rate retry policy
+        *          finishes.
+        * BIT(3) - Count initial frame transmission as part of
+        *          rate retry counting but not as a retry
+        *          attempt
+        */
+       u8 flags;
+       u8 rate_recoveries;
+       u8 reserved[3];
+       __le32 rate_count_indices[3];
+} __packed;
+
+struct wsm_set_tx_rate_retry_policy {
+       u8 num;
+       u8 reserved[3];
+       struct wsm_tx_rate_retry_policy tbl[8];
+} __packed;
+
+static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv,
+                               struct wsm_set_tx_rate_retry_policy *arg)
+{
+       size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy);
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg,
+                            size);
+}
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+struct wsm_ether_type_filter_hdr {
+       u8 num;         /* Up to WSM_MAX_FILTER_ELEMENTS */
+       u8 reserved[3];
+} __packed;
+
+struct wsm_ether_type_filter {
+       u8 action;      /* WSM_FILTER_ACTION_XXX */
+       u8 reserved;
+       __le16 type;    /* Type of ethernet frame */
+} __packed;
+
+static inline int wsm_set_ether_type_filter(struct cw1200_common *priv,
+                               struct wsm_ether_type_filter_hdr *arg)
+{
+       size_t size = sizeof(struct wsm_ether_type_filter_hdr) +
+               arg->num * sizeof(struct wsm_ether_type_filter);
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER,
+               arg, size);
+}
+
+/* 4.33 SetUDPPortDataFrameFilter */
+struct wsm_udp_port_filter_hdr {
+       u8 num;         /* Up to WSM_MAX_FILTER_ELEMENTS */
+       u8 reserved[3];
+} __packed;
+
+struct wsm_udp_port_filter {
+       u8 action;      /* WSM_FILTER_ACTION_XXX */
+       u8 type;                /* WSM_FILTER_PORT_TYPE_XXX */
+       __le16 port;            /* Port number */
+} __packed;
+
+static inline int wsm_set_udp_port_filter(struct cw1200_common *priv,
+                               struct wsm_udp_port_filter_hdr *arg)
+{
+       size_t size = sizeof(struct wsm_udp_port_filter_hdr) +
+               arg->num * sizeof(struct wsm_udp_port_filter);
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER,
+               arg, size);
+}
+
+/* Undocumented MIBs: */
+/* 4.35 P2PDeviceInfo */
+#define D11_MAX_SSID_LEN               (32)
+
+struct wsm_p2p_device_type {
+       __le16 category_id;
+       u8 oui[4];
+       __le16 subcategory_id;
+} __packed;
+
+struct wsm_p2p_device_info {
+       struct wsm_p2p_device_type primaryDevice;
+       u8 reserved1[3];
+       u8 devname_size;
+       u8 local_devname[D11_MAX_SSID_LEN];
+       u8 reserved2[3];
+       u8 num_secdev_supported;
+       struct wsm_p2p_device_type secdevs[0];
+} __packed;
+
+/* 4.36 SetWCDMABand - WO */
+struct wsm_cdma_band {
+       u8 wcdma_band;
+       u8 reserved[3];
+} __packed;
+
+/* 4.37 GroupTxSequenceCounter - RO */
+struct wsm_group_tx_seq {
+       __le32 bits_47_16;
+       __le16 bits_15_00;
+       __le16 reserved;
+} __packed;
+
+/* 4.39 SetHtProtection - WO */
+#define WSM_DUAL_CTS_PROT_ENB          (1 << 0)
+#define WSM_NON_GREENFIELD_STA_PRESENT  (1 << 1)
+#define WSM_HT_PROT_MODE__NO_PROT      (0 << 2)
+#define WSM_HT_PROT_MODE__NON_MEMBER   (1 << 2)
+#define WSM_HT_PROT_MODE__20_MHZ       (2 << 2)
+#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2)
+#define WSM_LSIG_TXOP_PROT_FULL                (1 << 4)
+#define WSM_LARGE_L_LENGTH_PROT                (1 << 5)
+
+struct wsm_ht_protection {
+       __le32 flags;
+} __packed;
+
+/* 4.40 GPIO Command - R/W */
+#define WSM_GPIO_COMMAND_SETUP 0
+#define WSM_GPIO_COMMAND_READ  1
+#define WSM_GPIO_COMMAND_WRITE 2
+#define WSM_GPIO_COMMAND_RESET 3
+#define WSM_GPIO_ALL_PINS      0xFF
+
+struct wsm_gpio_command {
+       u8 command;
+       u8 pin;
+       __le16 config;
+} __packed;
+
+/* 4.41 TSFCounter - RO */
+struct wsm_tsf_counter {
+       __le64 tsf_counter;
+} __packed;
+
+/* 4.43 Keep alive period */
+struct wsm_keep_alive_period {
+       __le16 period;
+       u8 reserved[2];
+} __packed;
+
+static inline int wsm_keep_alive_period(struct cw1200_common *priv,
+                                       int period)
+{
+       struct wsm_keep_alive_period arg = {
+               .period = __cpu_to_le16(period),
+       };
+       return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD,
+                       &arg, sizeof(arg));
+};
+
+/* BSSID filtering */
+struct wsm_set_bssid_filtering {
+       u8 filter;
+       u8 reserved[3];
+} __packed;
+
+static inline int wsm_set_bssid_filtering(struct cw1200_common *priv,
+                                         bool enabled)
+{
+       struct wsm_set_bssid_filtering arg = {
+               .filter = !enabled,
+       };
+       return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER,
+                       &arg, sizeof(arg));
+}
+
+/* Multicast filtering - 4.5 */
+struct wsm_mib_multicast_filter {
+       __le32 enable;
+       __le32 num_addrs;
+       u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN];
+} __packed;
+
+static inline int wsm_set_multicast_filter(struct cw1200_common *priv,
+                                          struct wsm_mib_multicast_filter *fp)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE,
+                            fp, sizeof(*fp));
+}
+
+/* ARP IPv4 filtering - 4.10 */
+struct wsm_mib_arp_ipv4_filter {
+       __le32 enable;
+       __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES];
+} __packed;
+
+static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv,
+                                         struct wsm_mib_arp_ipv4_filter *fp)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE,
+                           fp, sizeof(*fp));
+}
+
+/* P2P Power Save Mode Info - 4.31 */
+struct wsm_p2p_ps_modeinfo {
+       u8      opp_ps_ct_window;
+       u8      count;
+       u8      reserved;
+       u8      dtim_count;
+       __le32  duration;
+       __le32  interval;
+       __le32  start_time;
+} __packed;
+
+static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv,
+                                         struct wsm_p2p_ps_modeinfo *mi)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+                            mi, sizeof(*mi));
+}
+
+static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv,
+                                         struct wsm_p2p_ps_modeinfo *mi)
+{
+       return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+                           mi, sizeof(*mi));
+}
+
+/* UseMultiTxConfMessage */
+
+static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv,
+                                       bool enabled)
+{
+       __le32 arg = enabled ? __cpu_to_le32(1) : 0;
+
+       return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF,
+                       &arg, sizeof(arg));
+}
+
+
+/* 4.26 SetUpasdInformation */
+struct wsm_uapsd_info {
+       __le16 uapsd_flags;
+       __le16 min_auto_trigger_interval;
+       __le16 max_auto_trigger_interval;
+       __le16 auto_trigger_step;
+};
+
+static inline int wsm_set_uapsd_info(struct cw1200_common *priv,
+                                    struct wsm_uapsd_info *arg)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION,
+                               arg, sizeof(*arg));
+}
+
+/* 4.22 OverrideInternalTxRate */
+struct wsm_override_internal_txrate {
+       u8 internalTxRate;
+       u8 nonErpInternalTxRate;
+       u8 reserved[2];
+} __packed;
+
+static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv,
+                                    struct wsm_override_internal_txrate *arg)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+                               arg, sizeof(*arg));
+}
+
+/* ******************************************************************** */
+/* WSM TX port control                                                 */
+
+void wsm_lock_tx(struct cw1200_common *priv);
+void wsm_lock_tx_async(struct cw1200_common *priv);
+bool wsm_flush_tx(struct cw1200_common *priv);
+void wsm_unlock_tx(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* WSM / BH API                                                                */
+
+int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len);
+int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm,
+                 struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* wsm_buf API                                                         */
+
+struct wsm_buf {
+       u8 *begin;
+       u8 *data;
+       u8 *end;
+};
+
+void wsm_buf_init(struct wsm_buf *buf);
+void wsm_buf_deinit(struct wsm_buf *buf);
+
+/* ******************************************************************** */
+/* wsm_cmd API                                                         */
+
+struct wsm_cmd {
+       spinlock_t lock; /* Protect structure from multiple access */
+       int done;
+       u8 *ptr;
+       size_t len;
+       void *arg;
+       int ret;
+       u16 cmd;
+};
+
+/* ******************************************************************** */
+/* WSM TX buffer access                                                        */
+
+int wsm_get_tx(struct cw1200_common *priv, u8 **data,
+              size_t *tx_len, int *burst);
+void wsm_txed(struct cw1200_common *priv, u8 *data);
+
+/* ******************************************************************** */
+/* Queue mapping: WSM <---> linux                                      */
+/* Linux: VO VI BE BK                                                  */
+/* WSM:   BE BK VI VO                                                  */
+
+static inline u8 wsm_queue_id_to_linux(u8 queue_id)
+{
+       static const u8 queue_mapping[] = {
+               2, 3, 1, 0
+       };
+       return queue_mapping[queue_id];
+}
+
+static inline u8 wsm_queue_id_to_wsm(u8 queue_id)
+{
+       static const u8 queue_mapping[] = {
+               3, 2, 0, 1
+       };
+       return queue_mapping[queue_id];
+}
+
+#endif /* CW1200_HWIO_H_INCLUDED */