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

--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -26,5 +26,6 @@ source "drivers/net/wireless/ath/ath5k/K
 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
--- /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
+
--- /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 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 wireless_dev *wdev,
+		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 wireless_dev *wdev, 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);
+}
--- /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__ */
--- /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);
+}
--- /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;
+}
--- /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
+
--- /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);
+}
--- /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;
+}
+
--- /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
--- /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);
+}
--- /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");
--- /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
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/tools/Makefile
@@ -0,0 +1,4 @@
+hostprogs-y := trace
+
+always := $(hostprogs-y)
+
--- /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;
+}
--- /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
--- /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
--- /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
--- /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));
+	}
+}
--- /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 */
--- /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
+
--- /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__ */
--- /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 */
--- /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);
+}
--- /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__ */
--- 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
 
