Merge remote branch 'wireless-next/master' into ath6kl-next
diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig
index 3d5f8be..d755a5e 100644
--- a/drivers/net/wireless/ath/ath6kl/Kconfig
+++ b/drivers/net/wireless/ath/ath6kl/Kconfig
@@ -1,12 +1,29 @@
 config ATH6KL
-	tristate "Atheros ath6kl support"
+	tristate "Atheros mobile chipsets support"
+
+config ATH6KL_SDIO
+	tristate "Atheros ath6kl SDIO support"
+	depends on ATH6KL
 	depends on MMC
 	depends on CFG80211
 	---help---
 	  This module adds support for wireless adapters based on
-	  Atheros AR6003 chipset running over SDIO. If you choose to
-	  build it as a module, it will be called ath6kl. Pls note
-	  that AR6002 and AR6001 are not supported by this driver.
+	  Atheros AR6003 and AR6004 chipsets running over SDIO. If you
+	  choose to build it as a module, it will be called ath6kl_sdio.
+	  Please note that AR6002 and AR6001 are not supported by this
+	  driver.
+
+config ATH6KL_USB
+	tristate "Atheros ath6kl USB support"
+	depends on ATH6KL
+	depends on USB
+	depends on CFG80211
+	depends on EXPERIMENTAL
+	---help---
+	  This module adds support for wireless adapters based on
+	  Atheros AR6004 chipset running over USB. This is still under
+	  implementation and it isn't functional. If you choose to
+	  build it as a module, it will be called ath6kl_usb.
 
 config ATH6KL_DEBUG
 	bool "Atheros ath6kl debugging"
diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile
index 7070693..e14cef9 100644
--- a/drivers/net/wireless/ath/ath6kl/Makefile
+++ b/drivers/net/wireless/ath/ath6kl/Makefile
@@ -21,17 +21,30 @@
 # Author(s): ="Atheros"
 #------------------------------------------------------------------------------
 
-obj-$(CONFIG_ATH6KL) := ath6kl.o
-ath6kl-y += debug.o
-ath6kl-y += hif.o
-ath6kl-y += htc.o
-ath6kl-y += bmi.o
-ath6kl-y += cfg80211.o
-ath6kl-y += init.o
-ath6kl-y += main.o
-ath6kl-y += txrx.o
-ath6kl-y += wmi.o
-ath6kl-y += sdio.o
-ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o
+obj-$(CONFIG_ATH6KL_SDIO) := ath6kl_sdio.o
+ath6kl_sdio-y += debug.o
+ath6kl_sdio-y += hif.o
+ath6kl_sdio-y += htc.o
+ath6kl_sdio-y += bmi.o
+ath6kl_sdio-y += cfg80211.o
+ath6kl_sdio-y += init.o
+ath6kl_sdio-y += main.o
+ath6kl_sdio-y += txrx.o
+ath6kl_sdio-y += wmi.o
+ath6kl_sdio-y += sdio.o
+ath6kl_sdio-$(CONFIG_NL80211_TESTMODE) += testmode.o
+
+obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o
+ath6kl_usb-y += debug.o
+ath6kl_usb-y += hif.o
+ath6kl_usb-y += htc.o
+ath6kl_usb-y += bmi.o
+ath6kl_usb-y += cfg80211.o
+ath6kl_usb-y += init.o
+ath6kl_usb-y += main.o
+ath6kl_usb-y += txrx.o
+ath6kl_usb-y += wmi.o
+ath6kl_usb-y += usb.o
+ath6kl_usb-$(CONFIG_NL80211_TESTMODE) += testmode.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c
index a962fe4..aef00d5 100644
--- a/drivers/net/wireless/ath/ath6kl/bmi.c
+++ b/drivers/net/wireless/ath/ath6kl/bmi.c
@@ -19,165 +19,6 @@
 #include "target.h"
 #include "debug.h"
 
