compat-wireless: add the Wilocity wil6210 802.11ad / 60 GHz driver
authorLuis R. Rodriguez <mcgrof@frijolero.org>
Thu, 28 Jun 2012 22:16:14 +0000 (15:16 -0700)
committerLuis R. Rodriguez <mcgrof@frijolero.org>
Thu, 28 Jun 2012 22:40:17 +0000 (15:40 -0700)
This driver is still under development however some this driver
can now be used in monitor mode. Due to hardware limitation it
captures either only CP (control PHY) or DP (data PHY) frames.

Test compiled against 2.6.24..3.4

mcgrof@flash ~/devel/compat-wireless (git::master)$ ./scripts/driver-select wil6210
Processing new driver-select request...
Backing up makefile: Makefile.bk
Backup exists: Makefile.bk
Backup exists: Makefile.bk
Backup exists: Makefile.bk
Backup exists: Makefile.bk
Backing up makefile: drivers/net/wireless/Makefile.bk
Backing up makefile: drivers/net/wireless/ath/Makefile.bk
Backing up makefile: net/wireless/Makefile.bk
Backing up makefile: drivers/ssb/Makefile.bk
Backing up makefile: drivers/bcma/Makefile.bk
Backing up makefile: drivers/misc/eeprom/Makefile.bk
Backup exists: Makefile.bk

mcgrof@flash ~/devel/compat-wireless (git::master)$ ckmake
Trying kernel  3.4.0-030400rc4-generic [OK]
Trying kernel  3.3.3-030303-generic [OK]
Trying kernel  3.2.2-030202-generic [OK]
Trying kernel  3.1.10-030110-generic [OK]
Trying kernel  3.0.18-030018-generic [OK]
Trying kernel  2.6.39-02063904-generic [OK]
Trying kernel  2.6.38-02063808-generic [OK]
Trying kernel  2.6.37-02063706-generic [OK]
Trying kernel  2.6.36-02063604-generic [OK]
Trying kernel  2.6.35-02063512-generic [OK]
Trying kernel  2.6.34-02063410-generic [OK]
Trying kernel  2.6.33-02063305-generic [OK]
Trying kernel  2.6.32-02063255-generic [OK]
Trying kernel  2.6.31-02063113-generic [OK]
Trying kernel  2.6.30-02063010-generic [OK]
Trying kernel  2.6.29-02062906-generic [OK]
Trying kernel  2.6.28-02062810-generic [OK]
Trying kernel  2.6.27-020627-generic [OK]
Trying kernel  2.6.26-020626-generic [OK]
Trying kernel  2.6.25-020625-generic [OK]
Trying kernel  2.6.24-020624-generic [OK]

Signed-off-by: Luis R. Rodriguez <mcgrof@frijolero.org>
config.mk
crap/0004-wireless-adds-the-802.11ad-60-GHz-wil6210-driver.patch [new file with mode: 0644]
linux-next-pending/0001-wireless-add-802.11ad-60gHz-band.patch [new file with mode: 0644]
linux-next-pending/0002-wireless-rate-check-logic-for-60g.patch [new file with mode: 0644]
linux-next-pending/0003-wireless-regulatory-for-60g.patch [new file with mode: 0644]
linux-next-pending/0004-wireless-60g-protocol-constants.patch [new file with mode: 0644]
linux-next-pending/0005-wireless-bitrate-calculation-for-60g.patch [new file with mode: 0644]
scripts/driver-select

index 0978b687003b8ba11b6ff835a258942626e294f7..37faceb2e442025a653c969ab029fc2bd4bee4cc 100644 (file)
--- a/config.mk
+++ b/config.mk
@@ -268,6 +268,8 @@ export CONFIG_COMPAT_ATH9K_RATE_CONTROL=y
 
 export CONFIG_ATH9K_BTCOEX_SUPPORT=y
 
+export CONFIG_WIL6210=m
+
 ifndef CONFIG_COMPAT_KERNEL_2_6_27
 export CONFIG_ATH6KL=m
 # export CONFIG_ATH6KL_DEBUG=y