-static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar)
-{
-	u32 addr;
-	unsigned long timeout;
-	int ret;
-
-	ar->bmi.cmd_credits = 0;
-
-	/* Read the counter register to get the command credits */
-	addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4;
-
-	timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
-	while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
-
-		/*
-		 * Hit the credit counter with a 4-byte access, the first byte
-		 * read will hit the counter and cause a decrement, while the
-		 * remaining 3 bytes has no effect. The rationale behind this
-		 * is to make all HIF accesses 4-byte aligned.
-		 */
-		ret = hif_read_write_sync(ar, addr,
-					 (u8 *)&ar->bmi.cmd_credits, 4,
-					 HIF_RD_SYNC_BYTE_INC);
-		if (ret) {
-			ath6kl_err("Unable to decrement the command credit count register: %d\n",
-				   ret);
-			return ret;
-		}
-
-		/* The counter is only 8 bits.
-		 * Ignore anything in the upper 3 bytes
-		 */
-		ar->bmi.cmd_credits &= 0xFF;
-	}
-
-	if (!ar->bmi.cmd_credits) {
-		ath6kl_err("bmi communication timeout\n");
-		return -ETIMEDOUT;
-	}
-
-	return 0;
-}
-
-static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar)
-{
-	unsigned long timeout;
-	u32 rx_word = 0;
-	int ret = 0;
-
-	timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
-	while (time_before(jiffies, timeout) && !rx_word) {
-		ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS,
-					  (u8 *)&rx_word, sizeof(rx_word),
-					  HIF_RD_SYNC_BYTE_INC);
-		if (ret) {
-			ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n");
-			return ret;
-		}
-
-		 /* all we really want is one bit */
-		rx_word &= (1 << ENDPOINT1);
-	}
-
-	if (!rx_word) {
-		ath6kl_err("bmi_recv_buf FIFO empty\n");
-		return -EINVAL;
-	}
-
-	return ret;
-}
-
-static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len)
-{
-	int ret;
-	u32 addr;
-
-	ret = ath6kl_get_bmi_cmd_credits(ar);
-	if (ret)
-		return ret;
-
-	addr = ar->mbox_info.htc_addr;
-
-	ret = hif_read_write_sync(ar, addr, buf, len,
-				  HIF_WR_SYNC_BYTE_INC);
-	if (ret)
-		ath6kl_err("unable to send the bmi data to the device\n");
-
-	return ret;
-}
-
-static int ath6kl_bmi_recv_buf(struct ath6kl *ar, u8 *buf, u32 len)
-{
-	int ret;
-	u32 addr;
-
-	/*
-	 * During normal bootup, small reads may be required.
-	 * Rather than issue an HIF Read and then wait as the Target
-	 * adds successive bytes to the FIFO, we wait here until
-	 * we know that response data is available.
-	 *
-	 * This allows us to cleanly timeout on an unexpected
-	 * Target failure rather than risk problems at the HIF level.
-	 * In particular, this avoids SDIO timeouts and possibly garbage
-	 * data on some host controllers.  And on an interconnect
-	 * such as Compact Flash (as well as some SDIO masters) which
-	 * does not provide any indication on data timeout, it avoids
-	 * a potential hang or garbage response.
-	 *
-	 * Synchronization is more difficult for reads larger than the
-	 * size of the MBOX FIFO (128B), because the Target is unable
-	 * to push the 129th byte of data until AFTER the Host posts an
-	 * HIF Read and removes some FIFO data.  So for large reads the
-	 * Host proceeds to post an HIF Read BEFORE all the data is
-	 * actually available to read.  Fortunately, large BMI reads do
-	 * not occur in practice -- they're supported for debug/development.
-	 *
-	 * So Host/Target BMI synchronization is divided into these cases:
-	 *  CASE 1: length < 4
-	 *        Should not happen
-	 *
-	 *  CASE 2: 4 <= length <= 128
-	 *        Wait for first 4 bytes to be in FIFO
-	 *        If CONSERVATIVE_BMI_READ is enabled, also wait for
-	 *        a BMI command credit, which indicates that the ENTIRE
-	 *        response is available in the the FIFO
-	 *
-	 *  CASE 3: length > 128
-	 *        Wait for the first 4 bytes to be in FIFO
-	 *
-	 * For most uses, a small timeout should be sufficient and we will
-	 * usually see a response quickly; but there may be some unusual
-	 * (debug) cases of BMI_EXECUTE where we want an larger timeout.
-	 * For now, we use an unbounded busy loop while waiting for
-	 * BMI_EXECUTE.
-	 *
-	 * If BMI_EXECUTE ever needs to support longer-latency execution,
-	 * especially in production, this code needs to be enhanced to sleep
-	 * and yield.  Also note that BMI_COMMUNICATION_TIMEOUT is currently
-	 * a function of Host processor speed.
-	 */
-	if (len >= 4) { /* NB: Currently, always true */
-		ret = ath6kl_bmi_get_rx_lkahd(ar);
-		if (ret)
-			return ret;
-	}
-
-	addr = ar->mbox_info.htc_addr;
-	ret = hif_read_write_sync(ar, addr, buf, len,
-				  HIF_RD_SYNC_BYTE_INC);
-	if (ret) {
-		ath6kl_err("Unable to read the bmi data from the device: %d\n",
-			   ret);
-		return ret;
-	}
-
-	return 0;
-}
-
 int ath6kl_bmi_done(struct ath6kl *ar)
 {
 	int ret;
@@ -190,7 +31,7 @@
 
 	ar->bmi.done_sent = true;
 
-	ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
+	ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
 	if (ret) {
 		ath6kl_err("Unable to send bmi done: %d\n", ret);
 		return ret;
@@ -210,14 +51,20 @@
 		return -EACCES;
 	}
 
-	ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
+	ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
 	if (ret) {
 		ath6kl_err("Unable to send get target info: %d\n", ret);
 		return ret;
 	}
 
-	ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version,
-				  sizeof(targ_info->version));
+	if (ar->hif_type == ATH6KL_HIF_TYPE_USB) {
+		ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info,
+					  sizeof(*targ_info));
+	} else {
+		ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version,
+				sizeof(targ_info->version));
+	}
+
 	if (ret) {
 		ath6kl_err("Unable to recv target info: %d\n", ret);
 		return ret;
@@ -225,7 +72,7 @@
 
 	if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
 		/* Determine how many bytes are in the Target's targ_info */
-		ret = ath6kl_bmi_recv_buf(ar,
+		ret = ath6kl_hif_bmi_read(ar,
 				   (u8 *)&targ_info->byte_count,
 				   sizeof(targ_info->byte_count));
 		if (ret) {
@@ -244,7 +91,7 @@
 		}
 
 		/* Read the remainder of the targ_info */
-		ret = ath6kl_bmi_recv_buf(ar,
+		ret = ath6kl_hif_bmi_read(ar,
 				   ((u8 *)targ_info) +
 				   sizeof(targ_info->byte_count),
 				   sizeof(*targ_info) -
@@ -276,8 +123,8 @@
 		return -EACCES;
 	}
 
-	size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len);
-	if (size > MAX_BMI_CMDBUF_SZ) {
+	size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len);
+	if (size > ar->bmi.max_cmd_size) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
@@ -290,8 +137,8 @@
 	len_remain = len;
 
 	while (len_remain) {
-		rx_len = (len_remain < BMI_DATASZ_MAX) ?
-					len_remain : BMI_DATASZ_MAX;
+		rx_len = (len_remain < ar->bmi.max_data_size) ?
+					len_remain : ar->bmi.max_data_size;
 		offset = 0;
 		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 		offset += sizeof(cid);
@@ -300,13 +147,13 @@
 		memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
 		offset += sizeof(len);
 
-		ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 		if (ret) {
 			ath6kl_err("Unable to write to the device: %d\n",
 				   ret);
 			return ret;
 		}
-		ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len);
+		ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len);
 		if (ret) {
 			ath6kl_err("Unable to read from the device: %d\n",
 				   ret);
@@ -326,7 +173,7 @@
 	u32 offset;
 	u32 len_remain, tx_len;
 	const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
-	u8 aligned_buf[BMI_DATASZ_MAX];
+	u8 aligned_buf[400];
 	u8 *src;
 
 	if (ar->bmi.done_sent) {
@@ -334,12 +181,15 @@
 		return -EACCES;
 	}
 
-	if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) {
+	if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
 
-	memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header);
+	if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf)))
+		return -E2BIG;
+
+	memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header);
 
 	ath6kl_dbg(ATH6KL_DBG_BMI,
 		  "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
@@ -348,7 +198,7 @@
 	while (len_remain) {
 		src = &buf[len - len_remain];
 
-		if (len_remain < (BMI_DATASZ_MAX - header)) {
+		if (len_remain < (ar->bmi.max_data_size - header)) {
 			if (len_remain & 3) {
 				/* align it with 4 bytes */
 				len_remain = len_remain +
@@ -358,7 +208,7 @@
 			}
 			tx_len = len_remain;
 		} else {
-			tx_len = (BMI_DATASZ_MAX - header);
+			tx_len = (ar->bmi.max_data_size - header);
 		}
 
 		offset = 0;
@@ -371,7 +221,7 @@
 		memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
 		offset += tx_len;
 
-		ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 		if (ret) {
 			ath6kl_err("Unable to write to the device: %d\n",
 				   ret);
@@ -396,7 +246,7 @@
 	}
 
 	size = sizeof(cid) + sizeof(addr) + sizeof(param);
-	if (size > MAX_BMI_CMDBUF_SZ) {
+	if (size > ar->bmi.max_cmd_size) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
@@ -413,13 +263,13 @@
 	memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
 	offset += sizeof(*param);
 
-	ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 	if (ret) {
 		ath6kl_err("Unable to write to the device: %d\n", ret);
 		return ret;
 	}
 
-	ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
+	ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
 	if (ret) {
 		ath6kl_err("Unable to read from the device: %d\n", ret);
 		return ret;
@@ -443,7 +293,7 @@
 	}
 
 	size = sizeof(cid) + sizeof(addr);
-	if (size > MAX_BMI_CMDBUF_SZ) {
+	if (size > ar->bmi.max_cmd_size) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
@@ -457,7 +307,7 @@
 	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 	offset += sizeof(addr);
 
-	ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 	if (ret) {
 		ath6kl_err("Unable to write to the device: %d\n", ret);
 		return ret;
@@ -479,7 +329,7 @@
 	}
 
 	size = sizeof(cid) + sizeof(addr);
-	if (size > MAX_BMI_CMDBUF_SZ) {
+	if (size > ar->bmi.max_cmd_size) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
@@ -493,13 +343,13 @@
 	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 	offset += sizeof(addr);
 
-	ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 	if (ret) {
 		ath6kl_err("Unable to write to the device: %d\n", ret);
 		return ret;
 	}
 
-	ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
+	ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
 	if (ret) {
 		ath6kl_err("Unable to read from the device: %d\n", ret);
 		return ret;
@@ -522,7 +372,7 @@
 	}
 
 	size = sizeof(cid) + sizeof(addr) + sizeof(param);
-	if (size > MAX_BMI_CMDBUF_SZ) {
+	if (size > ar->bmi.max_cmd_size) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
@@ -540,7 +390,7 @@
 	memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
 	offset += sizeof(param);
 
-	ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 	if (ret) {
 		ath6kl_err("Unable to write to the device: %d\n", ret);
 		return ret;
@@ -563,8 +413,8 @@
 		return -EACCES;
 	}
 
-	size = BMI_DATASZ_MAX + header;
-	if (size > MAX_BMI_CMDBUF_SZ) {
+	size = ar->bmi.max_data_size + header;
+	if (size > ar->bmi.max_cmd_size) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
@@ -575,8 +425,8 @@
 
 	len_remain = len;
 	while (len_remain) {
-		tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ?
-			  len_remain : (BMI_DATASZ_MAX - header);
+		tx_len = (len_remain < (ar->bmi.max_data_size - header)) ?
+			  len_remain : (ar->bmi.max_data_size - header);
 
 		offset = 0;
 		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
@@ -587,7 +437,7 @@
 			tx_len);
 		offset += tx_len;
 
-		ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 		if (ret) {
 			ath6kl_err("Unable to write to the device: %d\n",
 				   ret);
@@ -613,7 +463,7 @@
 	}
 
 	size = sizeof(cid) + sizeof(addr);
-	if (size > MAX_BMI_CMDBUF_SZ) {
+	if (size > ar->bmi.max_cmd_size) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
@@ -629,7 +479,7 @@
 	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 	offset += sizeof(addr);
 
-	ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 	if (ret) {
 		ath6kl_err("Unable to start LZ stream to the device: %d\n",
 			   ret);
@@ -677,8 +527,13 @@
 
 int ath6kl_bmi_init(struct ath6kl *ar)
 {
-	ar->bmi.cmd_buf = kzalloc(MAX_BMI_CMDBUF_SZ, GFP_ATOMIC);
+	if (WARN_ON(ar->bmi.max_data_size == 0))
+		return -EINVAL;
 
+	/* cmd + addr + len + data_size */
+	ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3);
+
+	ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_ATOMIC);
 	if (!ar->bmi.cmd_buf)
 		return -ENOMEM;
 
diff --git a/drivers/net/wireless/ath/ath6kl/bmi.h b/drivers/net/wireless/ath/ath6kl/bmi.h
index 009e8f6..f1ca681 100644
--- a/drivers/net/wireless/ath/ath6kl/bmi.h
+++ b/drivers/net/wireless/ath/ath6kl/bmi.h
@@ -44,12 +44,6 @@
  * BMI handles all required Target-side cache flushing.
  */
 
-#define MAX_BMI_CMDBUF_SZ (BMI_DATASZ_MAX + \
-			   (sizeof(u32) * 3 /* cmd + addr + len */))
-
-/* Maximum data size used for BMI transfers */
-#define BMI_DATASZ_MAX                      256
-
 /* BMI Commands */
 
 #define BMI_NO_COMMAND                      0
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 4d1394a..b71d7a4 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -203,6 +203,10 @@
 		*ar_cipher = AES_CRYPT;
 		*ar_cipher_len = 0;
 		break;
+	case WLAN_CIPHER_SUITE_SMS4:
+		*ar_cipher = WAPI_CRYPT;
+		*ar_cipher_len = 0;
+		break;
 	default:
 		ath6kl_err("cipher 0x%x not supported\n", cipher);
 		return -ENOTSUPP;
@@ -353,7 +357,7 @@
 
 	if (type == NL80211_IFTYPE_STATION ||
 	    type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
-		for (i = 0; i < MAX_NUM_VIF; i++) {
+		for (i = 0; i < ar->vif_max; i++) {
 			if ((ar->avail_idx_map >> i) & BIT(0)) {
 				*if_idx = i;
 				return true;
@@ -363,7 +367,7 @@
 
 	if (type == NL80211_IFTYPE_P2P_CLIENT ||
 	    type == NL80211_IFTYPE_P2P_GO) {
-		for (i = ar->max_norm_iface; i < MAX_NUM_VIF; i++) {
+		for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
 			if ((ar->avail_idx_map >> i) & BIT(0)) {
 				*if_idx = i;
 				return true;
@@ -427,7 +431,8 @@
 		status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
 		if (status)
 			return status;
-	}
+	} else
+		ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
 
 	if (test_bit(CONNECTED, &vif->flags) &&
 	    vif->ssid_len == sme->ssid_len &&
@@ -561,17 +566,28 @@
 	return 0;
 }
 
-static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, const u8 *bssid,
+static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
+				    enum network_type nw_type,
+				    const u8 *bssid,
 				    struct ieee80211_channel *chan,
 				    const u8 *beacon_ie, size_t beacon_ie_len)
 {
 	struct ath6kl *ar = vif->ar;
 	struct cfg80211_bss *bss;
+	u16 cap_mask, cap_val;
 	u8 *ie;
 
+	if (nw_type & ADHOC_NETWORK) {
+		cap_mask = WLAN_CAPABILITY_IBSS;
+		cap_val = WLAN_CAPABILITY_IBSS;
+	} else {
+		cap_mask = WLAN_CAPABILITY_ESS;
+		cap_val = WLAN_CAPABILITY_ESS;
+	}
+
 	bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
-			       vif->ssid, vif->ssid_len, WLAN_CAPABILITY_ESS,
-			       WLAN_CAPABILITY_ESS);
+			       vif->ssid, vif->ssid_len,
+			       cap_mask, cap_val);
 	if (bss == NULL) {
 		/*
 		 * Since cfg80211 may not yet know about the BSS,
@@ -589,13 +605,12 @@
 		memcpy(ie + 2, vif->ssid, vif->ssid_len);
 		memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
 		bss = cfg80211_inform_bss(ar->wiphy, chan,
-					  bssid, 0, WLAN_CAPABILITY_ESS, 100,
+					  bssid, 0, cap_val, 100,
 					  ie, 2 + vif->ssid_len + beacon_ie_len,
 					  0, GFP_KERNEL);
 		if (bss)
-			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for "
-				   "%pM prior to indicating connect/roamed "
-				   "event\n", bssid);
+			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
+				   "cfg80211\n", bssid);
 		kfree(ie);
 	} else
 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
@@ -658,16 +673,16 @@
 
 	chan = ieee80211_get_channel(ar->wiphy, (int) channel);
 
-
-	if (nw_type & ADHOC_NETWORK) {
-		cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
+	if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
+				     beacon_ie_len) < 0) {
+		ath6kl_err("could not add cfg80211 bss entry\n");
 		return;
 	}
 
-	if (ath6kl_add_bss_if_needed(vif, bssid, chan, assoc_info,
-				     beacon_ie_len) < 0) {
-		ath6kl_err("could not add cfg80211 bss entry for "
-			   "connect/roamed notification\n");
+	if (nw_type & ADHOC_NETWORK) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
+			   nw_type & ADHOC_CREATOR ? "creator" : "joiner");
+		cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
 		return;
 	}
 
@@ -935,13 +950,19 @@
 		key_usage = GROUP_USAGE;
 
 	if (params) {
+		int seq_len = params->seq_len;
+		if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
+		    seq_len > ATH6KL_KEY_SEQ_LEN) {
+			/* Only first half of the WPI PN is configured */
+			seq_len = ATH6KL_KEY_SEQ_LEN;
+		}
 		if (params->key_len > WLAN_MAX_KEY_LEN ||
-		    params->seq_len > sizeof(key->seq))
+		    seq_len > sizeof(key->seq))
 			return -EINVAL;
 
 		key->key_len = params->key_len;
 		memcpy(key->key, params->key, key->key_len);
-		key->seq_len = params->seq_len;
+		key->seq_len = seq_len;
 		memcpy(key->seq, params->seq, key->seq_len);
 		key->cipher = params->cipher;
 	}
@@ -959,6 +980,9 @@
 	case WLAN_CIPHER_SUITE_CCMP:
 		key_type = AES_CRYPT;
 		break;
+	case WLAN_CIPHER_SUITE_SMS4:
+		key_type = WAPI_CRYPT;
+		break;
 
 	default:
 		return -ENOTSUPP;
@@ -974,8 +998,6 @@
 		   __func__, key_index, key->key_len, key_type,
 		   key_usage, key->seq_len);
 
-	vif->def_txkey_index = key_index;
-
 	if (vif->nw_type == AP_NETWORK && !pairwise &&
 	    (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
 		ar->ap_mode_bkey.valid = true;
@@ -1010,8 +1032,7 @@
 		return 0;
 	}
 
-	return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
-				     vif->def_txkey_index,
+	return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
 				     key_type, key_usage, key->key_len,
 				     key->seq, key->seq_len, key->key,
 				     KEY_OP_INIT_VAL,
@@ -1286,7 +1307,7 @@
 	struct net_device *ndev;
 	u8 if_idx, nw_type;
 
-	if (ar->num_vif == MAX_NUM_VIF) {
+	if (ar->num_vif == ar->vif_max) {
 		ath6kl_err("Reached maximum number of supported vif\n");
 		return ERR_PTR(-EINVAL);
 	}
@@ -1331,9 +1352,6 @@
 
 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
 
-	if (!ath6kl_cfg80211_ready(vif))
-		return -EIO;
-
 	switch (type) {
 	case NL80211_IFTYPE_STATION:
 		vif->next_mode = INFRA_NETWORK;
@@ -1451,6 +1469,7 @@
 	WLAN_CIPHER_SUITE_TKIP,
 	WLAN_CIPHER_SUITE_CCMP,
 	CCKM_KRK_CIPHER_SUITE,
+	WLAN_CIPHER_SUITE_SMS4,
 };
 
 static bool is_rate_legacy(s32 rate)
@@ -1985,7 +2004,7 @@
 	int ies_len;
 	struct wmi_connect_cmd p;
 	int res;
-	int i;
+	int i, ret;
 
 	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
 
@@ -2043,7 +2062,9 @@
 	if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
 		return -EOPNOTSUPP; /* TODO */
 
-	vif->dot11_auth_mode = OPEN_AUTH;
+	ret = ath6kl_set_auth_type(vif, info->auth_type);
+	if (ret)
+		return ret;
 
 	memset(&p, 0, sizeof(p));
 
@@ -2079,6 +2100,9 @@
 		case WLAN_CIPHER_SUITE_CCMP:
 			p.prwise_crypto_type |= AES_CRYPT;
 			break;
+		case WLAN_CIPHER_SUITE_SMS4:
+			p.prwise_crypto_type |= WAPI_CRYPT;
+			break;
 		}
 	}
 	if (p.prwise_crypto_type == 0) {
@@ -2098,6 +2122,9 @@
 	case WLAN_CIPHER_SUITE_CCMP:
 		p.grp_crypto_type = AES_CRYPT;
 		break;
+	case WLAN_CIPHER_SUITE_SMS4:
+		p.grp_crypto_type = WAPI_CRYPT;
+		break;
 	default:
 		p.grp_crypto_type = NONE_CRYPT;
 		break;
@@ -2430,6 +2457,8 @@
 	ar->wiphy = wiphy;
 	ar->dev = dev;
 
+	ar->vif_max = 1;
+
 	if (multi_norm_if_support)
 		ar->max_norm_iface = 2;
 	else
diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h
index 41e465f..bfd6597 100644
--- a/drivers/net/wireless/ath/ath6kl/common.h
+++ b/drivers/net/wireless/ath/ath6kl/common.h
@@ -71,6 +71,7 @@
 	WEP_CRYPT           = 0x02,
 	TKIP_CRYPT          = 0x04,
 	AES_CRYPT           = 0x08,
+	WAPI_CRYPT          = 0x10,
 };
 
 struct htc_endpoint_credit_dist;
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index e7e095e..b185564 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -70,6 +70,8 @@
 	ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5,
 	ATH6KL_FW_IE_CAPABILITIES = 6,
 	ATH6KL_FW_IE_PATCH_ADDR = 7,
+	ATH6KL_FW_IE_BOARD_ADDR = 8,
+	ATH6KL_FW_IE_VIF_MAX = 9,
 };
 
 enum ath6kl_fw_capability {
@@ -88,37 +90,47 @@
 };
 
 /* AR6003 1.0 definitions */
-#define AR6003_REV1_VERSION                 0x300002ba
+#define AR6003_HW_1_0_VERSION                 0x300002ba
 
 /* AR6003 2.0 definitions */
-#define AR6003_REV2_VERSION                 0x30000384
-#define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS  0x57e910
-#define AR6003_REV2_OTP_FILE                "ath6k/AR6003/hw2.0/otp.bin.z77"
-#define AR6003_REV2_FIRMWARE_FILE           "ath6k/AR6003/hw2.0/athwlan.bin.z77"
-#define AR6003_REV2_TCMD_FIRMWARE_FILE      "ath6k/AR6003/hw2.0/athtcmd_ram.bin"
-#define AR6003_REV2_PATCH_FILE              "ath6k/AR6003/hw2.0/data.patch.bin"
-#define AR6003_REV2_FIRMWARE_2_FILE         "ath6k/AR6003/hw2.0/fw-2.bin"
-#define AR6003_REV2_BOARD_DATA_FILE         "ath6k/AR6003/hw2.0/bdata.bin"
-#define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin"
+#define AR6003_HW_2_0_VERSION                 0x30000384
+#define AR6003_HW_2_0_PATCH_DOWNLOAD_ADDRESS  0x57e910
+#define AR6003_HW_2_0_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77"
+#define AR6003_HW_2_0_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77"
+#define AR6003_HW_2_0_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athtcmd_ram.bin"
+#define AR6003_HW_2_0_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin"
+#define AR6003_HW_2_0_FIRMWARE_2_FILE "ath6k/AR6003/hw2.0/fw-2.bin"
+#define AR6003_HW_2_0_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin"
+#define AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE \
+			"ath6k/AR6003/hw2.0/bdata.SD31.bin"
 
 /* AR6003 3.0 definitions */
-#define AR6003_REV3_VERSION                 0x30000582
-#define AR6003_REV3_OTP_FILE                "ath6k/AR6003/hw2.1.1/otp.bin"
-#define AR6003_REV3_FIRMWARE_FILE           "ath6k/AR6003/hw2.1.1/athwlan.bin"
-#define AR6003_REV3_TCMD_FIRMWARE_FILE    "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin"
-#define AR6003_REV3_PATCH_FILE            "ath6k/AR6003/hw2.1.1/data.patch.bin"
-#define AR6003_REV3_FIRMWARE_2_FILE           "ath6k/AR6003/hw2.1.1/fw-2.bin"
-#define AR6003_REV3_BOARD_DATA_FILE       "ath6k/AR6003/hw2.1.1/bdata.bin"
-#define AR6003_REV3_DEFAULT_BOARD_DATA_FILE	\
-	"ath6k/AR6003/hw2.1.1/bdata.SD31.bin"
+#define AR6003_HW_2_1_1_VERSION                 0x30000582
+#define AR6003_HW_2_1_1_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin"
+#define AR6003_HW_2_1_1_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin"
+#define AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE \
+			"ath6k/AR6003/hw2.1.1/athtcmd_ram.bin"
+#define AR6003_HW_2_1_1_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin"
+#define AR6003_HW_2_1_1_FIRMWARE_2_FILE "ath6k/AR6003/hw2.1.1/fw-2.bin"
+#define AR6003_HW_2_1_1_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin"
+#define AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE	\
+			"ath6k/AR6003/hw2.1.1/bdata.SD31.bin"
 
 /* AR6004 1.0 definitions */
-#define AR6004_REV1_VERSION                 0x30000623
-#define AR6004_REV1_FIRMWARE_FILE           "ath6k/AR6004/hw6.1/fw.ram.bin"
-#define AR6004_REV1_FIRMWARE_2_FILE         "ath6k/AR6004/hw6.1/fw-2.bin"
-#define AR6004_REV1_BOARD_DATA_FILE         "ath6k/AR6004/hw6.1/bdata.bin"
-#define AR6004_REV1_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.DB132.bin"
-#define AR6004_REV1_EPPING_FIRMWARE_FILE "ath6k/AR6004/hw6.1/endpointping.bin"
+#define AR6004_HW_1_0_VERSION                 0x30000623
+#define AR6004_HW_1_0_FIRMWARE_2_FILE         "ath6k/AR6004/hw1.0/fw-2.bin"
+#define AR6004_HW_1_0_FIRMWARE_FILE           "ath6k/AR6004/hw1.0/fw.ram.bin"
+#define AR6004_HW_1_0_BOARD_DATA_FILE         "ath6k/AR6004/hw1.0/bdata.bin"
+#define AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE \
+	"ath6k/AR6004/hw1.0/bdata.DB132.bin"
+
+/* AR6004 1.1 definitions */
+#define AR6004_HW_1_1_VERSION                 0x30000001
+#define AR6004_HW_1_1_FIRMWARE_2_FILE         "ath6k/AR6004/hw1.1/fw-2.bin"
+#define AR6004_HW_1_1_FIRMWARE_FILE           "ath6k/AR6004/hw1.1/fw.ram.bin"
+#define AR6004_HW_1_1_BOARD_DATA_FILE         "ath6k/AR6004/hw1.1/bdata.bin"
+#define AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE \
+	"ath6k/AR6004/hw1.1/bdata.DB132.bin"
 
 /* Per STA data, used in AP mode */
 #define STA_PS_AWAKE		BIT(0)
@@ -272,6 +284,8 @@
 	u32 cmd_credits;
 	bool done_sent;
 	u8 *cmd_buf;
+	u32 max_data_size;
+	u32 max_cmd_size;
 };
 
 struct target_stats {
@@ -381,7 +395,16 @@
 	u8 key_len;
 };
 
-#define MAX_NUM_VIF	1
+enum ath6kl_hif_type {
+	ATH6KL_HIF_TYPE_SDIO,
+	ATH6KL_HIF_TYPE_USB,
+};
+
+/*
+ * Driver's maximum limit, note that some firmwares support only one vif
+ * and the runtime (current) limit must be checked from ar->vif_max.
+ */
+#define ATH6KL_VIF_MAX	1
 
 /* vif flags info */
 enum ath6kl_vif_state {
@@ -474,11 +497,13 @@
 	int tx_pending[ENDPOINT_MAX];
 	int total_tx_data_pend;
 	struct htc_target *htc_target;
+	enum ath6kl_hif_type hif_type;
 	void *hif_priv;
 	struct list_head vif_list;
 	/* Lock to avoid race in vif_list entries among add/del/traverse */
 	spinlock_t list_lock;
 	u8 num_vif;
+	unsigned int vif_max;
 	u8 max_norm_iface;
 	u8 avail_idx_map;
 	spinlock_t lock;
@@ -525,12 +550,23 @@
 		size_t rx_report_len;
 	} tm;
 
-	struct {
+	struct ath6kl_hw {
+		u32 id;
+		const char *name;
 		u32 dataset_patch_addr;
 		u32 app_load_addr;
 		u32 app_start_override_addr;
 		u32 board_ext_data_addr;
 		u32 reserved_ram_size;
+		u32 board_addr;
+
+		const char *fw_otp;
+		const char *fw;
+		const char *fw_tcmd;
+		const char *fw_patch;
+		const char *fw_api2;
+		const char *fw_board;
+		const char *fw_default_board;
 	} hw;
 
 	u16 conf_flags;
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h
index c24d120..9b779aa 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.h
+++ b/drivers/net/wireless/ath/ath6kl/debug.h
@@ -41,6 +41,7 @@
 	ATH6KL_DBG_BOOT		= BIT(18),    /* driver init and fw boot */
 	ATH6KL_DBG_WMI_DUMP	= BIT(19),
 	ATH6KL_DBG_SUSPEND	= BIT(20),
+	ATH6KL_DBG_USB		= BIT(21),
 	ATH6KL_DBG_ANY	        = 0xffffffff  /* enable all logs */
 };
 
diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h
index eed2287..2fe1dad 100644
--- a/drivers/net/wireless/ath/ath6kl/hif-ops.h
+++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h
@@ -91,6 +91,36 @@
 	return ar->hif_ops->suspend(ar, wow);
 }
 
+/*
+ * Read from the ATH6KL through its diagnostic window. No cooperation from
+ * the Target is required for this.
+ */
+static inline int ath6kl_hif_diag_read32(struct ath6kl *ar, u32 address,
+					 u32 *value)
+{
+	return ar->hif_ops->diag_read32(ar, address, value);
+}
+
+/*
+ * Write to the ATH6KL through its diagnostic window. No cooperation from
+ * the Target is required for this.
+ */
+static inline int ath6kl_hif_diag_write32(struct ath6kl *ar, u32 address,
+					  __le32 value)
+{
+	return ar->hif_ops->diag_write32(ar, address, value);
+}
+
+static inline int ath6kl_hif_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
+{
+	return ar->hif_ops->bmi_read(ar, buf, len);
+}
+
+static inline int ath6kl_hif_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
+{
+	return ar->hif_ops->bmi_write(ar, buf, len);
+}
+
 static inline int ath6kl_hif_resume(struct ath6kl *ar)
 {
 	ath6kl_dbg(ATH6KL_DBG_HIF, "hif resume\n");
diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c
index e57da35..0772ef6 100644
--- a/drivers/net/wireless/ath/ath6kl/hif.c
+++ b/drivers/net/wireless/ath/ath6kl/hif.c
@@ -689,6 +689,11 @@
 	ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n",
 		   dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr);
 
+	/* usb doesn't support enabling interrupts */
+	/* FIXME: remove check once USB support is implemented */
+	if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB)
+		return 0;
+
 	status = ath6kl_hif_disable_intrs(dev);
 
 fail_setup:
diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h
index f2dc3bc..699a036 100644
--- a/drivers/net/wireless/ath/ath6kl/hif.h
+++ b/drivers/net/wireless/ath/ath6kl/hif.h
@@ -35,6 +35,7 @@
 #define MAX_SCATTER_REQ_TRANSFER_SIZE    (32 * 1024)
 
 #define MANUFACTURER_ID_AR6003_BASE        0x300
+#define MANUFACTURER_ID_AR6004_BASE        0x400
     /* SDIO manufacturer ID and Codes */
 #define MANUFACTURER_ID_ATH6KL_BASE_MASK     0xFF00
 #define MANUFACTURER_CODE                  0x271	/* Atheros */
@@ -244,6 +245,10 @@
 	void (*cleanup_scatter)(struct ath6kl *ar);
 	int (*suspend)(struct ath6kl *ar, struct cfg80211_wowlan *wow);
 	int (*resume)(struct ath6kl *ar);
+	int (*diag_read32)(struct ath6kl *ar, u32 address, u32 *value);
+	int (*diag_write32)(struct ath6kl *ar, u32 address, __le32 value);
+	int (*bmi_read)(struct ath6kl *ar, u8 *buf, u32 len);
+	int (*bmi_write)(struct ath6kl *ar, u8 *buf, u32 len);
 	int (*power_on)(struct ath6kl *ar);
 	int (*power_off)(struct ath6kl *ar);
 	void (*stop)(struct ath6kl *ar);
diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c
index f3b63ca..b017022 100644
--- a/drivers/net/wireless/ath/ath6kl/htc.c
+++ b/drivers/net/wireless/ath/ath6kl/htc.c
@@ -2543,6 +2543,12 @@
 	struct htc_service_connect_resp resp;
 	int status;
 
+	/* FIXME: remove once USB support is implemented */
+	if (target->dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) {
+		ath6kl_err("HTC doesn't support USB yet. Patience!\n");
+		return -EOPNOTSUPP;
+	}
+
 	/* we should be getting 1 control message that the target is ready */
 	packet = htc_wait_for_ctrl_msg(target);
 
@@ -2772,7 +2778,9 @@
 {
 	struct htc_packet *packet, *tmp_packet;
 
-	ath6kl_hif_cleanup_scatter(target->dev->ar);
+	/* FIXME: remove check once USB support is implemented */
+	if (target->dev->ar->hif_type != ATH6KL_HIF_TYPE_USB)
+		ath6kl_hif_cleanup_scatter(target->dev->ar);
 
 	list_for_each_entry_safe(packet, tmp_packet,
 			&target->free_ctrl_txbuf, list) {
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index 57529ac..6c4f6a9 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -33,6 +33,72 @@
 module_param(testmode, uint, 0644);
 module_param(suspend_cutpower, bool, 0444);
 
+static const struct ath6kl_hw hw_list[] = {
+	{
+		.id				= AR6003_HW_2_0_VERSION,
+		.name				= "ar6003 hw 2.0",
+		.dataset_patch_addr		= 0x57e884,
+		.app_load_addr			= 0x543180,
+		.board_ext_data_addr		= 0x57e500,
+		.reserved_ram_size		= 6912,
+
+		/* hw2.0 needs override address hardcoded */
+		.app_start_override_addr	= 0x944C00,
+
+		.fw_otp			= AR6003_HW_2_0_OTP_FILE,
+		.fw			= AR6003_HW_2_0_FIRMWARE_FILE,
+		.fw_tcmd		= AR6003_HW_2_0_TCMD_FIRMWARE_FILE,
+		.fw_patch		= AR6003_HW_2_0_PATCH_FILE,
+		.fw_api2		= AR6003_HW_2_0_FIRMWARE_2_FILE,
+		.fw_board		= AR6003_HW_2_0_BOARD_DATA_FILE,
+		.fw_default_board	= AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE,
+	},
+	{
+		.id				= AR6003_HW_2_1_1_VERSION,
+		.name				= "ar6003 hw 2.1.1",
+		.dataset_patch_addr		= 0x57ff74,
+		.app_load_addr			= 0x1234,
+		.board_ext_data_addr		= 0x542330,
+		.reserved_ram_size		= 512,
+
+		.fw_otp			= AR6003_HW_2_1_1_OTP_FILE,
+		.fw			= AR6003_HW_2_1_1_FIRMWARE_FILE,
+		.fw_tcmd		= AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE,
+		.fw_patch		= AR6003_HW_2_1_1_PATCH_FILE,
+		.fw_api2		= AR6003_HW_2_1_1_FIRMWARE_2_FILE,
+		.fw_board		= AR6003_HW_2_1_1_BOARD_DATA_FILE,
+		.fw_default_board	= AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE,
+	},
+	{
+		.id				= AR6004_HW_1_0_VERSION,
+		.name				= "ar6004 hw 1.0",
+		.dataset_patch_addr		= 0x57e884,
+		.app_load_addr			= 0x1234,
+		.board_ext_data_addr		= 0x437000,
+		.reserved_ram_size		= 19456,
+		.board_addr			= 0x433900,
+
+		.fw			= AR6004_HW_1_0_FIRMWARE_FILE,
+		.fw_api2		= AR6004_HW_1_0_FIRMWARE_2_FILE,
+		.fw_board		= AR6004_HW_1_0_BOARD_DATA_FILE,
+		.fw_default_board	= AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE,
+	},
+	{
+		.id				= AR6004_HW_1_1_VERSION,
+		.name				= "ar6004 hw 1.1",
+		.dataset_patch_addr		= 0x57e884,
+		.app_load_addr			= 0x1234,
+		.board_ext_data_addr		= 0x437000,
+		.reserved_ram_size		= 11264,
+		.board_addr			= 0x43d400,
+
+		.fw			= AR6004_HW_1_1_FIRMWARE_FILE,
+		.fw_api2		= AR6004_HW_1_1_FIRMWARE_2_FILE,
+		.fw_board		= AR6004_HW_1_1_BOARD_DATA_FILE,
+		.fw_default_board	= AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE,
+	},
+};
+
 /*
  * Include definitions here that can be used to tune the WLAN module
  * behavior. Different customers can tune the behavior as per their needs,
@@ -397,7 +463,7 @@
 	 */
 	fw_iftype = HI_OPTION_FW_MODE_BSS_STA;
 
-	for (i = 0; i < MAX_NUM_VIF; i++)
+	for (i = 0; i < ar->vif_max; i++)
 		fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS);
 
 	/*
@@ -411,7 +477,7 @@
 		fw_submode |= HI_OPTION_FW_SUBMODE_NONE <<
 			      (i * HI_OPTION_FW_SUBMODE_BITS);
 
-	for (i = ar->max_norm_iface; i < MAX_NUM_VIF; i++)
+	for (i = ar->max_norm_iface; i < ar->vif_max; i++)
 		fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
 			      (i * HI_OPTION_FW_SUBMODE_BITS);
 
@@ -442,7 +508,7 @@
 		return -EIO;
 	}
 
-	param |= (MAX_NUM_VIF << HI_OPTION_NUM_DEV_SHIFT);
+	param |= (ar->vif_max << HI_OPTION_NUM_DEV_SHIFT);
 	param |= fw_mode << HI_OPTION_FW_MODE_SHIFT;
 	param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT;
 
@@ -550,11 +616,11 @@
 static const char *get_target_ver_dir(const struct ath6kl *ar)
 {
 	switch (ar->version.target_ver) {
-	case AR6003_REV1_VERSION:
+	case AR6003_HW_1_0_VERSION:
 		return "ath6k/AR6003/hw1.0";
-	case AR6003_REV2_VERSION:
+	case AR6003_HW_2_0_VERSION:
 		return "ath6k/AR6003/hw2.0";
-	case AR6003_REV3_VERSION:
+	case AR6003_HW_2_1_1_VERSION:
 		return "ath6k/AR6003/hw2.1.1";
 	}
 	ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__,
@@ -612,17 +678,10 @@
 	if (ar->fw_board != NULL)
 		return 0;
 
-	switch (ar->version.target_ver) {
-	case AR6003_REV2_VERSION:
-		filename = AR6003_REV2_BOARD_DATA_FILE;
-		break;
-	case AR6004_REV1_VERSION:
-		filename = AR6004_REV1_BOARD_DATA_FILE;
-		break;
-	default:
-		filename = AR6003_REV3_BOARD_DATA_FILE;
-		break;
-	}
+	if (WARN_ON(ar->hw.fw_board == NULL))
+		return -EINVAL;
+
+	filename = ar->hw.fw_board;
 
 	ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
 			    &ar->fw_board_len);
@@ -640,17 +699,7 @@
 	ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",
 		    filename, ret);
 
-	switch (ar->version.target_ver) {
-	case AR6003_REV2_VERSION:
-		filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE;
-		break;
-	case AR6004_REV1_VERSION:
-		filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE;
-		break;
-	default:
-		filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE;
-		break;
-	}
+	filename = ar->hw.fw_default_board;
 
 	ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
 			    &ar->fw_board_len);
@@ -674,19 +723,14 @@
 	if (ar->fw_otp != NULL)
 		return 0;
 
-	switch (ar->version.target_ver) {
-	case AR6003_REV2_VERSION:
-		filename = AR6003_REV2_OTP_FILE;
-		break;
-	case AR6004_REV1_VERSION:
-		ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n");
+	if (ar->hw.fw_otp == NULL) {
+		ath6kl_dbg(ATH6KL_DBG_BOOT,
+			   "no OTP file configured for this hw\n");
 		return 0;
-		break;
-	default:
-		filename = AR6003_REV3_OTP_FILE;
-		break;
 	}
 
+	filename = ar->hw.fw_otp;
+
 	ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
 			    &ar->fw_otp_len);
 	if (ret) {
@@ -707,38 +751,22 @@
 		return 0;
 
 	if (testmode) {
-		switch (ar->version.target_ver) {
-		case AR6003_REV2_VERSION:
-			filename = AR6003_REV2_TCMD_FIRMWARE_FILE;
-			break;
-		case AR6003_REV3_VERSION:
-			filename = AR6003_REV3_TCMD_FIRMWARE_FILE;
-			break;
-		case AR6004_REV1_VERSION:
-			ath6kl_warn("testmode not supported with ar6004\n");
+		if (ar->hw.fw_tcmd == NULL) {
+			ath6kl_warn("testmode not supported\n");
 			return -EOPNOTSUPP;
-		default:
-			ath6kl_warn("unknown target version: 0x%x\n",
-				       ar->version.target_ver);
-			return -EINVAL;
 		}
 
+		filename = ar->hw.fw_tcmd;
+
 		set_bit(TESTMODE, &ar->flag);
 
 		goto get_fw;
 	}
 
-	switch (ar->version.target_ver) {
-	case AR6003_REV2_VERSION:
-		filename = AR6003_REV2_FIRMWARE_FILE;
-		break;
-	case AR6004_REV1_VERSION:
-		filename = AR6004_REV1_FIRMWARE_FILE;
-		break;
-	default:
-		filename = AR6003_REV3_FIRMWARE_FILE;
-		break;
-	}
+	if (WARN_ON(ar->hw.fw == NULL))
+		return -EINVAL;
+
+	filename = ar->hw.fw;
 
 get_fw:
 	ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
@@ -756,27 +784,20 @@
 	const char *filename;
 	int ret;
 
-	switch (ar->version.target_ver) {
-	case AR6003_REV2_VERSION:
-		filename = AR6003_REV2_PATCH_FILE;
-		break;
-	case AR6004_REV1_VERSION:
-		/* FIXME: implement for AR6004 */
+	if (ar->fw_patch != NULL)
 		return 0;
-		break;
-	default:
-		filename = AR6003_REV3_PATCH_FILE;
-		break;
-	}
 
-	if (ar->fw_patch == NULL) {
-		ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
-				    &ar->fw_patch_len);
-		if (ret) {
-			ath6kl_err("Failed to get patch file %s: %d\n",
-				   filename, ret);
-			return ret;
-		}
+	if (ar->hw.fw_patch == NULL)
+		return 0;
+
+	filename = ar->hw.fw_patch;
+
+	ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
+			    &ar->fw_patch_len);
+	if (ret) {
+		ath6kl_err("Failed to get patch file %s: %d\n",
+			   filename, ret);
+		return ret;
 	}
 
 	return 0;
@@ -811,19 +832,10 @@
 	int ret, ie_id, i, index, bit;
 	__le32 *val;
 
-	switch (ar->version.target_ver) {
-	case AR6003_REV2_VERSION:
-		filename = AR6003_REV2_FIRMWARE_2_FILE;
-		break;
-	case AR6003_REV3_VERSION:
-		filename = AR6003_REV3_FIRMWARE_2_FILE;
-		break;
-	case AR6004_REV1_VERSION:
-		filename = AR6004_REV1_FIRMWARE_2_FILE;
-		break;
-	default:
+	if (ar->hw.fw_api2 == NULL)
 		return -EOPNOTSUPP;
-	}
+
+	filename = ar->hw.fw_api2;
 
 	ret = request_firmware(&fw, filename, ar->dev);
 	if (ret)
@@ -937,9 +949,31 @@
 			ar->hw.dataset_patch_addr = le32_to_cpup(val);
 
 			ath6kl_dbg(ATH6KL_DBG_BOOT,
-				   "found patch address ie 0x%d\n",
+				   "found patch address ie 0x%x\n",
 				   ar->hw.dataset_patch_addr);
 			break;
+		case ATH6KL_FW_IE_BOARD_ADDR:
+			if (ie_len != sizeof(*val))
+				break;
+
+			val = (__le32 *) data;
+			ar->hw.board_addr = le32_to_cpup(val);
+
+			ath6kl_dbg(ATH6KL_DBG_BOOT,
+				   "found board address ie 0x%x\n",
+				   ar->hw.board_addr);
+			break;
+		case ATH6KL_FW_IE_VIF_MAX:
+			if (ie_len != sizeof(*val))
+				break;
+
+			val = (__le32 *) data;
+			ar->vif_max = min_t(unsigned int, le32_to_cpup(val),
+					    ATH6KL_VIF_MAX);
+
+			ath6kl_dbg(ATH6KL_DBG_BOOT,
+				   "found vif max ie %d\n", ar->vif_max);
+			break;
 		default:
 			ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n",
 				   le32_to_cpup(&hdr->id));
@@ -994,8 +1028,8 @@
 	 * For AR6004, host determine Target RAM address for
 	 * writing board data.
 	 */
-	if (ar->target_type == TARGET_TYPE_AR6004) {
-		board_address = AR6004_REV1_BOARD_DATA_ADDRESS;
+	if (ar->hw.board_addr != 0) {
+		board_address = ar->hw.board_addr;
 		ath6kl_bmi_write(ar,
 				ath6kl_get_hi_item_addr(ar,
 				HI_ITEM(hi_board_data)),
@@ -1013,7 +1047,8 @@
 			HI_ITEM(hi_board_ext_data)),
 			(u8 *) &board_ext_address, 4);
 
-	if (board_ext_address == 0) {
+	if (ar->target_type == TARGET_TYPE_AR6003 &&
+	    board_ext_address == 0) {
 		ath6kl_err("Failed to get board file target address.\n");
 		return -EINVAL;
 	}
@@ -1033,8 +1068,8 @@
 		break;
 	}
 
-	if (ar->fw_board_len == (board_data_size +
-				 board_ext_data_size)) {
+	if (board_ext_address &&
+	    ar->fw_board_len == (board_data_size + board_ext_data_size)) {
 
 		/* write extended board data */
 		ath6kl_dbg(ATH6KL_DBG_BOOT,
@@ -1092,8 +1127,8 @@
 	bool from_hw = false;
 	int ret;
 
-	if (WARN_ON(ar->fw_otp == NULL))
-		return -ENOENT;
+	if (ar->fw_otp == NULL)
+		return 0;
 
 	address = ar->hw.app_load_addr;
 
@@ -1142,7 +1177,7 @@
 	int ret;
 
 	if (WARN_ON(ar->fw == NULL))
-		return -ENOENT;
+		return 0;
 
 	address = ar->hw.app_load_addr;
 
@@ -1172,8 +1207,8 @@
 	u32 address, param;
 	int ret;
 
-	if (WARN_ON(ar->fw_patch == NULL))
-		return -ENOENT;
+	if (ar->fw_patch == NULL)
+		return 0;
 
 	address = ar->hw.dataset_patch_addr;
 
@@ -1258,7 +1293,7 @@
 		return status;
 
 	/* WAR to avoid SDIO CRC err */
-	if (ar->version.target_ver == AR6003_REV2_VERSION) {
+	if (ar->version.target_ver == AR6003_HW_2_0_VERSION) {
 		ath6kl_err("temporary war to avoid sdio crc error\n");
 
 		param = 0x20;
@@ -1327,35 +1362,24 @@
 
 static int ath6kl_init_hw_params(struct ath6kl *ar)
 {
-	switch (ar->version.target_ver) {
-	case AR6003_REV2_VERSION:
-		ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
-		ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS;
-		ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
-		ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE;
+	const struct ath6kl_hw *hw;
+	int i;
 
-		/* hw2.0 needs override address hardcoded */
-		ar->hw.app_start_override_addr = 0x944C00;
+	for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
+		hw = &hw_list[i];
 
-		break;
-	case AR6003_REV3_VERSION:
-		ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS;
-		ar->hw.app_load_addr = 0x1234;
-		ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
-		ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE;
-		break;
-	case AR6004_REV1_VERSION:
-		ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
-		ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS;
-		ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS;
-		ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE;
-		break;
-	default:
+		if (hw->id == ar->version.target_ver)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(hw_list)) {
 		ath6kl_err("Unsupported hardware version: 0x%x\n",
 			   ar->version.target_ver);
 		return -EINVAL;
 	}
 
+	ar->hw = *hw;
+
 	ath6kl_dbg(ATH6KL_DBG_BOOT,
 		   "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n",
 		   ar->version.target_ver, ar->target_type,
@@ -1368,6 +1392,18 @@
 	return 0;
 }
 
+static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type)
+{
+	switch (type) {
+	case ATH6KL_HIF_TYPE_SDIO:
+		return "sdio";
+	case ATH6KL_HIF_TYPE_USB:
+		return "usb";
+	}
+
+	return NULL;
+}
+
 int ath6kl_init_hw_start(struct ath6kl *ar)
 {
 	long timeleft;
@@ -1428,6 +1464,15 @@
 
 	ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
 
+
+	if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) {
+		ath6kl_info("%s %s fw %s%s\n",
+			    ar->hw.name,
+			    ath6kl_init_get_hif_name(ar->hif_type),
+			    ar->wiphy->fw_version,
+			    test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
+	}
+
 	if (ar->version.abi_ver != ATH6KL_ABI_VERSION) {
 		ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n",
 			   ATH6KL_ABI_VERSION, ar->version.abi_ver);
@@ -1448,7 +1493,7 @@
 	if ((ath6kl_set_host_app_area(ar)) != 0)
 		ath6kl_err("unable to set the host app area\n");
 
-	for (i = 0; i < MAX_NUM_VIF; i++) {
+	for (i = 0; i < ar->vif_max; i++) {
 		ret = ath6kl_target_config_wlan_params(ar, i);
 		if (ret)
 			goto err_htc_stop;
@@ -1558,7 +1603,7 @@
 		goto err_node_cleanup;
 	}
 
-	for (i = 0; i < MAX_NUM_VIF; i++)
+	for (i = 0; i < ar->vif_max; i++)
 		ar->avail_idx_map |= BIT(i);
 
 	rtnl_lock();
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index 5e5f4ca..d9b4ba4 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -175,64 +175,6 @@
 	ar->cookie_count++;
 }
 
-/* set the window address register (using 4-byte register access ). */
-static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
-{
-	int status;
-	s32 i;
-	__le32 addr_val;
-
-	/*
-	 * Write bytes 1,2,3 of the register to set the upper address bytes,
-	 * the LSB is written last to initiate the access cycle
-	 */
-
-	for (i = 1; i <= 3; i++) {
-		/*
-		 * Fill the buffer with the address byte value we want to
-		 * hit 4 times. No need to worry about endianness as the
-		 * same byte is copied to all four bytes of addr_val at
-		 * any time.
-		 */
-		memset((u8 *)&addr_val, ((u8 *)&addr)[i], 4);
-
-		/*
-		 * Hit each byte of the register address with a 4-byte
-		 * write operation to the same address, this is a harmless
-		 * operation.
-		 */
-		status = hif_read_write_sync(ar, reg_addr + i, (u8 *)&addr_val,
-					     4, HIF_WR_SYNC_BYTE_FIX);
-		if (status)
-			break;
-	}
-
-	if (status) {
-		ath6kl_err("failed to write initial bytes of 0x%x to window reg: 0x%X\n",
-			   addr, reg_addr);
-		return status;
-	}
-
-	/*
-	 * Write the address register again, this time write the whole
-	 * 4-byte value. The effect here is that the LSB write causes the
-	 * cycle to start, the extra 3 byte write to bytes 1,2,3 has no
-	 * effect since we are writing the same values again
-	 */
-	addr_val = cpu_to_le32(addr);
-	status = hif_read_write_sync(ar, reg_addr,
-				     (u8 *)&(addr_val),
-				     4, HIF_WR_SYNC_BYTE_INC);
-
-	if (status) {
-		ath6kl_err("failed to write 0x%x to window reg: 0x%X\n",
-			   addr, reg_addr);
-		return status;
-	}
-
-	return 0;
-}
-
 /*
  * Read from the hardware through its diagnostic window. No cooperation
  * from the firmware is required for this.
@@ -241,14 +183,7 @@
 {
 	int ret;
 
-	/* set window register to start read cycle */
-	ret = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, address);
-	if (ret)
-		return ret;
-
-	/* read the data */
-	ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) value,
-				  sizeof(*value), HIF_RD_SYNC_BYTE_INC);
+	ret = ath6kl_hif_diag_read32(ar, address, value);
 	if (ret) {
 		ath6kl_warn("failed to read32 through diagnose window: %d\n",
 			    ret);
@@ -266,18 +201,15 @@
 {
 	int ret;
 
-	/* set write data */
-	ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) &value,
-				  sizeof(value), HIF_WR_SYNC_BYTE_INC);
+	ret = ath6kl_hif_diag_write32(ar, address, value);
+
 	if (ret) {
 		ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n",
 			   address, value);
 		return ret;
 	}
 
-	/* set window register, which starts the write cycle */
-	return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
-				      address);
+	return 0;
 }
 
 int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length)
@@ -534,6 +466,18 @@
 				wpa_ie = pos; /* WPS IE */
 				break; /* overrides WPA/RSN IE */
 			}
+		} else if (pos[0] == 0x44 && wpa_ie == NULL) {
+			/*
+			 * Note: WAPI Parameter Set IE re-uses Element ID that
+			 * was officially allocated for BSS AC Access Delay. As
+			 * such, we need to be a bit more careful on when
+			 * parsing the frame. However, BSS AC Access Delay
+			 * element is not supposed to be included in
+			 * (Re)Association Request frames, so this should not
+			 * cause problems.
+			 */
+			wpa_ie = pos; /* WAPI IE */
+			break;
 		}
 		pos += 2 + pos[1];
 	}