diff --git a/crap/0004-wireless-adds-the-802.11ad-60-GHz-wil6210-driver.patch b/crap/0004-wireless-adds-the-802.11ad-60-GHz-wil6210-driver.patch
new file mode 100644 (file)
index 0000000..869698e
--- /dev/null
@@ -0,0 +1,5833 @@
+From 1202ba3f79d5190f18ab3588ad2ea667fca0f273 Mon Sep 17 00:00:00 2001
+From: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+Date: Thu, 28 Jun 2012 17:20:55 +0300
+Subject: [PATCH 6/6] wireless: adds the 802.11ad / 60 GHz wil6210 driver
+
+This is development snapshot of the driver for Wilocity 60ghz chip
+wil6210
+
+It depends on the 60ghz infrastructure patches submitted to the
+linux-wireless.
+
+What works:
+
+- sniffer. Due to hardware limitation it captures either only
+CP (control PHY) or DP (data PHY) frames
+
+- BSS between 2 peers. Both AP and managed modes supported. Single
+peer is hardware limitation. Iperf gives slightly above 1gbps
+
+Not yet:
+
+- P2P and FST flows
+- after disconnect, upon 2-nd connect hardware goes crazy
+- MSI interrupt
+- various offloads
+
+Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+Signed-off-by: Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
+---
+ drivers/net/wireless/ath/Kconfig                   |    1 +
+ drivers/net/wireless/ath/wil6210/Makefile          |   20 +
+ drivers/net/wireless/ath/wil6210/cfg80211.c        |  617 ++++++++++++++
+ drivers/net/wireless/ath/wil6210/cfg80211.h        |   23 +
+ drivers/net/wireless/ath/wil6210/debug.c           |   42 +
+ drivers/net/wireless/ath/wil6210/debugfs.c         |  458 ++++++++++
+ drivers/net/wireless/ath/wil6210/ifc.sh            |   20 +
+ drivers/net/wireless/ath/wil6210/interrupt.c       |  324 ++++++++
+ drivers/net/wireless/ath/wil6210/main.c            |  336 ++++++++
+ drivers/net/wireless/ath/wil6210/memdump.sh        |    9 +
+ drivers/net/wireless/ath/wil6210/netdev.c          |  166 ++++
+ drivers/net/wireless/ath/wil6210/pcie_bus.c        |  239 ++++++
+ drivers/net/wireless/ath/wil6210/read_dw.sh        |    7 +
+ drivers/net/wireless/ath/wil6210/tools/Makefile    |    4 +
+ drivers/net/wireless/ath/wil6210/tools/trace.c     |  190 +++++
+ drivers/net/wireless/ath/wil6210/tools/vhex2bin.py |   17 +
+ drivers/net/wireless/ath/wil6210/trace.sh          |    4 +
+ drivers/net/wireless/ath/wil6210/txdesc.sh         |    8 +
+ drivers/net/wireless/ath/wil6210/txrx.c            |  871 ++++++++++++++++++++
+ drivers/net/wireless/ath/wil6210/txrx.h            |  352 ++++++++
+ drivers/net/wireless/ath/wil6210/vrwatch.sh        |    6 +
+ drivers/net/wireless/ath/wil6210/wil6210.h         |  234 ++++++
+ drivers/net/wireless/ath/wil6210/wil6210_rgf.h     |   93 +++
+ drivers/net/wireless/ath/wil6210/wmi.c             |  710 ++++++++++++++++
+ drivers/net/wireless/ath/wil6210/wmi.h             |  837 +++++++++++++++++++
+ 25 files changed, 5588 insertions(+)
+ create mode 100644 drivers/net/wireless/ath/wil6210/Makefile
+ create mode 100644 drivers/net/wireless/ath/wil6210/cfg80211.c
+ create mode 100644 drivers/net/wireless/ath/wil6210/cfg80211.h
+ create mode 100644 drivers/net/wireless/ath/wil6210/debug.c
+ create mode 100644 drivers/net/wireless/ath/wil6210/debugfs.c
+ create mode 100755 drivers/net/wireless/ath/wil6210/ifc.sh
+ create mode 100644 drivers/net/wireless/ath/wil6210/interrupt.c
+ create mode 100644 drivers/net/wireless/ath/wil6210/main.c
+ create mode 100755 drivers/net/wireless/ath/wil6210/memdump.sh
+ create mode 100644 drivers/net/wireless/ath/wil6210/netdev.c
+ create mode 100644 drivers/net/wireless/ath/wil6210/pcie_bus.c
+ create mode 100755 drivers/net/wireless/ath/wil6210/read_dw.sh
+ create mode 100644 drivers/net/wireless/ath/wil6210/tools/Makefile
+ create mode 100644 drivers/net/wireless/ath/wil6210/tools/trace.c
+ create mode 100755 drivers/net/wireless/ath/wil6210/tools/vhex2bin.py
+ create mode 100755 drivers/net/wireless/ath/wil6210/trace.sh
+ create mode 100755 drivers/net/wireless/ath/wil6210/txdesc.sh
+ create mode 100644 drivers/net/wireless/ath/wil6210/txrx.c
+ create mode 100644 drivers/net/wireless/ath/wil6210/txrx.h
+ create mode 100755 drivers/net/wireless/ath/wil6210/vrwatch.sh
+ create mode 100644 drivers/net/wireless/ath/wil6210/wil6210.h
+ create mode 100644 drivers/net/wireless/ath/wil6210/wil6210_rgf.h
+ create mode 100644 drivers/net/wireless/ath/wil6210/wmi.c
+ create mode 100644 drivers/net/wireless/ath/wil6210/wmi.h
+
+diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
+index 0960224..623fb62 100644
+--- a/drivers/net/wireless/ath/Kconfig
++++ b/drivers/net/wireless/ath/Kconfig
+@@ -26,5 +26,6 @@ source "drivers/net/wireless/ath/ath5k/Kconfig"
+ source "drivers/net/wireless/ath/ath9k/Kconfig"
+ source "drivers/net/wireless/ath/carl9170/Kconfig"
+ source "drivers/net/wireless/ath/ath6kl/Kconfig"
++source "drivers/net/wireless/ath/wil6210/Kconfig"
+ endif
+diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
+new file mode 100644
+index 0000000..f54d053
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/Makefile
+@@ -0,0 +1,20 @@
++obj-$(CONFIG_WIL6210) += wil6210.o
++
++wil6210-objs := main.o
++wil6210-objs += netdev.o
++wil6210-objs += cfg80211.o
++wil6210-objs += pcie_bus.o
++wil6210-objs += debugfs.o
++wil6210-objs += wmi.o
++wil6210-objs += interrupt.o
++wil6210-objs += debug.o
++wil6210-objs += txrx.o
++
++subdir-ccflags-y += -Werror
++
++# Debug Tx/Rx flows
++#subdir-ccflags-y += -DWIL_DEBUG_TXRX
++
++# Use Clear-On-Read
++subdir-ccflags-y += -DWIL6210_ISR_COR=1
++
+diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
+new file mode 100644
+index 0000000..50e6829
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
+@@ -0,0 +1,615 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/sched.h>
++#include <linux/etherdevice.h>
++#include <linux/wireless.h>
++#include <linux/ieee80211.h>
++#include <linux/slab.h>
++#include <linux/version.h>
++#include <net/cfg80211.h>
++
++#include "wil6210.h"
++#include "cfg80211.h"
++#include "wmi.h"
++
++#define CHAN60G(_channel, _flags) {                           \
++      .band                   = IEEE80211_BAND_60GHZ,         \
++      .center_freq            = 56160 + (2160 * (_channel)),  \
++      .hw_value               = (_channel),                   \
++      .flags                  = (_flags),                     \
++      .max_antenna_gain       = 0,                            \
++      .max_power              = 40,                           \
++}
++
++static struct ieee80211_channel wil_60ghz_channels[] = {
++      CHAN60G(1, 0),
++      CHAN60G(2, 0),
++      CHAN60G(3, 0),
++#if 0
++      CHAN60G(4, 0),
++#endif
++};
++
++static struct ieee80211_supported_band wil_band_60ghz = {
++      .channels = wil_60ghz_channels,
++      .n_channels = ARRAY_SIZE(wil_60ghz_channels),
++      .ht_cap = {
++              .ht_supported = true,
++              .cap = 0, /* TODO */
++              .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */
++              .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */
++              .mcs = {
++                      .rx_mask = {0xfe, 0xff, 0xff, 0x0f}, /* 1..27 */
++                      .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */
++              },
++      },
++};
++
++static const struct ieee80211_txrx_stypes
++wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
++      [NL80211_IFTYPE_STATION] = {
++              .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
++              BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
++              .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
++              BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
++      },
++      [NL80211_IFTYPE_P2P_CLIENT] = {
++              .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
++              BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
++              .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
++              BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
++      },
++      [NL80211_IFTYPE_P2P_GO] = {
++              .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
++              BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
++              .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
++              BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
++      },
++};
++
++
++
++int iftype_nl2wmi(enum nl80211_iftype type)
++{
++      static const struct {
++              enum nl80211_iftype nl;
++              enum WMI_NETWORK_TYPE wmi;
++      } __nl2wmi[] = {
++              {NL80211_IFTYPE_ADHOC,        ADHOC_NETWORK},
++              {NL80211_IFTYPE_STATION,      INFRA_NETWORK},
++              {NL80211_IFTYPE_AP,           AP_NETWORK},
++              {NL80211_IFTYPE_P2P_CLIENT,   P2P_NETWORK},
++              {NL80211_IFTYPE_P2P_GO,       P2P_NETWORK},
++              {NL80211_IFTYPE_MONITOR,      ADHOC_NETWORK}, /* FIXME */
++      #if 0
++              {NL80211_IFTYPE_AP_VLAN,      0},
++              {NL80211_IFTYPE_WDS,          0},
++              {NL80211_IFTYPE_MESH_POINT,   0},
++                  INFRA_NETWORK       = 0x01,
++                  ADHOC_NETWORK       = 0x02,
++                  ADHOC_CREATOR       = 0x04,
++                  AP_NETWORK          = 0x10,
++                  P2P_NETWORK         = 0x20,
++                  WBE_NETWORK         = 0x40,
++      #endif
++      };
++      int i;
++      for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) {
++              if (__nl2wmi[i].nl == type)
++                      return __nl2wmi[i].wmi;
++      }
++      return -EOPNOTSUPP;
++}
++
++static int wil_cfg80211_get_station(struct wiphy *wiphy,
++                                  struct net_device *ndev,
++                                  u8 *mac, struct station_info *sinfo)
++{
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      int rc;
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_NOTIFY_REQ_CMD cmd;
++      } __packed wmi = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 1,
++                      .len = sizeof(wmi.wmi)
++                      + sizeof(wmi.cmd),
++              },
++              .wmi = {
++                      .id = WMI_NOTIFY_REQ_CMDID,
++                      .info1 = 0,
++              },
++              .cmd = {
++                      .cid = 0,
++                      .interval_usec = 0,
++              },
++      };
++
++#if 0
++      wil_info(wil, "%s(%pM)\n", __func__, mac);
++#endif
++      if (memcmp(mac, wil->dst_addr[0], ETH_ALEN))
++              return -ENOENT;
++      rc = wmi_call(wil, &wmi, WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20);
++      if (rc)
++              return rc;
++
++      sinfo->filled |= STATION_INFO_TX_BITRATE;
++      sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
++      sinfo->txrate.mcs = wil->stats.bf_mcs;
++      sinfo->filled |= STATION_INFO_RX_BITRATE;
++      sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
++      sinfo->rxrate.mcs = wil->stats.last_mcs_rx;
++
++      if (test_bit(wil_status_fwconnected, &wil->status)) {
++              sinfo->filled |= STATION_INFO_SIGNAL;
++              sinfo->signal = 12; /* TODO: provide real number */
++      }
++
++      return 0;
++}
++
++static int wil_cfg80211_change_iface(struct wiphy *wiphy,
++                                   struct net_device *ndev,
++                                   enum nl80211_iftype type, u32 *flags,
++                                   struct vif_params *params)
++{
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      struct wireless_dev *wdev = wil->wdev;
++      wil_info(wil, "%s()\n", __func__);
++
++      switch (type) {
++      case NL80211_IFTYPE_STATION:
++              wil_info(wil, "type: STATION\n");
++              break;
++      case NL80211_IFTYPE_AP:
++              wil_info(wil, "type: AP\n");
++              break;
++      case NL80211_IFTYPE_P2P_CLIENT:
++              wil_info(wil, "type: P2P_CLIENT\n");
++              break;
++      case NL80211_IFTYPE_P2P_GO:
++              wil_info(wil, "type: P2P_GO\n");
++              break;
++      case NL80211_IFTYPE_MONITOR:
++              wil_info(wil, "type: Monitor\n");
++              if (flags) {
++                      wil_info(wil, "Monitor flags: 0x%08x\n", *flags);
++                      wil->monitor_flags = *flags;
++              } else {
++                      wil->monitor_flags = 0;
++              }
++              break;
++      default:
++              return -EOPNOTSUPP;
++      }
++      wdev->iftype = type;
++      return 0;
++}
++
++static int wil_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
++                           struct cfg80211_scan_request *request)
++{
++#if 0
++      int rc;
++#endif
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      struct wireless_dev *wdev = wil->wdev;
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_START_SCAN_CMD scan;
++              u16 channels[4];
++      } __packed wmi_scan = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 0,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_START_SCAN_CMD),
++              },
++              .wmi = {
++                      .id = WMI_START_SCAN_CMDID,
++                      .info1 = 0,
++              },
++              .scan = {
++              },
++      };
++#if 0
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_SET_BSS_FILTER_CMD filter;
++      } __packed wmi_bss_filter = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 0,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_SET_BSS_FILTER_CMD),
++              },
++              .wmi = {
++                      .id = WMI_SET_BSS_FILTER_CMDID,
++                      .info1 = 0,
++              },
++              .filter = {
++                      .bssFilter = ALL_BSS_FILTER,
++              },
++      };
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_SET_SCAN_PARAMS_CMD params;
++      } __packed wmi_scan_params = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 0,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_SET_BSS_FILTER_CMD),
++              },
++              .wmi = {
++                      .id = WMI_SET_SCAN_PARAMS_CMDID,
++                      .info1 = 0,
++              },
++              .params = {
++                      .fg_start_period = 0,        /* seconds */
++                      .fg_end_period = 0,          /* seconds */
++                      .bg_period = 0,              /* seconds */
++                      .maxact_chdwell_time = 0,    /* msec */
++                      .pas_chdwell_time = 0,       /* msec */
++                      .shortScanRatio = 0,         /* how many short scans */
++                                              /* for one long */
++                      .scanCtrlFlags = DEFAULT_SCAN_CTRL_FLAGS,
++                      .minact_chdwell_time = 0,    /* msec */
++                      .maxact_scan_per_ssid = 0,   /* max active scans */
++                                              /* per ssid */
++                      .max_dfsch_act_time = 0,  /* msecs */
++              },
++      };
++#endif
++      int i, n;
++      wil_info(wil, "%s()\n", __func__);
++      if (wil->scan_request) {
++              wil_err(wil, "Already scanning\n");
++              return -EAGAIN;
++      }
++      /* check we are client side */
++      switch (wdev->iftype) {
++      case NL80211_IFTYPE_STATION:
++      case NL80211_IFTYPE_P2P_CLIENT:
++              break;
++      default:
++              return -EOPNOTSUPP;
++
++      }
++      /**
++       * FW don't support scan after connection attempt
++       */
++      if (test_bit(wil_status_dontscan, &wil->status)) {
++              wil_err(wil, "Scan after connect attempt not supported\n");
++              return -EBUSY;
++      }
++
++      wil->scan_request = request;
++      wmi_scan.scan.forceFgScan = 0;
++      wmi_scan.scan.isLegacy = 0;
++      wmi_scan.scan.homeDwellTime = 0;
++      wmi_scan.scan.forceScanInterval = 0;
++      wmi_scan.scan.scanType = 0;
++      wmi_scan.scan.numChannels = 0;
++      n = min(request->n_channels, 4U);
++      for (i = 0; i < n; i++) {
++              int ch = ieee80211_frequency_to_channel(
++                              request->channels[i]->center_freq);
++              if (ch == 0) {
++                      wil_err(wil,
++                              "Scan requested for unknown frequency %dMhz\n",
++                              request->channels[i]->center_freq);
++                      continue;
++              }
++              /* 0-based index */
++              /* TODO convert CPU to LE */
++              wmi_scan.scan.channelList[wmi_scan.scan.numChannels++] = ch-1;
++              wil_info(wil, "Scan for ch %d  : %d MHz\n", ch,
++                              request->channels[i]->center_freq);
++      }
++      wmi_scan.hdr.len += wmi_scan.scan.numChannels *
++                      sizeof(wmi_scan.scan.channelList[0]);
++#if 0
++      rc = wmi_send_cmd(wil, &wmi_bss_filter);
++      if (rc)
++              return rc;
++      rc = wmi_send_cmd(wil, &wmi_scan_params);
++      if (rc)
++              return rc;
++#endif
++      return wmi_send_cmd(wil, &wmi_scan);
++}
++
++static int wil_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
++                               struct cfg80211_connect_params *sme)
++{
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      struct wireless_dev *wdev = wil->wdev;
++      struct cfg80211_bss *bss;
++      const u8 *ssid_eid;
++      int rc = 0;
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_CONNECT_CMD conn;
++      } __packed wmi_conn = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 0,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_CONNECT_CMD),
++              },
++              .wmi = {
++                      .id = WMI_CONNECT_CMDID,
++                      .info1 = 0,
++              },
++              .conn = {
++              },
++      };
++      wil_info(wil, "%s()\n", __func__);
++      if (sme->channel)
++              wil_info(wil, "Channel : %d MHz\n",
++                              sme->channel->center_freq);
++      if (sme->bssid)
++              wil_info(wil, "BSSID   : %pM\n", sme->bssid);
++      if (sme->ssid && sme->ssid_len)
++              print_hex_dump(KERN_INFO, "SSID : ", DUMP_PREFIX_NONE, 16, 1,
++                              sme->ssid, sme->ssid_len, true);
++      bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
++                      sme->ssid, sme->ssid_len,
++                      WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
++      if (!bss) {
++              wil_err(wil, "Unable to find BSS\n");
++              return -ENOENT;
++      }
++      ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
++      if (!ssid_eid) {
++              wil_err(wil, "No SSID\n");
++              rc = -ENOENT;
++              goto out;
++      }
++      wmi_conn.conn.networkType = iftype_nl2wmi(wdev->iftype);
++      /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
++      wmi_conn.conn.networkType = iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
++      wmi_conn.conn.dot11AuthMode = OPEN_AUTH; /* TODO: crypto flow */
++      wmi_conn.conn.authMode = NONE_AUTH; /* TODO: crypto flow */
++      /* wmi_conn.conn.pairwiseCryptoType; */
++      /* wmi_conn.conn.pairwiseCryptoLen; */
++      wmi_conn.conn.groupCryptoType = NONE_CRYPT; /* TODO: crypto flow */
++      /* wmi_conn.conn.groupCryptoLen; */
++      wmi_conn.conn.ssidLength = min_t(u8, ssid_eid[1], 32);
++      memcpy(wmi_conn.conn.ssid, ssid_eid+2, wmi_conn.conn.ssidLength);
++      {
++              int ch = ieee80211_frequency_to_channel(
++                              bss->channel->center_freq);
++              if (ch == 0) {
++                      wil_err(wil, "BSS at unknown frequency %dMhz\n",
++                              bss->channel->center_freq);
++                      rc = -EOPNOTSUPP;
++                      goto out;
++              }
++              wmi_conn.conn.channel = ch - 1;
++      }
++      memcpy(wmi_conn.conn.bssid, bss->bssid, 6);
++      wmi_conn.conn.ctrl_flags = 0; /* TODO: set real value */
++      memcpy(wmi_conn.conn.destMacAddr, bss->bssid, 6);
++      /**
++       * FW don't support scan after connection attempt
++       */
++      set_bit(wil_status_dontscan, &wil->status);
++      rc = wmi_send_cmd(wil, &wmi_conn);
++      if (rc == 0) {
++              /* Connect can take lots of time */
++              mod_timer(&wil->connect_timer,
++                              jiffies + msecs_to_jiffies(2000));
++      }
++ out:
++      cfg80211_put_bss(bss);
++      return rc;
++}
++
++static int wil_cfg80211_disconnect(struct wiphy *wiphy,
++              struct net_device *ndev,
++              u16 reason_code)
++{
++      int rc;
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++      } __packed wmi_conn = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 0,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi),
++              },
++              .wmi = {
++                      .id = WMI_DISCONNECT_CMDID,
++                      .info1 = 0,
++              },
++      };
++      wil_info(wil, "%s()\n", __func__);
++      rc = wmi_send_cmd(wil, &wmi_conn);
++      return rc;
++}
++
++static int wil_cfg80211_set_txpower(struct wiphy *wiphy,
++              enum nl80211_tx_power_setting type, int mbm)
++{
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      wil_info(wil, "%s()\n", __func__);
++      return 0;
++}
++
++static int wil_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
++{
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      wil_info(wil, "%s()\n", __func__);
++
++      *dbm = 43;
++
++      return 0;
++}
++
++static int wil_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev,
++              struct ieee80211_channel *chan, bool offchan,
++              enum nl80211_channel_type channel_type,
++              bool channel_type_valid, unsigned int wait,
++              const u8 *buf, size_t len, bool no_cck,
++              bool dont_wait_for_ack,
++              u64 *cookie)
++{
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      wil_info(wil, "%s()\n", __func__);
++      print_hex_dump(KERN_INFO, "mgmt_tx ", DUMP_PREFIX_OFFSET, 16, 1,
++                      buf, len, true);
++      return 0;
++}
++
++static void wil_mgmt_frame_register(struct wiphy *wiphy,
++              struct net_device *ndev, u16 frame_type, bool reg)
++{
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      wil_info(wil, "%s()\n", __func__);
++      wil_info(wil, "frame_type = 0x%04x, reg = %d\n", frame_type, reg);
++}
++
++static int wil_set_monitor_channel(struct wiphy *wiphy,
++#if defined(OLD_SET_CHANNEL_API)
++              struct net_device *dev,
++#endif
++              struct ieee80211_channel *chan,
++              enum nl80211_channel_type channel_type)
++{
++      struct wil6210_priv *wil = wiphy_to_wil(wiphy);
++      wil_info(wil, "%s()\n", __func__);
++      wil_info(wil, "freq = %d\n", chan->center_freq);
++      wil->monitor_chan = chan;
++      return 0;
++}
++
++static struct cfg80211_ops wil_cfg80211_ops = {
++      .scan = wil_cfg80211_scan,
++      .connect = wil_cfg80211_connect,
++      .disconnect = wil_cfg80211_disconnect,
++      .set_tx_power = wil_cfg80211_set_txpower,
++      .get_tx_power = wil_cfg80211_get_txpower,
++      .change_virtual_intf = wil_cfg80211_change_iface,
++      .get_station = wil_cfg80211_get_station,
++      .mgmt_tx = wil_mgmt_tx,
++      .mgmt_frame_register = wil_mgmt_frame_register,
++#if defined(OLD_SET_CHANNEL_API)
++      .set_channel = wil_set_monitor_channel,
++#else
++      .set_monitor_channel = wil_set_monitor_channel,
++#endif /* defined(NEW_SET_CHANNEL_API) */
++};
++
++static const u32 cipher_suites[] = {
++      WLAN_CIPHER_SUITE_CCMP, /* keep for debug, TODO: remove */
++      WLAN_CIPHER_SUITE_GCMP,
++};
++
++static void wil_wiphy_init(struct wiphy *wiphy)
++{
++      /* TODO: figure this out */
++      wiphy->max_scan_ssids = 10;
++#if 0
++      wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
++#endif
++      wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
++                               BIT(NL80211_IFTYPE_AP) |
++                               BIT(NL80211_IFTYPE_P2P_CLIENT) |
++                               BIT(NL80211_IFTYPE_P2P_GO) |
++                               BIT(NL80211_IFTYPE_MONITOR);
++
++      wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz;
++
++      /* TODO: figure this out */
++      wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
++
++      wiphy->cipher_suites = cipher_suites;
++      wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
++      wiphy->mgmt_stypes = wil_mgmt_stypes;
++}
++
++struct wireless_dev *wil_cfg80211_init(struct device *dev)
++{
++      int r = 0;
++      struct wireless_dev *wdev;
++
++      wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
++      if (!wdev)
++              return ERR_PTR(-ENOMEM);
++
++      wdev->wiphy = wiphy_new(&wil_cfg80211_ops,
++                              sizeof(struct wil6210_priv));
++      if (!wdev->wiphy) {
++              r = -ENOMEM;
++              goto out;
++      }
++
++      set_wiphy_dev(wdev->wiphy, dev);
++      wil_wiphy_init(wdev->wiphy);
++      r = wiphy_register(wdev->wiphy);
++
++      if (r < 0)
++              goto out_failed_reg;
++
++      return wdev;
++
++out_failed_reg:
++      wiphy_free(wdev->wiphy);
++out:
++      kfree(wdev);
++      return ERR_PTR(r);
++}
++
++void wil_wdev_free(struct wil6210_priv *wil)
++{
++      struct wireless_dev *wdev = wil_to_wdev(wil);
++      struct device *dev = wil_to_dev(wil);
++      dev_info(dev, "%s()\n", __func__);
++
++      if (!wdev)
++              return;
++
++      wiphy_unregister(wdev->wiphy);
++      wiphy_free(wdev->wiphy);
++      kfree(wdev);
++}
+diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.h b/drivers/net/wireless/ath/wil6210/cfg80211.h
+new file mode 100644
+index 0000000..c4f3740
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/cfg80211.h
+@@ -0,0 +1,23 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef __wil_CFG80211_H__
++#define __wil_CFG80211_H__
++
++struct wireless_dev *wil_cfg80211_init(struct device *dev);
++void wil_wdev_free(struct wil6210_priv *wil);
++
++#endif /* __wil_CFG80211_H__ */
+diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
+new file mode 100644
+index 0000000..dc9934b
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/debug.c
+@@ -0,0 +1,42 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include "wil6210.h"
++#include "wmi.h"
++
++static struct {
++      struct wil6210_mbox_hdr hdr;
++      struct wil6210_mbox_hdr_wmi wmi;
++      u32 val;
++} __packed wmi_echo = {
++              .hdr = {
++                              .seq = 1,
++                              .ctx = 1,
++                              .type = 0,
++                              .flags = 1,
++                              .len = 8 + 4,
++              },
++              .wmi = {
++                              .id = WMI_ECHO_CMDID,
++                              .info1 = 0,
++              },
++              .val = 0x12345678,
++};
++
++void send_echo(struct wil6210_priv *wil)
++{
++      wmi_call(wil, &wmi_echo, WMI_ECHO_RSP_EVENTID, NULL, 0, 20);
++}
+diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
+new file mode 100644
+index 0000000..94a54c6
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/debugfs.c
+@@ -0,0 +1,458 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/module.h>
++#include <linux/debugfs.h>
++#include <linux/seq_file.h>
++#include <linux/pci.h>
++#include <linux/rtnetlink.h>
++
++#include "wil6210.h"
++#include "wil6210_rgf.h"
++#include "txrx.h"
++
++static void print_vring(struct seq_file *s, struct wil6210_priv *wil,
++              const char *name, struct vring *vring)
++{
++      void __iomem *x = wmi_addr(wil, vring->hwtail);
++      seq_printf(s, "VRING %s = {\n", name);
++      seq_printf(s, "  pa     = 0x%016llx\n", (unsigned long long)vring->pa);
++      seq_printf(s, "  va     = 0x%p\n", vring->va);
++      seq_printf(s, "  size   = %d\n", vring->size);
++      seq_printf(s, "  swtail = %d\n", vring->swtail);
++      seq_printf(s, "  swhead = %d\n", vring->swhead);
++      seq_printf(s, "  hwtail = [0x%08x] -> ", vring->hwtail);
++      if (x)
++              seq_printf(s, "0x%08x\n", ioread32(x));
++      else
++              seq_printf(s, "???\n");
++      if (vring->va && (vring->size < 1025)) {
++              int i;
++              for (i = 0; i < vring->size; i++) {
++                      struct vring_tx_desc *d = &vring->va[i].tx;
++                      if ((i % 64) == 0 && (i != 0))
++                              seq_printf(s, "\n");
++                      seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
++                                      "S" : (vring->ctx[i] ? "H" : "h"));
++              }
++              seq_printf(s, "\n");
++      }
++      seq_printf(s, "}\n");
++}
++
++static int vring_debugfs_show(struct seq_file *s, void *data)
++{
++      int i;
++      struct wil6210_priv *wil = s->private;
++      print_vring(s, wil, "rx", &wil->vring_rx);
++      for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
++              struct vring *vring = &(wil->vring_tx[i]);
++              if (vring->va) {
++                      char name[10];
++                      snprintf(name, sizeof(name), "tx_%2d", i);
++                      print_vring(s, wil, name, vring);
++              }
++      }
++      return 0;
++}
++
++static int vring_seq_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, vring_debugfs_show, inode->i_private);
++}
++
++static const struct file_operations fops_vring = {
++      .open           = vring_seq_open,
++      .release        = single_release,
++      .read           = seq_read,
++      .llseek         = seq_lseek,
++};
++
++static void print_ring(struct seq_file *s, const char *prefix
++              , void __iomem *off)
++{
++      struct wil6210_priv *wil = s->private;
++      struct wil6210_mbox_ring r;
++      int rsize;
++      int i;
++      memcpy_fromio_32(&r, off, sizeof(r));
++      /*
++       * we just read memory block from NIC. This memory may be
++       * garbage. Check validity before using it.
++       */
++      rsize = r.size / sizeof(struct wil6210_mbox_ring_desc);
++      seq_printf(s, "ring %s = {\n", prefix);
++      seq_printf(s, "  base = 0x%08x\n", r.base);
++      seq_printf(s, "  size = 0x%04x bytes -> %d entries\n", r.size, rsize);
++      seq_printf(s, "  tail = 0x%08x\n", r.tail);
++      seq_printf(s, "  head = 0x%08x\n", r.head);
++      if (r.size % sizeof(struct wil6210_mbox_ring_desc)) {
++              seq_printf(s, "  ??? size is not multiple of %zd, garbage?\n",
++                              sizeof(struct wil6210_mbox_ring_desc));
++              goto out;
++      }
++      if (!wmi_addr(wil, r.base) ||
++                      !wmi_addr(wil, r.tail) ||
++                      !wmi_addr(wil, r.head)) {
++              seq_printf(s, "  ??? pointers are garbage?\n");
++              goto out;
++      }
++      for (i = 0; i < rsize; i++) {
++              struct wil6210_mbox_ring_desc d;
++              struct wil6210_mbox_hdr hdr;
++              size_t delta = i * sizeof(d);
++              void __iomem *x = wil->csr + HOSTADDR(r.base)
++                              + delta;
++              memcpy_fromio_32(&d, x, sizeof(d));
++              seq_printf(s, "  [%2x] %s %s%s 0x%08x", i,
++                              d.sync ? "F" : "E",
++                              (r.tail - r.base == delta) ? "t" : " ",
++                              (r.head - r.base == delta) ? "h" : " ",
++                              d.addr);
++              if (0 == wmi_read_hdr(wil, d.addr, &hdr)) {
++                      seq_printf(s, " -> %04x %04x %04x %02x %02x\n",
++                              hdr.seq, hdr.ctx, hdr.type, hdr.flags, hdr.len);
++                      if (hdr.len <= MAX_MBOXITEM_SIZE) {
++                              int n = 0;
++                              unsigned char printbuf[16 * 3 + 2];
++                              unsigned char databuf[MAX_MBOXITEM_SIZE];
++                              void __iomem *src = wmi_buffer(wil, d.addr)
++                                      + sizeof(struct wil6210_mbox_hdr);
++                              /*
++                               * No need to check @src for validity -
++                               * we already validated @d.addr while
++                               * reading header
++                               */
++                              memcpy_fromio_32(databuf, src, hdr.len);
++                              while (n < hdr.len) {
++                                      int l = min(hdr.len - n, 16);
++                                      hex_dump_to_buffer(databuf + n, l,
++                                                      16, 1, printbuf,
++                                                      sizeof(printbuf),
++                                                      false);
++                                      seq_printf(s, "      : %s\n", printbuf);
++                                      n += l;
++                              }
++                      }
++              } else
++                      seq_printf(s, "\n");
++      }
++ out:
++      seq_printf(s, "}\n");
++}
++
++static int mbox_debugfs_show(struct seq_file *s, void *data)
++{
++      struct wil6210_priv *wil = s->private;
++      print_ring(s, "tx", wil->csr + HOST_MBOX
++                      + offsetof(struct wil6210_mbox_ctl, tx));
++      print_ring(s, "rx", wil->csr + HOST_MBOX
++                      + offsetof(struct wil6210_mbox_ctl, rx));
++      return 0;
++}
++
++static int mbox_seq_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, mbox_debugfs_show, inode->i_private);
++}
++
++static const struct file_operations fops_mbox = {
++      .open           = mbox_seq_open,
++      .release        = single_release,
++      .read           = seq_read,
++      .llseek         = seq_lseek,
++};
++
++static int debugfs_iomem_x32_set(void *data, u64 val)
++{
++      iowrite32(val, (void __iomem *)data);
++      wmb();
++      return 0;
++}
++
++static int debugfs_iomem_x32_get(void *data, u64 *val)
++{
++      *val = ioread32((void __iomem *)data);
++      return 0;
++}
++
++DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
++              debugfs_iomem_x32_set, "0x%08llx\n");
++
++static struct dentry *debugfs_create_iomem_x32(const char *name, mode_t mode,
++              struct dentry *parent, void __iomem *value)
++{
++      return debugfs_create_file(name, mode, parent,
++                      (void * __force)value, &fops_iomem_x32);
++}
++
++static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
++              const char *name, struct dentry *parent, u32 off) {
++      struct dentry *d = debugfs_create_dir(name, parent);
++      if (IS_ERR_OR_NULL(d))
++              return -ENODEV;
++      debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUGO, d,
++                      wil->csr + off);
++      debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUGO, d,
++                      wil->csr + off + 4);
++      debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUGO, d,
++                      wil->csr + off + 8);
++      debugfs_create_iomem_x32("ICS", S_IWUGO, d,
++                      wil->csr + off + 12);
++      debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUGO, d,
++                      wil->csr + off + 16);
++      debugfs_create_iomem_x32("IMS", S_IWUGO, d,
++                      wil->csr + off + 20);
++      debugfs_create_iomem_x32("IMC", S_IWUGO, d,
++                      wil->csr + off + 24);
++      return 0;
++}
++
++static u32 mem_addr;
++
++static int memread_debugfs_show(struct seq_file *s, void *data)
++{
++      struct wil6210_priv *wil = s->private;
++      void __iomem *a = wmi_buffer(wil, mem_addr);
++      if (a)
++              seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a));
++      else
++              seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);
++      return 0;
++}
++
++static int memread_seq_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, memread_debugfs_show, inode->i_private);
++}
++
++static const struct file_operations fops_memread = {
++      .open           = memread_seq_open,
++      .release        = single_release,
++      .read           = seq_read,
++      .llseek         = seq_lseek,
++};
++
++static int default_open(struct inode *inode, struct file *file)
++{
++      if (inode->i_private)
++              file->private_data = inode->i_private;
++
++      return 0;
++}
++
++static ssize_t read_file_ioblob(struct file *file, char __user *user_buf,
++                            size_t count, loff_t *ppos)
++{
++      enum { max_count = 4096 };
++      struct debugfs_blob_wrapper *blob = file->private_data;
++      loff_t pos = *ppos;
++      size_t available = blob->size;
++      void *buf;
++      size_t ret;
++      if (pos < 0)
++              return -EINVAL;
++      if (pos >= available || !count)
++              return 0;
++      if (count > available - pos)
++              count = available - pos;
++      if (count > max_count)
++              count = max_count;
++      buf = kmalloc(count, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++      memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + pos,
++                      count);
++      ret = copy_to_user(user_buf, buf, count);
++      kfree(buf);
++      if (ret == count)
++              return -EFAULT;
++      count -= ret;
++      *ppos = pos + count;
++      return count;
++}
++
++static const struct file_operations fops_ioblob = {
++      .read =         read_file_ioblob,
++      .open =         default_open,
++      .llseek =       default_llseek,
++};
++
++static struct dentry *debugfs_create_ioblob(const char *name, mode_t mode,
++                                 struct dentry *parent,
++                                 struct debugfs_blob_wrapper *blob)
++{
++      return debugfs_create_file(name, mode, parent, blob, &fops_ioblob);
++}
++/*---reset---*/
++static ssize_t write_file_reset(struct file *file, const char __user *buf,
++                        size_t len, loff_t *ppos)
++{
++      struct wil6210_priv *wil = file->private_data;
++      struct net_device *ndev = wil_to_ndev(wil);
++      /**
++       * BUG:
++       * this code does NOT sync device state with the rest of system
++       * use with care, debug only!!!
++       */
++      rtnl_lock();
++      dev_close(ndev);
++      ndev->flags &= ~IFF_UP;
++      rtnl_unlock();
++      wil_reset(wil);
++      return len;
++}
++
++static const struct file_operations fops_reset = {
++      .write =        write_file_reset,
++      .open =         default_open,
++};
++/*---------Tx descriptor------------*/
++
++static u32 dbg_txdesc_index; /* = 0; */
++
++static int txdesc_debugfs_show(struct seq_file *s, void *data)
++{
++      struct wil6210_priv *wil = s->private;
++      struct vring *vring = &(wil->vring_tx[0]);
++      if (!vring->va) {
++              seq_printf(s, "No Tx VRING\n");
++              return 0;
++      }
++      if (dbg_txdesc_index < vring->size) {
++              struct vring_tx_desc *d = &(vring->va[dbg_txdesc_index].tx);
++              u32 *u = (u32 *)d;
++              struct sk_buff *skb = vring->ctx[dbg_txdesc_index];
++              seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
++              seq_printf(s, "  MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
++                              u[0], u[1], u[2], u[3]);
++              seq_printf(s, "  DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
++                              u[4], u[5], u[6], u[7]);
++              seq_printf(s, "  SKB = %p\n", skb);
++              if (skb) {
++                      unsigned char printbuf[16 * 3 + 2];
++                      int i = 0;
++                      int len = skb_headlen(skb);
++                      void *p = skb->data;
++                      seq_printf(s, "    len = %d\n", len);
++                      while (i < len) {
++                              int l = min(len - i, 16);
++                              hex_dump_to_buffer(p + i, l, 16, 1
++                                      , printbuf, sizeof(printbuf)
++                                      , false);
++                              seq_printf(s, "      : %s\n", printbuf);
++                              i += l;
++                      }
++              }
++              seq_printf(s, "}\n");
++      } else {
++              seq_printf(s, "TxDesc index (%d) >= size (%d)\n",
++                              dbg_txdesc_index, vring->size);
++      }
++      return 0;
++}
++
++static int txdesc_seq_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, txdesc_debugfs_show, inode->i_private);
++}
++
++static const struct file_operations fops_txdesc = {
++      .open           = txdesc_seq_open,
++      .release        = single_release,
++      .read           = seq_read,
++      .llseek         = seq_lseek,
++};
++/*----------------*/
++int wil6210_debugfs_init(struct wil6210_priv *wil)
++{
++      struct dentry *pseudo;
++      struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
++                      wil_to_wiphy(wil)->debugfsdir);
++
++      if (IS_ERR_OR_NULL(dbg))
++              return -ENODEV;
++      debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox);
++      debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring);
++      debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc);
++      debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUGO, dbg,
++                      &dbg_txdesc_index);
++      wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg
++                      , HOSTADDR(RGF_USER_USER_ICR));
++      wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg
++                      , HOSTADDR(RGF_DMA_EP_TX_ICR));
++      wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg
++                      , HOSTADDR(RGF_DMA_EP_RX_ICR));
++      wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg
++                      , HOSTADDR(RGF_DMA_EP_MISC_ICR));
++      pseudo = debugfs_create_dir("PSEUDO_ISR", dbg);
++      if (!IS_ERR_OR_NULL(pseudo)) {
++              debugfs_create_iomem_x32("CAUSE", S_IRUGO, pseudo,
++                              wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE));
++              debugfs_create_iomem_x32("MASK_SW", S_IRUGO, pseudo,
++                      wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
++              debugfs_create_iomem_x32("MASK_FW", S_IRUGO, pseudo,
++                      wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW));
++      }
++      {
++              struct dentry *rst = debugfs_create_dir("Reset", dbg);
++              if (!IS_ERR_OR_NULL(rst)) {
++                      debugfs_create_iomem_x32("vec0", S_IRUGO | S_IWUGO, rst,
++                      wil->csr + HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_0));
++                      debugfs_create_iomem_x32("vec1", S_IRUGO | S_IWUGO, rst,
++                      wil->csr + HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_1));
++                      debugfs_create_iomem_x32("vec2", S_IRUGO | S_IWUGO, rst,
++                      wil->csr + HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_2));
++                      debugfs_create_iomem_x32("vec3", S_IRUGO | S_IWUGO, rst,
++                      wil->csr + HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_3));
++              }
++      }
++      debugfs_create_u32("mem_addr", S_IRUGO | S_IWUGO, dbg, &mem_addr);
++      debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
++      debugfs_create_file("reset", S_IWUGO, dbg, wil, &fops_reset);
++
++      wil->rgf_blob.data = (void * __force)wil->csr + 0;
++      wil->rgf_blob.size = 0xa000;
++      debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob);
++      wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000;
++      wil->fw_code_blob.size = 0x40000;
++      debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg,
++                      &wil->fw_code_blob);
++      wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000;
++      wil->fw_data_blob.size = 0x8000;
++      debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg,
++                      &wil->fw_data_blob);
++      wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000;
++      wil->fw_peri_blob.size = 0x18000;
++      debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg,
++                      &wil->fw_peri_blob);
++      wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000;
++      wil->uc_code_blob.size = 0x8000;
++      debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg,
++                      &wil->uc_code_blob);
++      wil->uc_data_blob.data = (void * __force)wil->csr + 0xa8000;
++      wil->uc_data_blob.size = 0x2000;
++      debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg,
++                      &wil->uc_data_blob);
++
++      return 0;
++}
++
++void wil6210_debugfs_remove(struct wil6210_priv *wil)
++{
++      debugfs_remove_recursive(wil->debug);
++      wil->debug = NULL;
++}
+diff --git a/drivers/net/wireless/ath/wil6210/ifc.sh b/drivers/net/wireless/ath/wil6210/ifc.sh
+new file mode 100755
+index 0000000..40dcc21
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/ifc.sh
+@@ -0,0 +1,20 @@
++#!/bin/bash
++
++#
++# Print and set $WLAN to the name of the
++# network interface for the 'wil6210' driver
++#
++
++DRV="wil6210"
++
++for f in /sys/class/net/*; do {
++      drv=`readlink $f/device/driver`;
++      drv=${drv##.*/}
++      if [[ $drv == $DRV ]]; then {
++              ifc=${f#/sys/class/net/}
++              echo $ifc
++              export WLAN=$ifc
++              break
++      } ; fi
++} ; done
++
+diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
+new file mode 100644
+index 0000000..ae0d21f
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/interrupt.c
+@@ -0,0 +1,324 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/interrupt.h>
++
++#include "wil6210.h"
++#include "wil6210_rgf.h"
++
++/**
++ * Theory of operation:
++ *
++ * There is ISR pseudo-cause register,
++ * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE
++ * Its bits represents OR'ed bits from 3 real ISR registers:
++ * TX, RX, and MISC.
++ *
++ * Registers may be configured to either "write 1 to clear" or
++ * "clear on read" mode
++ *
++ * When handling interrupt, one have to mask/unmask interrupts for the
++ * real ISR registers, or hardware may malfunction.
++ *
++ */
++
++#define WIL6210_IRQ_DISABLE   (0xFFFFFFFFUL)
++#define WIL6210_IMC_RX                BIT_DMA_EP_RX_ICR_RX_DONE
++#define WIL6210_IMC_TX                (BIT_DMA_EP_TX_ICR_TX_DONE | \
++                              BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
++#define WIL6210_IMC_MISC      (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT)
++
++static inline u32 ioread32_and_clear(void __iomem *addr)
++{
++      u32 x = ioread32(addr);
++#if !defined(WIL6210_ISR_COR)
++      iowrite32(x, addr);
++#endif
++      return x;
++}
++
++static void wil6210_mask_irq(struct wil6210_priv *wil)
++{
++#if 0
++      wil_info(wil, "%s()\n", __func__);
++#endif
++      clear_bit(wil_status_irqen, &wil->status);
++      iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_RX_ICR) +
++                      offsetof(struct RGF_ICR, IMS));
++      iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_TX_ICR) +
++                      offsetof(struct RGF_ICR, IMS));
++      iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_MISC_ICR) +
++                      offsetof(struct RGF_ICR, IMS));
++}
++
++static void wil6210_unmask_irq(struct wil6210_priv *wil)
++{
++      iowrite32(WIL6210_IMC_RX, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_RX_ICR) +
++                      offsetof(struct RGF_ICR, IMC));
++      iowrite32(WIL6210_IMC_TX, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_TX_ICR) +
++                      offsetof(struct RGF_ICR, IMC));
++      iowrite32(WIL6210_IMC_MISC, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_MISC_ICR) +
++                      offsetof(struct RGF_ICR, IMC));
++      set_bit(wil_status_irqen, &wil->status);
++}
++
++void wil6210_disable_irq(struct wil6210_priv *wil)
++{
++      wil6210_mask_irq(wil);
++}
++
++void wil6210_enable_irq(struct wil6210_priv *wil)
++{
++#if 0
++      wil_info(wil, "%s()\n", __func__);
++#endif
++#if defined(WIL6210_ISR_COR)
++      /* configure to Clear-On-Read */
++      iowrite32(0xFFFFFFFFUL, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_RX_ICR) +
++                      offsetof(struct RGF_ICR, ICC));
++      iowrite32(0xFFFFFFFFUL, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_TX_ICR) +
++                      offsetof(struct RGF_ICR, ICC));
++      iowrite32(0xFFFFFFFFUL, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_MISC_ICR) +
++                      offsetof(struct RGF_ICR, ICC));
++#else
++      iowrite32(0, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_RX_ICR) +
++                      offsetof(struct RGF_ICR, ICC));
++      iowrite32(0, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_TX_ICR) +
++                      offsetof(struct RGF_ICR, ICC));
++      iowrite32(0, wil->csr +
++                      HOSTADDR(RGF_DMA_EP_MISC_ICR) +
++                      offsetof(struct RGF_ICR, ICC));
++#endif
++
++      wil6210_unmask_irq(wil);
++}
++
++static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
++{
++      struct wil6210_priv *wil = cookie;
++      u32 isr = wil->isr_rx;
++#if 0
++      wil_info(wil, "ISR RX 0x%08x\n", isr);
++#endif
++      if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
++#if 0
++              wil_info(wil, "RX done\n");
++#endif
++              isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
++              rx_handle(wil);
++      }
++      if (isr)
++              wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
++{
++      struct wil6210_priv *wil = cookie;
++      u32 isr = wil->isr_tx;
++#if 0
++      wil_info(wil, "ISR TX 0x%08x\n", isr);
++#endif
++      if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
++              int i;
++#if 0
++              wil_info(wil, "TX done\n");
++#endif
++              isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
++              for (i = 0; i < 24; i++) {
++                      u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
++                      if (isr & mask) {
++                              isr &= ~mask;
++#if 0
++                              wil_info(wil, "TX done(%i)\n", i);
++#endif
++                              tx_complete(wil, i);
++                      }
++              }
++      }
++      if (isr)
++              wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
++{
++      struct wil6210_priv *wil = cookie;
++      u32 isr = wil->isr_misc;
++#if 0
++      wil_info(wil, "ISR MISC 0x%08x\n", isr);
++#endif
++      if (isr & ISR_MISC_FW_READY) {
++              wil_info(wil, "IRQ: FW ready\n");
++              /**
++               * Actual FW ready indicated by the
++               * WMI_FW_READY_EVENTID
++               */
++              isr &= ~ISR_MISC_FW_READY;
++      }
++      if (isr & ISR_MISC_MBOX_EVT) {
++#if 0
++              wil_info(wil, "MBOX event\n");
++#endif
++              wmi_recv_cmd(wil);
++              isr &= ~ISR_MISC_MBOX_EVT;
++      }
++      if (isr)
++              wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
++      return IRQ_HANDLED;
++}
++
++/**
++ * thread IRQ handler
++ */
++static irqreturn_t wil6210_thread_irq(int irq, void *cookie)
++{
++      struct wil6210_priv *wil = cookie;
++#if 0
++      wil_info(wil, "Thread IRQ\n");
++#endif
++      /* Discover real IRQ cause */
++      if (wil->isr_misc) {
++              wil6210_irq_misc(irq, cookie);
++              wil->isr_misc = 0;
++      }
++      wil6210_unmask_irq(wil);
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t wil6210_hardirq(int irq, void *cookie)
++{
++      irqreturn_t rc = IRQ_HANDLED;
++      struct wil6210_priv *wil = cookie;
++      u32 pseudo_cause = ioread32(wil->csr +
++                      HOSTADDR(RGF_DMA_PSEUDO_CAUSE));
++      /**
++       * pseudo_cause is Clear-On-Read, no need to ACK
++       */
++      if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff))
++              return IRQ_NONE;
++      /* FIXME: IRQ mask debug */
++      if (!test_bit(wil_status_irqen, &wil->status)) {
++              u32 icm_rx = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_RX_ICR) +
++                              offsetof(struct RGF_ICR, ICM));
++              u32 icr_rx = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_RX_ICR) +
++                              offsetof(struct RGF_ICR, ICR));
++              u32 imv_rx = ioread32(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_RX_ICR) +
++                              offsetof(struct RGF_ICR, IMV));
++              u32 icm_tx = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_TX_ICR) +
++                              offsetof(struct RGF_ICR, ICM));
++              u32 icr_tx = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_TX_ICR) +
++                              offsetof(struct RGF_ICR, ICR));
++              u32 imv_tx = ioread32(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_TX_ICR) +
++                              offsetof(struct RGF_ICR, IMV));
++              u32 icm_misc = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_MISC_ICR) +
++                              offsetof(struct RGF_ICR, ICM));
++              u32 icr_misc = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_MISC_ICR) +
++                              offsetof(struct RGF_ICR, ICR));
++              u32 imv_misc = ioread32(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_MISC_ICR) +
++                              offsetof(struct RGF_ICR, IMV));
++              wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n"
++                              "Rx   icm:icr:imv 0x%08x 0x%08x 0x%08x\n"
++                              "Tx   icm:icr:imv 0x%08x 0x%08x 0x%08x\n"
++                              "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n",
++                              pseudo_cause,
++                              icm_rx, icr_rx, imv_rx,
++                              icm_tx, icr_tx, imv_tx,
++                              icm_misc, icr_misc, imv_misc);
++              return IRQ_NONE;
++      }
++      wil6210_mask_irq(wil);
++      /* Discover real IRQ cause */
++      /* All ISR regs configured Clear-On-Read, no need to ACK */
++      if (pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) {
++              wil->isr_rx = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_RX_ICR) +
++                              offsetof(struct RGF_ICR, ICR));
++      }
++      if (pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) {
++              wil->isr_tx = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_TX_ICR) +
++                              offsetof(struct RGF_ICR, ICR));
++      }
++      if (pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) {
++              wil->isr_misc = ioread32_and_clear(wil->csr +
++                              HOSTADDR(RGF_DMA_EP_MISC_ICR) +
++                              offsetof(struct RGF_ICR, ICR));
++              rc = IRQ_WAKE_THREAD;
++      }
++      /* process what to be done right in hard IRQ */
++      if (wil->isr_rx) {
++              if (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)
++                      rc = IRQ_WAKE_THREAD;
++              else
++                      wil->isr_rx = 0;
++      }
++      if (wil->isr_tx) {
++              if (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)
++                      rc = IRQ_WAKE_THREAD;
++              else
++                      wil->isr_tx = 0;
++      }
++      /* if thread is requested, it will unmask IRQ */
++      if (rc != IRQ_WAKE_THREAD)
++              wil6210_unmask_irq(wil);
++#if 0
++      wil_info(wil, "Hard IRQ 0x%08x\n", pseudo_cause);
++#endif
++      return rc;
++}
++
++int wil6210_init_irq(struct wil6210_priv *wil, int irq)
++{
++      int rc;
++      wil_info(wil, "%s()\n", __func__);
++      /* TODO: handle multiple MSI */
++      rc = request_threaded_irq(irq,
++                      wil6210_hardirq, wil6210_thread_irq,
++                      wil->n_msi ? 0 : IRQF_SHARED,
++                      WIL_NAME, wil);
++      if (rc)
++              return rc;
++      wil6210_enable_irq(wil);
++      return 0;
++}
++
++void wil6210_fini_irq(struct wil6210_priv *wil, int irq)
++{
++      wil_info(wil, "%s()\n", __func__);
++      wil6210_disable_irq(wil);
++      free_irq(irq, wil);
++}
+diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
+new file mode 100644
+index 0000000..a56038f
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/main.c
+@@ -0,0 +1,336 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/sched.h>
++#include <linux/ieee80211.h>
++#include <linux/wireless.h>
++#include <linux/slab.h>
++#include <linux/moduleparam.h>
++#include <linux/if_arp.h>
++
++#include "wil6210.h"
++#include "wil6210_rgf.h"
++
++/**
++ * We have to read/write to/from NIC in 32-bit chunks;
++ * otherwise it is not work on 64-bit platform
++ */
++void memcpy_fromio_32(void *dst, const volatile void __iomem *src, size_t count)
++{
++      u32 *d = dst;
++      const volatile u32 __iomem *s = src;
++      /* size_t is unsigned, if (count%4 != 0) it will wrap */
++      for (count += 4; count > 4; count -= 4)
++              *d++ = readl(s++);
++}
++
++void memcpy_toio_32(volatile void __iomem *dst, const void *src, size_t count)
++{
++      volatile u32 __iomem *d = dst;
++      const u32 *s = src;
++      for (count += 4; count > 4; count -= 4)
++              writel(*s++, d++);
++}
++
++/* debug */
++void send_echo(struct wil6210_priv *wil);
++
++static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
++{
++      int i;
++      struct net_device *ndev = wil_to_ndev(wil);
++      struct wireless_dev *wdev = wil->wdev;
++      wil_info(wil, "%s()\n", __func__);
++      wil_link_off(wil);
++      clear_bit(wil_status_fwconnected, &wil->status);
++      switch (wdev->sme_state) {
++      case CFG80211_SME_CONNECTED:
++              cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
++                              NULL, 0, GFP_KERNEL);
++              break;
++      case CFG80211_SME_CONNECTING:
++              cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
++                              WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL);
++              break;
++      default:
++              ;
++      }
++      for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
++              vring_fini_tx(wil, i);
++}
++
++static void connect_timer_fn(unsigned long x)
++{
++      struct wil6210_priv *wil = (void *)x;
++      wil_info(wil, "Connect timeout\n");
++      _wil6210_disconnect(wil, NULL);
++}
++
++int wil_priv_init(struct wil6210_priv *wil)
++{
++      wil_info(wil, "%s()\n", __func__);
++
++      mutex_init(&wil->mutex);
++      mutex_init(&wil->wmi_mutex);
++      init_completion(&wil->wmi_ready);
++      wil->pending_connect_cid = -1;
++      setup_timer(&wil->connect_timer, connect_timer_fn,
++                      (unsigned long)wil);
++      INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker);
++      INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
++      INIT_LIST_HEAD(&wil->pending_wmi_ev);
++      spin_lock_init(&wil->wmi_ev_lock);
++      wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
++      if (!wil->wmi_wq)
++              return -EAGAIN;
++      wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect");
++      if (!wil->wmi_wq_conn) {
++              destroy_workqueue(wil->wmi_wq);
++              return -EAGAIN;
++      }
++      /* make shadow copy of registers that should not change on run time */
++      memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
++                      sizeof(struct wil6210_mbox_ctl));
++      return 0;
++}
++
++void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
++{
++      del_timer_sync(&wil->connect_timer);
++      _wil6210_disconnect(wil, bssid);
++}
++
++void wil_priv_deinit(struct wil6210_priv *wil)
++{
++      wil6210_disconnect(wil, NULL);
++      wmi_event_flush(wil);
++      destroy_workqueue(wil->wmi_wq_conn);
++      destroy_workqueue(wil->wmi_wq);
++}
++
++static void wil_target_reset(struct wil6210_priv *wil)
++{
++      u32 x;
++      u32 off;
++      wil_info(wil, "Resetting...\n");
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_MASK_0);
++      x = ioread32(wil->csr + off);
++      x |= BIT(6); /* hpal_perst_from_pad_src_n_mask */
++      iowrite32(x, wil->csr + off);
++      x |= BIT(7); /* car_perst_rst_src_n_mask */
++      iowrite32(x, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_MAC_CPU_0);
++      iowrite32(BIT(1), wil->csr + off); /* mac_cpu_man_rst */
++
++      off = HOSTADDR(RGF_USER_USER_CPU_0);
++      iowrite32(BIT(1), wil->csr + off); /* user_cpu_man_rst */
++
++      msleep(100);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_2);
++      iowrite32(0xFE000000, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_1);
++      iowrite32(0x0000003F, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_3);
++      iowrite32(0x00000170, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_0);
++      iowrite32(0xFFE7FC00, wil->csr + off);
++
++      msleep(100);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_3);
++      iowrite32(0, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_2);
++      iowrite32(0, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_1);
++      iowrite32(0, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_0);
++      iowrite32(0, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_3);
++      iowrite32(0x00000001, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_2);
++      iowrite32(0x00000080, wil->csr + off);
++
++      off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_0);
++      iowrite32(0, wil->csr + off);
++
++      msleep(2000);
++
++      off = HOSTADDR(RGF_USER_USER_CPU_0);
++      iowrite32(BIT(0), wil->csr + off); /* user_cpu_man_de_rst */
++
++      msleep(2000);
++      wil_info(wil, "Reset completed\n");
++}
++
++/*
++ * We reset all the structures, and we reset the UMAC.
++ * After calling this routine, you're expected to reload
++ * the firmware.
++ */
++int wil_reset(struct wil6210_priv *wil)
++{
++      wil_info(wil, "%s()\n", __func__);
++      wil6210_disconnect(wil, NULL);
++      wmi_event_flush(wil);
++      flush_workqueue(wil->wmi_wq);
++      flush_workqueue(wil->wmi_wq_conn);
++      wil6210_disable_irq(wil);
++      wil->status = 0;
++      /* TODO: put MAC in reset */
++      wil_target_reset(wil);
++      /* init after reset */
++      wil->pending_connect_cid = -1;
++      INIT_COMPLETION(wil->wmi_ready);
++      /* make shadow copy of registers that should not change on run time */
++      memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
++                      sizeof(struct wil6210_mbox_ctl));
++      /* TODO: release MAC reset */
++      wil6210_enable_irq(wil);
++      /* we just started MAC, wait for FW ready */
++      {
++              unsigned long to = msecs_to_jiffies(1000);
++              unsigned long left = wait_for_completion_timeout(
++                              &wil->wmi_ready, to);
++              if (0 == left) {
++                      wil_err(wil, "Firmware not ready\n");
++                      return -ETIME;
++              } else {
++                      wil_info(wil, "FW ready after %d ms\n",
++                                      jiffies_to_msecs(to-left));
++              }
++      }
++      return 0;
++}
++
++
++void wil_link_on(struct wil6210_priv *wil)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      wil_info(wil, "%s()\n", __func__);
++      netif_carrier_on(ndev);
++      netif_tx_wake_all_queues(ndev);
++}
++
++void wil_link_off(struct wil6210_priv *wil)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      wil_info(wil, "%s()\n", __func__);
++      netif_tx_stop_all_queues(ndev);
++      netif_carrier_off(ndev);
++}
++
++static int __wil_up(struct wil6210_priv *wil)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      struct wireless_dev *wdev = wil->wdev;
++      int rc = wil_reset(wil);
++      if (rc)
++              return rc;
++      /* Apply profile in the following order: */
++      /* MAC address - pre-requisite for other commands */
++      wil6210_set_mac_address(wil, ndev->dev_addr);
++      /* Interface type. Set up beaconing if required. After MAC */
++      {
++              u16 wmi_nettype = iftype_nl2wmi(wdev->iftype);
++              int bi;
++              /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
++              wmi_nettype = iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
++              switch (wdev->iftype) {
++              case NL80211_IFTYPE_STATION:
++                      wil_info(wil, "type: STATION\n");
++                      bi = 0;
++                      ndev->type = ARPHRD_ETHER;
++                      break;
++              case NL80211_IFTYPE_AP:
++                      wil_info(wil, "type: AP\n");
++                      bi = 100;
++                      ndev->type = ARPHRD_ETHER;
++                      break;
++              case NL80211_IFTYPE_P2P_CLIENT:
++                      wil_info(wil, "type: P2P_CLIENT\n");
++                      bi = 0;
++                      ndev->type = ARPHRD_ETHER;
++                      break;
++              case NL80211_IFTYPE_P2P_GO:
++                      wil_info(wil, "type: P2P_GO\n");
++                      bi = 100;
++                      ndev->type = ARPHRD_ETHER;
++                      break;
++              case NL80211_IFTYPE_MONITOR:
++                      wil_info(wil, "type: Monitor\n");
++                      bi = 0;
++                      ndev->type = ARPHRD_IEEE80211_RADIOTAP;
++                      /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
++                      break;
++              default:
++                      return -EOPNOTSUPP;
++              }
++              rc = wil6210_set_bcon(wil, bi, wmi_nettype);
++              if (rc)
++                      return rc;
++      }
++      /* Rx VRING. After MAC and beacon */
++      rx_init(wil);
++      return 0;
++}
++
++int wil_up(struct wil6210_priv *wil)
++{
++      int ret;
++      wil_info(wil, "%s()\n", __func__);
++
++      mutex_lock(&wil->mutex);
++      ret = __wil_up(wil);
++      mutex_unlock(&wil->mutex);
++
++      return ret;
++}
++
++static int __wil_down(struct wil6210_priv *wil)
++{
++      if (wil->scan_request) {
++              cfg80211_scan_done(wil->scan_request, true);
++              wil->scan_request = NULL;
++      }
++      wil6210_disconnect(wil, NULL);
++      rx_fini(wil);
++      return 0;
++}
++
++int wil_down(struct wil6210_priv *wil)
++{
++      int ret;
++      wil_info(wil, "%s()\n", __func__);
++
++      mutex_lock(&wil->mutex);
++      ret = __wil_down(wil);
++      mutex_unlock(&wil->mutex);
++
++      return ret;
++}
++
+diff --git a/drivers/net/wireless/ath/wil6210/memdump.sh b/drivers/net/wireless/ath/wil6210/memdump.sh
+new file mode 100755
+index 0000000..5ad2ac3
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/memdump.sh
+@@ -0,0 +1,9 @@
++#!/bin/bash
++### parameter - memdump prefix
++P=$1
++### where is wil6210 debugfs?
++D=$(find /sys/kernel/debug/ieee80211/ -name wil6210)
++
++for f in fw_data fw_peri uc_data; do {
++  cat $D/blob_${f} > ${P}${f}
++} done
+diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
+new file mode 100644
+index 0000000..721753f
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/netdev.c
+@@ -0,0 +1,166 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/module.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/slab.h>
++
++#include "wil6210.h"
++#include "cfg80211.h"
++
++static int wil_open(struct net_device *ndev)
++{
++      struct wil6210_priv *wil = ndev_to_wil(ndev);
++      wil_info(wil, "%s()\n", __func__);
++
++      return wil_up(wil);
++}
++
++static int wil_stop(struct net_device *ndev)
++{
++      struct wil6210_priv *wil = ndev_to_wil(ndev);
++      wil_info(wil, "%s()\n", __func__);
++
++      return wil_down(wil);
++}
++
++/*
++ * AC to queue mapping
++ *
++ * AC_VO -> queue 3
++ * AC_VI -> queue 2
++ * AC_BE -> queue 1
++ * AC_BK -> queue 0
++ */
++static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
++#if 0
++static int wil_tid_to_queue(u16 tid)
++{
++      if (tid >= ARRAY_SIZE(wil_1d_to_queue))
++              return -EINVAL;
++
++      return wil_1d_to_queue[tid];
++}
++#endif
++static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb)
++{
++#ifdef WIL_DEBUG_TXRX
++      struct wil6210_priv *wil = ndev_to_wil(ndev);
++#endif
++      u16 ret;
++      skb->priority = cfg80211_classify8021d(skb);
++
++      ret = wil_1d_to_queue[skb->priority];
++#ifdef WIL_DEBUG_TXRX
++      wil_info(wil, "%s() %d -> %d\n", __func__,
++                      (int)skb->priority, (int)ret);
++#endif
++      return ret;
++}
++
++static const struct net_device_ops wil_netdev_ops = {
++      .ndo_open               = wil_open,
++      .ndo_stop               = wil_stop,
++      .ndo_start_xmit         = wil_start_xmit,
++      .ndo_select_queue       = wil_select_queue,
++      .ndo_set_mac_address    = eth_mac_addr,
++      .ndo_validate_addr      = eth_validate_addr,
++};
++
++void *wil_if_alloc(struct device *dev, void __iomem *csr)
++{
++      struct net_device *ndev;
++      struct wireless_dev *wdev;
++      struct wil6210_priv *wil;
++      int ret = 0;
++      wdev = wil_cfg80211_init(dev);
++      if (IS_ERR(wdev)) {
++              dev_err(dev, "wil_cfg80211_init failed\n");
++              return wdev;
++      }
++
++      wil = wdev_to_wil(wdev);
++      wil->csr = csr;
++      wil->wdev = wdev;
++
++      ret = wil_priv_init(wil);
++      if (ret) {
++              dev_err(dev, "wil_priv_init failed\n");
++              goto out_wdev;
++      }
++
++      wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */
++      /* default monitor channel */
++      wil->monitor_chan = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels;
++
++      ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1);
++      if (!ndev) {
++              dev_err(dev, "alloc_netdev_mqs failed\n");
++              ret = -ENOMEM;
++              goto out_priv;
++      }
++
++      ndev->netdev_ops = &wil_netdev_ops;
++      ndev->ieee80211_ptr = wdev;
++      SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
++      wdev->netdev = ndev;
++
++      wil_link_off(wil);
++      return wil;
++
++/*out_profile:*/
++      free_netdev(ndev);
++
++ out_priv:
++      wil_priv_deinit(wil);
++
++ out_wdev:
++      wil_wdev_free(wil);
++      return ERR_PTR(ret);
++}
++
++void wil_if_free(struct wil6210_priv *wil)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      if (!ndev)
++              return;
++
++      free_netdev(ndev);
++      wil_priv_deinit(wil);
++      wil_wdev_free(wil);
++}
++
++int wil_if_add(struct wil6210_priv *wil)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      int ret;
++
++      ret = register_netdev(ndev);
++      if (ret < 0) {
++              dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret);
++              return ret;
++      }
++      wil_link_off(wil);
++
++      return 0;
++}
++
++void wil_if_remove(struct wil6210_priv *wil)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      unregister_netdev(ndev);
++}
+diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
+new file mode 100644
+index 0000000..21d806b
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
+@@ -0,0 +1,239 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/netdevice.h>
++#include <linux/debugfs.h>
++#include <linux/pci.h>
++#include <linux/moduleparam.h>
++
++#include "wil6210.h"
++
++static int use_msi; /* TODO: set default to 1 or 3 when hardware fixed */
++module_param(use_msi, int, S_IRUGO);
++MODULE_PARM_DESC(use_msi,
++              " Use MSI interrupt: 0 - (default) - don't, 1 - single, or 3");
++
++/* debug */
++void send_echo(struct wil6210_priv *wil);
++
++/* Bus ops */
++static int if_pcie_enable(struct wil6210_priv *wil)
++{
++      struct pci_dev *pdev = wil->pdev;
++      int rc;
++      wil_info(wil, "%s()\n", __func__);
++      /*
++       * how many MSI interrupts to request?
++       */
++      wil->n_msi = use_msi;
++      /* TODO: how to deal with 3 MSI? */
++      if (wil->n_msi) {
++              wil_info(wil, "Setup %d MSI interrupts\n", use_msi);
++              rc = pci_enable_msi_block(pdev, wil->n_msi);
++              if (rc) {
++                      wil_err(wil, "pci_enable_msi failed, use INTx\n");
++                      wil->n_msi = 0;
++              }
++      } else {
++              wil_info(wil, "MSI interrupts disabled, use INTx\n");
++      }
++      rc = wil6210_init_irq(wil, pdev->irq);
++      if (rc)
++              return rc;
++      /* need reset here to obtain MAC */
++      rc = wil_reset(wil);
++      if (rc)
++              goto release_irq;
++      pci_set_master(pdev);
++
++      return 0;
++ release_irq:
++      wil6210_fini_irq(wil, pdev->irq);
++      /* safe to call if no MSI */
++      pci_disable_msi(pdev);
++      return rc;
++}
++
++static int if_pcie_disable(struct wil6210_priv *wil)
++{
++      struct pci_dev *pdev = wil->pdev;
++      struct device *dev = wil_to_dev(wil);
++      dev_info(dev, "%s()\n", __func__);
++
++      pci_clear_master(pdev);
++      /* disable IRQ */
++      /* release IRQ */
++      wil6210_fini_irq(wil, pdev->irq);
++      /* safe to call if no MSI */
++      pci_disable_msi(pdev);
++      /* disable HW */
++#if 0
++      wil_reset(wil);
++#endif
++      return 0;
++}
++
++static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
++{
++      struct wil6210_priv *wil;
++      struct device *dev = &pdev->dev;
++      void __iomem *csr;
++      int rc;
++      dev_info(dev, "%s()\n", __func__);
++
++      /* check HW */
++      dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n",
++               (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
++      if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
++              dev_err(&pdev->dev, "Not " WIL_NAME "? "
++                              "BAR0 size is %lu while expecting %lu\n",
++                              (unsigned long)pci_resource_len(pdev, 0),
++                              WIL6210_MEM_SIZE);
++              return -ENODEV;
++      }
++      rc = pci_enable_device(pdev);
++      if (rc) {
++              dev_err(&pdev->dev, "pci_enable_device failed\n");
++              return -ENODEV;
++      }
++      /* rollback to err_disable_pdev */
++
++      rc = pci_request_region(pdev, 0, WIL_NAME);
++      if (rc) {
++              dev_err(&pdev->dev, "pci_request_region failed\n");
++              goto err_disable_pdev;
++      }
++      /* rollback to err_release_reg */
++      csr = pci_ioremap_bar(pdev, 0);
++      if (!csr) {
++              dev_err(&pdev->dev, "pci_ioremap_bar failed\n");
++              rc = -ENODEV;
++              goto err_release_reg;
++      }
++      /* rollback to err_iounmap */
++      dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0],
++                      csr);
++
++      wil = wil_if_alloc(dev, csr);
++      if (IS_ERR(wil)) {
++              rc = (int)PTR_ERR(wil);
++              dev_err(dev, "wil_if_alloc failed: %d\n", rc);
++              goto err_iounmap;
++      }
++      /* rollback to if_free */
++
++      pci_set_drvdata(pdev, wil);
++      wil->pdev = pdev;
++
++      /* FW should raise IRQ when ready */
++      rc = if_pcie_enable(wil);
++      if (rc) {
++              wil_err(wil, "Enable device failed\n");
++              goto if_free;
++      }
++      /* rollback to bus_disable */
++
++      rc = wil_if_add(wil);
++      if (rc) {
++              wil_err(wil, "wil_if_add failed: %d\n", rc);
++              goto bus_disable;
++      }
++      wil6210_debugfs_init(wil);
++      /* rollback to debugfs_exit */
++
++      { /* print various info */
++              struct net_device *ndev = wil_to_ndev(wil);
++              const char *pdev_name = pci_name(pdev);
++              const char *wiphydev_name = dev_name(wil_to_dev(wil));
++              const char *ndev_name = netdev_name(ndev);
++              const char *ifc_name = ndev->name;
++              struct pci_driver *drv = pci_dev_driver(pdev);
++              const char *drv_name = drv ? drv->name : "(no drv)";
++              pr_info("Driver  : <%s>\n", drv_name ?: "(null)");
++              pr_info("PCI dev : <%s>\n", pdev_name ?: "(null)");
++              pr_info("Net dev : <%s>\n", ndev_name ?: "(null)");
++              pr_info("Net ifc : <%s>\n", ifc_name ?: "(null)");
++              pr_info("Wiphy   : <%s>\n", wiphydev_name ?: "(null)");
++
++      }
++      send_echo(wil);
++      return 0;
++#if 0
++ debugfs_exit:
++      wil6210_debugfs_remove(wil);
++#endif
++ bus_disable:
++      if_pcie_disable(wil);
++ if_free:
++      wil_if_free(wil);
++ err_iounmap:
++      pci_iounmap(pdev, csr);
++ err_release_reg:
++      pci_release_region(pdev, 0);
++ err_disable_pdev:
++      pci_disable_device(pdev);
++      return rc;
++}
++
++static void wil_pcie_remove(struct pci_dev *pdev)
++{
++      struct wil6210_priv *wil = pci_get_drvdata(pdev);
++      wil_info(wil, "%s()\n", __func__);
++
++      wil6210_debugfs_remove(wil);
++      if_pcie_disable(wil);
++      wil_if_remove(wil);
++      wil_if_free(wil);
++      pci_iounmap(pdev, wil->csr);
++      pci_release_region(pdev, 0);
++      pci_disable_device(pdev);
++      pci_set_drvdata(pdev, NULL);
++}
++
++static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = {
++      { PCI_DEVICE(PCI_VENDOR_ID_WIL6210, PCI_DEVICE_ID_WIL6210),
++                      /*.driver_data  = (kernel_ulong_t)0,*/},
++      { /* end: all zeroes */ },
++};
++MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
++
++static struct pci_driver wil_driver = {
++      .probe          = wil_pcie_probe,
++      .remove         = __devexit_p(wil_pcie_remove),
++      .id_table       = wil6210_pcie_ids,
++      .name           = WIL_NAME,
++};
++
++
++static int __init wil_init_module(void)
++{
++      return pci_register_driver(&wil_driver);
++}
++
++static void __exit wil_exit_module(void)
++{
++      pci_unregister_driver(&wil_driver);
++}
++
++module_init(wil_init_module);
++module_exit(wil_exit_module);
++
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_AUTHOR("Qualcomm Atheros <wil6210@qualcomm.com>");
++MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card");
+diff --git a/drivers/net/wireless/ath/wil6210/read_dw.sh b/drivers/net/wireless/ath/wil6210/read_dw.sh
+new file mode 100755
+index 0000000..dd6a500
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/read_dw.sh
+@@ -0,0 +1,7 @@
++#!/bin/bash
++### parameter - FW address to read
++A=$1
++### where is wil6210 debugfs?
++D=$(find /sys/kernel/debug/ieee80211/ -name wil6210)
++echo $A > $D/mem_addr
++cat $D/mem_val
+diff --git a/drivers/net/wireless/ath/wil6210/tools/Makefile b/drivers/net/wireless/ath/wil6210/tools/Makefile
+new file mode 100644
+index 0000000..5285614
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/tools/Makefile
+@@ -0,0 +1,4 @@
++hostprogs-y := trace
++
++always := $(hostprogs-y)
++
+diff --git a/drivers/net/wireless/ath/wil6210/tools/trace.c b/drivers/net/wireless/ath/wil6210/tools/trace.c
+new file mode 100644
+index 0000000..40d1cd6
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/tools/trace.c
+@@ -0,0 +1,190 @@
++#include <stdio.h>
++#include <stdint.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++
++/*
++ * Dumps firmware trace.
++ *
++ * Uses binary representation of 'strings' file,
++ * it should be named fw_strings.bin and be in the current directory
++ * Periodically reads peripheral memory like in blob_fw_peri on the debugfs
++ * Name of peripheral memory file passed as parameter
++ */
++
++typedef uint32_t u32;
++typedef unsigned int uint;
++
++struct module_level_enable {  /* Little Endian */
++      uint    error_level_enable:1;
++      uint    warn_level_enable:1;
++      uint    info_level_enable:1;
++      uint    verbose_level_enable:1;
++      uint    reserved0:4;
++} __attribute__((packed));
++
++struct log_trace_header {     /* Little Endian */
++      uint    strring_offset:20;          /* the offset of the trace string in the strings sections */
++      uint    module:4;                   /* module that outputs the trace */
++      uint    level:2;                    /*  0 - Error
++                                              1- WARN
++                                              2 - INFO
++                                              3 - VERBOSE */
++      uint    parameters_num:2;           /* [0..3] */
++      uint    is_string:1;                            /* this bit was timestamp_present:1; and changed to indicate if the printf uses %s */
++      uint    signature:3;                /* should be 5 (2'101) in valid header */
++} __attribute__((packed));
++
++union log_event {
++      struct log_trace_header hdr;
++      u32 param;
++} __attribute__((packed));
++
++struct log_table_header {
++      u32 write_ptr;                      /* incremented by trace producer every write */
++      struct module_level_enable module_level_enable[16];
++      union log_event evt[0];
++} __attribute__((packed));
++
++static size_t read_all(int f, void *buf, size_t n)
++{
++      size_t actual = 0, r;
++      do {
++              r = read(f, buf + actual, n - actual);
++              actual += r;
++      } while ((r > 0) && (actual < n));
++      return actual;
++}
++
++static void *read_file(const char *name, size_t *size)
++{
++      int f = open(name, O_RDONLY);
++      size_t sz = *size;
++      size_t r;
++      void *buf;
++      if (f < 0)
++              return NULL;
++
++      if (!sz) {
++              sz = lseek(f, 0, SEEK_END);
++              lseek(f, 0, SEEK_SET);
++      }
++      buf = malloc(sz);
++      if (!buf) {
++              close(f);
++              return NULL;
++      }
++      r = read_all(f, buf, sz);
++      close(f);
++      if (r != sz) {
++              printf("Error: from %s read %zd bytes out of %zd\n",
++                              name, r, sz);
++              free(buf);
++              return NULL;
++      }
++      *size = sz;
++      return buf;
++}
++
++static char *strings_bin = "fw_strings.bin";
++static char *peri = "peri.dump";
++enum {
++      log_buf_sizedw = 0x1000/4,
++      str_mask = 0xFFFF,
++};
++
++static void *peri_buf;
++static void *str_buf;
++static size_t str_sz;
++static u32 rptr = 0;
++static const char* levels[] = {
++      "E",
++      "W",
++      "I",
++      "V",
++};
++
++static const char *modules[16];
++
++static void do_parse(void)
++{
++      struct log_table_header *h = peri_buf;
++      u32 wptr = h->write_ptr;
++      if ((wptr - rptr) >= log_buf_sizedw) {
++              /* overflow; try to parse last wrap */
++              rptr = wptr - log_buf_sizedw;
++      }
++      for (;(long)(wptr - rptr) > 0; rptr++) {
++              int i;
++              u32 p[3] = {0};
++              union log_event *evt = &h->evt[rptr % log_buf_sizedw];
++              const char *fmt;
++              if (evt->hdr.signature != 5)
++                      continue;
++              if (evt->hdr.strring_offset > str_sz)
++                      continue;
++              if (evt->hdr.parameters_num > 3)
++                      continue;
++              fmt = str_buf + evt->hdr.strring_offset;
++              for (i = 0; i < evt->hdr.parameters_num; i++) {
++                      p[i] = h->evt[(rptr + i + 1) % log_buf_sizedw].param;
++              }
++              printf("[%6d] %9s %s :", rptr, modules[evt->hdr.module],
++                              levels[evt->hdr.level]);
++              if (evt->hdr.is_string) {
++                      printf(fmt, str_buf + (p[0] & str_mask),
++                                      str_buf + (p[1] & str_mask),
++                                      str_buf + (p[2] & str_mask));
++              } else {
++                      printf(fmt, p[0], p[1], p[2]);
++              }
++              printf("\n");
++              rptr += evt->hdr.parameters_num;
++      }
++      fflush(stdout);
++}
++
++int main(int argc, char* argv[])
++{
++      if (argc > 1)
++              peri = argv[1];
++      const char *mod;
++      size_t peri_sz = 8*1024;
++      int i;
++      str_buf = read_file(strings_bin, &str_sz);
++      mod = str_buf;
++      peri_buf = read_file(peri, &peri_sz);
++      if (!str_buf || !peri_buf)
++              return -1;
++      struct log_table_header *h = peri_buf;
++      u32 wptr = h->write_ptr;
++      if ((wptr - rptr) >= log_buf_sizedw) {
++              /* overflow; try to parse last wrap */
++              rptr = wptr - log_buf_sizedw;
++      }
++      printf("  wptr = %d rptr = %d\n", wptr, rptr);
++      for (i = 0; i < 16; i++) {
++              modules[i] = mod;
++              struct module_level_enable *m = &h->module_level_enable[i];
++              printf("  %s[%2d] : %s%s%s%s\n", modules[i], i,
++                              m->error_level_enable   ? "E" : " ",
++                              m->warn_level_enable    ? "W" : " ",
++                              m->info_level_enable    ? "I" : " ",
++                              m->verbose_level_enable ? "V" : " ");
++              mod = strchr(mod, '\0') + 1;
++      }
++      for(;;) {
++              do_parse();
++              sleep(1);
++              int f = open(peri, O_RDONLY);
++              size_t r = read_all(f, peri_buf, peri_sz);
++              close(f);
++              if (r != peri_sz)
++                      break;
++      }
++      return 0;
++}
+diff --git a/drivers/net/wireless/ath/wil6210/tools/vhex2bin.py b/drivers/net/wireless/ath/wil6210/tools/vhex2bin.py
+new file mode 100755
+index 0000000..20dc170
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/tools/vhex2bin.py
+@@ -0,0 +1,17 @@
++#!/usr/bin/python
++
++'''
++Convert .vhex file into binary representation
++'''
++
++import sys
++import fileinput
++import re
++import struct
++
++for line in fileinput.input():
++  if not re.match("[0-9a-f]+", line):
++    continue
++  x = int(line,16)
++  s = struct.pack("<I", x)
++  sys.stdout.write(s)
+\ No newline at end of file
+diff --git a/drivers/net/wireless/ath/wil6210/trace.sh b/drivers/net/wireless/ath/wil6210/trace.sh
+new file mode 100755
+index 0000000..c9952bb
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/trace.sh
+@@ -0,0 +1,4 @@
++#!/bin/bash
++### where is wil6210 debugfs?
++D=$(find /sys/kernel/debug/ieee80211/ -name wil6210)
++exec tools/trace $D/blob_fw_peri
+diff --git a/drivers/net/wireless/ath/wil6210/txdesc.sh b/drivers/net/wireless/ath/wil6210/txdesc.sh
+new file mode 100755
+index 0000000..d224777
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/txdesc.sh
+@@ -0,0 +1,8 @@
++#!/bin/bash
++# Display Tx descriptor content
++# Descriptor index
++I=$1
++### where is wil6210 debugfs?
++D=$(find /sys/kernel/debug/ieee80211/ -name wil6210)
++echo $I > $D/txdesc_index
++cat $D/txdesc
+diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
+new file mode 100644
+index 0000000..c8177f2
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/txrx.c
+@@ -0,0 +1,871 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/hardirq.h>
++#include <net/ieee80211_radiotap.h>
++#include <linux/if_arp.h>
++#include <linux/moduleparam.h>
++
++#include "wil6210.h"
++#include "wmi.h"
++#include "txrx.h"
++#include "wil6210_rgf.h"
++
++static bool rtap_include_phy_info;
++module_param(rtap_include_phy_info, bool, S_IRUGO);
++MODULE_PARM_DESC(rtap_include_phy_info,
++              " Include PHY info in the radiotap header, default - no");
++
++static inline int vring_is_empty(struct vring *vring)
++{
++      return vring->swhead == vring->swtail;
++}
++
++static inline u32 vring_next_tail(struct vring *vring)
++{
++      return (vring->swtail+1)%vring->size;
++}
++
++static inline void vring_advance_head(struct vring *vring)
++{
++      vring->swhead = (vring->swhead+1)%vring->size;
++}
++
++static inline int vring_is_full(struct vring *vring)
++{
++      return vring_next_tail(vring) == vring->swhead;
++}
++
++static int vring_alloc(struct wil6210_priv *wil, struct vring *vring)
++{
++      struct device *dev = wil_to_dev(wil);
++      size_t sz = vring->size * sizeof(vring->va[0]);
++      int i;
++      BUILD_BUG_ON(sizeof(vring->va[0]) != 32);
++      wil_info(wil, "%s()\n", __func__);
++      vring->swhead = 0;
++      vring->swtail = 0;
++      vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL);
++      if (!vring->ctx) {
++              wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n",
++                              vring->size);
++              vring->va = NULL;
++              return -ENOMEM;
++      }
++      vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL);
++      if (!vring->va) {
++              wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n",
++                              vring->size);
++              kfree(vring->ctx);
++              vring->ctx = NULL;
++              return -ENOMEM;
++      }
++      /* initially, all descriptors are SW owned
++       * For Tx and Rx, ownership bit is at the same location, thus
++       * we can use any
++       */
++      for (i = 0; i < vring->size; i++) {
++              struct vring_tx_desc *d = &(vring->va[i].tx);
++              d->dma.status = TX_DMA_STATUS_DU;
++      }
++      wil_info(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
++                      vring->va, (unsigned long long)vring->pa, vring->ctx);
++      return 0;
++}
++
++static void vring_free(struct wil6210_priv *wil, struct vring *vring, int tx)
++{
++      struct device *dev = wil_to_dev(wil);
++      size_t sz = vring->size * sizeof(vring->va[0]);
++      wil_info(wil, "%s()\n", __func__);
++      while (!vring_is_empty(vring)) {
++              if (tx) {
++                      struct vring_tx_desc *d = &vring->va[vring->swtail].tx;
++                      dma_addr_t pa = d->dma.addr_low |
++                                      ((u64)d->dma.addr_high << 32);
++                      struct sk_buff *skb = vring->ctx[vring->swtail];
++                      if (skb) {
++                              dma_unmap_single(dev, pa, d->dma.length,
++                                              DMA_TO_DEVICE);
++                              dev_kfree_skb_any(skb);
++                              vring->ctx[vring->swtail] = NULL;
++                      } else {
++                              dma_unmap_page(dev, pa, d->dma.length,
++                                              DMA_TO_DEVICE);
++                      }
++                      vring->swtail = vring_next_tail(vring);
++              } else { /* rx */
++                      struct vring_rx_desc *d = &vring->va[vring->swtail].rx;
++                      dma_addr_t pa = d->dma.addr_low |
++                                      ((u64)d->dma.addr_high << 32);
++                      struct sk_buff *skb = vring->ctx[vring->swhead];
++                      dma_unmap_single(dev, pa, d->dma.length,
++                                      DMA_FROM_DEVICE);
++                      kfree_skb(skb);
++                      vring_advance_head(vring);
++              }
++      }
++      dma_free_coherent(dev, sz, vring->va, vring->pa);
++      kfree(vring->ctx);
++      vring->pa = 0;
++      vring->va = NULL;
++      vring->ctx = NULL;
++}
++
++/**
++ * Allocate one skb for Rx VRING
++ *
++ * Safe to call from IRQ
++ */
++static int vring_alloc_skb(struct wil6210_priv *wil,
++              struct vring *vring, u32 i, int headroom)
++{
++      struct device *dev = wil_to_dev(wil);
++      unsigned int sz = RX_BUF_LEN;
++      struct vring_rx_desc *d = &(vring->va[i].rx);
++      dma_addr_t pa;
++      /* TODO align */
++      struct sk_buff *skb = dev_alloc_skb(sz + headroom);
++      if (unlikely(!skb))
++              return -ENOMEM;
++      skb_reserve(skb, headroom);
++      skb_put(skb, sz);
++      pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE);
++      if (unlikely(dma_mapping_error(dev, pa))) {
++              kfree_skb(skb);
++              return -ENOMEM;
++      }
++      d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
++      d->dma.addr_low = lower_32_bits(pa);
++      d->dma.addr_high = (u16)upper_32_bits(pa);
++      /* ip_length don't care */
++      /* b11 don't care */
++      /* error don't care */
++      d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
++      d->dma.length = sz;
++      vring->ctx[i] = skb;
++      return 0;
++}
++
++/**
++ * Adds radiotap header
++ *
++ * Any error indicated as "Bad FCS"
++ *
++ * Vendor data for 04:ce:14-1 (Wilocity-1) consists of:
++ *  - Rx descriptor: 32 bytes
++ *  - Phy info
++ */
++static void rx_add_radiotap_header(struct wil6210_priv *wil,
++                               struct sk_buff *skb,
++                               struct vring_rx_desc *d)
++{
++      struct wil6210_rtap {
++              struct ieee80211_radiotap_header rthdr;
++              /* fields should be in the order of bits in rthdr.it_present */
++              /* flags */
++              u8 flags;
++              /* channel */
++              __le16 chnl_freq __aligned(2);
++              __le16 chnl_flags;
++              /* MCS */
++              u8 mcs_present;
++              u8 mcs_flags;
++              u8 mcs_index;
++      } __packed;
++      struct wil6210_rtap_vendor {
++              struct wil6210_rtap rtap;
++              /* vendor */
++              u8 vendor_oui[3] __aligned(2);
++              u8 vendor_ns;
++              __le16 vendor_skip;
++              u8 vendor_data[0];
++      } __packed;
++      struct wil6210_rtap_vendor *rtap_vendor;
++      int rtap_len = sizeof(struct wil6210_rtap);
++      int phy_length = 0; /* phy info header size, bytes */
++      static char phy_data[128];
++      if (rtap_include_phy_info) {
++              rtap_len = sizeof(*rtap_vendor) + sizeof(*d);
++              /* calculate additional length */
++              if (d->dma.status & RX_DMA_STATUS_PHY_INFO) {
++                      /**
++                       * PHY info starts from 8-byte boundary
++                       * there are 8-byte lines, last line may be partially
++                       * written (HW bug), thus FW configures for last line
++                       * to be excessive. Driver skips this last line.
++                       */
++                      int len = min_t(int, 8 + sizeof(phy_data),
++                                      rxdesc_phy_length(d));
++                      if (len > 8) {
++                              void *p = skb_tail_pointer(skb);
++                              void *pa = PTR_ALIGN(p, 8);
++                              if (skb_tailroom(skb) >= len + (pa - p)) {
++                                      phy_length = len - 8;
++                                      memcpy(phy_data, pa, phy_length);
++                              }
++                      }
++              }
++              rtap_len += phy_length;
++      }
++
++      if (skb_headroom(skb) < rtap_len &&
++          pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) {
++              wil_err(wil, "Unable to expand headrom to %d\n", rtap_len);
++              return;
++      }
++
++      rtap_vendor = (void *)skb_push(skb, rtap_len);
++      memset(rtap_vendor, 0, rtap_len);
++
++      rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION;
++      rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len);
++      rtap_vendor->rtap.rthdr.it_present = cpu_to_le32(
++                      (1 << IEEE80211_RADIOTAP_FLAGS) |
++                      (1 << IEEE80211_RADIOTAP_CHANNEL) |
++                      (1 << IEEE80211_RADIOTAP_MCS));
++      if (d->dma.status & RX_DMA_STATUS_ERROR)
++              rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS;
++
++      rtap_vendor->rtap.chnl_freq = cpu_to_le16(wil->monitor_chan ?
++                      wil->monitor_chan->center_freq : 58320);
++      rtap_vendor->rtap.chnl_flags = cpu_to_le16(0);
++
++      rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS;
++      rtap_vendor->rtap.mcs_flags = 0;
++      rtap_vendor->rtap.mcs_index = rxdesc_mcs(d);
++
++      if (rtap_include_phy_info) {
++              rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 <<
++                              IEEE80211_RADIOTAP_VENDOR_NAMESPACE);
++              /* OUI for Wilocity 04:ce:14 */
++              rtap_vendor->vendor_oui[0] = 0x04;
++              rtap_vendor->vendor_oui[1] = 0xce;
++              rtap_vendor->vendor_oui[2] = 0x14;
++              rtap_vendor->vendor_ns = 1;
++              /* Rx descriptor + PHY data  */
++              rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) +
++                              phy_length);
++              memcpy(rtap_vendor->vendor_data, d, sizeof(*d));
++              memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data,
++                              phy_length);
++      }
++}
++
++/**
++ * reap 1 frame from @swhead
++ *
++ * Safe to call from IRQ
++ */
++static struct sk_buff *vring_reap_rx(struct wil6210_priv *wil,
++              struct vring *vring)
++{
++      struct device *dev = wil_to_dev(wil);
++      struct net_device *ndev = wil_to_ndev(wil);
++      struct vring_rx_desc *d;
++      struct sk_buff *skb;
++      dma_addr_t pa;
++      unsigned int sz = RX_BUF_LEN;
++      if (vring_is_empty(vring))
++              return NULL;
++      d = &(vring->va[vring->swhead].rx);
++      if (!(d->dma.status & RX_DMA_STATUS_DU)) {
++              /* it is not error, we just reached end of Rx done area */
++              return NULL;
++      }
++      pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
++      skb = vring->ctx[vring->swhead];
++      dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
++      skb_trim(skb, d->dma.length);
++      wil->stats.last_mcs_rx = rxdesc_mcs(d);
++      /* use radiotap header only if required */
++      if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
++              rx_add_radiotap_header(wil, skb, d);
++#ifdef WIL_DEBUG_TXRX
++      wil_info(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
++      print_hex_dump(KERN_INFO, "Rx ", DUMP_PREFIX_NONE, 32, 4, d
++                      , sizeof(*d), false);
++#endif
++      vring_advance_head(vring);
++      return skb;
++}
++
++/**
++ * allocate and fill up to @count buffers in rx ring
++ * buffers posted at @swtail
++ */
++static int rx_refill(struct wil6210_priv *wil, int count)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      struct vring *v = &wil->vring_rx;
++      u32 next_tail;
++      int rc = 0;
++      int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ?
++                      WIL6210_RTAP_SIZE : 0;
++      for (; next_tail = vring_next_tail(v),
++                      (next_tail != v->swhead) && (count-- > 0);
++                      v->swtail = next_tail) {
++              rc = vring_alloc_skb(wil, v, v->swtail, headroom);
++              if (rc) {
++                      wil_err(wil, "Error %d in rx_refill[%d]\n",
++                                      rc, v->swtail);
++                      break;
++              }
++      }
++      iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail));
++      return rc;
++}
++
++static int netif_rx_any(struct sk_buff *skb)
++{
++      if (in_interrupt())
++              return netif_rx(skb);
++      else
++              return netif_rx_ni(skb);
++}
++
++/**
++ * Proceed all completed skb's from Rx VRING
++ *
++ * Safe to call from IRQ
++ */
++void rx_handle(struct wil6210_priv *wil)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      struct vring *v = &wil->vring_rx;
++      struct sk_buff *skb;
++      if (!v->va) {
++              wil_err(wil, "Rx IRQ while Rx not yet initialized\n");
++              return;
++      }
++#ifdef WIL_DEBUG_TXRX
++      wil_info(wil, "%s()\n", __func__);
++#endif
++      while (NULL != (skb = vring_reap_rx(wil, v))) {
++#ifdef WIL_DEBUG_TXRX
++              print_hex_dump(KERN_INFO, "Rx ", DUMP_PREFIX_OFFSET, 16, 1,
++                              skb->data, skb_headlen(skb), false);
++#endif
++              skb_orphan(skb);
++              if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
++                      skb->dev = ndev;
++                      skb_reset_mac_header(skb);
++                      skb->ip_summed = CHECKSUM_UNNECESSARY;
++                      skb->pkt_type = PACKET_OTHERHOST;
++                      skb->protocol = htons(ETH_P_802_2);
++
++              } else {
++                      skb->protocol = eth_type_trans(skb, ndev);
++              }
++              if (likely(netif_rx_any(skb) == NET_RX_SUCCESS)) {
++                      ndev->stats.rx_packets++;
++                      ndev->stats.rx_bytes += skb->len;
++
++              } else {
++                      ndev->stats.rx_dropped++;
++              }
++      }
++      rx_refill(wil, v->size);
++}
++
++int rx_init(struct wil6210_priv *wil)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      struct wireless_dev *wdev = wil->wdev;
++      struct vring *vring = &wil->vring_rx;
++      int rc;
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_CFG_RX_CHAIN_CMD cfg;
++      } __packed wmi_rx_cfg = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 1,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_CFG_RX_CHAIN_CMD),
++              },
++              .wmi = {
++                      .id = WMI_CFG_RX_CHAIN_CMDID,
++                      .info1 = 0,
++              },
++              .cfg = {
++                      .action = ADD_RX_CHAIN,
++                      .sw_ring = {
++                              .max_mpdu_size = RX_BUF_LEN,
++                      },
++                      .mid = 0, /* TODO - what is it? */
++                      .decap_trans_type = DECAP_TYPE_802_3,
++              },
++      };
++      struct {
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_CFG_RX_CHAIN_DONE_EVENT cfg;
++      } __packed wmi_rx_cfg_reply;
++      wil_info(wil, "%s()\n", __func__);
++      vring->size = WIL6210_RX_RING_SIZE;
++      rc = vring_alloc(wil, vring);
++      if (rc)
++              return rc;
++      wmi_rx_cfg.cfg.sw_ring.ring_mem_base = vring->pa;
++      wmi_rx_cfg.cfg.sw_ring.ring_size = vring->size;
++      if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
++              wmi_rx_cfg.cfg.sniffer_mode = 1;
++              if (wil->monitor_chan)
++                      wmi_rx_cfg.cfg.sniffer_channel =
++                                      wil->monitor_chan->hw_value - 1;
++              wmi_rx_cfg.cfg.sniffer_phy_info =
++                              (ndev->type == ARPHRD_IEEE80211_RADIOTAP);
++              /* 0 - CP, 1 - DP */
++              wmi_rx_cfg.cfg.sniffer_phy =
++                              (wil->monitor_flags & MONITOR_FLAG_CONTROL) ?
++                                              0 : 1;
++      }
++      rc = wmi_call(wil, &wmi_rx_cfg, WMI_CFG_RX_CHAIN_DONE_EVENTID,
++                      &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), 200);
++      if (rc)
++              goto err_free;
++      vring->hwtail = wmi_rx_cfg_reply.cfg.rx_ring_tail_ptr;
++      wil_info(wil, "Rx init: status %d tail 0x%08x\n",
++                      wmi_rx_cfg_reply.cfg.status, vring->hwtail);
++      rc = rx_refill(wil, vring->size);
++      if (rc)
++              goto err_free;
++      return 0;
++ err_free:
++      vring_free(wil, vring, 0);
++      return rc;
++}
++
++void rx_fini(struct wil6210_priv *wil)
++{
++      struct vring *vring = &wil->vring_rx;
++      wil_info(wil, "%s()\n", __func__);
++      if (vring->va) {
++              int rc;
++              struct {
++                      struct wil6210_mbox_hdr hdr;
++                      struct wil6210_mbox_hdr_wmi wmi;
++                      struct WMI_CFG_RX_CHAIN_CMD cfg;
++              } __packed wmi_rx_cfg = {
++                      .hdr = {
++                              .seq = 1,
++                              .ctx = 1,
++                              .type = 0,
++                              .flags = 1,
++                              .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                              + sizeof(struct WMI_CFG_RX_CHAIN_CMD),
++                      },
++                      .wmi = {
++                              .id = WMI_CFG_RX_CHAIN_CMDID,
++                              .info1 = 0,
++                      },
++                      .cfg = {
++                              .action = DELETE_RX_CHAIN,
++                              .sw_ring = {
++                                      .max_mpdu_size = RX_BUF_LEN,
++                              },
++                      },
++              };
++              struct {
++                      struct wil6210_mbox_hdr_wmi wmi;
++                      struct WMI_CFG_RX_CHAIN_DONE_EVENT cfg;
++              } __packed wmi_rx_cfg_reply;
++              rc = wmi_call(wil, &wmi_rx_cfg, WMI_CFG_RX_CHAIN_DONE_EVENTID,
++                              &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply),
++                              100);
++              vring_free(wil, vring, 0);
++      }
++}
++
++int vring_init_tx(struct wil6210_priv *wil, int id, int size,
++              int cid, int tid)
++{
++      struct wireless_dev *wdev = wil->wdev;
++      int rc;
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_VRING_CFG_CMD cfg;
++      } __packed wmi_tx_cfg = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 1,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_VRING_CFG_CMD),
++              },
++              .wmi = {
++                      .id = WMI_VRING_CFG_CMDID,
++                      .info1 = 0,
++              },
++              .cfg = {
++                      .action = ADD_VRING,
++                      .sw_ring = {
++                              .max_mpdu_size = TX_BUF_LEN,
++                      },
++                      .ringid = id,
++                      .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4),
++                      .encap_trans_type = ENC_TYPE_802_3,
++                      .mac_ctrl = 0,
++                      .to_resolution = 0,
++                      .agg_max_wsize = 0,
++                      .priority = 0,
++                      .timeslot_us = 0xfff,
++              },
++      };
++      struct {
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_VRING_CFG_DONE_EVENT cfg;
++      } __packed reply;
++      struct vring *vring = &wil->vring_tx[id];
++      wil_info(wil, "%s(%d)\n", __func__, id);
++      if (vring->va) {
++              wil_err(wil, "Tx ring [%d] already allocated\n", id);
++              rc = -EINVAL;
++              goto out;
++      }
++      vring->size = size;
++      rc = vring_alloc(wil, vring);
++      if (rc)
++              goto out;
++      wmi_tx_cfg.cfg.sw_ring.ring_mem_base = vring->pa;
++      wmi_tx_cfg.cfg.sw_ring.ring_size = vring->size;
++      /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
++      switch (wdev->iftype) {
++      case NL80211_IFTYPE_STATION:
++#if 0
++              wmi_tx_cfg.cfg.ds_cfg = STATION_MODE;
++#else
++              wmi_tx_cfg.cfg.ds_cfg = PBSS_MODE;
++#endif
++              break;
++      case NL80211_IFTYPE_AP:
++#if 0
++              wmi_tx_cfg.cfg.ds_cfg = AP_MODE;
++#else
++              wmi_tx_cfg.cfg.ds_cfg = PBSS_MODE;
++#endif
++              break;
++      case NL80211_IFTYPE_P2P_CLIENT:
++              wmi_tx_cfg.cfg.ds_cfg = STATION_MODE;
++              break;
++      case NL80211_IFTYPE_P2P_GO:
++              wmi_tx_cfg.cfg.ds_cfg = AP_MODE;
++              break;
++      default:
++              rc = -EOPNOTSUPP;
++              goto out_free;
++
++      }
++      rc = wmi_call(wil, &wmi_tx_cfg, WMI_VRING_CFG_DONE_EVENTID,
++                      &reply, sizeof(reply), 100);
++      if (rc)
++              goto out_free;
++      if (reply.cfg.status != VRING_CFG_SUCCESS) {
++              wil_err(wil, "Tx config failed, status 0x%02x\n",
++                              reply.cfg.status);
++              goto out_free;
++      }
++      vring->hwtail = reply.cfg.tx_vring_tail_ptr;
++      return 0;
++ out_free:
++      vring_free(wil, vring, 1);
++ out:
++      return rc;
++}
++
++void vring_fini_tx(struct wil6210_priv *wil, int id)
++{
++#if 0
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_VRING_CFG_CMD cfg;
++      } __packed wmi_tx_cfg = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 1,
++                      .type = 0,
++                      .flags = 1,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_VRING_CFG_CMD),
++              },
++              .wmi = {
++                      .id = WMI_VRING_CFG_CMDID,
++                      .info1 = 0,
++              },
++              .cfg = {
++                      .action = DELETE_VRING,
++                      .sw_ring = {
++                      },
++                      .ringid = id,
++              },
++      };
++      struct {
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_VRING_CFG_DONE_EVENT cfg;
++      } __packed reply;
++#endif
++      struct vring *vring = &wil->vring_tx[id];
++      if (!vring->va)
++              return;
++      wil_info(wil, "%s(%d)\n", __func__, id);
++#if 0
++      wmi_call(wil, &wmi_tx_cfg, WMI_VRING_CFG_DONE_EVENTID,
++                      &reply, sizeof(reply), 100);
++#endif
++      vring_free(wil, vring, 1);
++}
++
++static struct vring *find_tx_vring(struct wil6210_priv *wil,
++              struct sk_buff *skb)
++{
++      struct vring *v = &wil->vring_tx[0];
++      if (v->va)
++              return v;
++      return NULL;
++}
++
++static int tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len)
++{
++      d->dma.addr_low = lower_32_bits(pa);
++      d->dma.addr_high = (u16)upper_32_bits(pa);
++      d->dma.ip_length = 0;
++      /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
++      d->dma.b11 = 0/*14 | BIT(7)*/;
++      d->dma.error = 0;
++      d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
++      d->dma.length = len;
++      d->dma.d0 = 0;
++      d->mac.d[0] = 0;
++      d->mac.d[1] = 0;
++      d->mac.d[2] = 0;
++      d->mac.ucode_cmd = 0;
++#if 0
++      /* use MCS 1 */
++      d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_EN_POS) |
++                      (1 << MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_INDEX_POS);
++#endif
++#if 1
++      /* use dst index 0 */
++      d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_EN_POS) |
++                      (0 << MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_POS);
++#endif
++#if 0
++      d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_STATUS_EN_POS);
++      d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_INTERRUP_EN_POS);
++#endif
++      /* translation type:  0 - bypass; 1 - 802.3; 2 - native wifi */
++      d->mac.d[2] = BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_SNAP_HDR_INSERTION_EN_POS) |
++                      (1 << MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_L2_TRANSLATION_TYPE_POS);
++      return 0;
++}
++
++static int tx_vring(struct wil6210_priv *wil, struct vring *vring,
++              struct sk_buff *skb)
++{
++      struct device *dev = wil_to_dev(wil);
++      struct vring_tx_desc *d;
++      u32 swhead = vring->swhead;
++      u32 swtail = vring->swtail;
++      int used = (vring->size + swhead - swtail) % vring->size;
++      int avail = vring->size - used - 1;
++      int nr_frags = skb_shinfo(skb)->nr_frags;
++      int f;
++      int vring_index = vring - wil->vring_tx;
++      int i = swhead;
++      dma_addr_t pa;
++#ifdef WIL_DEBUG_TXRX
++      wil_info(wil, "%s()\n", __func__);
++#endif
++      if (avail < vring->size/8)
++              netif_tx_stop_all_queues(wil_to_ndev(wil));
++      if (avail < 1 + nr_frags) {
++              wil_err(wil, "Tx ring full. No space for %d fragments\n",
++                              1 + nr_frags);
++              return -ENOMEM;
++      }
++      d = &(vring->va[i].tx);
++
++      /* FIXME FW can accept only unicast frames for the peer */
++      memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
++
++      pa = dma_map_single(dev, skb->data,
++                      skb_headlen(skb), DMA_TO_DEVICE);
++#ifdef WIL_DEBUG_TXRX
++      wil_info(wil, "Tx skb %d bytes %p -> %#08llx\n",
++                      skb_headlen(skb), skb->data, (unsigned long long)pa);
++      print_hex_dump(KERN_INFO, "Tx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data
++                      , skb_headlen(skb), false);
++#endif
++      if (unlikely(dma_mapping_error(dev, pa)))
++              return -EINVAL;
++      /* 1-st segment */
++      tx_desc_map(d, pa, skb_headlen(skb));
++      d->mac.d[2] |= ((nr_frags + 1) <<
++              MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_NUM_OF_DESCRIPTORS_POS);
++      vring->ctx[i] = skb;
++      /* middle segments */
++      for (f = 0; f < nr_frags; f++) {
++              const struct skb_frag_struct *frag =
++                              &skb_shinfo(skb)->frags[f];
++              int len = skb_frag_size(frag);
++              i = (swhead + f + 1) % vring->size;
++              d = &(vring->va[i].tx);
++              pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
++                              DMA_TO_DEVICE);
++              if (unlikely(dma_mapping_error(dev, pa)))
++                      goto dma_error;
++              tx_desc_map(d, pa, len);
++              vring->ctx[i] = NULL;
++      }
++      /* for the last seg only */
++      d->dma.d0 |= BIT(DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_EOP_POS);
++      d->dma.d0 |= BIT(9);
++      d->dma.d0 |= BIT(DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_DMA_IT_POS);
++      d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_QID_POS);
++#ifdef WIL_DEBUG_TXRX
++      print_hex_dump(KERN_INFO, "Tx ", DUMP_PREFIX_NONE, 32, 4, d
++                      , sizeof(*d), false);
++#endif
++      /* advance swhead */
++      vring->swhead = (swhead + nr_frags + 1) % vring->size;
++#ifdef WIL_DEBUG_TXRX
++      wil_info(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
++#endif
++      iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
++      return 0;
++ dma_error:
++      /* unmap what we have mapped */
++      for (; f > -1; f--) {
++              i = (swhead + f + 1) % vring->size;
++              d = &(vring->va[i].tx);
++              d->dma.status = TX_DMA_STATUS_DU;
++              pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
++              if (vring->ctx[i])
++                      dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
++              else
++                      dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
++      }
++      return -EINVAL;
++}
++
++
++netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
++{
++      struct wil6210_priv *wil = ndev_to_wil(ndev);
++      struct vring *vring;
++#ifdef WIL_DEBUG_TXRX
++      wil_info(wil, "%s()\n", __func__);
++#endif
++      if (!test_bit(wil_status_fwready, &wil->status)) {
++              wil_err(wil, "FW not ready\n");
++              goto drop;
++      }
++      if (!test_bit(wil_status_fwconnected, &wil->status)) {
++              wil_err(wil, "FW not connected\n");
++              goto drop;
++      }
++      if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
++              wil_err(wil, "Xmit in monitor mode not supported\n");
++              goto drop;
++      }
++      /* find vring */
++      vring = find_tx_vring(wil, skb);
++      if (!vring) {
++              wil_err(wil, "No Tx VRING available\n");
++              goto drop;
++      }
++      /* set up vring entry */
++      switch (tx_vring(wil, vring, skb)) {
++      case 0:
++              ndev->stats.tx_packets++;
++              ndev->stats.tx_bytes += skb->len;
++              return NETDEV_TX_OK;
++              break;
++      case -ENOMEM:
++              return NETDEV_TX_BUSY;
++      default:
++              ; /* goto drop; */
++              break;
++      }
++ drop:
++      netif_tx_stop_all_queues(ndev);
++      ndev->stats.tx_dropped++;
++      dev_kfree_skb_any(skb);
++      return NET_XMIT_DROP;
++}
++
++/**
++ * Clean up transmitted skb's from the Tx VRING
++ *
++ * Safe to call from IRQ
++ */
++void tx_complete(struct wil6210_priv *wil, int ringid)
++{
++      struct device *dev = wil_to_dev(wil);
++      struct vring *vring = &wil->vring_tx[ringid];
++      if (!vring->va) {
++              wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
++              return;
++      }
++#ifdef WIL_DEBUG_TXRX
++      wil_info(wil, "%s(%d)\n", __func__, ringid);
++#endif
++      while (!vring_is_empty(vring)) {
++              struct vring_tx_desc *d = &vring->va[vring->swtail].tx;
++              dma_addr_t pa;
++              struct sk_buff *skb;
++              if (!(d->dma.status & TX_DMA_STATUS_DU))
++                      break;
++#ifdef WIL_DEBUG_TXRX
++              wil_info(wil, "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
++                              vring->swtail, d->dma.length, d->dma.status,
++                              d->dma.error);
++              print_hex_dump(KERN_INFO, "TxC ", DUMP_PREFIX_NONE, 32, 4, d
++                              , sizeof(*d), false);
++#endif
++              pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
++              skb = vring->ctx[vring->swtail];
++              if (skb) {
++                      dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
++                      dev_kfree_skb_any(skb);
++                      vring->ctx[vring->swtail] = NULL;
++              } else {
++                      dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
++              }
++              d->dma.addr_low = 0;
++              d->dma.addr_high = 0;
++              d->dma.length = 0;
++              d->dma.status = TX_DMA_STATUS_DU;
++              vring->swtail = (vring->swtail + 1) % vring->size;
++      }
++      {
++              u32 swhead = vring->swhead;
++              u32 swtail = vring->swtail;
++              int used = (vring->size + swhead - swtail) % vring->size;
++              int avail = vring->size - used - 1;
++              if (avail > vring->size/4)
++                      netif_tx_wake_all_queues(wil_to_ndev(wil));
++      }
++}
+diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
+new file mode 100644
+index 0000000..5ec2951
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/txrx.h
+@@ -0,0 +1,352 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef WIL6210_TXRX_H
++#define WIL6210_TXRX_H
++
++#define BUF_SW_OWNED    (1)
++#define BUF_HW_OWNED    (0)
++
++/* size of max. Rx packet */
++#define RX_BUF_LEN      (2048)
++#define TX_BUF_LEN      (2048)
++/* how many bytes to reserve for rtap header? */
++#define WIL6210_RTAP_SIZE (128)
++
++/* Tx/Rx path */
++/*
++ * Tx descriptor - MAC part
++ * [dword 0]
++ * bit  0.. 9 : lifetime_expiry_value:10
++ * bit     10 : interrup_en:1
++ * bit     11 : status_en:1
++ * bit 12..13 : txss_override:2
++ * bit     14 : timestamp_insertion:1
++ * bit     15 : duration_preserve:1
++ * bit 16..21 : reserved0:6
++ * bit 22..26 : mcs_index:5
++ * bit     27 : mcs_en:1
++ * bit 28..29 : reserved1:2
++ * bit     30 : reserved2:1
++ * bit     31 : sn_preserved:1
++ * [dword 1]
++ * bit  0.. 3 : pkt_mode:4
++ * bit      4 : pkt_mode_en:1
++ * bit  5.. 7 : reserved0:3
++ * bit  8..13 : reserved1:6
++ * bit     14 : reserved2:1
++ * bit     15 : ack_policy_en:1
++ * bit 16..19 : dst_index:4
++ * bit     20 : dst_index_en:1
++ * bit 21..22 : ack_policy:2
++ * bit     23 : lifetime_en:1
++ * bit 24..30 : max_retry:7
++ * bit     31 : max_retry_en:1
++ * [dword 2]
++ * bit  0.. 7 : num_of_descriptors:8
++ * bit  8..17 : reserved:10
++ * bit 18..19 : l2_translation_type:2
++ * bit     20 : snap_hdr_insertion_en:1
++ * bit     21 : vlan_removal_en:1
++ * bit 22..31 : reserved0:10
++ * [dword 3]
++ * bit  0.. 31: ucode_cmd:32
++ */
++struct vring_tx_mac {
++      u32 d[3];
++      u32 ucode_cmd;
++} __packed;
++
++/* TX MAC Dword 0 */
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_LIFETIME_EXPIRY_VALUE_POS 0
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_LIFETIME_EXPIRY_VALUE_LEN 10
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_INTERRUP_EN_POS 10
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_INTERRUP_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_INTERRUP_EN_MSK 0x400
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_STATUS_EN_POS 11
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_STATUS_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_STATUS_EN_MSK 0x800
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TXSS_OVERRIDE_POS 12
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TXSS_OVERRIDE_LEN 2
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TXSS_OVERRIDE_MSK 0x3000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TIMESTAMP_INSERTION_POS 14
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TIMESTAMP_INSERTION_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TIMESTAMP_INSERTION_MSK 0x4000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_DURATION_PRESERVE_POS 15
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_DURATION_PRESERVE_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_DURATION_PRESERVE_MSK 0x8000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_INDEX_POS 22
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_INDEX_LEN 5
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_INDEX_MSK 0x7C00000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_EN_POS 27
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_EN_MSK 0x8000000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_SN_PRESERVED_POS 31
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_SN_PRESERVED_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_SN_PRESERVED_MSK 0x80000000
++
++/* TX MAC Dword 1 */
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_POS 0
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_LEN 4
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_MSK 0xF
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_EN_POS 4
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_EN_MSK 0x10
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_EN_POS 15
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_EN_MSK 0x8000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_POS 16
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_LEN 4
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_MSK 0xF0000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_EN_POS 20
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_EN_MSK 0x100000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_POS 21
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_LEN 2
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_MSK 0x600000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_LIFETIME_EN_POS 23
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_LIFETIME_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_LIFETIME_EN_MSK 0x800000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_POS 24
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_LEN 7
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_MSK 0x7F000000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_EN_POS 31
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_EN_MSK 0x80000000
++
++/* TX MAC Dword 2 */
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_NUM_OF_DESCRIPTORS_POS 0
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_NUM_OF_DESCRIPTORS_LEN 8
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_NUM_OF_DESCRIPTORS_MSK 0xFF
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_RESERVED_POS 8
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_RESERVED_LEN 10
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_RESERVED_MSK 0x3FF00
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_L2_TRANSLATION_TYPE_POS 18
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_L2_TRANSLATION_TYPE_LEN 2
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_L2_TRANSLATION_TYPE_MSK 0xC0000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_SNAP_HDR_INSERTION_EN_POS 20
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_SNAP_HDR_INSERTION_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_SNAP_HDR_INSERTION_EN_MSK 0x100000
++
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_VLAN_REMOVAL_EN_POS 21
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_VLAN_REMOVAL_EN_LEN 1
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_VLAN_REMOVAL_EN_MSK 0x200000
++
++/* TX MAC Dword 3 */
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_3_UCODE_CMD_POS 0
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_3_UCODE_CMD_LEN 32
++#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_3_UCODE_CMD_MSK 0xFFFFFFFF
++
++/* TX DMA Dword 0 */
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_LENGTH_POS 0
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_LENGTH_LEN 8
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_LENGTH_MSK 0xFF
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_EOP_POS 8
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_EOP_LEN 1
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_EOP_MSK 0x100
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_DMA_IT_POS 10
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_DMA_IT_LEN 1
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_DMA_IT_MSK 0x400
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_SEGMENT_BUF_DETAILS_POS 11
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_SEGMENT_BUF_DETAILS_LEN 2
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_SEGMENT_BUF_DETAILS_MSK 0x1800
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_SEG_EN_POS 13
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_SEG_EN_LEN 1
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_SEG_EN_MSK 0x2000
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_IPV4_CHECKSUM_EN_POS 14
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_IPV4_CHECKSUM_EN_LEN 1
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_IPV4_CHECKSUM_EN_MSK 0x4000
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_UDP_CHECKSUM_EN_POS 15
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_UDP_CHECKSUM_EN_LEN 1
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_QID_POS 16
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_QID_LEN 5
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_QID_MSK 0x1F0000
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_PSEUDO_HEADER_CALC_EN_POS 21
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_PSEUDO_HEADER_CALC_EN_LEN 1
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000
++
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_TYPE_POS 30
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_TYPE_LEN 2
++#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_TYPE_MSK 0xC0000000
++
++
++#define TX_DMA_STATUS_DU         BIT(0)
++
++struct vring_tx_dma {
++      u32 d0;
++      u32 addr_low;
++      u16 addr_high;
++      u8  ip_length;
++      u8  b11;       /* 0..6: mac_length; 7:ip_version */
++      u8  error;     /* 0..2: err; 3..7: reserved; */
++      u8  status;    /* 0: used; 1..7; reserved */
++      u16 length;
++} __packed;
++
++/*
++ * Rx descriptor - MAC part
++ * [dword 0]
++ * bit  0.. 3 : tid:4 The QoS (b3-0) TID Field
++ * bit  4.. 6 : connection_id:3 :The Source index that  was found during
++ *  Parsing the TA.  This field is used to  define the source of the packet
++ * bit      7 : reserved:1
++ * bit  8.. 9 : mac_id:2 : The MAC virtual  Ring number (always zero)
++ * bit 10..11 : frame_type:2 : The FC Control  (b3-2) -  MPDU Type
++ *              (management, data, control  and extension)
++ * bit 12..15 : frame_subtype:4 : The FC Control  (b7-4) -  Frame Subtype
++ * bit 16..27 : seq_number:12 The received Sequence number field
++ * bit 28..31 : extended:4 extended subtype
++ * [dword 1]
++ * bit  0.. 3 : reserved
++ * bit  4.. 5 : key_id:2
++ * bit      6 : decrypt_bypass:1
++ * bit      7 : security:1
++ * bit  8.. 9 : ds_bits:2
++ * bit     10 : a_msdu_present:1  from qos header
++ * bit     11 : a_msdu_type:1  from qos header
++ * bit     12 : a_mpdu:1  part of AMPDU aggregation
++ * bit     13 : broadcast:1
++ * bit     14 : mutlicast:1
++ * bit     15 : reserved:1
++ * bit 16..20 : rx_mac_qid:5   The Queue Identifier that the packet
++ *                             is received from
++ * bit 21..24 : mcs:4
++ * bit 25..28 : mic_icr:4
++ * bit 29..31 : reserved:3
++ * [dword 2]
++ * bit  0.. 2 : time_slot:3 The timeslot that the MPDU is received
++ * bit      3 : fc_protocol_ver:1 The FC Control  (b0) - Protocol  Version
++ * bit      4 : fc_order:1 The FC Control (b15) -Order
++ * bit  5.. 7 : qos_ack_policy:3  The QoS (b6-5) ack policy Field
++ * bit      8 : esop:1 The QoS (b4) ESOP field
++ * bit      9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG  field
++ * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved  field
++ * bit     15 : qos_ac_constraint:1
++ * bit 16..31 : pn_15_0:16 low 2 bytes of PN
++ * [dword 3]
++ * bit  0..31 : pn_47_16:32 high 4 bytes of PN
++ */
++struct vring_rx_mac {
++      u32 d0;
++      u32 d1;
++      u16 w4;
++      u16 pn_15_0;
++      u32 pn_47_16;
++} __packed;
++
++/*
++ * Rx descriptor - DMA part
++ * [dword 0]
++ * bit  0.. 7 : l4_length:8 layer 4 length
++ * bit  8.. 9 : reserved:2
++ * bit     10 : cmd_dma_it:1
++ * bit 11..15 : reserved:5
++ * bit 16..29 : phy_info_length:14
++ * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field
++ * [dword 1]
++ * bit  0..31 : addr_low:32 The payload buffer low address
++ * [dword 2]
++ * bit  0..15 : addr_high:16 The payload buffer high address
++ * bit 16..23 : ip_length:8
++ * bit 24..30 : mac_length:7
++ * bit     31 : ip_version:1
++ * [dword 3]
++ *  [byte 12] error
++ *  [byte 13] status
++ * bit      0 : du:1
++ * bit      1 : eop:1
++ * bit      2 : error:1
++ * bit      3 : mi:1
++ * bit      4 : l3_identified:1
++ * bit      5 : l4_identified:1
++ * bit      6 : phy_info_included:1
++ * bit      7 : reserved:1
++ *  [word 7] length
++ *
++ */
++
++#define RX_DMA_D0_CMD_DMA_IT     BIT(10)
++
++#define RX_DMA_STATUS_DU         BIT(0)
++#define RX_DMA_STATUS_ERROR      BIT(2)
++#define RX_DMA_STATUS_PHY_INFO   BIT(6)
++
++struct vring_rx_dma {
++      u32 d0;
++      u32 addr_low;
++      u16 addr_high;
++      u8  ip_length;
++      u8  b11;
++      u8  error;
++      u8  status;
++      u16 length;
++} __packed;
++
++struct vring_tx_desc {
++      struct vring_tx_mac mac;
++      struct vring_tx_dma dma;
++} __packed;
++
++struct vring_rx_desc {
++      struct vring_rx_mac mac;
++      struct vring_rx_dma dma;
++} __packed;
++
++union vring_desc {
++      struct vring_tx_desc tx;
++      struct vring_rx_desc rx;
++} __packed;
++
++static inline int rxdesc_phy_length(struct vring_rx_desc *d)
++{
++      return GET_BITS(d->dma.d0, 16, 29);
++}
++
++static inline int rxdesc_mcs(struct vring_rx_desc *d)
++{
++      return GET_BITS(d->mac.d1, 21, 24);
++}
++
++#endif /* WIL6210_TXRX_H */
+diff --git a/drivers/net/wireless/ath/wil6210/vrwatch.sh b/drivers/net/wireless/ath/wil6210/vrwatch.sh
+new file mode 100755
+index 0000000..8beb208
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/vrwatch.sh
+@@ -0,0 +1,6 @@
++#!/bin/bash
++# Watch VRING's
++### where is wil6210 debugfs?
++D=$(find /sys/kernel/debug/ieee80211/ -name wil6210)
++exec watch -n 0.2 -d cat $D/vrings
++
+diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
+new file mode 100644
+index 0000000..66fead6
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/wil6210.h
+@@ -0,0 +1,234 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef __WIL6210_H__
++#define __WIL6210_H__
++
++#include <linux/netdevice.h>
++#include <linux/wireless.h>
++#include <net/cfg80211.h>
++
++#define WIL_NAME "wil6210"
++#define PCI_VENDOR_ID_WIL6210 (0x1ae9)
++#define PCI_DEVICE_ID_WIL6210 (0x0301)
++
++/**
++ * extract bits [@b0:@b1] (inclusive) from the value @x
++ * it should be @b0 <= @b1, or result is incorrect
++ */
++static inline u32 GET_BITS(u32 x, int b0, int b1)
++{
++      return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1);
++}
++
++#define WIL6210_MEM_SIZE (2*1024*1024UL)
++
++#define WIL6210_TX_QUEUES (4)
++
++#define WIL6210_RX_RING_SIZE (128)
++#define WIL6210_TX_RING_SIZE (128)
++#define WIL6210_MAX_TX_RINGS (24)
++
++struct wil6210_mbox_ring {
++      u32 base;
++      u16 unused;
++      u16 size;
++      u32 tail;
++      u32 head;
++} __packed;
++
++struct wil6210_mbox_ring_desc {
++      u32 sync;
++      u32 addr;
++} __packed;
++
++/* at HOST_OFF_WIL6210_MBOX_CTL */
++struct wil6210_mbox_ctl {
++      struct wil6210_mbox_ring tx;
++      struct wil6210_mbox_ring rx;
++} __packed;
++
++struct wil6210_mbox_hdr {
++      u16 seq;
++      u16 ctx;
++      u16 type;
++      u8 flags;
++      u8 len; /* payload, bytes after this header */
++} __packed;
++
++/* max. value for wil6210_mbox_hdr.len */
++#define MAX_MBOXITEM_SIZE   (120)
++
++struct wil6210_mbox_hdr_wmi {
++      u16 reserved0;
++      u16 id;
++      u16 info1;
++      u16 reserved1;
++} __packed;
++
++struct pending_wmi_event {
++      struct list_head list;
++      struct {
++              struct wil6210_mbox_hdr mbox_hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              u8 data[0];
++      } __packed event;
++};
++
++union vring_desc;
++
++struct vring {
++      dma_addr_t pa;
++      union vring_desc *va; /* vring_desc[size] */
++      u16 size; /* number of vring_desc elements */
++      u32 swtail;
++      u32 swhead;
++      u32 hwtail; /* write here to inform hw */
++      void **ctx; /* void *ctx[size] - software context */
++};
++
++enum { /* for wil6210_priv.status */
++      wil_status_fwready = 0,
++      wil_status_fwconnected,
++      wil_status_dontscan,
++      wil_status_irqen, /* FIXME: interrupts enabled - for debug */
++};
++
++struct pci_dev;
++
++struct wil6210_stats {
++      u16 last_mcs_rx;
++      u16 bf_mcs; /* last BF, used for Tx */
++};
++
++struct wil6210_priv {
++      struct pci_dev *pdev;
++      int n_msi;
++      struct wireless_dev *wdev;
++      void __iomem *csr;
++      unsigned long status;
++      /* profile */
++      u32 monitor_flags;
++      struct ieee80211_channel *monitor_chan;
++      /* cached ISR registers */
++      u32 isr_rx;
++      u32 isr_tx;
++      u32 isr_misc;
++      /* mailbox related */
++      struct mutex wmi_mutex;
++      struct wil6210_mbox_ctl mbox_ctl;
++      struct completion wmi_ready;
++      u16 wmi_seq;
++      u16 reply_id; /**< wait for this WMI event */
++      void *reply_buf;
++      u8 reply_size;
++      struct workqueue_struct *wmi_wq; /* for deferred calls */
++      struct work_struct wmi_event_worker;
++      struct workqueue_struct *wmi_wq_conn; /* for connect worker */
++      struct work_struct wmi_connect_worker;
++      struct timer_list connect_timer;
++      int pending_connect_cid;
++      struct list_head pending_wmi_ev;
++      spinlock_t wmi_ev_lock;
++
++      /* DMA related */
++      struct vring vring_rx;
++      struct vring vring_tx[WIL6210_MAX_TX_RINGS];
++      u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN];
++      /* scan */
++      struct cfg80211_scan_request *scan_request;
++
++      struct mutex mutex;
++      /* statistics */
++      struct wil6210_stats stats;
++      /* debugfs */
++      struct dentry *debug;
++      struct debugfs_blob_wrapper fw_code_blob;
++      struct debugfs_blob_wrapper fw_data_blob;
++      struct debugfs_blob_wrapper fw_peri_blob;
++      struct debugfs_blob_wrapper uc_code_blob;
++      struct debugfs_blob_wrapper uc_data_blob;
++      struct debugfs_blob_wrapper rgf_blob;
++};
++
++#define wil_to_wiphy(i) (i->wdev->wiphy)
++#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i)))
++#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w))
++#define wil_to_wdev(i) (i->wdev)
++#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
++#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
++#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
++
++#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
++#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
++
++void memcpy_fromio_32(void *dst, const volatile void __iomem *src,
++              size_t count);
++void memcpy_toio_32(volatile void __iomem *dst, const void *src, size_t count);
++
++void *wil_if_alloc(struct device *dev, void __iomem *csr);
++void wil_if_free(struct wil6210_priv *wil);
++int wil_if_add(struct wil6210_priv *wil);
++void wil_if_remove(struct wil6210_priv *wil);
++int wil_priv_init(struct wil6210_priv *wil);
++void wil_priv_deinit(struct wil6210_priv *wil);
++int wil_reset(struct wil6210_priv *wil);
++void wil_link_on(struct wil6210_priv *wil);
++void wil_link_off(struct wil6210_priv *wil);
++int wil_up(struct wil6210_priv *wil);
++int wil_down(struct wil6210_priv *wil);
++
++void __iomem *wmi_buffer(struct wil6210_priv *wil, u32 ptr);
++void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
++int wmi_read_hdr(struct wil6210_priv *wil, u32 ptr,
++              struct wil6210_mbox_hdr *hdr);
++int wmi_send_cmd(struct wil6210_priv *wil, void *buf);
++void wmi_recv_cmd(struct wil6210_priv *wil);
++int wmi_call(struct wil6210_priv *wil, void *buf,
++              u16 reply_id, void *reply, u8 reply_size, int to_msec);
++void wmi_connect_worker(struct work_struct *work);
++void wmi_event_worker(struct work_struct *work);
++void wmi_event_flush(struct wil6210_priv *wil);
++
++int wil6210_init_irq(struct wil6210_priv *wil, int irq);
++void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
++void wil6210_disable_irq(struct wil6210_priv *wil);
++void wil6210_enable_irq(struct wil6210_priv *wil);
++
++int wil6210_debugfs_init(struct wil6210_priv *wil);
++void wil6210_debugfs_remove(struct wil6210_priv *wil);
++
++int wil6210_set_mac_address(struct wil6210_priv *wil, void *addr);
++int wil6210_set_bcon(struct wil6210_priv *wil, int bi, u16 wmi_nettype);
++void wil6210_disconnect(struct wil6210_priv *wil, void *bssid);
++
++int rx_init(struct wil6210_priv *wil);
++void rx_fini(struct wil6210_priv *wil);
++
++/* TX API */
++int vring_init_tx(struct wil6210_priv *wil, int id,
++              int size, int cid, int tid);
++void vring_fini_tx(struct wil6210_priv *wil, int id);
++
++netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
++void tx_complete(struct wil6210_priv *wil, int ringid);
++
++/* RX API */
++void rx_handle(struct wil6210_priv *wil);
++
++int iftype_nl2wmi(enum nl80211_iftype type);
++
++#endif /* __WIL6210_H__ */
+diff --git a/drivers/net/wireless/ath/wil6210/wil6210_rgf.h b/drivers/net/wireless/ath/wil6210/wil6210_rgf.h
+new file mode 100644
+index 0000000..803ec83
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/wil6210_rgf.h
+@@ -0,0 +1,93 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef WIL6210_RGF_H
++#define WIL6210_RGF_H
++
++/**
++ * Hardware registers map
++ *
++ */
++
++/**
++ * Mapping
++ * RGF File      | Host addr    |  FW addr
++ *               |              |
++ * user_rgf      | 0x000000     | 0x880000
++ *  dma_rgf      | 0x001000     | 0x881000
++ * pcie_rgf      | 0x002000     | 0x882000
++ *               |              |
++ */
++
++/* Where various structures placed in host address space */
++#define WIL6210_FW_HOST_OFF      (0x880000UL)
++
++#define HOSTADDR(fwaddr)        (fwaddr - WIL6210_FW_HOST_OFF)
++
++/**
++ * Interrupt control registers block
++ *
++ * each interrupt controlled by the same bit in all registers
++ */
++struct RGF_ICR {
++      u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */
++      u32 ICR; /* Cause, W1C/COR depending on ICC */
++      u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */
++      u32 ICS; /* Cause Set, WO */
++      u32 IMV; /* Mask, RW+S/C */
++      u32 IMS; /* Mask Set, write 1 to set */
++      u32 IMC; /* Mask Clear, write 1 to clear */
++} __packed;
++
++/* registers - FW addresses */
++#define RGF_USER_USER_SCRATCH_PAD       (0x8802bc)
++#define RGF_USER_USER_ICR               (0x880b4c) /* struct RGF_ICR */
++      #define BIT_USER_USER_ICR_SW_INT_2  BIT(18)
++#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
++#define RGF_USER_MAC_CPU_0              (0x8801fc)
++#define RGF_USER_USER_CPU_0             (0x8801e0)
++#define RGF_USER_CLKS_CTL_SW_RST_VEC_0  (0x880b04)
++#define RGF_USER_CLKS_CTL_SW_RST_VEC_1  (0x880b08)
++#define RGF_USER_CLKS_CTL_SW_RST_VEC_2  (0x880b0c)
++#define RGF_USER_CLKS_CTL_SW_RST_VEC_3  (0x880b10)
++
++#define RGF_DMA_PSEUDO_CAUSE            (0x881c68)
++#define RGF_DMA_PSEUDO_CAUSE_MASK_SW    (0x881c6c)
++#define RGF_DMA_PSEUDO_CAUSE_MASK_FW    (0x881c70)
++      #define BIT_DMA_PSEUDO_CAUSE_RX     BIT(0)
++      #define BIT_DMA_PSEUDO_CAUSE_TX     BIT(1)
++      #define BIT_DMA_PSEUDO_CAUSE_MISC   BIT(2)
++
++#define RGF_DMA_EP_TX_ICR               (0x881bb4) /* struct RGF_ICR */
++      #define BIT_DMA_EP_TX_ICR_TX_DONE   BIT(0)
++      #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n)   BIT(n+1) /* n = [0..23] */
++#define RGF_DMA_EP_RX_ICR               (0x881bd0) /* struct RGF_ICR */
++      #define BIT_DMA_EP_RX_ICR_RX_DONE   BIT(0)
++#define RGF_DMA_EP_MISC_ICR             (0x881bec) /* struct RGF_ICR */
++      #define BIT_DMA_EP_MISC_ICR_FW_INT0      BIT(28)
++      #define BIT_DMA_EP_MISC_ICR_FW_INT1      BIT(29)
++
++/* popular locations */
++#define HOST_MBOX   HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
++#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
++      offsetof(struct RGF_ICR, ICS))
++#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2
++
++/* ISR register bits */
++#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0
++#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1
++
++#endif /* WIL6210_RGF_H */
+diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
+new file mode 100644
+index 0000000..64efc82
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/wmi.c
+@@ -0,0 +1,710 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/pci.h>
++#include <linux/io.h>
++#include <linux/list.h>
++#include <linux/etherdevice.h>
++
++#include "wil6210.h"
++#include "wil6210_rgf.h"
++#include "wmi.h"
++
++/**
++ * WMI event receiving - theory of operations
++ *
++ * When firmware about to report WMI event, it fills memory area
++ * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for
++ * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler.
++ *
++ * @wmi_recv_cmd reads event, allocates memory chunk  and attaches it to the
++ * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up
++ * and handles events within the @wmi_event_worker. Every event get detached
++ * from list, processed and deleted.
++ *
++ * Purpose for this mechanism is to release IRQ thread; otherwise,
++ * if WMI event handling involves another WMI command flow, this 2-nd flow
++ * won't be completed because of blocked IRQ thread.
++ */
++
++/**
++ * Addressing - theory of operations
++ *
++ * There are several buses present on the WIL6210 card.
++ * Same memory areas are visible at different address on
++ * the different busses. There are 3 main bus masters:
++ *  - MAC CPU (ucode)
++ *  - User CPU (firmware)
++ *  - AHB (host)
++ *
++ * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing
++ * AHB addresses starting from 0x880000
++ *
++ * Internally, firmware uses addresses that allows faster access but
++ * are invisible from the host. To read from these addresses, alternative
++ * AHB address must be used.
++ *
++ * Memory mapping
++ * Linker address         PCI/Host address
++ *                        0x880000 .. 0xa80000  2Mb BAR0
++ * 0x800000 .. 0x807000   0x900000 .. 0x907000  28k DCCM
++ * 0x840000 .. 0x857000   0x908000 .. 0x91f000  92k PERIPH
++ */
++
++/**
++ * @fw_mapping provides memory remapping table
++ */
++static const struct {
++      u32 from; /* linker address - from, inclusive */
++      u32 to;   /* linker address - to, exclusive */
++      u32 host; /* PCI/Host address */
++} fw_mapping[] = {
++      {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */
++      {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
++      {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
++      {0x880000, 0x88a000, 0x880000}, /* various RGF */
++      {0x8c0000, 0x92a000, 0x8c0000}, /* trivial mapping for upper area */
++      /*
++       * 920000..928000 ucode code RAM
++       * 928000..92a000 ucode data RAM
++       */
++};
++
++/**
++ * return AHB address for given firmware/ucode internal (linker) address
++ * @x - internal address
++ * If address have no valid AHB mapping, return 0
++ */
++static u32 wmi_addr_remap(u32 x)
++{
++      int i;
++      for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
++              if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to))
++                      return x + fw_mapping[i].host - fw_mapping[i].from;
++      }
++      return 0;
++}
++
++/**
++ * Check address validity for WMI buffer; remap if needed
++ * @ptr - internal (linker) fw/ucode address
++ *
++ * Valid buffer should be DWORD aligned
++ *
++ * return address for accessing buffer from the host;
++ * if buffer is not valid, return NULL.
++ */
++void __iomem *wmi_buffer(struct wil6210_priv *wil, u32 ptr)
++{
++      u32 off;
++      if (ptr % 4)
++              return NULL;
++      ptr = wmi_addr_remap(ptr);
++      if (ptr < WIL6210_FW_HOST_OFF)
++              return NULL;
++      off = HOSTADDR(ptr);
++      if (off > WIL6210_MEM_SIZE - 4)
++              return NULL;
++      return wil->csr + off;
++}
++
++/**
++ * Check address validity
++ */
++void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr)
++{
++      u32 off;
++      if (ptr % 4)
++              return NULL;
++      if (ptr < WIL6210_FW_HOST_OFF)
++              return NULL;
++      off = HOSTADDR(ptr);
++      if (off > WIL6210_MEM_SIZE - 4)
++              return NULL;
++      return wil->csr + off;
++}
++
++int wmi_read_hdr(struct wil6210_priv *wil, u32 ptr,
++              struct wil6210_mbox_hdr *hdr)
++{
++      void __iomem *src = wmi_buffer(wil, ptr);
++      if (!src)
++              return -EINVAL;
++      memcpy_fromio_32(hdr, src, sizeof(*hdr));
++      return 0;
++}
++
++static int __wmi_send_cmd(struct wil6210_priv *wil, void *buf)
++{
++      struct wil6210_mbox_hdr *hdr = buf;
++      struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
++      struct wil6210_mbox_ring_desc d_head;
++      u32 next_head;
++      void __iomem *dst;
++      void __iomem *head = wmi_addr(wil, r->head);
++      int retry;
++      wil_info(wil, "%s()\n", __func__);
++      might_sleep();
++      if (!test_bit(wil_status_fwready, &wil->status)) {
++              wil_err(wil, "FW not ready\n");
++              return -EAGAIN;
++      }
++      if (!head) {
++              wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head);
++              return -EINVAL;
++      }
++      /* read Tx head */
++      memcpy_fromio_32(&d_head, head
++                      , sizeof(struct wil6210_mbox_ring_desc));
++      if (d_head.sync != 0) {
++              wil_err(wil, "WMI head busy\n");
++              return -EBUSY;
++      }
++      /* next head */
++      next_head = r->base + ((r->head - r->base
++                      + sizeof(struct wil6210_mbox_ring_desc)) % r->size);
++      wil_info(wil, "Head 0x%08x -> 0x%08x\n"
++                      , r->head, next_head);
++      /* wait till FW finish with previous command */
++      for (retry = 5; retry > 0; retry--) {
++              r->tail = ioread32(wil->csr + HOST_MBOX
++                              + offsetof(struct wil6210_mbox_ctl, tx.tail));
++              if (next_head != r->tail)
++                      break;
++              msleep(20);
++      }
++      if (next_head == r->tail) {
++              wil_err(wil, "WMI ring full\n");
++              return -EBUSY;
++      }
++      dst = wmi_buffer(wil, d_head.addr);
++      if (!dst) {
++              wil_err(wil, "invalid WMI buffer: 0x%08x\n", d_head.addr);
++              return -EINVAL;
++      }
++      hdr->seq = ++wil->wmi_seq;
++      /* set command */
++      if ((hdr->type == 0) &&  /* TODO: define */
++              (hdr->len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
++              struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]);
++              wil_info(wil, "WMI command 0x%04x\n", wmi->id);
++      }
++      print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf
++                      , sizeof(*hdr) + hdr->len, true);
++      memcpy_toio_32(dst, buf, sizeof(*hdr) + hdr->len);
++      /* mark entry as full */
++      iowrite32(1, wil->csr + HOSTADDR(r->head)
++                      + offsetof(struct wil6210_mbox_ring_desc, sync));
++      /* advance next ptr */
++      iowrite32(r->head = next_head, wil->csr + HOST_MBOX
++                      + offsetof(struct wil6210_mbox_ctl, tx.head));
++
++      /* interrupt to FW */
++      iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
++
++      return 0;
++}
++
++int wmi_send_cmd(struct wil6210_priv *wil, void *buf)
++{
++      int rc;
++      mutex_lock(&wil->wmi_mutex);
++      rc = __wmi_send_cmd(wil, buf);
++      mutex_unlock(&wil->wmi_mutex);
++      return rc;
++}
++
++/*=== Event handlers ===*/
++static void wmi_evt_ready(struct wil6210_priv *wil, int id,
++              void *d, int len)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      struct wireless_dev *wdev = wil->wdev;
++      struct WMI_READY_EVENT *evt = d;
++      wil_info(wil, "FW ver. %d; MAC %pM\n",
++                      evt->sw_version, evt->macaddr);
++      if (!is_valid_ether_addr(ndev->dev_addr)) {
++              memcpy(ndev->dev_addr, evt->macaddr, ETH_ALEN);
++              memcpy(ndev->perm_addr, evt->macaddr, ETH_ALEN);
++      }
++      snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version),
++                      "%d", evt->sw_version);
++}
++
++static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id,
++              void *d, int len)
++{
++      wil_info(wil, "WMI: FW ready\n");
++      set_bit(wil_status_fwready, &wil->status);
++      /* reuse wmi_ready for the firmware ready indication */
++      complete(&wil->wmi_ready);
++}
++
++static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id,
++              void *d, int len)
++{
++      struct WMI_RX_MGMT_PACKET_EVENT *data = d;
++      struct wiphy *wiphy = wil_to_wiphy(wil);
++      struct ieee80211_mgmt *rx_mgmt_frame =
++                      (struct ieee80211_mgmt *)data->payload;
++      int ch_no = data->channel+1;
++      u32 freq = ieee80211_channel_to_frequency(ch_no,
++                      IEEE80211_BAND_60GHZ);
++      struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq);
++      /* TODO convert LE to CPU */
++      s32 signal = data->rssi; /* TODO */
++      __le16 fc = rx_mgmt_frame->frame_control;
++      wil_info(wil, "MGMT: channel %d MCS %d RSSI %d\n",
++                      data->channel, data->mcs, data->rssi);
++      wil_info(wil, "status 0x%08x len %d\n",
++                      data->status, data->length);
++      wil_info(wil, "qid %d mid %d cid %d\n",
++                      data->qid, data->mid, data->cid);
++      if (!channel) {
++              wil_err(wil, "Frame on unsupported channel\n");
++              return;
++      }
++      if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
++              struct cfg80211_bss *bss;
++              u64 tsf = rx_mgmt_frame->u.beacon.timestamp;
++              u16 cap = rx_mgmt_frame->u.beacon.capab_info;
++              u16 bi = rx_mgmt_frame->u.beacon.beacon_int;
++              const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
++              size_t ie_len = data->length -
++                              offsetof(struct ieee80211_mgmt,
++                                              u.beacon.variable);
++              /* Hack to work around wrong cap. info TODO: remove when fixed*/
++              if (!(cap & WLAN_CAPABILITY_ESS)) {
++                      wil_info(wil, "No ESS bit in capabitity. Set it.\n");
++                      cap |= WLAN_CAPABILITY_ESS;
++              }
++              if (cap & WLAN_CAPABILITY_IBSS) {
++                      wil_info(wil, "IBSS bit in capabitity. Clear it.\n");
++                      cap &= ~WLAN_CAPABILITY_IBSS;
++              }
++              bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid,
++                              tsf, cap, bi, ie_buf, ie_len,
++                              signal, GFP_KERNEL);
++              if (bss) {
++                      wil_info(wil, "Added BSS %pM\n",
++                                      rx_mgmt_frame->bssid);
++                      cfg80211_put_bss(bss);
++              } else {
++                      wil_err(wil, "cfg80211_inform_bss() failed\n");
++              }
++      }
++}
++
++static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
++              void *d, int len)
++{
++      if (wil->scan_request) {
++              struct WMI_SCAN_COMPLETE_EVENT *data = d;
++              bool aborted = (data->status != 0);
++              wil_info(wil, "SCAN_COMPLETE(0x%08x)\n",
++                              data->status);
++              cfg80211_scan_done(wil->scan_request, aborted);
++              wil->scan_request = NULL;
++      } else {
++              wil_err(wil, "SCAN_COMPLETE while not scanning\n");
++      }
++}
++
++static void wmi_evt_connect(struct wil6210_priv *wil, int id,
++              void *d, int len)
++{
++      struct net_device *ndev = wil_to_ndev(wil);
++      struct wireless_dev *wdev = wil->wdev;
++      struct WMI_CONNECT_EVENT *evt = d;
++
++      if (len < sizeof(*evt)) {
++              wil_err(wil, "Connect event too short : %d bytes\n", len);
++              return;
++      }
++      if (len != sizeof(*evt) + evt->beaconIeLen + evt->assocReqLen
++                      + evt->assocRespLen) {
++              wil_err(wil,
++                      "Connect event corrupted : %d != %d + %d + %d + %d\n",
++                              len, (int)sizeof(*evt), evt->beaconIeLen,
++                              evt->assocReqLen, evt->assocRespLen);
++              return;
++      }
++      wil_info(wil, "Connect %pM channel [%d] cid %d\n",
++                      evt->bssid, evt->channel, evt->cid);
++      print_hex_dump(KERN_INFO, "connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
++                      evt->assocInfo, len - sizeof(*evt), true);
++      if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
++                      (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
++              /* capinfo + listen interval */
++              u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
++              /* capinfo + status code +  associd */
++              u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) +
++                              sizeof(u16);
++              u8 *assoc_req_ie = &evt->assocInfo[evt->beaconIeLen
++                              + assoc_req_ie_offset];
++              u8 *assoc_resp_ie = &evt->assocInfo[evt->beaconIeLen
++                              + evt->assocReqLen + assoc_resp_ie_offset];
++
++              u8 assoc_req_ielen = evt->assocReqLen - assoc_req_ie_offset;
++              u8 assoc_resp_ielen = evt->assocRespLen - assoc_resp_ie_offset;
++              if (wdev->sme_state != CFG80211_SME_CONNECTING) {
++                      wil_err(wil, "Not in connecting state\n");
++                      return;
++              }
++              if (evt->assocReqLen < assoc_req_ie_offset) {
++                      wil_info(wil, "No Assoc. Req. info\n");
++                      assoc_req_ie = NULL;
++                      assoc_req_ielen = 0;
++              }
++              if (evt->assocRespLen < assoc_resp_ie_offset) {
++                      wil_info(wil, "No Assoc. Resp. info\n");
++                      assoc_resp_ie = NULL;
++                      assoc_resp_ielen = 0;
++              }
++              del_timer_sync(&wil->connect_timer);
++              cfg80211_connect_result(ndev, evt->bssid,
++                              assoc_req_ie, assoc_req_ielen,
++                              assoc_resp_ie, assoc_resp_ielen,
++                              WLAN_STATUS_SUCCESS, GFP_KERNEL);
++
++      }
++      set_bit(wil_status_fwconnected, &wil->status);
++
++      /* FIXME FW can transmit only ucast frames to peer */
++      /* FIXME real ring_id instead of hard coded 0 */
++      memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN);
++
++      wil->pending_connect_cid = evt->cid;
++      queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker);
++}
++
++static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
++              void *d, int len)
++{
++      struct WMI_DISCONNECT_EVENT *evt = d;
++      wil_info(wil, "Disconnect %pM reason %d proto %d wmi\n",
++                      evt->bssid,
++                      evt->protocolReasonStatus, evt->disconnectReason);
++      wil6210_disconnect(wil, evt->bssid);
++      clear_bit(wil_status_dontscan, &wil->status);
++}
++
++static void wmi_evt_notify(struct wil6210_priv *wil, int id,
++              void *d, int len)
++{
++      struct WMI_NOTIFY_REQ_DONE_EVENT *evt = d;
++      union {
++              u64 tsf;
++              u32 tsf_part[2];
++      } tsf;
++      if (len < sizeof(*evt)) {
++              wil_err(wil, "Short NOTIFY event\n");
++              return;
++      }
++      tsf.tsf_part[0] = evt->tsf_lo;
++      tsf.tsf_part[1] = evt->tsf_hi;
++
++      wil_info(wil, "Link status, MCS %d TSF 0x%016llx\n"
++                      "BF status 0x%08x metric 0x%08x\n"
++                      "Tx Tpt %d goodput %d Rx goodput %d\n"
++                      "Sectors(rx:tx) my %d:%d peer %d:%d\n",
++                      evt->bf_mcs, tsf.tsf,
++                      evt->bf_status, evt->bf_metric,
++                      evt->tx_tpt, evt->tx_gput, evt->rx_gput,
++                      evt->my_rx_sector, evt->my_tx_sector,
++                      evt->peer_rx_sector, evt->peer_tx_sector);
++      wil->stats.bf_mcs = evt->bf_mcs;
++}
++
++static const struct {
++      int eventid;
++      void (*handler)(struct wil6210_priv *wil, int eventid,
++                      void *data, int data_len);
++} wmi_evt_handlers[] = {
++      {WMI_READY_EVENTID,          wmi_evt_ready},
++      {WMI_FW_READY_EVENTID,       wmi_evt_fw_ready},
++      {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt},
++      {WMI_SCAN_COMPLETE_EVENTID,  wmi_evt_scan_complete},
++      {WMI_CONNECT_EVENTID,        wmi_evt_connect},
++      {WMI_DISCONNECT_EVENTID,     wmi_evt_disconnect},
++      {WMI_NOTIFY_REQ_DONE_EVENTID,     wmi_evt_notify},
++};
++
++void wmi_recv_cmd(struct wil6210_priv *wil)
++{
++      struct wil6210_mbox_ring_desc d_tail;
++      struct wil6210_mbox_hdr hdr;
++      struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx;
++      struct pending_wmi_event *evt;
++      u8 *cmd;
++      void __iomem *src;
++      unsigned long flags;
++      wil_info(wil, "%s()\n", __func__);
++      for (;;) {
++              r->head = ioread32(wil->csr + HOST_MBOX
++                              + offsetof(struct wil6210_mbox_ctl, rx.head));
++              if (r->tail == r->head)
++                      return;
++              /* read cmd from tail */
++              memcpy_fromio_32(&d_tail,
++                              wil->csr + HOSTADDR(r->tail),
++                              sizeof(struct wil6210_mbox_ring_desc));
++              if (d_tail.sync == 0) {
++                      wil_err(wil, "Mbox evt not owned by FW?\n");
++                      return;
++              }
++              if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
++                      wil_err(wil, "Mbox evt at 0x%08x?\n",
++                                      d_tail.addr);
++                      return;
++              }
++              src = wmi_buffer(wil, d_tail.addr)
++                      + sizeof(struct wil6210_mbox_hdr);
++              evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event,
++                              event.wmi) + hdr.len, 4), GFP_KERNEL);
++              if (!evt) {
++                      wil_err(wil, "kmalloc for WMI event (%d) failed\n",
++                                      hdr.len);
++                      return;
++              }
++              evt->event.mbox_hdr = hdr;
++              cmd = (void *)&evt->event.wmi;
++              memcpy_fromio_32(cmd, src, hdr.len);
++              /* mark entry as empty */
++              iowrite32(0, wil->csr + HOSTADDR(r->tail)
++                      + offsetof(struct wil6210_mbox_ring_desc, sync));
++              /* indicate */
++              wil_info(wil, "Mbox evt %04x %04x %04x %02x %02x\n",
++                      hdr.seq, hdr.ctx, hdr.type, hdr.flags, hdr.len);
++              if ((hdr.type == 0) &&  /* TODO: define */
++                      (hdr.len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
++                      wil_info(wil, "WMI event 0x%04x\n",
++                                      evt->event.wmi.id);
++              }
++              print_hex_dump(KERN_INFO, "evt ", DUMP_PREFIX_OFFSET, 16, 1,
++                              &evt->event.mbox_hdr, sizeof(hdr) + hdr.len,
++                              true);
++              /* advance tail */
++              r->tail = r->base + ((r->tail - r->base
++                      + sizeof(struct wil6210_mbox_ring_desc)) % r->size);
++              iowrite32(r->tail, wil->csr + HOST_MBOX
++                              + offsetof(struct wil6210_mbox_ctl, rx.tail));
++              /* add to the pending list */
++              spin_lock_irqsave(&wil->wmi_ev_lock, flags);
++              list_add_tail(&evt->list, &wil->pending_wmi_ev);
++              spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
++              {
++                      int q = queue_work(wil->wmi_wq,
++                                      &wil->wmi_event_worker);
++                      wil_info(wil, "queue_work -> %d\n", q);
++              }
++      }
++}
++
++int wmi_call(struct wil6210_priv *wil, void *buf,
++              u16 reply_id, void *reply, u8 reply_size, int to_msec)
++{
++      int rc;
++      int remain;
++      struct wil6210_mbox_wmi {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++      } __packed * wmi = buf;
++      mutex_lock(&wil->wmi_mutex);
++      rc = __wmi_send_cmd(wil, buf);
++      if (rc)
++              goto out;
++      wil->reply_id = reply_id;
++      wil->reply_buf = reply;
++      wil->reply_size = reply_size;
++      remain = wait_for_completion_timeout(&wil->wmi_ready,
++                      msecs_to_jiffies(to_msec));
++      if (0 == remain) {
++              wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n",
++                              wmi->wmi.id, reply_id, to_msec);
++              rc = -ETIME;
++      } else {
++              wil_info(wil,
++                      "wmi_call(0x%04x->0x%04x) completed in %d msec\n",
++                      wmi->wmi.id, reply_id,
++                      to_msec - jiffies_to_msecs(remain));
++      }
++      wil->reply_id = 0;
++      wil->reply_buf = NULL;
++      wil->reply_size = 0;
++ out:
++      mutex_unlock(&wil->wmi_mutex);
++      return rc;
++}
++
++int wil6210_set_mac_address(struct wil6210_priv *wil, void *addr)
++{
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_SET_MAC_ADDRESS_CMD mac;
++      } __packed cmd = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 0,
++                      .type = 0,
++                      .flags = 0,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_SET_MAC_ADDRESS_CMD),
++              },
++              .wmi = {
++                      .id = WMI_SET_MAC_ADDRESS_CMDID,
++                      .info1 = 0,
++              },
++              .mac = {
++              },
++      };
++      memcpy(cmd.mac.macaddr, addr, ETH_ALEN);
++      wil_info(wil, "Set MAC %pM\n", addr);
++      return wmi_send_cmd(wil, &cmd);
++}
++
++int wil6210_set_bcon(struct wil6210_priv *wil, int bi, u16 wmi_nettype)
++{
++      struct {
++              struct wil6210_mbox_hdr hdr;
++              struct wil6210_mbox_hdr_wmi wmi;
++              struct WMI_SET_BEACON_INT_CMD bcon;
++      } __packed cmd = {
++              .hdr = {
++                      .seq = 1,
++                      .ctx = 0,
++                      .type = 0,
++                      .flags = 0,
++                      .len = sizeof(struct wil6210_mbox_hdr_wmi)
++                      + sizeof(struct WMI_SET_BEACON_INT_CMD),
++              },
++              .wmi = {
++                      .id = WMI_SET_BEACON_INT_CMDID,
++                      .info1 = 0,
++              },
++              .bcon = {
++                      .bcon_interval = bi,
++                      .network_type = wmi_nettype,
++              },
++      };
++      return wmi_send_cmd(wil, &cmd);
++}
++
++void wmi_event_flush(struct wil6210_priv *wil)
++{
++      struct pending_wmi_event *evt, *t;
++      wil_info(wil, "%s()\n", __func__);
++      list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) {
++              list_del(&evt->list);
++              kfree(evt);
++      }
++}
++
++static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id,
++              void *d, int len)
++{
++      int i;
++      for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) {
++              if (wmi_evt_handlers[i].eventid == id) {
++                      wmi_evt_handlers[i].handler(wil, id, d, len);
++                      return true;
++              }
++      }
++      return false;
++}
++
++static void wmi_event_handle(struct wil6210_priv *wil,
++              struct wil6210_mbox_hdr *hdr)
++{
++      wil_info(wil, "%s()\n", __func__);
++      if ((hdr->type == 0) &&  /* TODO: define */
++              (hdr->len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
++              struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]);
++              void *evt_data = (void *)(&wmi[1]);
++              /* check if someone waits for this event */
++              if (wil->reply_id && wil->reply_id == wmi->id) {
++                      if (wil->reply_buf) {
++                              memcpy(wil->reply_buf, wmi,
++                                      min(hdr->len, wil->reply_size));
++                      } else {
++                              wmi_evt_call_handler(wil, wmi->id, evt_data,
++                                              hdr->len - sizeof(*wmi));
++                      }
++                      wil_info(wil, "Complete WMI 0x%04x\n",
++                                      wmi->id);
++                      complete(&wil->wmi_ready);
++                      return;
++              }
++              /* unsolicited event */
++              /* search for handler */
++              if (!wmi_evt_call_handler(wil, wmi->id, evt_data,
++                              hdr->len - sizeof(*wmi))) {
++                      wil_err(wil, "Unhandled event 0x%04x\n",
++                                      wmi->id);
++              }
++      } else {
++              wil_err(wil, "Unknown event type\n");
++              print_hex_dump(KERN_INFO, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1,
++                              hdr, sizeof(*hdr) + hdr->len, true);
++      }
++}
++
++static struct list_head *next_wmi_ev(struct wil6210_priv *wil)
++{
++      unsigned long flags;
++      struct list_head *ret = NULL;
++      spin_lock_irqsave(&wil->wmi_ev_lock, flags);
++      if (!list_empty(&wil->pending_wmi_ev)) {
++              ret = wil->pending_wmi_ev.next;
++              list_del(ret);
++      }
++      spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
++      return ret;
++}
++
++void wmi_event_worker(struct work_struct *work)
++{
++      struct wil6210_priv *wil = container_of(work,
++                      struct wil6210_priv, wmi_event_worker);
++      struct pending_wmi_event *evt;
++      struct list_head *lh;
++      wil_info(wil, "%s()\n", __func__);
++      while ((lh = next_wmi_ev(wil)) != NULL) {
++              evt = list_entry(lh, struct pending_wmi_event, list);
++              wmi_event_handle(wil, &evt->event.mbox_hdr);
++              kfree(evt);
++      }
++      wil_info(wil, "%s() done\n", __func__);
++}
++
++void wmi_connect_worker(struct work_struct *work)
++{
++      int rc;
++      struct wil6210_priv *wil = container_of(work,
++                      struct wil6210_priv, wmi_connect_worker);
++      if (wil->pending_connect_cid < 0) {
++              wil_err(wil, "No connection pending\n");
++              return;
++      }
++      wil_info(wil, "Configure for connection CID %d\n",
++                      wil->pending_connect_cid);
++      rc = vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE,
++                      wil->pending_connect_cid, 0);
++      wil->pending_connect_cid = -1;
++      if (rc == 0)
++              wil_link_on(wil);
++}
+diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
+new file mode 100644
+index 0000000..98c1a6f
+--- /dev/null
++++ b/drivers/net/wireless/ath/wil6210/wmi.h
+@@ -0,0 +1,837 @@
++/*
++ * Copyright (c) 2012 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef __WMI_H__
++#define __WMI_H__
++/* WMI API */
++
++/*
++ * List of Commnands
++ */
++enum WMI_COMMAND_ID {
++      WMI_CONNECT_CMDID = 0x0001,
++      WMI_RECONNECT_CMDID,
++      WMI_DISCONNECT_CMDID,
++      WMI_SYNCHRONIZE_CMDID,
++      WMI_CREATE_PSTREAM_CMDID,
++      WMI_DELETE_PSTREAM_CMDID,
++      WMI_START_SCAN_CMDID,
++      WMI_SET_SCAN_PARAMS_CMDID,
++      WMI_SET_BSS_FILTER_CMDID,
++      WMI_SET_PROBED_SSID_CMDID, /* 10 */
++      WMI_SET_LISTEN_INT_CMDID,
++      WMI_SET_BMISS_TIME_CMDID,
++      WMI_SET_DISC_TIMEOUT_CMDID,
++      WMI_GET_CHANNEL_LIST_CMDID,
++      WMI_SET_BEACON_INT_CMDID,
++      WMI_GET_STATISTICS_CMDID,
++      WMI_SET_CHANNEL_PARAMS_CMDID,
++      WMI_SET_POWER_MODE_CMDID,
++      WMI_SET_IBSS_PM_CAPS_CMDID,
++      WMI_SET_POWER_PARAMS_CMDID, /* 20 */
++      WMI_SET_POWERSAVE_TIMERS_POLICY_CMDID,
++      WMI_ADD_CIPHER_KEY_CMDID,
++      WMI_DELETE_CIPHER_KEY_CMDID,
++      WMI_ADD_KRK_CMDID,
++      WMI_DELETE_KRK_CMDID,
++      WMI_SET_PMKID_CMDID,
++      WMI_SET_TX_PWR_CMDID,
++      WMI_GET_TX_PWR_CMDID,
++      WMI_SET_ASSOC_INFO_CMDID,
++      WMI_ADD_BAD_AP_CMDID, /* 30 */
++      WMI_DELETE_BAD_AP_CMDID,
++      WMI_SET_TKIP_COUNTERMEASURES_CMDID,
++      WMI_RSSI_THRESHOLD_PARAMS_CMDID,
++      WMI_TARGET_ERROR_REPORT_BITMASK_CMDID,
++      WMI_SET_ACCESS_PARAMS_CMDID,
++      WMI_SET_RETRY_LIMITS_CMDID,
++      WMI_SET_OPT_MODE_CMDID,
++      WMI_OPT_TX_FRAME_CMDID,
++      WMI_SET_VOICE_PKT_SIZE_CMDID,
++      WMI_SET_MAX_SP_LEN_CMDID, /* 40 */
++      WMI_SET_ROAM_CTRL_CMDID,
++      WMI_GET_ROAM_TBL_CMDID,
++      WMI_GET_ROAM_DATA_CMDID,
++      WMI_ENABLE_RM_CMDID,
++      WMI_SET_MAX_OFFHOME_DURATION_CMDID,
++      WMI_EXTENSION_CMDID, /* Non-wireless extensions */
++      WMI_SNR_THRESHOLD_PARAMS_CMDID,
++      WMI_LQ_THRESHOLD_PARAMS_CMDID,
++      WMI_SET_LPREAMBLE_CMDID,
++      WMI_SET_RTS_CMDID, /* 50 */
++      WMI_CLR_RSSI_SNR_CMDID,
++      WMI_SET_FIXRATES_CMDID,
++      WMI_GET_FIXRATES_CMDID,
++      WMI_SET_AUTH_MODE_CMDID,
++      WMI_SET_REASSOC_MODE_CMDID,
++      WMI_SET_WMM_CMDID,
++      WMI_SET_WMM_TXOP_CMDID,
++      WMI_TEST_CMDID,
++      /* COEX AR6002 only*/
++      WMI_SET_BT_STATUS_CMDID,
++      WMI_SET_BT_PARAMS_CMDID, /* 60 */
++
++      WMI_SET_KEEPALIVE_CMDID,
++      WMI_GET_KEEPALIVE_CMDID,
++      WMI_SET_APPIE_CMDID,
++      WMI_GET_APPIE_CMDID,
++      WMI_SET_WSC_STATUS_CMDID,
++
++      /* Wake on Wireless */
++      WMI_SET_HOST_SLEEP_MODE_CMDID,
++      WMI_SET_WOW_MODE_CMDID,
++      WMI_GET_WOW_LIST_CMDID,
++      WMI_ADD_WOW_PATTERN_CMDID,
++      WMI_DEL_WOW_PATTERN_CMDID, /* 70 */
++
++      WMI_SET_FRAMERATES_CMDID,
++      WMI_SET_AP_PS_CMDID,
++      WMI_SET_QOS_SUPP_CMDID,
++
++      /* WILOCITY types */
++      WMI_MEM_READ_CMDID = 0x0800, /* WILOCITY types */
++      WMI_MEM_WR_CMDID = 0x0801,
++      WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300,
++      WMI_ECHO_CMDID = 0x0803,
++      WMI_DEEP_ECHO_CMDID = 0x0804,
++      WMI_CONFIG_MAC_CMDID = 0x0805,
++      WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806,
++      WMI_ADD_STATION_CMDID = 0x0807,
++      WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808,
++      WMI_PHY_GET_STATISTICS_CMDID = 0x0809,
++      WMI_FS_TUNE_CMDID = 0x080A,
++      WMI_CORR_MEASURE_CMDID = 0x080B,
++      WMI_TEMP_SENSE_CMDID = 0x080E,
++      WMI_DC_CALIB_CMDID = 0x080F,
++      WMI_SEND_TONE_CMDID = 0x0810,
++      WMI_IQ_TX_CALIB_CMDID = 0x0811,
++      WMI_IQ_RX_CALIB_CMDID = 0x0812,
++      WMI_SET_UCODE_IDLE_CMDID = 0x0813,
++      WMI_SET_CHANNEL_IND_CMDID = 0x0814,
++      WMI_SET_WORK_MODE_CMDID = 0x0815,
++      WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816,
++      WMI_WIL6210_R_ACTIVATE_CMDID = 0x0817,
++      WMI_WIL6210_R_READ_CMDID = 0x0818,
++      WMI_WIL6210_R_WRITE_CMDID = 0x0819,
++      WMI_WIL6210_R_TXRX_SEL_CMDID = 0x081A,
++      MAC_IO_STATIC_PARAMS_CMD = 0x081B,
++      MAC_IO_DYNAMIC_PARAMS_CMD = 0x081C,
++      WMI_SILENT_RSSI_CALIB_CMDID = 0x081D,
++
++      WMI_CFG_RX_CHAIN_CMDID = 0x0820,
++      WMI_VRING_CFG_CMDID = 0x0821,
++      WMI_RN_ON_CMDID = 0x0822,
++      WMI_VRING_BA_EN_CMDID = 0x0823,
++      WMI_VRING_BA_DIS_CMDID = 0x0824,
++      WMI_RCP_ADDBA_RESP_CMDID = 0x0825,
++      WMI_RCP_DELBA_CMDID = 0x0826,
++
++      WMI_READ_MAC_RXQ_CMDID = 0x0830,
++      WMI_READ_MAC_TXQ_CMDID = 0x0831,
++      WMI_WRITE_MAC_RXQ_CMDID = 0x0832,
++      WMI_WRITE_MAC_TXQ_CMDID = 0x0833,
++      WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x0834,
++
++      WMI_MLME_PUSH_CMDID = 0x0835,
++      WMI_BF_START_TXSS_CMDID = 0x0836,
++      WMI_BF_TXSS_MGMT_CMDID = 0x0837,
++      WMI_BF_SM_MGMT_CMDID = 0x0838,
++      WMI_BF_RXSS_MGMT_CMDID = 0x0839,
++      WMI_RS_MGMT_CMDID = 0x0852,
++
++      WMI_LINK_START_CMDID = 0x0850,
++      WMI_LINK_STOP_CMDID = 0x0851,
++
++      /* Performance monitoring commands */
++      WMI_BF_CTRL_CMDID = 0x0862,
++      WMI_NOTIFY_REQ_CMDID = 0x0863,
++
++      WMI_UNIT_TEST_CMD_ID = 0x0900,
++      WMI_HICCUP_CMD_ID = 0x0901,
++
++#if 0
++      WMI_TXRX_SEND_CMDID                             = 0x0810,
++#endif
++      /* WMI_THIN_RESERVED_... mark the start and end
++       * values for WMI_THIN_RESERVED command IDs. These
++       * command IDs can be found in wmi_thin.h */
++      WMI_THIN_RESERVED_START = 0x8000,
++      WMI_THIN_RESERVED_END = 0x8fff,
++      /*
++       * Developer commands starts at 0xF000
++       */
++      WMI_SET_BITRATE_CMDID = 0xF000,
++      WMI_GET_BITRATE_CMDID,
++      WMI_SET_WHALPARAM_CMDID,
++
++      /*Should add the new command to the tail for compatible with
++       * etna.
++       */
++      WMI_SET_MAC_ADDRESS_CMDID,
++      WMI_SET_AKMP_PARAMS_CMDID,
++      WMI_SET_PMKID_LIST_CMDID,
++      WMI_GET_PMKID_LIST_CMDID,
++      WMI_ABORT_SCAN_CMDID,
++      WMI_SET_TARGET_EVENT_REPORT_CMDID,
++
++      /* Unused */
++      WMI_UNUSED1,
++      WMI_UNUSED2,
++
++      /*
++       * AP mode commands
++       */
++      WMI_AP_HIDDEN_SSID_CMDID, /* F00B */
++      WMI_AP_SET_NUM_STA_CMDID,
++      WMI_AP_ACL_POLICY_CMDID,
++      WMI_AP_ACL_MAC_LIST_CMDID,
++      WMI_AP_CONFIG_COMMIT_CMDID,
++      WMI_AP_SET_MLME_CMDID, /* F010 */
++      WMI_AP_SET_PVB_CMDID,
++      WMI_AP_CONN_INACT_CMDID,
++      WMI_AP_PROT_SCAN_TIME_CMDID,
++      WMI_AP_SET_COUNTRY_CMDID,
++      WMI_AP_SET_DTIM_CMDID,
++      WMI_AP_MODE_STAT_CMDID,
++
++      WMI_SET_IP_CMDID, /* F017 */
++      WMI_SET_PARAMS_CMDID,
++      WMI_SET_MCAST_FILTER_CMDID,
++      WMI_DEL_MCAST_FILTER_CMDID,
++
++      WMI_ALLOW_AGGR_CMDID, /* F01B */
++      WMI_ADDBA_REQ_CMDID,
++      WMI_DELBA_REQ_CMDID,
++      WMI_SET_HT_CAP_CMDID,
++      WMI_SET_HT_OP_CMDID,
++      WMI_SET_TX_SELECT_RATES_CMDID,
++      WMI_SET_TX_SGI_PARAM_CMDID,
++      WMI_SET_RATE_POLICY_CMDID,
++
++      WMI_HCI_CMD_CMDID, /* F023 */
++      WMI_RX_FRAME_FORMAT_CMDID,
++      WMI_SET_THIN_MODE_CMDID,
++      WMI_SET_BT_WLAN_CONN_PRECEDENCE_CMDID,
++
++      WMI_AP_SET_11BG_RATESET_CMDID, /* F027 */
++      WMI_SET_PMK_CMDID,
++      WMI_MCAST_FILTER_CMDID,
++      /* COEX CMDID AR6003*/
++      WMI_SET_BTCOEX_FE_ANT_CMDID, /* F02A */
++      WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID,
++      WMI_SET_BTCOEX_SCO_CONFIG_CMDID,
++      WMI_SET_BTCOEX_A2DP_CONFIG_CMDID,
++      WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMDID,
++      WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMDID,
++      WMI_SET_BTCOEX_DEBUG_CMDID,
++      WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID,
++      WMI_GET_BTCOEX_STATS_CMDID,
++      WMI_GET_BTCOEX_CONFIG_CMDID,
++
++      WMI_SET_DFS_ENABLE_CMDID, /* F034 */
++      WMI_SET_DFS_MINRSSITHRESH_CMDID,
++      WMI_SET_DFS_MAXPULSEDUR_CMDID,
++      WMI_DFS_RADAR_DETECTED_CMDID,
++
++      /* P2P CMDS */
++      WMI_P2P_SET_CONFIG_CMDID, /* F038 */
++      WMI_WPS_SET_CONFIG_CMDID,
++      WMI_SET_REQ_DEV_ATTR_CMDID,
++      WMI_P2P_FIND_CMDID,
++      WMI_P2P_STOP_FIND_CMDID,
++      WMI_P2P_GO_NEG_START_CMDID,
++      WMI_P2P_LISTEN_CMDID,
++
++      WMI_CONFIG_TX_MAC_RULES_CMDID, /* F040 */
++      WMI_SET_PROMISCUOUS_MODE_CMDID,
++      WMI_RX_FRAME_FILTER_CMDID,
++      WMI_SET_CHANNEL_CMDID,
++
++      /* WAC commands */
++      WMI_ENABLE_WAC_CMDID,
++      WMI_WAC_SCAN_REPLY_CMDID,
++      WMI_WAC_CTRL_REQ_CMDID,
++      WMI_SET_DIV_PARAMS_CMDID,
++
++      WMI_GET_PMK_CMDID,
++      WMI_SET_PASSPHRASE_CMDID,
++      WMI_SEND_ASSOC_RES_CMDID,
++      WMI_SET_ASSOC_REQ_RELAY_CMDID,
++};
++
++/*
++ * List of Events (target to host)
++ */
++enum WMI_EVENT_ID {
++      WMI_READY_EVENTID = 0x1001,
++      WMI_CONNECT_EVENTID,
++      WMI_DISCONNECT_EVENTID,
++      WMI_BSSINFO_EVENTID,
++      WMI_CMDERROR_EVENTID,
++      WMI_REGDOMAIN_EVENTID,
++      WMI_PSTREAM_TIMEOUT_EVENTID,
++      WMI_NEIGHBOR_REPORT_EVENTID,
++      WMI_TKIP_MICERR_EVENTID,
++      WMI_SCAN_COMPLETE_EVENTID, /* 0x100a */
++      WMI_REPORT_STATISTICS_EVENTID,
++      WMI_RSSI_THRESHOLD_EVENTID,
++      WMI_ERROR_REPORT_EVENTID,
++      WMI_OPT_RX_FRAME_EVENTID,
++      WMI_REPORT_ROAM_TBL_EVENTID,
++      WMI_EXTENSION_EVENTID,
++      WMI_CAC_EVENTID,
++      WMI_SNR_THRESHOLD_EVENTID,
++      WMI_LQ_THRESHOLD_EVENTID,
++      WMI_TX_RETRY_ERR_EVENTID, /* 0x1014 */
++      WMI_REPORT_ROAM_DATA_EVENTID,
++      WMI_TEST_EVENTID,
++      WMI_APLIST_EVENTID,
++      WMI_GET_WOW_LIST_EVENTID,
++      WMI_GET_PMKID_LIST_EVENTID,
++      WMI_CHANNEL_CHANGE_EVENTID,
++      WMI_PEER_NODE_EVENTID,
++      WMI_PSPOLL_EVENTID,
++      WMI_DTIMEXPIRY_EVENTID,
++      WMI_WLAN_VERSION_EVENTID,
++      WMI_SET_PARAMS_REPLY_EVENTID,
++      WMI_ADDBA_REQ_EVENTID, /*0x1020 */
++      WMI_ADDBA_RESP_EVENTID,
++      WMI_DELBA_REQ_EVENTID,
++      WMI_TX_COMPLETE_EVENTID,
++      WMI_HCI_EVENT_EVENTID,
++      WMI_ACL_DATA_EVENTID,
++      WMI_REPORT_SLEEP_STATE_EVENTID,
++      WMI_REPORT_BTCOEX_STATS_EVENTID,
++      WMI_REPORT_BTCOEX_CONFIG_EVENTID,
++      WMI_GET_PMK_EVENTID,
++
++      /* DFS Events */
++      WMI_DFS_HOST_ATTACH_EVENTID,
++      WMI_DFS_HOST_INIT_EVENTID,
++      WMI_DFS_RESET_DELAYLINES_EVENTID,
++      WMI_DFS_RESET_RADARQ_EVENTID,
++      WMI_DFS_RESET_AR_EVENTID,
++      WMI_DFS_RESET_ARQ_EVENTID,
++      WMI_DFS_SET_DUR_MULTIPLIER_EVENTID,
++      WMI_DFS_SET_BANGRADAR_EVENTID,
++      WMI_DFS_SET_DEBUGLEVEL_EVENTID,
++      WMI_DFS_PHYERR_EVENTID,
++      /* CCX Evants */
++      WMI_CCX_RM_STATUS_EVENTID,
++
++      /* P2P Events */
++      WMI_P2P_GO_NEG_RESULT_EVENTID,
++
++      WMI_WAC_SCAN_DONE_EVENTID,
++      WMI_WAC_REPORT_BSS_EVENTID,
++      WMI_WAC_START_WPS_EVENTID,
++      WMI_WAC_CTRL_REQ_REPLY_EVENTID,
++      WMI_REPORT_WMM_PARAMS_EVENTID,
++
++      /* WILOCITY types */
++      WMI_IMM_RSP_EVENTID = 0x0000,
++      WMI_RD_MEM_RSP_EVENTID = 0x1800,
++      WMI_FW_READY_EVENTID = 0x1801,
++      WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200,
++      WMI_ECHO_RSP_EVENTID = 0x1803,
++      WMI_CONFIG_MAC_DONE_EVENTID = 0x1805,
++      WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806,
++      WMI_ADD_STATION_DONE_EVENTID = 0x1807,
++      WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808,
++      WMI_PHY_GET_STATISTICS_EVENTID = 0x1809,
++      WMI_FS_TUNE_DONE_EVENTID = 0x180A,
++      WMI_CORR_MEASURE_DONE_EVENTID = 0x180B,
++      WMI_TEMP_SENSE_DONE_EVENTID = 0x180E,
++      WMI_DC_CALIB_DONE_EVENTID = 0x180F,
++      WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811,
++      WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812,
++      WMI_SET_CHANNEL_DONE_EVENTID = 0x1814,
++      WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815,
++      WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816,
++      WMI_WIL6210_R_ACTIVATE_DONE_EVENTID = 0x1817,
++      WMI_WIL6210_R_READ_DONE_EVENTID = 0x1818,
++      WMI_WIL6210_R_WRITE_DONE_EVENTID = 0x1819,
++      WMI_WIL6210_R_TXRX_SEL_DONE_EVENTID = 0x181A,
++      WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181D,
++
++      WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820,
++      WMI_VRING_CFG_DONE_EVENTID = 0x1821,
++      WMI_RX_ON_EVENTID = 0x1822,
++      WMI_BA_STATUS_EVENTID = 0x1823,
++      WMI_RCP_ADDBA_REQ_EVENTID = 0x1824,
++      WMI_ADDBA_RESP_SENT_EVENTID = 0x1825,
++      WMI_DELBA_EVENTID = 0x1826,
++
++      WMI_READ_MAC_RXQ_EVENTID = 0x1830,
++      WMI_READ_MAC_TXQ_EVENTID = 0x1831,
++      WMI_WRITE_MAC_RXQ_EVENTID = 0x1832,
++      WMI_WRITE_MAC_TXQ_EVENTID = 0x1833,
++      WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834,
++
++      WMI_BF_START_TXSS_DONE_EVENTID = 0x1836,
++      WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837,
++      WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839,
++      WMI_RS_MGMT_DONE_EVENTID = 0x1852,
++      WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838,
++      WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
++      WMI_LINK_START_DONE_EVENTID = 0x1850,
++      WMI_LINK_STOP_DONE_EVENTID = 0x1851,
++
++      /* Performance monitoring events */
++      WMI_WBE_LINKUP_EVENTID = 0x1860,
++      WMI_WBE_LINKDOWN_EVENTID = 0x1861,
++
++      WMI_BF_CTRL_DONE_EVENTID = 0x1862,
++      WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863,
++
++      WMI_UNIT_TEST_EVENTID = 0x1900,
++
++      WMI_THIN_RESERVED_START_EVENTID = 0x8000,
++      /* Events in this range are reserved for thinmode
++       * See wmi_thin.h for actual definitions */
++      WMI_THIN_RESERVED_END_EVENTID = 0x8fff,
++
++      WMI_SET_CHANNEL_EVENTID,
++      WMI_ASSOC_REQ_EVENTID
++};
++
++/*
++ * WMI_READY_EVENTID
++ */
++struct WMI_READY_EVENT {
++      u32 sw_version;
++      u32 abi_version;
++      u8 macaddr[ETH_ALEN];
++      u8 phyCapability; /* WMI_PHY_CAPABILITY */
++} __packed;
++
++enum WMI_NETWORK_TYPE {
++      INFRA_NETWORK = 0x01,
++      ADHOC_NETWORK = 0x02,
++      ADHOC_CREATOR = 0x04,
++      AP_NETWORK = 0x10,
++      P2P_NETWORK = 0x20,
++      WBE_NETWORK = 0x40,
++};
++
++enum DOT11_AUTH_MODE {
++      OPEN_AUTH = 0x01,
++      SHARED_AUTH = 0x02,
++      LEAP_AUTH = 0x04, /* different from IEEE_AUTH_MODE definitions */
++};
++
++enum AUTH_MODE {
++      NONE_AUTH = 0x01,
++      WPA_AUTH = 0x02,
++      WPA2_AUTH = 0x04,
++      WPA_PSK_AUTH = 0x08,
++      WPA2_PSK_AUTH = 0x10,
++      WPA_AUTH_CCKM = 0x20,
++      WPA2_AUTH_CCKM = 0x40,
++};
++
++enum CRYPTO_TYPE {
++      NONE_CRYPT = 0x01,
++      WEP_CRYPT = 0x02,
++      TKIP_CRYPT = 0x04,
++      AES_CRYPT = 0x08,
++#ifdef WAPI_ENABLE
++      WAPI_CRYPT = 0x10,
++#endif /*WAPI_ENABLE*/
++};
++
++#define WMI_MIN_CRYPTO_TYPE NONE_CRYPT
++#define WMI_MAX_CRYPTO_TYPE (AES_CRYPT + 1)
++
++#ifdef WAPI_ENABLE
++#undef WMI_MAX_CRYPTO_TYPE
++#define WMI_MAX_CRYPTO_TYPE (WAPI_CRYPT + 1)
++#endif /* WAPI_ENABLE */
++
++#ifdef WAPI_ENABLE
++#define IW_ENCODE_ALG_SM4       0x20
++#define IW_AUTH_WAPI_ENABLED    0x20
++#endif
++
++#define WMI_MIN_KEY_INDEX   0
++#define WMI_MAX_KEY_INDEX   3
++
++#ifdef WAPI_ENABLE
++#undef WMI_MAX_KEY_INDEX
++#define WMI_MAX_KEY_INDEX   7 /* wapi grpKey 0-3, prwKey 4-7 */
++#endif /* WAPI_ENABLE */
++
++enum WMI_CONNECT_CTRL_FLAGS_BITS {
++      CONNECT_ASSOC_POLICY_USER = 0x0001,
++      CONNECT_SEND_REASSOC = 0x0002,
++      CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004,
++      CONNECT_PROFILE_MATCH_DONE = 0x0008,
++      CONNECT_IGNORE_AAC_BEACON = 0x0010,
++      CONNECT_CSA_FOLLOW_BSS = 0x0020,
++      CONNECT_DO_WPA_OFFLOAD = 0x0040,
++      CONNECT_DO_NOT_DEAUTH = 0x0080,
++};
++#define DEFAULT_CONNECT_CTRL_FLAGS    (CONNECT_CSA_FOLLOW_BSS)
++
++/*
++ * WMI_CONNECT_CMDID
++ */
++struct WMI_CONNECT_CMD {
++      u8 networkType;
++      u8 dot11AuthMode;
++      u8 authMode;
++      u8 pairwiseCryptoType;
++      u8 pairwiseCryptoLen;
++      u8 groupCryptoType;
++      u8 groupCryptoLen;
++      u8 ssidLength;
++      u8 ssid[32];
++      u16 channel;
++      u8 bssid[ETH_ALEN];
++      u32 ctrl_flags;
++      u8 destMacAddr[ETH_ALEN];
++      u16 reserved;
++} __packed;
++
++/*
++ * WMI_CONNECT_EVENTID
++ */
++struct WMI_CONNECT_EVENT {
++      u16 channel;
++      u8 bssid[ETH_ALEN];
++      u16 listenInterval;
++      u16 beaconInterval;
++      u32 networkType;
++      u8 beaconIeLen;
++      u8 assocReqLen;
++      u8 assocRespLen;
++      u8 cid;
++      u8 reserved0;
++      u16 reserved1;
++      u8 assocInfo[0];
++} __packed;
++
++/*
++ * WMI_DISCONNECT_EVENTID
++ */
++enum WMI_DISCONNECT_REASON {
++      NO_NETWORK_AVAIL = 0x01,
++      LOST_LINK = 0x02, /* bmiss */
++      DISCONNECT_CMD = 0x03,
++      BSS_DISCONNECTED = 0x04,
++      AUTH_FAILED = 0x05,
++      ASSOC_FAILED = 0x06,
++      NO_RESOURCES_AVAIL = 0x07,
++      CSERV_DISCONNECT = 0x08,
++      INVALID_PROFILE = 0x0a,
++      DOT11H_CHANNEL_SWITCH = 0x0b,
++      PROFILE_MISMATCH = 0x0c,
++      CONNECTION_EVICTED = 0x0d,
++      IBSS_MERGE = 0xe,
++};
++
++struct WMI_DISCONNECT_EVENT {
++      u16 protocolReasonStatus; /* reason code, see 802.11 spec. */
++      u8 bssid[ETH_ALEN]; /* set if known */
++      u8 disconnectReason; /* see WMI_DISCONNECT_REASON */
++      u8 assocRespLen;
++      u8 assocInfo[0];
++} __packed;
++
++/*
++ * WMI_START_SCAN_CMDID
++ */
++enum WMI_SCAN_TYPE {
++      WMI_LONG_SCAN = 0, WMI_SHORT_SCAN = 1,
++};
++
++struct WMI_START_SCAN_CMD {
++      u32 forceFgScan;
++      u32 isLegacy; /* For Legacy Cisco AP compatibility */
++      u32 homeDwellTime; /* Maximum duration in the home channel(msec) */
++      u32 forceScanInterval; /* Time interval between scans (milliseconds)*/
++      u8 scanType; /* WMI_SCAN_TYPE */
++      u8 numChannels; /* how many channels follow */
++      u16 channelList[0]; /* channels in Mhz */
++} __packed;
++
++/*
++ * WMI_SET_SCAN_PARAMS_CMDID
++ */
++#define WMI_SHORTSCANRATIO_DEFAULT      3
++/*
++ *  Warning: ScanCtrlFlag value of 0xFF is used
++ *  to disable all flags in WMI_SCAN_PARAMS_CMD
++ *  Do not add any more flags to WMI_SCAN_CTRL_FLAG_BITS
++ */
++enum WMI_SCAN_CTRL_FLAGS_BITS {
++      CONNECT_SCAN_CTRL_FLAGS = 0x01, /* set if can scan in the Connect cmd */
++      SCAN_CONNECTED_CTRL_FLAGS = 0x02, /* set if scan for the SSID it is */
++                                      /* already connected to */
++      ACTIVE_SCAN_CTRL_FLAGS = 0x04, /* set if enable active scan */
++      ROAM_SCAN_CTRL_FLAGS = 0x08, /* set if enable roam scan */
++                                      /* when bmiss and lowrssi */
++      REPORT_BSSINFO_CTRL_FLAGS = 0x10, /* set if follows customer */
++                                      /* BSSINFO reporting rule */
++      ENABLE_AUTO_CTRL_FLAGS = 0x20, /* if disabled, target doesn't */
++                                      /* scan after a disconnect event  */
++      ENABLE_SCAN_ABORT_EVENT = 0x40, /* Scan complete event with canceled */
++                                      /* status will be generated */
++                                      /* when a scan is prempted */
++                                      /* before it gets completed */
++};
++
++#define CAN_SCAN_IN_CONNECT(flags)      (flags & CONNECT_SCAN_CTRL_FLAGS)
++#define CAN_SCAN_CONNECTED(flags)       (flags & SCAN_CONNECTED_CTRL_FLAGS)
++#define ENABLE_ACTIVE_SCAN(flags)       (flags & ACTIVE_SCAN_CTRL_FLAGS)
++#define ENABLE_ROAM_SCAN(flags)         (flags & ROAM_SCAN_CTRL_FLAGS)
++#define CONFIG_REPORT_BSSINFO(flags)     (flags & REPORT_BSSINFO_CTRL_FLAGS)
++#define IS_AUTO_SCAN_ENABLED(flags)      (flags & ENABLE_AUTO_CTRL_FLAGS)
++#define SCAN_ABORT_EVENT_ENABLED(flags) (flags & ENABLE_SCAN_ABORT_EVENT)
++
++#define DEFAULT_SCAN_CTRL_FLAGS         (CONNECT_SCAN_CTRL_FLAGS | \
++              SCAN_CONNECTED_CTRL_FLAGS | ACTIVE_SCAN_CTRL_FLAGS | \
++              ROAM_SCAN_CTRL_FLAGS | ENABLE_AUTO_CTRL_FLAGS)
++
++struct WMI_SET_SCAN_PARAMS_CMD {
++      u16 fg_start_period; /* seconds */
++      u16 fg_end_period; /* seconds */
++      u16 bg_period; /* seconds */
++      u16 maxact_chdwell_time; /* msec */
++      u16 pas_chdwell_time; /* msec */
++      u8 shortScanRatio; /* how many shorts scan for one long */
++      u8 scanCtrlFlags;
++      u16 minact_chdwell_time; /* msec */
++      u16 maxact_scan_per_ssid; /* max active scans per ssid */
++      u32 max_dfsch_act_time; /* msecs */
++} __packed;
++
++/*
++ * WMI_SET_BSS_FILTER_CMDID
++ */
++enum WMI_BSS_FILTER {
++      NONE_BSS_FILTER = 0x0, /* no beacons forwarded */
++      ALL_BSS_FILTER, /* all beacons forwarded */
++      PROFILE_FILTER, /* only beacons matching profile */
++      ALL_BUT_PROFILE_FILTER, /* all but beacons matching profile */
++      CURRENT_BSS_FILTER, /* only beacons matching current BSS */
++      ALL_BUT_BSS_FILTER, /* all but beacons matching BSS */
++      PROBED_SSID_FILTER, /* beacons matching probed ssid */
++      LAST_BSS_FILTER, /* marker only */
++};
++
++struct WMI_SET_BSS_FILTER_CMD {
++      u8 bssFilter; /* see WMI_BSS_FILTER */
++      u8 reserved1; /* For alignment */
++      u16 reserved2; /* For alignment */
++      u32 ieMask;
++} __packed;
++
++/*
++ * WMI_SCAN_COMPLETE_EVENTID - status parameter
++ */
++struct WMI_SCAN_COMPLETE_EVENT {
++      u32 status;
++} __packed;
++
++/*
++ * WMI_RX_MGMT_PACKET_EVENTID
++ */
++struct WMI_RX_MGMT_PACKET_EVENT {
++      u16 mcs;
++      u16 rssi;
++      u32 status;
++      u32 length;
++      u8 qid; /* Not resolved when == 0xFFFFFFFF  ==> Broadcast to all MIDS */
++      u8 mid; /* Not resolved when == 0xFFFFFFFF  ==> Broadcast to all MIDS */
++      u8 cid;
++      u8 channel; /* From Radio MNGR */
++      u8 payload[0]; /* struct ieee80211_mgmt */
++} __packed;
++
++/* DMA rings */
++
++struct wmi_sw_ring_cfg {
++      u64 ring_mem_base; /* 48 bit; upper 16 bits reserved */
++      u16 ring_size;
++      u16 max_mpdu_size;
++} __packed;
++
++enum wmi_cfg_rx_chain_cmd_action {
++      ADD_RX_CHAIN = 0x0, DELETE_RX_CHAIN = 0x1,
++};
++
++enum wmi_cfg_rx_chain_cmd_decap_trans_type {
++      DECAP_TYPE_802_3 = 0x0, DECAP_TYPE_NATIVE_WIFI = 0x1,
++};
++
++enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type {
++      NWIFI_RX_NO_TRANS_MODE = 0x0,
++      NWIFI_RX_PBSS2AP_TRANS_MODE = 0x1,
++      NWIFI_RX_PBSS2STA_TRANS_MODE = 0x2,
++};
++
++/*
++ * WMI_CFG_RX_CHAIN_CMDID
++ */
++struct WMI_CFG_RX_CHAIN_CMD {
++      u32 action; /* enum wmi_cfg_rx_chain_cmd_action */
++      struct wmi_sw_ring_cfg sw_ring;
++      u8 mid;
++      u8 decap_trans_type; /* enum wmi_cfg_rx_chain_cmd_decap_trans_type */
++      u8 l2_802_3_offload_ctrl;
++      u8 l2_nwifi_offload_ctrl;
++      u8 vlan_id;
++      u8 nwifi_ds_trans_type;
++      /* enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type */
++      u8 l3_l4_ctrl;
++      u8 ring_ctrl;
++      u16 prefetch_thrsh;
++      u16 wb_thrsh;
++      u32 itr_value;
++      u16 host_thrsh;
++      u16 reserved;
++      /* added for sniffer mode */
++      u32 sniffer_mode; /* 0/1, other sniffer fields ignored if 0 */
++      u32 sniffer_phy_info; /* 0/1, should phy_info be included? */
++      u32 sniffer_phy; /* 0 - CP; 1 - DP */
++      u32 sniffer_channel; /* channel index 0..2 */
++} __packed;
++
++enum wmi_cfg_rx_chain_done_event_status {
++      CFG_RX_CHAIN_SUCCESS = 0x1,
++};
++
++/*
++ * WMI_CFG_RX_CHAIN_DONE_EVENTID
++ */
++struct WMI_CFG_RX_CHAIN_DONE_EVENT {
++      u32 rx_ring_tail_ptr; /* Rx V-Ring Tail pointer */
++      u32 status; /* enum wmi_cfg_rx_chain_done_event_status */
++} __packed;
++
++/*
++ * WMI_VRING_CFG_CMDID
++ */
++enum wmi_vring_cfg_cmd_action {
++      ADD_VRING = 0x0,
++      MODIFY_VRING = 0x1,
++      DELETE_VRING = 0x2,
++};
++
++enum wmi_vring_cfg_encap_trans_type {
++      ENC_TYPE_802_3 = 0x0,
++      ENC_TYPE_NATIVE_WIFI = 0x1,
++};
++
++enum wmi_vring_cfg_ds_cfg {
++      PBSS_MODE = 0x0,
++      STATION_MODE = 0x1,
++      AP_MODE = 0x2,
++      ADDR4_MODE = 0x3,
++};
++
++enum wmi_vring_cfg_nwifi_ds_trans_type {
++      NWIFI_TX_NO_TRANS_MODE = 0x0,
++      NWIFI_TX_AP2PBSS_TRANS_MODE = 0x1,
++      NWIFI_TX_STA2PBSS_TRANS_MODE = 0x2,
++};
++
++enum wmi_vring_cfg_schd_params_priority {
++      REGULAR = 0x0,
++      HIGH = 0x1,
++};
++
++struct WMI_VRING_CFG_CMD {
++      u32 action; /* enum wmi_vring_cfg_cmd_action */
++      struct wmi_sw_ring_cfg sw_ring;
++      u8 ringid; /* 0-23 */
++      u8 cidxtid; /* 0..3: cid; 4..7: tid */
++      u8 encap_trans_type; /* enum wmi_vring_cfg_encap_trans_type */
++      u8 ds_cfg; /* enum wmi_vring_cfg_ds_cfg - 802.3 DS cfg */
++      u8 nwifi_ds_trans_type; /* enum wmi_vring_cfg_nwifi_ds_trans_type */
++      u8 mac_ctrl; /* 0: lifetime_en; 1: aggr_en */
++#define VRING_CFG_MAC_CTRL_LIFETIME_EN BIT(0)
++#define VRING_CFG_MAC_CTRL_AGGR_EN     BIT(1)
++      u8 to_resolution; /* 0..5: value; 6..7: reserved */
++      u8 agg_max_wsize;
++      /* schd_params */
++      u16 priority; /* enum wmi_vring_cfg_schd_params_priority */
++      u16 timeslot_us;
++} __packed;
++
++/*
++ * WMI_VRING_CFG_DONE_EVENTID
++ */
++enum wmi_vring_cfg_done_event_status {
++      VRING_CFG_SUCCESS = 0x0, VRING_CFG_FAILURE = 0x1,
++};
++
++struct WMI_VRING_CFG_DONE_EVENT {
++      u8 ringid;
++      u8 status;
++      u16 reserved;
++      u32 tx_vring_tail_ptr; /* Tx vring tail pointer */
++} __packed;
++
++/*
++ * WMI_SET_MAC_ADDRESS_CMDID
++ */
++struct WMI_SET_MAC_ADDRESS_CMD {
++      u8 macaddr[ETH_ALEN];
++      u16 reserved;
++} __packed;
++
++/*
++ * WMI_SET_BEACON_INT_CMDID
++ */
++struct WMI_SET_BEACON_INT_CMD {
++      u16 bcon_interval;
++      u16 frag_num;
++      u32 ss_mask_low;
++      u32 ss_mask_high;
++      u16 network_type;
++      u16 reserved;
++} __packed;
++
++/*
++ * WMI_NOTIFY_REQ_CMDID
++ */
++struct WMI_NOTIFY_REQ_CMD {
++      u32 cid;
++      u32 interval_usec;
++} __packed;
++
++/*
++ * WMI_NOTIFY_REQ_DONE_EVENTID
++ */
++struct WMI_NOTIFY_REQ_DONE_EVENT {
++      u32 bf_status;
++      u32 tsf_hi;
++      u32 tsf_lo;
++      u32 bf_metric;
++      u32 tx_tpt;
++      u32 tx_gput;
++      u32 rx_gput;
++      u16 bf_mcs;
++      u16 my_rx_sector;
++      u16 my_tx_sector;
++      u16 peer_rx_sector;
++      u16 peer_tx_sector;
++      u16 reserved;
++} __packed;
++
++#endif /* __WMI_H__ */
+diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
+index d716b74..b1af57b 100644
+--- a/drivers/net/wireless/ath/Makefile
++++ b/drivers/net/wireless/ath/Makefile
+@@ -2,6 +2,7 @@ obj-$(CONFIG_ATH5K)            += ath5k/
+ obj-$(CONFIG_ATH9K_HW)                += ath9k/
+ obj-$(CONFIG_CARL9170)                += carl9170/
+ obj-$(CONFIG_ATH6KL)          += ath6kl/
++obj-$(CONFIG_WIL6210)         += wil6210/
+ obj-$(CONFIG_ATH_COMMON)      += ath.o
diff --git a/linux-next-pending/0001-wireless-add-802.11ad-60gHz-band.patch b/linux-next-pending/0001-wireless-add-802.11ad-60gHz-band.patch
new file mode 100644 (file)
index 0000000..f950845
--- /dev/null
@@ -0,0 +1,126 @@
+From ffbbf562f5163558caf818fa22307b933658e867 Mon Sep 17 00:00:00 2001
+From: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+Date: Thu, 28 Jun 2012 14:16:56 +0300
+Subject: [PATCH 1/5] wireless: add 802.11ad (60gHz band)
+
+Add enumerations for both cfg80211 and nl80211.
+This expands wiphy.bands etc. arrays.
+
+Extend channel <-> frequency translation to cover 60g band
+
+Small fix for mac80211/tx.c required to fix compiler warning
+
+Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+---
+ include/linux/nl80211.h |    2 ++
+ include/net/cfg80211.h  |    2 ++
+ net/mac80211/tx.c       |    2 ++
+ net/wireless/util.c     |   30 ++++++++++++++++++++++--------
+ 4 files changed, 28 insertions(+), 8 deletions(-)
+
+diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
+index c0fc5d2..679831e 100644
+--- a/include/linux/nl80211.h
++++ b/include/linux/nl80211.h
+@@ -2539,10 +2539,12 @@ enum nl80211_tx_rate_attributes {
+  * enum nl80211_band - Frequency band
+  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
+  * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
++ * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz)
+  */
+ enum nl80211_band {
+       NL80211_BAND_2GHZ,
+       NL80211_BAND_5GHZ,
++      NL80211_BAND_60GHZ,
+ };
+ /**
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 061c019..56e840d 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -70,11 +70,13 @@
+  *
+  * @IEEE80211_BAND_2GHZ: 2.4GHz ISM band
+  * @IEEE80211_BAND_5GHZ: around 5GHz band (4.9-5.7)
++ * @IEEE80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz)
+  * @IEEE80211_NUM_BANDS: number of defined bands
+  */
+ enum ieee80211_band {
+       IEEE80211_BAND_2GHZ = NL80211_BAND_2GHZ,
+       IEEE80211_BAND_5GHZ = NL80211_BAND_5GHZ,
++      IEEE80211_BAND_60GHZ = NL80211_BAND_60GHZ,
+       /* keep last */
+       IEEE80211_NUM_BANDS
+diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
+index ec8f5346..eebd33a 100644
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -140,6 +140,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
+                       if (r->flags & IEEE80211_RATE_MANDATORY_A)
+                               mrate = r->bitrate;
+                       break;
++              case IEEE80211_BAND_60GHZ:
++                      /* TODO, for now fall through */
+               case IEEE80211_NUM_BANDS:
+                       WARN_ON(1);
+                       break;
+diff --git a/net/wireless/util.c b/net/wireless/util.c
+index 316cfd0..5c7195e 100644
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -35,19 +35,29 @@ int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band)
+ {
+       /* see 802.11 17.3.8.3.2 and Annex J
+        * there are overlapping channel numbers in 5GHz and 2GHz bands */
+-      if (band == IEEE80211_BAND_5GHZ) {
+-              if (chan >= 182 && chan <= 196)
+-                      return 4000 + chan * 5;
+-              else
+-                      return 5000 + chan * 5;
+-      } else { /* IEEE80211_BAND_2GHZ */
++      if (chan <= 0)
++              return 0; /* not supported */
++      switch (band) {
++      case IEEE80211_BAND_2GHZ:
+               if (chan == 14)
+                       return 2484;
+               else if (chan < 14)
+                       return 2407 + chan * 5;
++              break;
++      case IEEE80211_BAND_5GHZ:
++              if (chan >= 182 && chan <= 196)
++                      return 4000 + chan * 5;
+               else
+-                      return 0; /* not supported */
++                      return 5000 + chan * 5;
++              break;
++      case IEEE80211_BAND_60GHZ:
++              if (chan < 5)
++                      return 56160 + chan * 2160;
++              break;
++      default:
++              ;
+       }
++      return 0; /* not supported */
+ }
+ EXPORT_SYMBOL(ieee80211_channel_to_frequency);
+@@ -60,8 +70,12 @@ int ieee80211_frequency_to_channel(int freq)
+               return (freq - 2407) / 5;
+       else if (freq >= 4910 && freq <= 4980)
+               return (freq - 4000) / 5;
+-      else
++      else if (freq <= 6000) /* TODO: check band 5.2 upper limit */
+               return (freq - 5000) / 5;
++      else if (freq >= 58320 && freq <= 64800)
++              return (freq - 56160) / 2160;
++      else
++              return 0;
+ }
+ EXPORT_SYMBOL(ieee80211_frequency_to_channel);
+-- 
+1.7.10.rc1.22.gf5241
+
diff --git a/linux-next-pending/0002-wireless-rate-check-logic-for-60g.patch b/linux-next-pending/0002-wireless-rate-check-logic-for-60g.patch
new file mode 100644 (file)
index 0000000..7494bef
--- /dev/null
@@ -0,0 +1,57 @@
+From 93a62bb38dc0ff546cc9f0cd4ae8f8fc7991522a Mon Sep 17 00:00:00 2001
+From: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+Date: Thu, 28 Jun 2012 14:16:57 +0300
+Subject: [PATCH 2/5] wireless: rate check logic for 60g
+
+On the 60g band, there is no 'basic rates'. Only MCS used.
+Instead of mandatory basic rates, standard requires support for
+mandatory MCS 1..4
+
+Modify logic to comply with 60g requirements
+
+Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+---
+ net/wireless/core.c |   10 ++++++++--
+ net/wireless/util.c |    5 +++++
+ 2 files changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/net/wireless/core.c b/net/wireless/core.c
+index 907f62c..bef6dbe 100644
+--- a/net/wireless/core.c
++++ b/net/wireless/core.c
+@@ -458,8 +458,14 @@ int wiphy_register(struct wiphy *wiphy)
+                       continue;
+               sband->band = band;
+-
+-              if (WARN_ON(!sband->n_channels || !sband->n_bitrates))
++              if (WARN_ON(!sband->n_channels))
++                      return -EINVAL;
++              /*
++               * on 60gHz band, there are no legacy rates, so
++               * n_bitrates is 0
++               */
++              if (WARN_ON((band != IEEE80211_BAND_60GHZ) &&
++                  !sband->n_bitrates))
+                       return -EINVAL;
+               /*
+diff --git a/net/wireless/util.c b/net/wireless/util.c
+index 5c7195e..98b2a8d 100644
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -151,6 +151,11 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband,
+               }
+               WARN_ON(want != 0 && want != 3 && want != 6);
+               break;
++      case IEEE80211_BAND_60GHZ:
++              /* check for mandatory HT MCS 1..4 */
++              WARN_ON(!sband->ht_cap.ht_supported);
++              WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e);
++              break;
+       case IEEE80211_NUM_BANDS:
+               WARN_ON(1);
+               break;
+-- 
+1.7.10.rc1.22.gf5241
+
diff --git a/linux-next-pending/0003-wireless-regulatory-for-60g.patch b/linux-next-pending/0003-wireless-regulatory-for-60g.patch
new file mode 100644 (file)
index 0000000..3d7ddd7
--- /dev/null
@@ -0,0 +1,38 @@
+From ec2828d220e424f2ceccfcf423fd5c247d6dea82 Mon Sep 17 00:00:00 2001
+From: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+Date: Thu, 28 Jun 2012 14:16:58 +0300
+Subject: [PATCH 3/5] wireless: regulatory for 60g
+
+Add regulatory rule for the 60g band
+
+Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+---
+ net/wireless/reg.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index baf5704..b2b3222 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -129,7 +129,7 @@ static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work);
+ /* We keep a static world regulatory domain in case of the absence of CRDA */
+ static const struct ieee80211_regdomain world_regdom = {
+-      .n_reg_rules = 5,
++      .n_reg_rules = 6,
+       .alpha2 =  "00",
+       .reg_rules = {
+               /* IEEE 802.11b/g, channels 1..11 */
+@@ -156,6 +156,9 @@ static const struct ieee80211_regdomain world_regdom = {
+               REG_RULE(5745-10, 5825+10, 40, 6, 20,
+                       NL80211_RRF_PASSIVE_SCAN |
+                       NL80211_RRF_NO_IBSS),
++
++              /* IEEE 802.11ad (60gHz), channels 1..3 */
++              REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
+       }
+ };
+-- 
+1.7.10.rc1.22.gf5241
+
diff --git a/linux-next-pending/0004-wireless-60g-protocol-constants.patch b/linux-next-pending/0004-wireless-60g-protocol-constants.patch
new file mode 100644 (file)
index 0000000..47640e4
--- /dev/null
@@ -0,0 +1,189 @@
+From 45be57ec351e960bb8f5e063d23d64333b6a91e2 Mon Sep 17 00:00:00 2001
+From: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+Date: Thu, 28 Jun 2012 14:16:59 +0300
+Subject: [PATCH 4/5] wireless: 60g protocol constants
+
+Provide various constants as defined by the 802.11ad:
+frame types, IE's, capability bits, action categories
+
+Introduce GCMP cipher, mandatory by 802.11ad
+
+Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+---
+ include/linux/ieee80211.h |   89 ++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 88 insertions(+), 1 deletion(-)
+
+diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
+index 318fc1f..ae80378 100644
+--- a/include/linux/ieee80211.h
++++ b/include/linux/ieee80211.h
+@@ -47,6 +47,7 @@
+ #define IEEE80211_FCTL_MOREDATA               0x2000
+ #define IEEE80211_FCTL_PROTECTED      0x4000
+ #define IEEE80211_FCTL_ORDER          0x8000
++#define IEEE80211_FCTL_CTL_EXT                0x0f00
+ #define IEEE80211_SCTL_FRAG           0x000F
+ #define IEEE80211_SCTL_SEQ            0xFFF0
+@@ -54,6 +55,7 @@
+ #define IEEE80211_FTYPE_MGMT          0x0000
+ #define IEEE80211_FTYPE_CTL           0x0004
+ #define IEEE80211_FTYPE_DATA          0x0008
++#define IEEE80211_FTYPE_EXT           0x000c
+ /* management */
+ #define IEEE80211_STYPE_ASSOC_REQ     0x0000
+@@ -70,6 +72,7 @@
+ #define IEEE80211_STYPE_ACTION                0x00D0
+ /* control */
++#define IEEE80211_STYPE_CTL_EXT               0x0060
+ #define IEEE80211_STYPE_BACK_REQ      0x0080
+ #define IEEE80211_STYPE_BACK          0x0090
+ #define IEEE80211_STYPE_PSPOLL                0x00A0
+@@ -97,6 +100,18 @@
+ #define IEEE80211_STYPE_QOS_CFPOLL            0x00E0
+ #define IEEE80211_STYPE_QOS_CFACKPOLL         0x00F0
++/* extension, added by 802.11ad */
++#define IEEE80211_STYPE_DMG_BEACON            0x0000
++
++/* control extension - for IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTL_EXT */
++#define IEEE80211_CTL_EXT_POLL                0x2000
++#define IEEE80211_CTL_EXT_SPR         0x3000
++#define IEEE80211_CTL_EXT_GRANT       0x4000
++#define IEEE80211_CTL_EXT_DMG_CTS     0x5000
++#define IEEE80211_CTL_EXT_DMG_DTS     0x6000
++#define IEEE80211_CTL_EXT_SSW         0x8000
++#define IEEE80211_CTL_EXT_SSW_FBACK   0x9000
++#define IEEE80211_CTL_EXT_SSW_ACK     0xa000
+ /* miscellaneous IEEE 802.11 constants */
+ #define IEEE80211_MAX_FRAG_THRESHOLD  2352
+@@ -1124,6 +1139,21 @@ struct ieee80211_ht_operation {
+ #define WLAN_CAPABILITY_QOS           (1<<9)
+ #define WLAN_CAPABILITY_SHORT_SLOT_TIME       (1<<10)
+ #define WLAN_CAPABILITY_DSSS_OFDM     (1<<13)
++
++/* DMG (60gHz) 802.11ad */
++/* type - bits 0..1 */
++#define WLAN_CAPABILITY_DMG_TYPE_IBSS         (1<<0) /* Tx by: STA */
++#define WLAN_CAPABILITY_DMG_TYPE_PBSS         (2<<0) /* Tx by: PCP */
++#define WLAN_CAPABILITY_DMG_TYPE_AP           (3<<0) /* Tx by: AP */
++
++#define WLAN_CAPABILITY_DMG_CBAP_ONLY         (1<<2)
++#define WLAN_CAPABILITY_DMG_CBAP_SOURCE       (1<<3)
++#define WLAN_CAPABILITY_DMG_PRIVACY           (1<<4)
++#define WLAN_CAPABILITY_DMG_ECPAC             (1<<5)
++
++#define WLAN_CAPABILITY_DMG_SPECTRUM_MGMT     (1<<8)
++#define WLAN_CAPABILITY_DMG_RADIO_MEASURE     (1<<12)
++
+ /* measurement */
+ #define IEEE80211_SPCT_MSR_RPRT_MODE_LATE     (1<<0)
+ #define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE        (1<<1)
+@@ -1133,7 +1163,6 @@ struct ieee80211_ht_operation {
+ #define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA      1
+ #define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI      2
+-
+ /* 802.11g ERP information element */
+ #define WLAN_ERP_NON_ERP_PRESENT (1<<0)
+ #define WLAN_ERP_USE_PROTECTION (1<<1)
+@@ -1145,6 +1174,16 @@ enum {
+       WLAN_ERP_PREAMBLE_LONG = 1,
+ };
++/* Band ID, 802.11ad #8.4.1.45 */
++enum {
++      IEEE80211_BANDID_TV_WS = 0, /* TV white spaces */
++      IEEE80211_BANDID_SUB1  = 1, /* Sub-1 GHz (excluding TV white spaces) */
++      IEEE80211_BANDID_2G    = 2, /* 2.4 GHz */
++      IEEE80211_BANDID_3G    = 3, /* 3.6 GHz */
++      IEEE80211_BANDID_5G    = 4, /* 4.9 and 5 GHz */
++      IEEE80211_BANDID_60G   = 5, /* 60 GHz */
++};
++
+ /* Status codes */
+ enum ieee80211_statuscode {
+       WLAN_STATUS_SUCCESS = 0,
+@@ -1196,6 +1235,17 @@ enum ieee80211_statuscode {
+       WLAN_STATUS_ANTI_CLOG_REQUIRED = 76,
+       WLAN_STATUS_FCG_NOT_SUPP = 78,
+       WLAN_STATUS_STA_NO_TBTT = 78,
++      /* 802.11ad */
++      WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES             = 39,
++      WLAN_STATUS_REJECTED_FOR_DELAY_PERIOD                   = 47,
++      WLAN_STATUS_REJECT_WITH_SCHEDULE                        = 83,
++      WLAN_STATUS_PENDING_ADMITTING_FST_SESSION               = 86,
++      WLAN_STATUS_PERFORMING_FST_NOW                          = 87,
++      WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW                    = 88,
++      WLAN_STATUS_REJECT_U_PID_SETTING                        = 89,
++      WLAN_STATUS_REJECT_DSE_BAND                             = 96,
++      WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL      = 99,
++      WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT           = 103,
+ };
+@@ -1352,6 +1402,39 @@ enum ieee80211_eid {
+       WLAN_EID_DSE_REGISTERED_LOCATION = 58,
+       WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59,
+       WLAN_EID_EXT_CHANSWITCH_ANN = 60,
++      /* 802.11ad */
++      WLAN_EID_NON_TX_BSSID_CAP               =  83,
++      WLAN_EID_WAKEUP_SCHEDULE                = 143,
++      WLAN_EID_EXT_SCHEDULE                   = 144,
++      WLAN_EID_STA_AVAILABILITY               = 145,
++      WLAN_EID_DMG_TSPEC                      = 146,
++      WLAN_EID_DMG_AT                         = 147,
++      WLAN_EID_DMG_CAP                        = 148,
++      WLAN_EID_DMG_OPERATION                  = 151,
++      WLAN_EID_DMG_BSS_PARAM_CHANGE           = 152,
++      WLAN_EID_DMG_BEAM_REFINEMENT            = 153,
++      WLAN_EID_CHANNEL_MEASURE_FEEDBACK       = 154,
++      WLAN_EID_AWAKE_WINDOW                   = 157,
++      WLAN_EID_MULTI_BAND                     = 158,
++      WLAN_EID_ADDBA_EXT                      = 159,
++      WLAN_EID_NEXT_PCP_LIST                  = 160,
++      WLAN_EID_PCP_HANDOVER                   = 161,
++      WLAN_EID_DMG_LINK_MARGIN                = 162,
++      WLAN_EID_SWITCHING_STREAM               = 163,
++      WLAN_EID_SESSION_TRANSITION             = 164,
++      WLAN_EID_DYN_TONE_PAIRING_REPORT        = 165,
++      WLAN_EID_CLUSTER_REPORT                 = 166,
++      WLAN_EID_RELAY_CAP                      = 167,
++      WLAN_EID_RELAY_XFER_PARAM_SET           = 168,
++      WLAN_EID_BEAM_LINK_MAINT                = 169,
++      WLAN_EID_MULTIPLE_MAC_ADDR              = 170,
++      WLAN_EID_U_PID                          = 171,
++      WLAN_EID_DMG_LINK_ADAPT_ACK             = 172,
++      WLAN_EID_QUIET_PERIOD_REQ               = 175,
++      WLAN_EID_QUIET_PERIOD_RESP              = 177,
++      WLAN_EID_EPAC_POLICY                    = 182,
++      WLAN_EID_CLISTER_TIME_OFF               = 183,
++      WLAN_EID_ANTENNA_SECTOR_ID_PATTERN      = 190,
+ };
+ /* Action category code */
+@@ -1368,7 +1451,10 @@ enum ieee80211_category {
+       WLAN_CATEGORY_MESH_ACTION = 13,
+       WLAN_CATEGORY_MULTIHOP_ACTION = 14,
+       WLAN_CATEGORY_SELF_PROTECTED = 15,
++      WLAN_CATEGORY_DMG = 16,
+       WLAN_CATEGORY_WMM = 17,
++      WLAN_CATEGORY_FST = 18,
++      WLAN_CATEGORY_UNPROT_DMG = 20,
+       WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126,
+       WLAN_CATEGORY_VENDOR_SPECIFIC = 127,
+ };
+@@ -1616,6 +1702,7 @@ enum ieee80211_sa_query_action {
+ #define WLAN_CIPHER_SUITE_CCMP                0x000FAC04
+ #define WLAN_CIPHER_SUITE_WEP104      0x000FAC05
+ #define WLAN_CIPHER_SUITE_AES_CMAC    0x000FAC06
++#define WLAN_CIPHER_SUITE_GCMP                0x000FAC08
+ #define WLAN_CIPHER_SUITE_SMS4                0x00147201
+-- 
+1.7.10.rc1.22.gf5241
+
diff --git a/linux-next-pending/0005-wireless-bitrate-calculation-for-60g.patch b/linux-next-pending/0005-wireless-bitrate-calculation-for-60g.patch
new file mode 100644 (file)
index 0000000..225da55
--- /dev/null
@@ -0,0 +1,101 @@
+From 0a608689d20ee4751176d71cf98ce63ea76d243a Mon Sep 17 00:00:00 2001
+From: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+Date: Thu, 28 Jun 2012 14:17:00 +0300
+Subject: [PATCH 5/5] wireless: bitrate calculation for 60g
+
+60g band uses different from .11n MCS scheme, so bitrate should be calculated
+differently
+
+Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+---
+ include/net/cfg80211.h |    2 ++
+ net/wireless/util.c    |   49 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 51 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 56e840d..f760520 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -563,11 +563,13 @@ enum station_info_flags {
+  * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled
+  * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission
+  * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
++ * @RATE_INFO_FLAGS_60G: 60gHz MCS
+  */
+ enum rate_info_flags {
+       RATE_INFO_FLAGS_MCS             = 1<<0,
+       RATE_INFO_FLAGS_40_MHZ_WIDTH    = 1<<1,
+       RATE_INFO_FLAGS_SHORT_GI        = 1<<2,
++      RATE_INFO_FLAGS_60G             = 1<<3,
+ };
+ /**
+diff --git a/net/wireless/util.c b/net/wireless/util.c
+index 98b2a8d..77319a6 100644
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -890,12 +890,61 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
+       return err;
+ }
++static u16 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
++{
++      static const u16 __mcs2bitrate[] = {
++              /* control PHY */
++              [0] =   275,
++              /* SC PHY */
++              [1] =  3850,
++              [2] =  7700,
++              [3] =  9625,
++              [4] = 11550,
++              [5] = 12512, /* 1251.25 mbps */
++              [6] = 15400,
++              [7] = 19250,
++              [8] = 23100,
++              [9] = 25025,
++              [10] = 30800,
++              [11] = 38500,
++              [12] = 46200,
++              /* OFDM PHY */
++              [13] =  6930,
++              [14] =  8662, /* 866.25 mbps */
++              [15] = 13860,
++              [16] = 17325,
++              [17] = 20790,
++              [18] = 27720,
++              [19] = 34650,
++              [20] = 41580,
++              [21] = 45045,
++              [22] = 51975,
++              [23] = 62370,
++              [24] = 67568, /* 6756.75 mbps */
++              /* LP-SC PHY */
++              [25] =  6260,
++              [26] =  8340,
++              [27] = 11120,
++              [28] = 12510,
++              [29] = 16680,
++              [30] = 22240,
++              [31] = 25030,
++      };
++
++      if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate)))
++              return 0;
++
++      return __mcs2bitrate[rate->mcs];
++}
++
+ u16 cfg80211_calculate_bitrate(struct rate_info *rate)
+ {
+       int modulation, streams, bitrate;
+       if (!(rate->flags & RATE_INFO_FLAGS_MCS))
+               return rate->legacy;
++      if (rate->flags & RATE_INFO_FLAGS_60G)
++              return cfg80211_calculate_bitrate_60g(rate);
+       /* the formula below does only work for MCS values smaller than 32 */
+       if (WARN_ON_ONCE(rate->mcs >= 32))
+-- 
+1.7.10.rc1.22.gf5241
+
index 3d164679f8c029a6f0f899ec99bae042fff8f511..35e0eafa246d314f98c5dd30e3599f350688998c 100755 (executable)
@@ -34,7 +34,7 @@ PURPLE="\033[35m"
 CYAN="\033[36m"
 UNDERLINE="\033[02m"
 
-SUPPORTED_80211_DRIVERS="ath5k ath9k ath9k_ap ath9k_htc carl9170 ath6kl b43 zd1211rw rt2x00 wl1251 wl12xx brcmsmac brcmfmac"
+SUPPORTED_80211_DRIVERS="ath5k ath9k ath9k_ap ath9k_htc carl9170 ath6kl wil6210 b43 zd1211rw rt2x00 wl1251 wl12xx brcmsmac brcmfmac"
 
 # b43 needs some more work for driver-select, the SSB stuff, plus
 # what if you update b44 but not b43? It will bust.
@@ -57,8 +57,8 @@ function usage {
 
        # These should match the switch below.
        echo -e "\nSupported group drivers:"
-       echo -e "\t${CYAN}atheros${NORMAL} < ${PURPLE} ath5k ath9k carl9170 zd1211rw ath6kl ${NORMAL}>"
-       echo -e "\t${CYAN}ath${NORMAL} < ${PURPLE} ath5k ath9k carl9170 ath6kl ${NORMAL}>"
+       echo -e "\t${CYAN}atheros${NORMAL} < ${PURPLE} ath5k ath9k carl9170 zd1211rw ath6kl wil6210${NORMAL}>"
+       echo -e "\t${CYAN}ath${NORMAL} < ${PURPLE} ath5k ath9k carl9170 ath6kl wil6210${NORMAL}>"
        echo -e "\t${CYAN}brcm80211${NORMAL} < ${PURPLE} brcmsmac brcmfmac ${NORMAL}>"
        echo -e "\t${CYAN}intel${NORMAL} < ${PURPLE} iwlwifi, iwlegacy ${NORMAL}>"
        echo -e "\t${CYAN}rtl818x${NORMAL} < ${PURPLE} rtl8180 rtl8187 ${NORMAL}>"
@@ -413,6 +413,12 @@ case $1 in
                select_driver           CONFIG_ATH_COMMON
                select_ath_driver       CONFIG_ATH6KL
                ;;
+       wil6210)
+               disable_staging
+               disable_bt_usb_ethernet_var
+               select_driver           CONFIG_ATH_COMMON
+               select_ath_driver       CONFIG_WIL6210
+               ;;
        brcmsmac)
                disable_staging
                disable_bt_usb_ethernet_var