@@ -581,20 +525,6 @@
 
 /* WMI Event handlers */
 
-static const char *get_hw_id_string(u32 id)
-{
-	switch (id) {
-	case AR6003_REV1_VERSION:
-		return "1.0";
-	case AR6003_REV2_VERSION:
-		return "2.0";
-	case AR6003_REV3_VERSION:
-		return "2.1.1";
-	default:
-		return "unknown";
-	}
-}
-
 void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver)
 {
 	struct ath6kl *ar = devt;
@@ -617,13 +547,6 @@
 	/* indicate to the waiting thread that the ready event was received */
 	set_bit(WMI_READY, &ar->flag);
 	wake_up(&ar->event_wq);
-
-	if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) {
-		ath6kl_info("hw %s fw %s%s\n",
-			    get_hw_id_string(ar->wiphy->hw_version),
-			    ar->wiphy->fw_version,
-			    test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
-	}
 }
 
 void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status)
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c
index beb5f9b..b52b90d 100644
--- a/drivers/net/wireless/ath/ath6kl/sdio.c
+++ b/drivers/net/wireless/ath/ath6kl/sdio.c
@@ -845,6 +845,264 @@
 	return 0;
 }
 
+/* set the window address register (using 4-byte register access ). */
+static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
+{
+	int status;
+	u8 addr_val[4];
+	s32 i;
+
+	/*
+	 * Write bytes 1,2,3 of the register to set the upper address bytes,
+	 * the LSB is written last to initiate the access cycle
+	 */
+
+	for (i = 1; i <= 3; i++) {
+		/*
+		 * Fill the buffer with the address byte value we want to
+		 * hit 4 times.
+		 */
+		memset(addr_val, ((u8 *)&addr)[i], 4);
+
+		/*
+		 * Hit each byte of the register address with a 4-byte
+		 * write operation to the same address, this is a harmless
+		 * operation.
+		 */
+		status = ath6kl_sdio_read_write_sync(ar, reg_addr + i, addr_val,
+					     4, HIF_WR_SYNC_BYTE_FIX);
+		if (status)
+			break;
+	}
+
+	if (status) {
+		ath6kl_err("%s: failed to write initial bytes of 0x%x "
+			   "to window reg: 0x%X\n", __func__,
+			   addr, reg_addr);
+		return status;
+	}
+
+	/*
+	 * Write the address register again, this time write the whole
+	 * 4-byte value. The effect here is that the LSB write causes the
+	 * cycle to start, the extra 3 byte write to bytes 1,2,3 has no
+	 * effect since we are writing the same values again
+	 */
+	status = ath6kl_sdio_read_write_sync(ar, reg_addr, (u8 *)(&addr),
+				     4, HIF_WR_SYNC_BYTE_INC);
+
+	if (status) {
+		ath6kl_err("%s: failed to write 0x%x to window reg: 0x%X\n",
+			   __func__, addr, reg_addr);
+		return status;
+	}
+
+	return 0;
+}
+
+static int ath6kl_sdio_diag_read32(struct ath6kl *ar, u32 address, u32 *data)
+{
+	int status;
+
+	/* set window register to start read cycle */
+	status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS,
+					address);
+
+	if (status)
+		return status;
+
+	/* read the data */
+	status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS,
+				(u8 *)data, sizeof(u32), HIF_RD_SYNC_BYTE_INC);
+	if (status) {
+		ath6kl_err("%s: failed to read from window data addr\n",
+			__func__);
+		return status;
+	}
+
+	return status;
+}
+
+static int ath6kl_sdio_diag_write32(struct ath6kl *ar, u32 address,
+				    __le32 data)
+{
+	int status;
+	u32 val = (__force u32) data;
+
+	/* set write data */
+	status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS,
+				(u8 *) &val, sizeof(u32), HIF_WR_SYNC_BYTE_INC);
+	if (status) {
+		ath6kl_err("%s: failed to write 0x%x to window data addr\n",
+			   __func__, data);
+		return status;
+	}
+
+	/* set window register, which starts the write cycle */
+	return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
+				      address);
+}
+
+static int ath6kl_sdio_bmi_credits(struct ath6kl *ar)
+{
+	u32 addr;
+	unsigned long timeout;
+	int ret;
+
+	ar->bmi.cmd_credits = 0;
+
+	/* Read the counter register to get the command credits */
+	addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4;
+
+	timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
+	while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
+
+		/*
+		 * Hit the credit counter with a 4-byte access, the first byte
+		 * read will hit the counter and cause a decrement, while the
+		 * remaining 3 bytes has no effect. The rationale behind this
+		 * is to make all HIF accesses 4-byte aligned.
+		 */
+		ret = ath6kl_sdio_read_write_sync(ar, addr,
+					 (u8 *)&ar->bmi.cmd_credits, 4,
+					 HIF_RD_SYNC_BYTE_INC);
+		if (ret) {
+			ath6kl_err("Unable to decrement the command credit "
+						"count register: %d\n", ret);
+			return ret;
+		}
+
+		/* The counter is only 8 bits.
+		 * Ignore anything in the upper 3 bytes
+		 */
+		ar->bmi.cmd_credits &= 0xFF;
+	}
+
+	if (!ar->bmi.cmd_credits) {
+		ath6kl_err("bmi communication timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar)
+{
+	unsigned long timeout;
+	u32 rx_word = 0;
+	int ret = 0;
+
+	timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
+	while ((time_before(jiffies, timeout)) && !rx_word) {
+		ret = ath6kl_sdio_read_write_sync(ar,
+					RX_LOOKAHEAD_VALID_ADDRESS,
+					(u8 *)&rx_word, sizeof(rx_word),
+					HIF_RD_SYNC_BYTE_INC);
+		if (ret) {
+			ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n");
+			return ret;
+		}
+
+		 /* all we really want is one bit */
+		rx_word &= (1 << ENDPOINT1);
+	}
+
+	if (!rx_word) {
+		ath6kl_err("bmi_recv_buf FIFO empty\n");
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int ath6kl_sdio_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
+{
+	int ret;
+	u32 addr;
+
+	ret = ath6kl_sdio_bmi_credits(ar);
+	if (ret)
+		return ret;
+
+	addr = ar->mbox_info.htc_addr;
+
+	ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len,
+					  HIF_WR_SYNC_BYTE_INC);
+	if (ret)
+		ath6kl_err("unable to send the bmi data to the device\n");
+
+	return ret;
+}
+
+static int ath6kl_sdio_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
+{
+	int ret;
+	u32 addr;
+
+	/*
+	 * During normal bootup, small reads may be required.
+	 * Rather than issue an HIF Read and then wait as the Target
+	 * adds successive bytes to the FIFO, we wait here until
+	 * we know that response data is available.
+	 *
+	 * This allows us to cleanly timeout on an unexpected
+	 * Target failure rather than risk problems at the HIF level.
+	 * In particular, this avoids SDIO timeouts and possibly garbage
+	 * data on some host controllers.  And on an interconnect
+	 * such as Compact Flash (as well as some SDIO masters) which
+	 * does not provide any indication on data timeout, it avoids
+	 * a potential hang or garbage response.
+	 *
+	 * Synchronization is more difficult for reads larger than the
+	 * size of the MBOX FIFO (128B), because the Target is unable
+	 * to push the 129th byte of data until AFTER the Host posts an
+	 * HIF Read and removes some FIFO data.  So for large reads the
+	 * Host proceeds to post an HIF Read BEFORE all the data is
+	 * actually available to read.  Fortunately, large BMI reads do
+	 * not occur in practice -- they're supported for debug/development.
+	 *
+	 * So Host/Target BMI synchronization is divided into these cases:
+	 *  CASE 1: length < 4
+	 *        Should not happen
+	 *
+	 *  CASE 2: 4 <= length <= 128
+	 *        Wait for first 4 bytes to be in FIFO
+	 *        If CONSERVATIVE_BMI_READ is enabled, also wait for
+	 *        a BMI command credit, which indicates that the ENTIRE
+	 *        response is available in the the FIFO
+	 *
+	 *  CASE 3: length > 128
+	 *        Wait for the first 4 bytes to be in FIFO
+	 *
+	 * For most uses, a small timeout should be sufficient and we will
+	 * usually see a response quickly; but there may be some unusual
+	 * (debug) cases of BMI_EXECUTE where we want an larger timeout.
+	 * For now, we use an unbounded busy loop while waiting for
+	 * BMI_EXECUTE.
+	 *
+	 * If BMI_EXECUTE ever needs to support longer-latency execution,
+	 * especially in production, this code needs to be enhanced to sleep
+	 * and yield.  Also note that BMI_COMMUNICATION_TIMEOUT is currently
+	 * a function of Host processor speed.
+	 */
+	if (len >= 4) { /* NB: Currently, always true */
+		ret = ath6kl_bmi_get_rx_lkahd(ar);
+		if (ret)
+			return ret;
+	}
+
+	addr = ar->mbox_info.htc_addr;
+	ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len,
+				  HIF_RD_SYNC_BYTE_INC);
+	if (ret) {
+		ath6kl_err("Unable to read the bmi data from the device: %d\n",
+			   ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 static void ath6kl_sdio_stop(struct ath6kl *ar)
 {
 	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
@@ -889,6 +1147,10 @@
 	.cleanup_scatter = ath6kl_sdio_cleanup_scatter,
 	.suspend = ath6kl_sdio_suspend,
 	.resume = ath6kl_sdio_resume,
+	.diag_read32 = ath6kl_sdio_diag_read32,
+	.diag_write32 = ath6kl_sdio_diag_write32,
+	.bmi_read = ath6kl_sdio_bmi_read,
+	.bmi_write = ath6kl_sdio_bmi_write,
 	.power_on = ath6kl_sdio_power_on,
 	.power_off = ath6kl_sdio_power_off,
 	.stop = ath6kl_sdio_stop,
@@ -975,8 +1237,10 @@
 	}
 
 	ar_sdio->ar = ar;
+	ar->hif_type = ATH6KL_HIF_TYPE_SDIO;
 	ar->hif_priv = ar_sdio;
 	ar->hif_ops = &ath6kl_sdio_ops;
+	ar->bmi.max_data_size = 256;
 
 	ath6kl_sdio_set_mbox_info(ar);
 
@@ -1026,13 +1290,15 @@
 static const struct sdio_device_id ath6kl_sdio_devices[] = {
 	{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
 	{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
+	{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
+	{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
 	{},
 };
 
 MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices);
 
 static struct sdio_driver ath6kl_sdio_driver = {
-	.name = "ath6kl",
+	.name = "ath6kl_sdio",
 	.id_table = ath6kl_sdio_devices,
 	.probe = ath6kl_sdio_probe,
 	.remove = ath6kl_sdio_remove,
@@ -1062,13 +1328,19 @@
 MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices");
 MODULE_LICENSE("Dual BSD/GPL");
 
-MODULE_FIRMWARE(AR6003_REV2_OTP_FILE);
-MODULE_FIRMWARE(AR6003_REV2_FIRMWARE_FILE);
-MODULE_FIRMWARE(AR6003_REV2_PATCH_FILE);
-MODULE_FIRMWARE(AR6003_REV2_BOARD_DATA_FILE);
-MODULE_FIRMWARE(AR6003_REV2_DEFAULT_BOARD_DATA_FILE);
-MODULE_FIRMWARE(AR6003_REV3_OTP_FILE);
-MODULE_FIRMWARE(AR6003_REV3_FIRMWARE_FILE);
-MODULE_FIRMWARE(AR6003_REV3_PATCH_FILE);
-MODULE_FIRMWARE(AR6003_REV3_BOARD_DATA_FILE);
-MODULE_FIRMWARE(AR6003_REV3_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_OTP_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_PATCH_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_OTP_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_PATCH_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h
index 687e2b3..108a723 100644
--- a/drivers/net/wireless/ath/ath6kl/target.h
+++ b/drivers/net/wireless/ath/ath6kl/target.h
@@ -20,7 +20,7 @@
 #define AR6003_BOARD_DATA_SZ		1024
 #define AR6003_BOARD_EXT_DATA_SZ	768
 
-#define AR6004_BOARD_DATA_SZ     7168
+#define AR6004_BOARD_DATA_SZ     6144
 #define AR6004_BOARD_EXT_DATA_SZ 0
 
 #define RESET_CONTROL_ADDRESS		0x00000000
@@ -334,20 +334,6 @@
 	(((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \
 	(((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : 0))
 
-#define AR6003_REV2_APP_LOAD_ADDRESS            0x543180
-#define AR6003_REV2_BOARD_EXT_DATA_ADDRESS      0x57E500
-#define AR6003_REV2_DATASET_PATCH_ADDRESS       0x57e884
-#define AR6003_REV2_RAM_RESERVE_SIZE            6912
-
-#define AR6003_REV3_APP_LOAD_ADDRESS            0x545000
-#define AR6003_REV3_BOARD_EXT_DATA_ADDRESS      0x542330
-#define AR6003_REV3_DATASET_PATCH_ADDRESS       0x57FF74
-#define AR6003_REV3_RAM_RESERVE_SIZE            512
-
-#define AR6004_REV1_BOARD_DATA_ADDRESS          0x435400
-#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS      0x437000
-#define AR6004_REV1_RAM_RESERVE_SIZE            11264
-
 #define ATH6KL_FWLOG_PAYLOAD_SIZE		1500
 
 struct ath6kl_dbglog_buf {
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
index d9cff2b..506a303 100644
--- a/drivers/net/wireless/ath/ath6kl/txrx.c
+++ b/drivers/net/wireless/ath/ath6kl/txrx.c
@@ -453,11 +453,11 @@
 		set_bit(WMI_CTRL_EP_FULL, &ar->flag);
 		spin_unlock_bh(&ar->lock);
 		ath6kl_err("wmi ctrl ep is full\n");
-		goto stop_adhoc_netq;
+		return action;
 	}
 
 	if (packet->info.tx.tag == ATH6KL_CONTROL_PKT_TAG)
-		goto stop_adhoc_netq;
+		return action;
 
 	/*
 	 * The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for
@@ -465,20 +465,18 @@
 	 */
 	if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] <
 	    ar->hiac_stream_active_pri &&
-	    ar->cookie_count <= MAX_HI_COOKIE_NUM) {
+	    ar->cookie_count <= MAX_HI_COOKIE_NUM)
 		/*
 		 * Give preference to the highest priority stream by
 		 * dropping the packets which overflowed.
 		 */
 		action = HTC_SEND_FULL_DROP;
-		goto stop_adhoc_netq;
-	}
 
-stop_adhoc_netq:
 	/* FIXME: Locking */
 	spin_lock_bh(&ar->list_lock);
 	list_for_each_entry(vif, &ar->vif_list, list) {
-		if (vif->nw_type == ADHOC_NETWORK) {
+		if (vif->nw_type == ADHOC_NETWORK ||
+		    action != HTC_SEND_FULL_DROP) {
 			spin_unlock_bh(&ar->list_lock);
 
 			spin_lock_bh(&vif->if_lock);
@@ -543,7 +541,7 @@
 	int status;
 	enum htc_endpoint_id eid;
 	bool wake_event = false;
-	bool flushing[MAX_NUM_VIF] = {false};
+	bool flushing[ATH6KL_VIF_MAX] = {false};
 	u8 if_idx;
 	struct ath6kl_vif *vif;
 
@@ -571,8 +569,6 @@
 		if (!skb || !skb->data)
 			goto fatal;
 
-		packet->buf = skb->data;
-
 		__skb_queue_tail(&skb_queue, skb);
 
 		if (!status && (packet->act_len != skb->len))
@@ -593,10 +589,10 @@
 
 		if (eid == ar->ctrl_ep) {
 			if_idx = wmi_cmd_hdr_get_if_idx(
-				(struct wmi_cmd_hdr *) skb->data);
+				(struct wmi_cmd_hdr *) packet->buf);
 		} else {
 			if_idx = wmi_data_hdr_get_if_idx(
-				(struct wmi_data_hdr *) skb->data);
+				(struct wmi_data_hdr *) packet->buf);
 		}
 
 		vif = ath6kl_get_vif_by_index(ar, if_idx);
diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c
new file mode 100644
index 0000000..e3cf397
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/usb.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2007-2011 Atheros Communications 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/usb.h>
+
+#include "debug.h"
+#include "core.h"
+
+/* usb device object */
+struct ath6kl_usb {
+	struct usb_device *udev;
+	struct usb_interface *interface;
+	u8 *diag_cmd_buffer;
+	u8 *diag_resp_buffer;
+	struct ath6kl *ar;
+};
+
+/* diagnostic command defnitions */
+#define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD        1
+#define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP       2
+#define ATH6KL_USB_CONTROL_REQ_DIAG_CMD            3
+#define ATH6KL_USB_CONTROL_REQ_DIAG_RESP           4
+
+#define ATH6KL_USB_CTRL_DIAG_CC_READ               0
+#define ATH6KL_USB_CTRL_DIAG_CC_WRITE              1
+
+struct ath6kl_usb_ctrl_diag_cmd_write {
+	__le32 cmd;
+	__le32 address;
+	__le32 value;
+	__le32 _pad[1];
+} __packed;
+
+struct ath6kl_usb_ctrl_diag_cmd_read {
+	__le32 cmd;
+	__le32 address;
+} __packed;
+
+struct ath6kl_usb_ctrl_diag_resp_read {
+	__le32 value;
+} __packed;
+
+#define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write))
+#define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read))
+
+static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb)
+{
+	usb_set_intfdata(ar_usb->interface, NULL);
+
+	kfree(ar_usb->diag_cmd_buffer);
+	kfree(ar_usb->diag_resp_buffer);
+
+	kfree(ar_usb);
+}
+
+static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface)
+{
+	struct ath6kl_usb *ar_usb = NULL;
+	struct usb_device *dev = interface_to_usbdev(interface);
+	int status = 0;
+
+	ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL);
+	if (ar_usb == NULL)
+		goto fail_ath6kl_usb_create;
+
+	memset(ar_usb, 0, sizeof(struct ath6kl_usb));
+	usb_set_intfdata(interface, ar_usb);
+	ar_usb->udev = dev;
+	ar_usb->interface = interface;
+
+	ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL);
+	if (ar_usb->diag_cmd_buffer == NULL) {
+		status = -ENOMEM;
+		goto fail_ath6kl_usb_create;
+	}
+
+	ar_usb->diag_resp_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_RESP,
+					   GFP_KERNEL);
+	if (ar_usb->diag_resp_buffer == NULL) {
+		status = -ENOMEM;
+		goto fail_ath6kl_usb_create;
+	}
+
+fail_ath6kl_usb_create:
+	if (status != 0) {
+		ath6kl_usb_destroy(ar_usb);
+		ar_usb = NULL;
+	}
+	return ar_usb;
+}
+
+static void ath6kl_usb_device_detached(struct usb_interface *interface)
+{
+	struct ath6kl_usb *ar_usb;
+
+	ar_usb = usb_get_intfdata(interface);
+	if (ar_usb == NULL)
+		return;
+
+	ath6kl_stop_txrx(ar_usb->ar);
+
+	ath6kl_core_cleanup(ar_usb->ar);
+
+	ath6kl_usb_destroy(ar_usb);
+}
+
+static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb,
+				   u8 req, u16 value, u16 index, void *data,
+				   u32 size)
+{
+	u8 *buf = NULL;
+	int ret;
+
+	if (size > 0) {
+		buf = kmalloc(size, GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
+
+		memcpy(buf, data, size);
+	}
+
+	/* note: if successful returns number of bytes transfered */
+	ret = usb_control_msg(ar_usb->udev,
+			      usb_sndctrlpipe(ar_usb->udev, 0),
+			      req,
+			      USB_DIR_OUT | USB_TYPE_VENDOR |
+			      USB_RECIP_DEVICE, value, index, buf,
+			      size, 1000);
+
+	if (ret < 0) {
+		ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n",
+			   __func__, ret);
+	}
+
+	kfree(buf);
+
+	return 0;
+}
+
+static int ath6kl_usb_submit_ctrl_in(struct ath6kl_usb *ar_usb,
+				  u8 req, u16 value, u16 index, void *data,
+				  u32 size)
+{
+	u8 *buf = NULL;
+	int ret;
+
+	if (size > 0) {
+		buf = kmalloc(size, GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
+	}
+
+	/* note: if successful returns number of bytes transfered */
+	ret = usb_control_msg(ar_usb->udev,
+				 usb_rcvctrlpipe(ar_usb->udev, 0),
+				 req,
+				 USB_DIR_IN | USB_TYPE_VENDOR |
+				 USB_RECIP_DEVICE, value, index, buf,
+				 size, 2 * HZ);
+
+	if (ret < 0) {
+		ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n",
+			   __func__, ret);
+	}
+
+	memcpy((u8 *) data, buf, size);
+
+	kfree(buf);
+
+	return 0;
+}
+
+static int ath6kl_usb_ctrl_msg_exchange(struct ath6kl_usb *ar_usb,
+				     u8 req_val, u8 *req_buf, u32 req_len,
+				     u8 resp_val, u8 *resp_buf, u32 *resp_len)
+{
+	int ret;
+
+	/* send command */
+	ret = ath6kl_usb_submit_ctrl_out(ar_usb, req_val, 0, 0,
+					 req_buf, req_len);
+
+	if (ret != 0)
+		return ret;
+
+	if (resp_buf == NULL) {
+		/* no expected response */
+		return ret;
+	}
+
+	/* get response */
+	ret = ath6kl_usb_submit_ctrl_in(ar_usb, resp_val, 0, 0,
+					resp_buf, *resp_len);
+
+	return ret;
+}
+
+static int ath6kl_usb_diag_read32(struct ath6kl *ar, u32 address, u32 *data)
+{
+	struct ath6kl_usb *ar_usb = ar->hif_priv;
+	struct ath6kl_usb_ctrl_diag_resp_read *resp;
+	struct ath6kl_usb_ctrl_diag_cmd_read *cmd;
+	u32 resp_len;
+	int ret;
+
+	cmd = (struct ath6kl_usb_ctrl_diag_cmd_read *) ar_usb->diag_cmd_buffer;
+
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->cmd = ATH6KL_USB_CTRL_DIAG_CC_READ;
+	cmd->address = cpu_to_le32(address);
+	resp_len = sizeof(*resp);
+
+	ret = ath6kl_usb_ctrl_msg_exchange(ar_usb,
+				ATH6KL_USB_CONTROL_REQ_DIAG_CMD,
+				(u8 *) cmd,
+				sizeof(struct ath6kl_usb_ctrl_diag_cmd_write),
+				ATH6KL_USB_CONTROL_REQ_DIAG_RESP,
+				ar_usb->diag_resp_buffer, &resp_len);
+
+	if (ret)
+		return ret;
+
+	resp = (struct ath6kl_usb_ctrl_diag_resp_read *)
+		ar_usb->diag_resp_buffer;
+
+	*data = le32_to_cpu(resp->value);
+
+	return ret;
+}
+
+static int ath6kl_usb_diag_write32(struct ath6kl *ar, u32 address, __le32 data)
+{
+	struct ath6kl_usb *ar_usb = ar->hif_priv;
+	struct ath6kl_usb_ctrl_diag_cmd_write *cmd;
+
+	cmd = (struct ath6kl_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer;
+
+	memset(cmd, 0, sizeof(struct ath6kl_usb_ctrl_diag_cmd_write));
+	cmd->cmd = cpu_to_le32(ATH6KL_USB_CTRL_DIAG_CC_WRITE);
+	cmd->address = cpu_to_le32(address);
+	cmd->value = data;
+
+	return ath6kl_usb_ctrl_msg_exchange(ar_usb,
+					    ATH6KL_USB_CONTROL_REQ_DIAG_CMD,
+					    (u8 *) cmd,
+					    sizeof(*cmd),
+					    0, NULL, NULL);
+
+}
+
+static int ath6kl_usb_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
+{
+	struct ath6kl_usb *ar_usb = ar->hif_priv;
+	int ret;
+
+	/* get response */
+	ret = ath6kl_usb_submit_ctrl_in(ar_usb,
+					ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP,
+					0, 0, buf, len);
+	if (ret != 0) {
+		ath6kl_err("Unable to read the bmi data from the device: %d\n",
+			   ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
+{
+	struct ath6kl_usb *ar_usb = ar->hif_priv;
+	int ret;
+
+	/* send command */
+	ret = ath6kl_usb_submit_ctrl_out(ar_usb,
+					 ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD,
+					 0, 0, buf, len);
+	if (ret != 0) {
+		ath6kl_err("unable to send the bmi data to the device: %d\n",
+			   ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ath6kl_usb_power_on(struct ath6kl *ar)
+{
+	return 0;
+}
+
+static int ath6kl_usb_power_off(struct ath6kl *ar)
+{
+	return 0;
+}
+
+static const struct ath6kl_hif_ops ath6kl_usb_ops = {
+	.diag_read32 = ath6kl_usb_diag_read32,
+	.diag_write32 = ath6kl_usb_diag_write32,
+	.bmi_read = ath6kl_usb_bmi_read,
+	.bmi_write = ath6kl_usb_bmi_write,
+	.power_on = ath6kl_usb_power_on,
+	.power_off = ath6kl_usb_power_off,
+};
+
+/* ath6kl usb driver registered functions */
+static int ath6kl_usb_probe(struct usb_interface *interface,
+			    const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(interface);
+	struct ath6kl *ar;
+	struct ath6kl_usb *ar_usb = NULL;
+	int vendor_id, product_id;
+	int ret = 0;
+
+	usb_get_dev(dev);
+
+	vendor_id = le16_to_cpu(dev->descriptor.idVendor);
+	product_id = le16_to_cpu(dev->descriptor.idProduct);
+
+	ath6kl_dbg(ATH6KL_DBG_USB, "vendor_id = %04x\n", vendor_id);
+	ath6kl_dbg(ATH6KL_DBG_USB, "product_id = %04x\n", product_id);
+
+	if (interface->cur_altsetting)
+		ath6kl_dbg(ATH6KL_DBG_USB, "USB Interface %d\n",
+			   interface->cur_altsetting->desc.bInterfaceNumber);
+
+
+	if (dev->speed == USB_SPEED_HIGH)
+		ath6kl_dbg(ATH6KL_DBG_USB, "USB 2.0 Host\n");
+	else
+		ath6kl_dbg(ATH6KL_DBG_USB, "USB 1.1 Host\n");
+
+	ar_usb = ath6kl_usb_create(interface);
+
+	if (ar_usb == NULL) {
+		ret = -ENOMEM;
+		goto err_usb_put;
+	}
+
+	ar = ath6kl_core_alloc(&ar_usb->udev->dev);
+	if (ar == NULL) {
+		ath6kl_err("Failed to alloc ath6kl core\n");
+		ret = -ENOMEM;
+		goto err_usb_destroy;
+	}
+
+	ar->hif_priv = ar_usb;
+	ar->hif_type = ATH6KL_HIF_TYPE_USB;
+	ar->hif_ops = &ath6kl_usb_ops;
+	ar->mbox_info.block_size = 16;
+	ar->bmi.max_data_size = 252;
+
+	ar_usb->ar = ar;
+
+	ret = ath6kl_core_init(ar);
+	if (ret) {
+		ath6kl_err("Failed to init ath6kl core: %d\n", ret);
+		goto err_core_free;
+	}
+
+	return ret;
+
+err_core_free:
+	ath6kl_core_free(ar);
+err_usb_destroy:
+	ath6kl_usb_destroy(ar_usb);
+err_usb_put:
+	usb_put_dev(dev);
+
+	return ret;
+}
+
+static void ath6kl_usb_remove(struct usb_interface *interface)
+{
+	usb_put_dev(interface_to_usbdev(interface));
+	ath6kl_usb_device_detached(interface);
+}
+
+/* table of devices that work with this driver */
+static struct usb_device_id ath6kl_usb_ids[] = {
+	{USB_DEVICE(0x0cf3, 0x9374)},
+	{ /* Terminating entry */ },
+};
+
+MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids);
+
+static struct usb_driver ath6kl_usb_driver = {
+	.name = "ath6kl_usb",
+	.probe = ath6kl_usb_probe,
+	.disconnect = ath6kl_usb_remove,
+	.id_table = ath6kl_usb_ids,
+};
+
+static int ath6kl_usb_init(void)
+{
+	usb_register(&ath6kl_usb_driver);
+	return 0;
+}
+
+static void ath6kl_usb_exit(void)
+{
+	usb_deregister(&ath6kl_usb_driver);
+}
+
+module_init(ath6kl_usb_init);
+module_exit(ath6kl_usb_exit);
+
+MODULE_AUTHOR("Atheros Communications, Inc.");
+MODULE_DESCRIPTION("Driver support for Atheros AR600x USB devices");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index 922344d..aa1a252 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -85,7 +85,7 @@
 {
 	struct ath6kl_vif *vif, *found = NULL;
 
-	if (WARN_ON(if_idx > (MAX_NUM_VIF - 1)))
+	if (WARN_ON(if_idx > (ar->vif_max - 1)))
 		return NULL;
 
 	/* FIXME: Locking */
@@ -187,7 +187,7 @@
 	struct wmi_data_hdr *data_hdr;
 	int ret;
 
-	if (WARN_ON(skb == NULL || (if_idx > MAX_NUM_VIF - 1)))
+	if (WARN_ON(skb == NULL || (if_idx > wmi->parent_dev->vif_max - 1)))
 		return -EINVAL;
 
 	if (tx_meta_info) {
@@ -1620,7 +1620,7 @@
 	int ret;
 	u16 info1;
 
-	if (WARN_ON(skb == NULL || (if_idx > (MAX_NUM_VIF - 1))))
+	if (WARN_ON(skb == NULL || (if_idx > (wmi->parent_dev->vif_max - 1))))
 		return -EINVAL;
 
 	ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n",
@@ -3024,8 +3024,12 @@
 {
 	struct sk_buff *skb;
 	struct wmi_p2p_probe_response_cmd *p;
+	size_t cmd_len = sizeof(*p) + data_len;
 
-	skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
+	if (data_len == 0)
+		cmd_len++; /* work around target minimum length requirement */
+
+	skb = ath6kl_wmi_get_new_buf(cmd_len);
 	if (!skb)
 		return -ENOMEM;