浏览代码

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem

Conflicts:
	drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsparser.c
	drivers/staging/ath6kl/os/linux/ar6000_drv.c
John W. Linville 14 年之前
父节点
当前提交
b38d355eaa
共有 100 个文件被更改,包括 20239 次插入681 次删除
  1. 1 7
      arch/mips/Kconfig
  2. 31 0
      arch/mips/bcm47xx/Kconfig
  3. 2 1
      arch/mips/bcm47xx/Makefile
  4. 61 21
      arch/mips/bcm47xx/gpio.c
  5. 12 0
      arch/mips/bcm47xx/irq.c
  6. 25 4
      arch/mips/bcm47xx/nvram.c
  7. 44 2
      arch/mips/bcm47xx/serial.c
  8. 83 7
      arch/mips/bcm47xx/setup.c
  9. 14 2
      arch/mips/bcm47xx/time.c
  10. 9 5
      arch/mips/bcm47xx/wgt634u.c
  11. 24 2
      arch/mips/include/asm/mach-bcm47xx/bcm47xx.h
  12. 93 15
      arch/mips/include/asm/mach-bcm47xx/gpio.h
  13. 6 0
      arch/mips/pci/pci-bcm47xx.c
  14. 13 0
      drivers/bcma/Kconfig
  15. 2 0
      drivers/bcma/Makefile
  16. 16 0
      drivers/bcma/bcma_private.h
  17. 2 0
      drivers/bcma/core.c
  18. 53 0
      drivers/bcma/driver_chipcommon.c
  19. 133 0
      drivers/bcma/driver_chipcommon_pmu.c
  20. 256 0
      drivers/bcma/driver_mips.c
  21. 14 2
      drivers/bcma/driver_pci.c
  22. 183 0
      drivers/bcma/host_soc.c
  23. 69 1
      drivers/bcma/main.c
  24. 237 111
      drivers/bcma/scan.c
  25. 1 0
      drivers/net/wireless/ath/Kconfig
  26. 1 0
      drivers/net/wireless/ath/Makefile
  27. 2 2
      drivers/net/wireless/ath/ath5k/ahb.c
  28. 0 1
      drivers/net/wireless/ath/ath5k/ani.c
  29. 4 0
      drivers/net/wireless/ath/ath5k/ani.h
  30. 4 94
      drivers/net/wireless/ath/ath5k/ath5k.h
  31. 7 11
      drivers/net/wireless/ath/ath5k/attach.c
  32. 12 19
      drivers/net/wireless/ath/ath5k/base.c
  33. 42 13
      drivers/net/wireless/ath/ath5k/base.h
  34. 1 1
      drivers/net/wireless/ath/ath5k/caps.c
  35. 6 9
      drivers/net/wireless/ath/ath5k/debug.c
  36. 0 1
      drivers/net/wireless/ath/ath5k/desc.c
  37. 0 1
      drivers/net/wireless/ath/ath5k/dma.c
  38. 4 6
      drivers/net/wireless/ath/ath5k/eeprom.c
  39. 0 1
      drivers/net/wireless/ath/ath5k/gpio.c
  40. 0 1
      drivers/net/wireless/ath/ath5k/initvals.c
  41. 0 1
      drivers/net/wireless/ath/ath5k/led.c
  42. 2 3
      drivers/net/wireless/ath/ath5k/mac80211-ops.c
  43. 2 2
      drivers/net/wireless/ath/ath5k/pci.c
  44. 2 3
      drivers/net/wireless/ath/ath5k/pcu.c
  45. 50 41
      drivers/net/wireless/ath/ath5k/phy.c
  46. 1 9
      drivers/net/wireless/ath/ath5k/qcu.c
  47. 30 49
      drivers/net/wireless/ath/ath5k/reset.c
  48. 1 1
      drivers/net/wireless/ath/ath5k/rfkill.c
  49. 0 1
      drivers/net/wireless/ath/ath5k/sysfs.c
  50. 2 1
      drivers/net/wireless/ath/ath5k/trace.h
  51. 15 0
      drivers/net/wireless/ath/ath6kl/Kconfig
  52. 35 0
      drivers/net/wireless/ath/ath6kl/Makefile
  53. 692 0
      drivers/net/wireless/ath/ath6kl/bmi.c
  54. 250 0
      drivers/net/wireless/ath/ath6kl/bmi.h
  55. 1538 0
      drivers/net/wireless/ath/ath6kl/cfg80211.c
  56. 39 0
      drivers/net/wireless/ath/ath6kl/cfg80211.h
  57. 180 0
      drivers/net/wireless/ath/ath6kl/common.h
  58. 544 0
      drivers/net/wireless/ath/ath6kl/core.h
  59. 150 0
      drivers/net/wireless/ath/ath6kl/debug.c
  60. 105 0
      drivers/net/wireless/ath/ath6kl/debug.h
  61. 72 0
      drivers/net/wireless/ath/ath6kl/hif-ops.h
  62. 207 0
      drivers/net/wireless/ath/ath6kl/hif.h
  63. 2457 0
      drivers/net/wireless/ath/ath6kl/htc.c
  64. 607 0
      drivers/net/wireless/ath/ath6kl/htc.h
  65. 641 0
      drivers/net/wireless/ath/ath6kl/htc_hif.c
  66. 92 0
      drivers/net/wireless/ath/ath6kl/htc_hif.h
  67. 1303 0
      drivers/net/wireless/ath/ath6kl/init.c
  68. 1337 0
      drivers/net/wireless/ath/ath6kl/main.c
  69. 234 0
      drivers/net/wireless/ath/ath6kl/node.c
  70. 912 0
      drivers/net/wireless/ath/ath6kl/sdio.c
  71. 331 0
      drivers/net/wireless/ath/ath6kl/target.h
  72. 1457 0
      drivers/net/wireless/ath/ath6kl/txrx.c
  73. 2743 0
      drivers/net/wireless/ath/ath6kl/wmi.c
  74. 2018 0
      drivers/net/wireless/ath/ath6kl/wmi.h
  75. 1 1
      drivers/net/wireless/ath/ath9k/ahb.c
  76. 95 95
      drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
  77. 130 20
      drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
  78. 8 7
      drivers/net/wireless/ath/ath9k/ar9003_mac.c
  79. 2 2
      drivers/net/wireless/ath/ath9k/ar9003_phy.c
  80. 1 5
      drivers/net/wireless/ath/ath9k/ath9k.h
  81. 6 12
      drivers/net/wireless/ath/ath9k/beacon.c
  82. 15 0
      drivers/net/wireless/ath/ath9k/calib.c
  83. 1 0
      drivers/net/wireless/ath/ath9k/calib.h
  84. 60 0
      drivers/net/wireless/ath/ath9k/debug.c
  85. 2 0
      drivers/net/wireless/ath/ath9k/eeprom.h
  86. 115 20
      drivers/net/wireless/ath/ath9k/eeprom_4k.c
  87. 108 14
      drivers/net/wireless/ath/ath9k/eeprom_9287.c
  88. 133 17
      drivers/net/wireless/ath/ath9k/eeprom_def.c
  89. 2 0
      drivers/net/wireless/ath/ath9k/gpio.c
  90. 0 3
      drivers/net/wireless/ath/ath9k/htc.h
  91. 0 1
      drivers/net/wireless/ath/ath9k/htc_drv_init.c
  92. 7 3
      drivers/net/wireless/ath/ath9k/hw.c
  93. 8 1
      drivers/net/wireless/ath/ath9k/hw.h
  94. 4 4
      drivers/net/wireless/ath/ath9k/init.c
  95. 12 4
      drivers/net/wireless/ath/ath9k/mac.c
  96. 7 3
      drivers/net/wireless/ath/ath9k/main.c
  97. 1 3
      drivers/net/wireless/ath/ath9k/pci.c
  98. 2 2
      drivers/net/wireless/ath/ath9k/rc.c
  99. 0 6
      drivers/net/wireless/ath/ath9k/rc.h
  100. 3 7
      drivers/net/wireless/ath/ath9k/recv.c

+ 1 - 7
arch/mips/Kconfig

@@ -91,15 +91,8 @@ config BCM47XX
 	select DMA_NONCOHERENT
 	select HW_HAS_PCI
 	select IRQ_CPU
-	select SYS_HAS_CPU_MIPS32_R1
 	select SYS_SUPPORTS_32BIT_KERNEL
 	select SYS_SUPPORTS_LITTLE_ENDIAN
-	select SSB
-	select SSB_DRIVER_MIPS
-	select SSB_DRIVER_EXTIF
-	select SSB_EMBEDDED
-	select SSB_B43_PCI_BRIDGE if PCI
-	select SSB_PCICORE_HOSTMODE if PCI
 	select GENERIC_GPIO
 	select SYS_HAS_EARLY_PRINTK
 	select CFE
@@ -788,6 +781,7 @@ endchoice
 
 source "arch/mips/alchemy/Kconfig"
 source "arch/mips/ath79/Kconfig"
+source "arch/mips/bcm47xx/Kconfig"
 source "arch/mips/bcm63xx/Kconfig"
 source "arch/mips/jazz/Kconfig"
 source "arch/mips/jz4740/Kconfig"

+ 31 - 0
arch/mips/bcm47xx/Kconfig

@@ -0,0 +1,31 @@
+if BCM47XX
+
+config BCM47XX_SSB
+	bool "SSB Support for Broadcom BCM47XX"
+	select SYS_HAS_CPU_MIPS32_R1
+	select SSB
+	select SSB_DRIVER_MIPS
+	select SSB_DRIVER_EXTIF
+	select SSB_EMBEDDED
+	select SSB_B43_PCI_BRIDGE if PCI
+	select SSB_PCICORE_HOSTMODE if PCI
+	default y
+	help
+	 Add support for old Broadcom BCM47xx boards with Sonics Silicon Backplane support.
+
+	 This will generate an image with support for SSB and MIPS32 R1 instruction set.
+
+config BCM47XX_BCMA
+	bool "BCMA Support for Broadcom BCM47XX"
+	select SYS_HAS_CPU_MIPS32_R2
+	select BCMA
+	select BCMA_HOST_SOC
+	select BCMA_DRIVER_MIPS
+	select BCMA_DRIVER_PCI_HOSTMODE if PCI
+	default y
+	help
+	 Add support for new Broadcom BCM47xx boards with Broadcom specific Advanced Microcontroller Bus.
+
+	 This will generate an image with support for BCMA and MIPS32 R2 instruction set.
+
+endif

+ 2 - 1
arch/mips/bcm47xx/Makefile

@@ -3,4 +3,5 @@
 # under Linux.
 #
 
-obj-y := gpio.o irq.o nvram.o prom.o serial.o setup.o time.o wgt634u.o
+obj-y 				+= gpio.o irq.o nvram.o prom.o serial.o setup.o time.o
+obj-$(CONFIG_BCM47XX_SSB)	+= wgt634u.o

+ 61 - 21
arch/mips/bcm47xx/gpio.c

@@ -20,42 +20,82 @@ static DECLARE_BITMAP(gpio_in_use, BCM47XX_EXTIF_GPIO_LINES);
 
 int gpio_request(unsigned gpio, const char *tag)
 {
-	if (ssb_chipco_available(&ssb_bcm47xx.chipco) &&
-	    ((unsigned)gpio >= BCM47XX_CHIPCO_GPIO_LINES))
-		return -EINVAL;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		if (ssb_chipco_available(&bcm47xx_bus.ssb.chipco) &&
+		    ((unsigned)gpio >= BCM47XX_CHIPCO_GPIO_LINES))
+			return -EINVAL;
 
-	if (ssb_extif_available(&ssb_bcm47xx.extif) &&
-	    ((unsigned)gpio >= BCM47XX_EXTIF_GPIO_LINES))
-		return -EINVAL;
+		if (ssb_extif_available(&bcm47xx_bus.ssb.extif) &&
+		    ((unsigned)gpio >= BCM47XX_EXTIF_GPIO_LINES))
+			return -EINVAL;
 
-	if (test_and_set_bit(gpio, gpio_in_use))
-		return -EBUSY;
+		if (test_and_set_bit(gpio, gpio_in_use))
+			return -EBUSY;
 
-	return 0;
+		return 0;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		if (gpio >= BCM47XX_CHIPCO_GPIO_LINES)
+			return -EINVAL;
+
+		if (test_and_set_bit(gpio, gpio_in_use))
+			return -EBUSY;
+
+		return 0;
+#endif
+	}
+	return -EINVAL;
 }
 EXPORT_SYMBOL(gpio_request);
 
 void gpio_free(unsigned gpio)
 {
-	if (ssb_chipco_available(&ssb_bcm47xx.chipco) &&
-	    ((unsigned)gpio >= BCM47XX_CHIPCO_GPIO_LINES))
-		return;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		if (ssb_chipco_available(&bcm47xx_bus.ssb.chipco) &&
+		    ((unsigned)gpio >= BCM47XX_CHIPCO_GPIO_LINES))
+			return;
+
+		if (ssb_extif_available(&bcm47xx_bus.ssb.extif) &&
+		    ((unsigned)gpio >= BCM47XX_EXTIF_GPIO_LINES))
+			return;
 
-	if (ssb_extif_available(&ssb_bcm47xx.extif) &&
-	    ((unsigned)gpio >= BCM47XX_EXTIF_GPIO_LINES))
+		clear_bit(gpio, gpio_in_use);
 		return;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		if (gpio >= BCM47XX_CHIPCO_GPIO_LINES)
+			return;
 
-	clear_bit(gpio, gpio_in_use);
+		clear_bit(gpio, gpio_in_use);
+		return;
+#endif
+	}
 }
 EXPORT_SYMBOL(gpio_free);
 
 int gpio_to_irq(unsigned gpio)
 {
-	if (ssb_chipco_available(&ssb_bcm47xx.chipco))
-		return ssb_mips_irq(ssb_bcm47xx.chipco.dev) + 2;
-	else if (ssb_extif_available(&ssb_bcm47xx.extif))
-		return ssb_mips_irq(ssb_bcm47xx.extif.dev) + 2;
-	else
-		return -EINVAL;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		if (ssb_chipco_available(&bcm47xx_bus.ssb.chipco))
+			return ssb_mips_irq(bcm47xx_bus.ssb.chipco.dev) + 2;
+		else if (ssb_extif_available(&bcm47xx_bus.ssb.extif))
+			return ssb_mips_irq(bcm47xx_bus.ssb.extif.dev) + 2;
+		else
+			return -EINVAL;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		return bcma_core_mips_irq(bcm47xx_bus.bcma.bus.drv_cc.core) + 2;
+#endif
+	}
+	return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(gpio_to_irq);

+ 12 - 0
arch/mips/bcm47xx/irq.c

@@ -26,6 +26,7 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <asm/irq_cpu.h>
+#include <bcm47xx.h>
 
 void plat_irq_dispatch(void)
 {
@@ -51,5 +52,16 @@ void plat_irq_dispatch(void)
 
 void __init arch_init_irq(void)
 {
+#ifdef CONFIG_BCM47XX_BCMA
+	if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA) {
+		bcma_write32(bcm47xx_bus.bcma.bus.drv_mips.core,
+			     BCMA_MIPS_MIPS74K_INTMASK(5), 1 << 31);
+		/*
+		 * the kernel reads the timer irq from some register and thinks
+		 * it's #5, but we offset it by 2 and route to #7
+		 */
+		cp0_compare_irq = 7;
+	}
+#endif
 	mips_cpu_irq_init();
 }

+ 25 - 4
arch/mips/bcm47xx/nvram.c

@@ -26,14 +26,35 @@ static char nvram_buf[NVRAM_SPACE];
 /* Probe for NVRAM header */
 static void early_nvram_init(void)
 {
-	struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore;
+#ifdef CONFIG_BCM47XX_SSB
+	struct ssb_mipscore *mcore_ssb;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	struct bcma_drv_cc *bcma_cc;
+#endif
 	struct nvram_header *header;
 	int i;
-	u32 base, lim, off;
+	u32 base = 0;
+	u32 lim = 0;
+	u32 off;
 	u32 *src, *dst;
 
-	base = mcore->flash_window;
-	lim = mcore->flash_window_size;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		mcore_ssb = &bcm47xx_bus.ssb.mipscore;
+		base = mcore_ssb->flash_window;
+		lim = mcore_ssb->flash_window_size;
+		break;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc;
+		base = bcma_cc->pflash.window;
+		lim = bcma_cc->pflash.window_size;
+		break;
+#endif
+	}
 
 	off = FLASH_MIN;
 	while (off <= lim) {

+ 44 - 2
arch/mips/bcm47xx/serial.c

@@ -23,10 +23,11 @@ static struct platform_device uart8250_device = {
 	},
 };
 
-static int __init uart8250_init(void)
+#ifdef CONFIG_BCM47XX_SSB
+static int __init uart8250_init_ssb(void)
 {
 	int i;
-	struct ssb_mipscore *mcore = &(ssb_bcm47xx.mipscore);
+	struct ssb_mipscore *mcore = &(bcm47xx_bus.ssb.mipscore);
 
 	memset(&uart8250_data, 0,  sizeof(uart8250_data));
 
@@ -44,6 +45,47 @@ static int __init uart8250_init(void)
 	}
 	return platform_device_register(&uart8250_device);
 }
+#endif
+
+#ifdef CONFIG_BCM47XX_BCMA
+static int __init uart8250_init_bcma(void)
+{
+	int i;
+	struct bcma_drv_cc *cc = &(bcm47xx_bus.bcma.bus.drv_cc);
+
+	memset(&uart8250_data, 0,  sizeof(uart8250_data));
+
+	for (i = 0; i < cc->nr_serial_ports; i++) {
+		struct plat_serial8250_port *p = &(uart8250_data[i]);
+		struct bcma_serial_port *bcma_port;
+		bcma_port = &(cc->serial_ports[i]);
+
+		p->mapbase = (unsigned int) bcma_port->regs;
+		p->membase = (void *) bcma_port->regs;
+		p->irq = bcma_port->irq + 2;
+		p->uartclk = bcma_port->baud_base;
+		p->regshift = bcma_port->reg_shift;
+		p->iotype = UPIO_MEM;
+		p->flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	}
+	return platform_device_register(&uart8250_device);
+}
+#endif
+
+static int __init uart8250_init(void)
+{
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		return uart8250_init_ssb();
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		return uart8250_init_bcma();
+#endif
+	}
+	return -EINVAL;
+}
 
 module_init(uart8250_init);
 

+ 83 - 7
arch/mips/bcm47xx/setup.c

@@ -29,21 +29,36 @@
 #include <linux/types.h>
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb_embedded.h>
+#include <linux/bcma/bcma_soc.h>
 #include <asm/bootinfo.h>
 #include <asm/reboot.h>
 #include <asm/time.h>
 #include <bcm47xx.h>
 #include <asm/mach-bcm47xx/nvram.h>
 
-struct ssb_bus ssb_bcm47xx;
-EXPORT_SYMBOL(ssb_bcm47xx);
+union bcm47xx_bus bcm47xx_bus;
+EXPORT_SYMBOL(bcm47xx_bus);
+
+enum bcm47xx_bus_type bcm47xx_bus_type;
+EXPORT_SYMBOL(bcm47xx_bus_type);
 
 static void bcm47xx_machine_restart(char *command)
 {
 	printk(KERN_ALERT "Please stand by while rebooting the system...\n");
 	local_irq_disable();
 	/* Set the watchdog timer to reset immediately */
-	ssb_watchdog_timer_set(&ssb_bcm47xx, 1);
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 1);
+		break;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 1);
+		break;
+#endif
+	}
 	while (1)
 		cpu_relax();
 }
@@ -52,11 +67,23 @@ static void bcm47xx_machine_halt(void)
 {
 	/* Disable interrupts and watchdog and spin forever */
 	local_irq_disable();
-	ssb_watchdog_timer_set(&ssb_bcm47xx, 0);
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0);
+		break;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 0);
+		break;
+#endif
+	}
 	while (1)
 		cpu_relax();
 }
 
+#ifdef CONFIG_BCM47XX_SSB
 #define READ_FROM_NVRAM(_outvar, name, buf) \
 	if (nvram_getprefix(prefix, name, buf, sizeof(buf)) >= 0)\
 		sprom->_outvar = simple_strtoul(buf, NULL, 0);
@@ -247,7 +274,7 @@ static int bcm47xx_get_invariants(struct ssb_bus *bus,
 	return 0;
 }
 
-void __init plat_mem_setup(void)
+static void __init bcm47xx_register_ssb(void)
 {
 	int err;
 	char buf[100];
@@ -258,12 +285,12 @@ void __init plat_mem_setup(void)
 		printk(KERN_WARNING "bcm47xx: someone else already registered"
 			" a ssb SPROM callback handler (err %d)\n", err);
 
-	err = ssb_bus_ssbbus_register(&ssb_bcm47xx, SSB_ENUM_BASE,
+	err = ssb_bus_ssbbus_register(&(bcm47xx_bus.ssb), SSB_ENUM_BASE,
 				      bcm47xx_get_invariants);
 	if (err)
 		panic("Failed to initialize SSB bus (err %d)\n", err);
 
-	mcore = &ssb_bcm47xx.mipscore;
+	mcore = &bcm47xx_bus.ssb.mipscore;
 	if (nvram_getenv("kernel_args", buf, sizeof(buf)) >= 0) {
 		if (strstr(buf, "console=ttyS1")) {
 			struct ssb_serial_port port;
@@ -276,8 +303,57 @@ void __init plat_mem_setup(void)
 			memcpy(&mcore->serial_ports[1], &port, sizeof(port));
 		}
 	}
+}
+#endif
+
+#ifdef CONFIG_BCM47XX_BCMA
+static void __init bcm47xx_register_bcma(void)
+{
+	int err;
+
+	err = bcma_host_soc_register(&bcm47xx_bus.bcma);
+	if (err)
+		panic("Failed to initialize BCMA bus (err %d)\n", err);
+}
+#endif
+
+void __init plat_mem_setup(void)
+{
+	struct cpuinfo_mips *c = &current_cpu_data;
+
+	if (c->cputype == CPU_74K) {
+		printk(KERN_INFO "bcm47xx: using bcma bus\n");
+#ifdef CONFIG_BCM47XX_BCMA
+		bcm47xx_bus_type = BCM47XX_BUS_TYPE_BCMA;
+		bcm47xx_register_bcma();
+#endif
+	} else {
+		printk(KERN_INFO "bcm47xx: using ssb bus\n");
+#ifdef CONFIG_BCM47XX_SSB
+		bcm47xx_bus_type = BCM47XX_BUS_TYPE_SSB;
+		bcm47xx_register_ssb();
+#endif
+	}
 
 	_machine_restart = bcm47xx_machine_restart;
 	_machine_halt = bcm47xx_machine_halt;
 	pm_power_off = bcm47xx_machine_halt;
 }
+
+static int __init bcm47xx_register_bus_complete(void)
+{
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		/* Nothing to do */
+		break;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		bcma_bus_register(&bcm47xx_bus.bcma.bus);
+		break;
+#endif
+	}
+	return 0;
+}
+device_initcall(bcm47xx_register_bus_complete);

+ 14 - 2
arch/mips/bcm47xx/time.c

@@ -30,7 +30,7 @@
 
 void __init plat_time_init(void)
 {
-	unsigned long hz;
+	unsigned long hz = 0;
 
 	/*
 	 * Use deterministic values for initial counter interrupt
@@ -39,7 +39,19 @@ void __init plat_time_init(void)
 	write_c0_count(0);
 	write_c0_compare(0xffff);
 
-	hz = ssb_cpu_clock(&ssb_bcm47xx.mipscore) / 2;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		hz = ssb_cpu_clock(&bcm47xx_bus.ssb.mipscore) / 2;
+		break;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		hz = bcma_cpu_clock(&bcm47xx_bus.bcma.bus.drv_mips) / 2;
+		break;
+#endif
+	}
+
 	if (!hz)
 		hz = 100000000;
 

+ 9 - 5
arch/mips/bcm47xx/wgt634u.c

@@ -108,7 +108,7 @@ static irqreturn_t gpio_interrupt(int irq, void *ignored)
 
 	/* Interrupts are shared, check if the current one is
 	   a GPIO interrupt. */
-	if (!ssb_chipco_irq_status(&ssb_bcm47xx.chipco,
+	if (!ssb_chipco_irq_status(&bcm47xx_bus.ssb.chipco,
 				   SSB_CHIPCO_IRQ_GPIO))
 		return IRQ_NONE;
 
@@ -132,22 +132,26 @@ static int __init wgt634u_init(void)
 	 * machine. Use the MAC address as an heuristic. Netgear Inc. has
 	 * been allocated ranges 00:09:5b:xx:xx:xx and 00:0f:b5:xx:xx:xx.
 	 */
+	u8 *et0mac;
 
-	u8 *et0mac = ssb_bcm47xx.sprom.et0mac;
+	if (bcm47xx_bus_type != BCM47XX_BUS_TYPE_SSB)
+		return -ENODEV;
+
+	et0mac = bcm47xx_bus.ssb.sprom.et0mac;
 
 	if (et0mac[0] == 0x00 &&
 	    ((et0mac[1] == 0x09 && et0mac[2] == 0x5b) ||
 	     (et0mac[1] == 0x0f && et0mac[2] == 0xb5))) {
-		struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore;
+		struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore;
 
 		printk(KERN_INFO "WGT634U machine detected.\n");
 
 		if (!request_irq(gpio_to_irq(WGT634U_GPIO_RESET),
 				 gpio_interrupt, IRQF_SHARED,
-				 "WGT634U GPIO", &ssb_bcm47xx.chipco)) {
+				 "WGT634U GPIO", &bcm47xx_bus.ssb.chipco)) {
 			gpio_direction_input(WGT634U_GPIO_RESET);
 			gpio_intmask(WGT634U_GPIO_RESET, 1);
-			ssb_chipco_irq_mask(&ssb_bcm47xx.chipco,
+			ssb_chipco_irq_mask(&bcm47xx_bus.ssb.chipco,
 					    SSB_CHIPCO_IRQ_GPIO,
 					    SSB_CHIPCO_IRQ_GPIO);
 		}

+ 24 - 2
arch/mips/include/asm/mach-bcm47xx/bcm47xx.h

@@ -19,7 +19,29 @@
 #ifndef __ASM_BCM47XX_H
 #define __ASM_BCM47XX_H
 
-/* SSB bus */
-extern struct ssb_bus ssb_bcm47xx;
+#include <linux/ssb/ssb.h>
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_soc.h>
+
+enum bcm47xx_bus_type {
+#ifdef CONFIG_BCM47XX_SSB
+	BCM47XX_BUS_TYPE_SSB,
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	BCM47XX_BUS_TYPE_BCMA,
+#endif
+};
+
+union bcm47xx_bus {
+#ifdef CONFIG_BCM47XX_SSB
+	struct ssb_bus ssb;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	struct bcma_soc bcma;
+#endif
+};
+
+extern union bcm47xx_bus bcm47xx_bus;
+extern enum bcm47xx_bus_type bcm47xx_bus_type;
 
 #endif /* __ASM_BCM47XX_H */

+ 93 - 15
arch/mips/include/asm/mach-bcm47xx/gpio.h

@@ -10,6 +10,7 @@
 #define __BCM47XX_GPIO_H
 
 #include <linux/ssb/ssb_embedded.h>
+#include <linux/bcma/bcma.h>
 #include <asm/mach-bcm47xx/bcm47xx.h>
 
 #define BCM47XX_EXTIF_GPIO_LINES	5
@@ -21,41 +22,118 @@ extern int gpio_to_irq(unsigned gpio);
 
 static inline int gpio_get_value(unsigned gpio)
 {
-	return ssb_gpio_in(&ssb_bcm47xx, 1 << gpio);
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		return ssb_gpio_in(&bcm47xx_bus.ssb, 1 << gpio);
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		return bcma_chipco_gpio_in(&bcm47xx_bus.bcma.bus.drv_cc,
+					   1 << gpio);
+#endif
+	}
+	return -EINVAL;
 }
 
 static inline void gpio_set_value(unsigned gpio, int value)
 {
-	ssb_gpio_out(&ssb_bcm47xx, 1 << gpio, value ? 1 << gpio : 0);
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		ssb_gpio_out(&bcm47xx_bus.ssb, 1 << gpio,
+			     value ? 1 << gpio : 0);
+		return;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		bcma_chipco_gpio_out(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio,
+				     value ? 1 << gpio : 0);
+		return;
+#endif
+	}
 }
 
 static inline int gpio_direction_input(unsigned gpio)
 {
-	ssb_gpio_outen(&ssb_bcm47xx, 1 << gpio, 0);
-	return 0;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		ssb_gpio_outen(&bcm47xx_bus.ssb, 1 << gpio, 0);
+		return 0;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		bcma_chipco_gpio_outen(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio,
+				       0);
+		return 0;
+#endif
+	}
+	return -EINVAL;
 }
 
 static inline int gpio_direction_output(unsigned gpio, int value)
 {
-	/* first set the gpio out value */
-	ssb_gpio_out(&ssb_bcm47xx, 1 << gpio, value ? 1 << gpio : 0);
-	/* then set the gpio mode */
-	ssb_gpio_outen(&ssb_bcm47xx, 1 << gpio, 1 << gpio);
-	return 0;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		/* first set the gpio out value */
+		ssb_gpio_out(&bcm47xx_bus.ssb, 1 << gpio,
+			     value ? 1 << gpio : 0);
+		/* then set the gpio mode */
+		ssb_gpio_outen(&bcm47xx_bus.ssb, 1 << gpio, 1 << gpio);
+		return 0;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		/* first set the gpio out value */
+		bcma_chipco_gpio_out(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio,
+				     value ? 1 << gpio : 0);
+		/* then set the gpio mode */
+		bcma_chipco_gpio_outen(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio,
+				       1 << gpio);
+		return 0;
+#endif
+	}
+	return -EINVAL;
 }
 
 static inline int gpio_intmask(unsigned gpio, int value)
 {
-	ssb_gpio_intmask(&ssb_bcm47xx, 1 << gpio,
-			 value ? 1 << gpio : 0);
-	return 0;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		ssb_gpio_intmask(&bcm47xx_bus.ssb, 1 << gpio,
+				 value ? 1 << gpio : 0);
+		return 0;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		bcma_chipco_gpio_intmask(&bcm47xx_bus.bcma.bus.drv_cc,
+					 1 << gpio, value ? 1 << gpio : 0);
+		return 0;
+#endif
+	}
+	return -EINVAL;
 }
 
 static inline int gpio_polarity(unsigned gpio, int value)
 {
-	ssb_gpio_polarity(&ssb_bcm47xx, 1 << gpio,
-			  value ? 1 << gpio : 0);
-	return 0;
+	switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+	case BCM47XX_BUS_TYPE_SSB:
+		ssb_gpio_polarity(&bcm47xx_bus.ssb, 1 << gpio,
+				  value ? 1 << gpio : 0);
+		return 0;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	case BCM47XX_BUS_TYPE_BCMA:
+		bcma_chipco_gpio_polarity(&bcm47xx_bus.bcma.bus.drv_cc,
+					  1 << gpio, value ? 1 << gpio : 0);
+		return 0;
+#endif
+	}
+	return -EINVAL;
 }
 
 

+ 6 - 0
arch/mips/pci/pci-bcm47xx.c

@@ -25,6 +25,7 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 #include <linux/ssb/ssb.h>
+#include <bcm47xx.h>
 
 int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
@@ -33,9 +34,13 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 
 int pcibios_plat_dev_init(struct pci_dev *dev)
 {
+#ifdef CONFIG_BCM47XX_SSB
 	int res;
 	u8 slot, pin;
 
+	if (bcm47xx_bus_type !=  BCM47XX_BUS_TYPE_SSB)
+		return 0;
+
 	res = ssb_pcibios_plat_dev_init(dev);
 	if (res < 0) {
 		printk(KERN_ALERT "PCI: Failed to init device %s\n",
@@ -55,5 +60,6 @@ int pcibios_plat_dev_init(struct pci_dev *dev)
 	}
 
 	dev->irq = res;
+#endif
 	return 0;
 }

+ 13 - 0
drivers/bcma/Kconfig

@@ -33,6 +33,19 @@ config BCMA_DRIVER_PCI_HOSTMODE
 	help
 	  PCI core hostmode operation (external PCI bus).
 
+config BCMA_HOST_SOC
+	bool
+	depends on BCMA_DRIVER_MIPS
+
+config BCMA_DRIVER_MIPS
+	bool "BCMA Broadcom MIPS core driver"
+	depends on BCMA && MIPS
+	help
+	  Driver for the Broadcom MIPS core attached to Broadcom specific
+	  Advanced Microcontroller Bus.
+
+	  If unsure, say N
+
 config BCMA_DEBUG
 	bool "BCMA debugging"
 	depends on BCMA

+ 2 - 0
drivers/bcma/Makefile

@@ -2,7 +2,9 @@ bcma-y					+= main.o scan.o core.o sprom.o
 bcma-y					+= driver_chipcommon.o driver_chipcommon_pmu.o
 bcma-y					+= driver_pci.o
 bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE)	+= driver_pci_host.o
+bcma-$(CONFIG_BCMA_DRIVER_MIPS)		+= driver_mips.o
 bcma-$(CONFIG_BCMA_HOST_PCI)		+= host_pci.o
+bcma-$(CONFIG_BCMA_HOST_SOC)		+= host_soc.o
 obj-$(CONFIG_BCMA)			+= bcma.o
 
 ccflags-$(CONFIG_BCMA_DEBUG)		:= -DDEBUG

+ 16 - 0
drivers/bcma/bcma_private.h

@@ -15,13 +15,29 @@ struct bcma_bus;
 /* main.c */
 int bcma_bus_register(struct bcma_bus *bus);
 void bcma_bus_unregister(struct bcma_bus *bus);
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+				   struct bcma_device *core_cc,
+				   struct bcma_device *core_mips);
 
 /* scan.c */
 int bcma_bus_scan(struct bcma_bus *bus);
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+			       struct bcma_device_id *match,
+			       struct bcma_device *core);
+void bcma_init_bus(struct bcma_bus *bus);
 
 /* sprom.c */
 int bcma_sprom_get(struct bcma_bus *bus);
 
+/* driver_chipcommon.c */
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+
+/* driver_chipcommon_pmu.c */
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc);
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc);
+
 #ifdef CONFIG_BCMA_HOST_PCI
 /* host_pci.c */
 extern int __init bcma_host_pci_init(void);

+ 2 - 0
drivers/bcma/core.c

@@ -110,6 +110,8 @@ EXPORT_SYMBOL_GPL(bcma_core_pll_ctl);
 u32 bcma_core_dma_translation(struct bcma_device *core)
 {
 	switch (core->bus->hosttype) {
+	case BCMA_HOSTTYPE_SOC:
+		return 0;
 	case BCMA_HOSTTYPE_PCI:
 		if (bcma_aread32(core, BCMA_IOST) & BCMA_IOST_DMA64)
 			return BCMA_DMA_TRANSLATION_DMA64_CMT;

+ 53 - 0
drivers/bcma/driver_chipcommon.c

@@ -26,6 +26,9 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
 	u32 leddc_on = 10;
 	u32 leddc_off = 90;
 
+	if (cc->setup_done)
+		return;
+
 	if (cc->core->id.rev >= 11)
 		cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
 	cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP);
@@ -52,6 +55,8 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
 			((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) |
 			 (leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT)));
 	}
+
+	cc->setup_done = true;
 }
 
 /* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
@@ -101,3 +106,51 @@ u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value)
 {
 	return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
 }
+
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
+{
+	unsigned int irq;
+	u32 baud_base;
+	u32 i;
+	unsigned int ccrev = cc->core->id.rev;
+	struct bcma_serial_port *ports = cc->serial_ports;
+
+	if (ccrev >= 11 && ccrev != 15) {
+		/* Fixed ALP clock */
+		baud_base = bcma_pmu_alp_clock(cc);
+		if (ccrev >= 21) {
+			/* Turn off UART clock before switching clocksource. */
+			bcma_cc_write32(cc, BCMA_CC_CORECTL,
+				       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+				       & ~BCMA_CC_CORECTL_UARTCLKEN);
+		}
+		/* Set the override bit so we don't divide it */
+		bcma_cc_write32(cc, BCMA_CC_CORECTL,
+			       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+			       | BCMA_CC_CORECTL_UARTCLK0);
+		if (ccrev >= 21) {
+			/* Re-enable the UART clock. */
+			bcma_cc_write32(cc, BCMA_CC_CORECTL,
+				       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+				       | BCMA_CC_CORECTL_UARTCLKEN);
+		}
+	} else {
+		pr_err("serial not supported on this device ccrev: 0x%x\n",
+		       ccrev);
+		return;
+	}
+
+	irq = bcma_core_mips_irq(cc->core);
+
+	/* Determine the registers of the UARTs */
+	cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART);
+	for (i = 0; i < cc->nr_serial_ports; i++) {
+		ports[i].regs = cc->core->io_addr + BCMA_CC_UART0_DATA +
+				(i * 256);
+		ports[i].irq = irq;
+		ports[i].baud_base = baud_base;
+		ports[i].reg_shift = 0;
+	}
+}
+#endif /* CONFIG_BCMA_DRIVER_MIPS */

+ 133 - 0
drivers/bcma/driver_chipcommon_pmu.c

@@ -11,6 +11,13 @@
 #include "bcma_private.h"
 #include <linux/bcma/bcma.h>
 
+static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
+{
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+	return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
+}
+
 static void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
 					u32 offset, u32 mask, u32 set)
 {
@@ -136,3 +143,129 @@ void bcma_pmu_init(struct bcma_drv_cc *cc)
 	bcma_pmu_swreg_init(cc);
 	bcma_pmu_workarounds(cc);
 }
+
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4716:
+	case 0x4748:
+	case 47162:
+	case 0x4313:
+	case 0x5357:
+	case 0x4749:
+	case 53572:
+		/* always 20Mhz */
+		return 20000 * 1000;
+	case 0x5356:
+	case 0x5300:
+		/* always 25Mhz */
+		return 25000 * 1000;
+	default:
+		pr_warn("No ALP clock specified for %04X device, "
+			"pmu rev. %d, using default %d Hz\n",
+			bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK);
+	}
+	return BCMA_CC_PMU_ALP_CLOCK;
+}
+
+/* Find the output of the "m" pll divider given pll controls that start with
+ * pllreg "pll0" i.e. 12 for main 6 for phy, 0 for misc.
+ */
+static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m)
+{
+	u32 tmp, div, ndiv, p1, p2, fc;
+	struct bcma_bus *bus = cc->core->bus;
+
+	BUG_ON((pll0 & 3) || (pll0 > BCMA_CC_PMU4716_MAINPLL_PLL0));
+
+	BUG_ON(!m || m > 4);
+
+	if (bus->chipinfo.id == 0x5357 || bus->chipinfo.id == 0x4749) {
+		/* Detect failure in clock setting */
+		tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
+		if (tmp & 0x40000)
+			return 133 * 1000000;
+	}
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_P1P2_OFF);
+	p1 = (tmp & BCMA_CC_PPL_P1_MASK) >> BCMA_CC_PPL_P1_SHIFT;
+	p2 = (tmp & BCMA_CC_PPL_P2_MASK) >> BCMA_CC_PPL_P2_SHIFT;
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_M14_OFF);
+	div = (tmp >> ((m - 1) * BCMA_CC_PPL_MDIV_WIDTH)) &
+		BCMA_CC_PPL_MDIV_MASK;
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_NM5_OFF);
+	ndiv = (tmp & BCMA_CC_PPL_NDIV_MASK) >> BCMA_CC_PPL_NDIV_SHIFT;
+
+	/* Do calculation in Mhz */
+	fc = bcma_pmu_alp_clock(cc) / 1000000;
+	fc = (p1 * ndiv * fc) / p2;
+
+	/* Return clock in Hertz */
+	return (fc / div) * 1000000;
+}
+
+/* query bus clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4716:
+	case 0x4748:
+	case 47162:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5356:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5357:
+	case 0x4749:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5300:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 53572:
+		return 75000000;
+	default:
+		pr_warn("No backplane clock specified for %04X device, "
+			"pmu rev. %d, using default %d Hz\n",
+			bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK);
+	}
+	return BCMA_CC_PMU_HT_CLOCK;
+}
+
+/* query cpu clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	if (bus->chipinfo.id == 53572)
+		return 300000000;
+
+	if (cc->pmu.rev >= 5) {
+		u32 pll;
+		switch (bus->chipinfo.id) {
+		case 0x5356:
+			pll = BCMA_CC_PMU5356_MAINPLL_PLL0;
+			break;
+		case 0x5357:
+		case 0x4749:
+			pll = BCMA_CC_PMU5357_MAINPLL_PLL0;
+			break;
+		default:
+			pll = BCMA_CC_PMU4716_MAINPLL_PLL0;
+			break;
+		}
+
+		/* TODO: if (bus->chipinfo.id == 0x5300)
+		  return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */
+		return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU);
+	}
+
+	return bcma_pmu_get_clockcontrol(cc);
+}

+ 256 - 0
drivers/bcma/driver_mips.c

@@ -0,0 +1,256 @@
+/*
+ * Broadcom specific AMBA
+ * Broadcom MIPS32 74K core driver
+ *
+ * Copyright 2009, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2010, Bernhard Loos <bernhardloos@googlemail.com>
+ * Copyright 2011, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+
+/* The 47162a0 hangs when reading MIPS DMP registers registers */
+static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
+{
+	return dev->bus->chipinfo.id == 47162 && dev->bus->chipinfo.rev == 0 &&
+	       dev->id.id == BCMA_CORE_MIPS_74K;
+}
+
+/* The 5357b0 hangs when reading USB20H DMP registers */
+static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev)
+{
+	return (dev->bus->chipinfo.id == 0x5357 ||
+		dev->bus->chipinfo.id == 0x4749) &&
+	       dev->bus->chipinfo.pkg == 11 &&
+	       dev->id.id == BCMA_CORE_USB20_HOST;
+}
+
+static inline u32 mips_read32(struct bcma_drv_mips *mcore,
+			      u16 offset)
+{
+	return bcma_read32(mcore->core, offset);
+}
+
+static inline void mips_write32(struct bcma_drv_mips *mcore,
+				u16 offset,
+				u32 value)
+{
+	bcma_write32(mcore->core, offset, value);
+}
+
+static const u32 ipsflag_irq_mask[] = {
+	0,
+	BCMA_MIPS_IPSFLAG_IRQ1,
+	BCMA_MIPS_IPSFLAG_IRQ2,
+	BCMA_MIPS_IPSFLAG_IRQ3,
+	BCMA_MIPS_IPSFLAG_IRQ4,
+};
+
+static const u32 ipsflag_irq_shift[] = {
+	0,
+	BCMA_MIPS_IPSFLAG_IRQ1_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ2_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ3_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ4_SHIFT,
+};
+
+static u32 bcma_core_mips_irqflag(struct bcma_device *dev)
+{
+	u32 flag;
+
+	if (bcma_core_mips_bcm47162a0_quirk(dev))
+		return dev->core_index;
+	if (bcma_core_mips_bcm5357b0_quirk(dev))
+		return dev->core_index;
+	flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30);
+
+	return flag & 0x1F;
+}
+
+/* Get the MIPS IRQ assignment for a specified device.
+ * If unassigned, 0 is returned.
+ */
+unsigned int bcma_core_mips_irq(struct bcma_device *dev)
+{
+	struct bcma_device *mdev = dev->bus->drv_mips.core;
+	u32 irqflag;
+	unsigned int irq;
+
+	irqflag = bcma_core_mips_irqflag(dev);
+
+	for (irq = 1; irq <= 4; irq++)
+		if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) &
+		    (1 << irqflag))
+			return irq;
+
+	return 0;
+}
+EXPORT_SYMBOL(bcma_core_mips_irq);
+
+static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
+{
+	unsigned int oldirq = bcma_core_mips_irq(dev);
+	struct bcma_bus *bus = dev->bus;
+	struct bcma_device *mdev = bus->drv_mips.core;
+	u32 irqflag;
+
+	irqflag = bcma_core_mips_irqflag(dev);
+	BUG_ON(oldirq == 6);
+
+	dev->irq = irq + 2;
+
+	/* clear the old irq */
+	if (oldirq == 0)
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+			    bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) &
+			    ~(1 << irqflag));
+	else
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), 0);
+
+	/* assign the new one */
+	if (irq == 0) {
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+			    bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) |
+			    (1 << irqflag));
+	} else {
+		u32 oldirqflag = bcma_read32(mdev,
+					     BCMA_MIPS_MIPS74K_INTMASK(irq));
+		if (oldirqflag) {
+			struct bcma_device *core;
+
+			/* backplane irq line is in use, find out who uses
+			 * it and set user to irq 0
+			 */
+			list_for_each_entry_reverse(core, &bus->cores, list) {
+				if ((1 << bcma_core_mips_irqflag(core)) ==
+				    oldirqflag) {
+					bcma_core_mips_set_irq(core, 0);
+					break;
+				}
+			}
+		}
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq),
+			     1 << irqflag);
+	}
+
+	pr_info("set_irq: core 0x%04x, irq %d => %d\n",
+		dev->id.id, oldirq + 2, irq + 2);
+}
+
+static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq)
+{
+	int i;
+	static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
+	printk(KERN_INFO KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id);
+	for (i = 0; i <= 6; i++)
+		printk(" %s%s", irq_name[i], i == irq ? "*" : " ");
+	printk("\n");
+}
+
+static void bcma_core_mips_dump_irq(struct bcma_bus *bus)
+{
+	struct bcma_device *core;
+
+	list_for_each_entry_reverse(core, &bus->cores, list) {
+		bcma_core_mips_print_irq(core, bcma_core_mips_irq(core));
+	}
+}
+
+u32 bcma_cpu_clock(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus = mcore->core->bus;
+
+	if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU)
+		return bcma_pmu_get_clockcpu(&bus->drv_cc);
+
+	pr_err("No PMU available, need this to get the cpu clock\n");
+	return 0;
+}
+EXPORT_SYMBOL(bcma_cpu_clock);
+
+static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus = mcore->core->bus;
+
+	switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) {
+	case BCMA_CC_FLASHT_STSER:
+	case BCMA_CC_FLASHT_ATSER:
+		pr_err("Serial flash not supported.\n");
+		break;
+	case BCMA_CC_FLASHT_PARA:
+		pr_info("found parallel flash.\n");
+		bus->drv_cc.pflash.window = 0x1c000000;
+		bus->drv_cc.pflash.window_size = 0x02000000;
+
+		if ((bcma_read32(bus->drv_cc.core, BCMA_CC_FLASH_CFG) &
+		     BCMA_CC_FLASH_CFG_DS) == 0)
+			bus->drv_cc.pflash.buswidth = 1;
+		else
+			bus->drv_cc.pflash.buswidth = 2;
+		break;
+	default:
+		pr_err("flash not supported.\n");
+	}
+}
+
+void bcma_core_mips_init(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus;
+	struct bcma_device *core;
+	bus = mcore->core->bus;
+
+	pr_info("Initializing MIPS core...\n");
+
+	if (!mcore->setup_done)
+		mcore->assigned_irqs = 1;
+
+	/* Assign IRQs to all cores on the bus */
+	list_for_each_entry_reverse(core, &bus->cores, list) {
+		int mips_irq;
+		if (core->irq)
+			continue;
+
+		mips_irq = bcma_core_mips_irq(core);
+		if (mips_irq > 4)
+			core->irq = 0;
+		else
+			core->irq = mips_irq + 2;
+		if (core->irq > 5)
+			continue;
+		switch (core->id.id) {
+		case BCMA_CORE_PCI:
+		case BCMA_CORE_PCIE:
+		case BCMA_CORE_ETHERNET:
+		case BCMA_CORE_ETHERNET_GBIT:
+		case BCMA_CORE_MAC_GBIT:
+		case BCMA_CORE_80211:
+		case BCMA_CORE_USB20_HOST:
+			/* These devices get their own IRQ line if available,
+			 * the rest goes on IRQ0
+			 */
+			if (mcore->assigned_irqs <= 4)
+				bcma_core_mips_set_irq(core,
+						       mcore->assigned_irqs++);
+			break;
+		}
+	}
+	pr_info("IRQ reconfiguration done\n");
+	bcma_core_mips_dump_irq(bus);
+
+	if (mcore->setup_done)
+		return;
+
+	bcma_chipco_serial_init(&bus->drv_cc);
+	bcma_core_mips_flash_detect(mcore);
+	mcore->setup_done = true;
+}

+ 14 - 2
drivers/bcma/driver_pci.c

@@ -173,7 +173,7 @@ static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
 		return false;
 
 #ifdef CONFIG_SSB_DRIVER_PCICORE
-	if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI)
+	if (bus->sprom.boardflags_lo & SSB_BFL_NOPCI)
 		return false;
 #endif /* CONFIG_SSB_DRIVER_PCICORE */
 
@@ -189,6 +189,9 @@ static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
 
 void bcma_core_pci_init(struct bcma_drv_pci *pc)
 {
+	if (pc->setup_done)
+		return;
+
 	if (bcma_core_pci_is_in_hostmode(pc)) {
 #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
 		bcma_core_pci_hostmode_init(pc);
@@ -198,6 +201,8 @@ void bcma_core_pci_init(struct bcma_drv_pci *pc)
 	} else {
 		bcma_core_pci_clientmode_init(pc);
 	}
+
+	pc->setup_done = true;
 }
 
 int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
@@ -205,7 +210,14 @@ int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
 {
 	struct pci_dev *pdev = pc->core->bus->host_pci;
 	u32 coremask, tmp;
-	int err;
+	int err = 0;
+
+	if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
+		/* This bcma device is not on a PCI host-bus. So the IRQs are
+		 * not routed through the PCI core.
+		 * So we must not enable routing through the PCI core. */
+		goto out;
+	}
 
 	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
 	if (err)

+ 183 - 0
drivers/bcma/host_soc.c

@@ -0,0 +1,183 @@
+/*
+ * Broadcom specific AMBA
+ * System on Chip (SoC) Host
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include "scan.h"
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_soc.h>
+
+static u8 bcma_host_soc_read8(struct bcma_device *core, u16 offset)
+{
+	return readb(core->io_addr + offset);
+}
+
+static u16 bcma_host_soc_read16(struct bcma_device *core, u16 offset)
+{
+	return readw(core->io_addr + offset);
+}
+
+static u32 bcma_host_soc_read32(struct bcma_device *core, u16 offset)
+{
+	return readl(core->io_addr + offset);
+}
+
+static void bcma_host_soc_write8(struct bcma_device *core, u16 offset,
+				 u8 value)
+{
+	writeb(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write16(struct bcma_device *core, u16 offset,
+				 u16 value)
+{
+	writew(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write32(struct bcma_device *core, u16 offset,
+				 u32 value)
+{
+	writel(value, core->io_addr + offset);
+}
+
+#ifdef CONFIG_BCMA_BLOCKIO
+static void bcma_host_soc_block_read(struct bcma_device *core, void *buffer,
+				     size_t count, u16 offset, u8 reg_width)
+{
+	void __iomem *addr = core->io_addr + offset;
+
+	switch (reg_width) {
+	case sizeof(u8): {
+		u8 *buf = buffer;
+
+		while (count) {
+			*buf = __raw_readb(addr);
+			buf++;
+			count--;
+		}
+		break;
+	}
+	case sizeof(u16): {
+		__le16 *buf = buffer;
+
+		WARN_ON(count & 1);
+		while (count) {
+			*buf = (__force __le16)__raw_readw(addr);
+			buf++;
+			count -= 2;
+		}
+		break;
+	}
+	case sizeof(u32): {
+		__le32 *buf = buffer;
+
+		WARN_ON(count & 3);
+		while (count) {
+			*buf = (__force __le32)__raw_readl(addr);
+			buf++;
+			count -= 4;
+		}
+		break;
+	}
+	default:
+		WARN_ON(1);
+	}
+}
+
+static void bcma_host_soc_block_write(struct bcma_device *core,
+				      const void *buffer,
+				      size_t count, u16 offset, u8 reg_width)
+{
+	void __iomem *addr = core->io_addr + offset;
+
+	switch (reg_width) {
+	case sizeof(u8): {
+		const u8 *buf = buffer;
+
+		while (count) {
+			__raw_writeb(*buf, addr);
+			buf++;
+			count--;
+		}
+		break;
+	}
+	case sizeof(u16): {
+		const __le16 *buf = buffer;
+
+		WARN_ON(count & 1);
+		while (count) {
+			__raw_writew((__force u16)(*buf), addr);
+			buf++;
+			count -= 2;
+		}
+		break;
+	}
+	case sizeof(u32): {
+		const __le32 *buf = buffer;
+
+		WARN_ON(count & 3);
+		while (count) {
+			__raw_writel((__force u32)(*buf), addr);
+			buf++;
+			count -= 4;
+		}
+		break;
+	}
+	default:
+		WARN_ON(1);
+	}
+}
+#endif /* CONFIG_BCMA_BLOCKIO */
+
+static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset)
+{
+	return readl(core->io_wrap + offset);
+}
+
+static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset,
+				  u32 value)
+{
+	writel(value, core->io_wrap + offset);
+}
+
+const struct bcma_host_ops bcma_host_soc_ops = {
+	.read8		= bcma_host_soc_read8,
+	.read16		= bcma_host_soc_read16,
+	.read32		= bcma_host_soc_read32,
+	.write8		= bcma_host_soc_write8,
+	.write16	= bcma_host_soc_write16,
+	.write32	= bcma_host_soc_write32,
+#ifdef CONFIG_BCMA_BLOCKIO
+	.block_read	= bcma_host_soc_block_read,
+	.block_write	= bcma_host_soc_block_write,
+#endif
+	.aread32	= bcma_host_soc_aread32,
+	.awrite32	= bcma_host_soc_awrite32,
+};
+
+int __init bcma_host_soc_register(struct bcma_soc *soc)
+{
+	struct bcma_bus *bus = &soc->bus;
+	int err;
+
+	/* iomap only first core. We have to read some register on this core
+	 * to scan the bus.
+	 */
+	bus->mmio = ioremap_nocache(BCMA_ADDR_BASE, BCMA_CORE_SIZE * 1);
+	if (!bus->mmio)
+		return -ENOMEM;
+
+	/* Host specific */
+	bus->hosttype = BCMA_HOSTTYPE_SOC;
+	bus->ops = &bcma_host_soc_ops;
+
+	/* Register */
+	err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
+	if (err)
+		iounmap(bus->mmio);
+
+	return err;
+}

+ 69 - 1
drivers/bcma/main.c

@@ -66,6 +66,10 @@ static struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid)
 static void bcma_release_core_dev(struct device *dev)
 {
 	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	if (core->io_addr)
+		iounmap(core->io_addr);
+	if (core->io_wrap)
+		iounmap(core->io_wrap);
 	kfree(core);
 }
 
@@ -80,6 +84,7 @@ static int bcma_register_cores(struct bcma_bus *bus)
 		case BCMA_CORE_CHIPCOMMON:
 		case BCMA_CORE_PCI:
 		case BCMA_CORE_PCIE:
+		case BCMA_CORE_MIPS_74K:
 			continue;
 		}
 
@@ -93,7 +98,10 @@ static int bcma_register_cores(struct bcma_bus *bus)
 			core->dma_dev = &bus->host_pci->dev;
 			core->irq = bus->host_pci->irq;
 			break;
-		case BCMA_HOSTTYPE_NONE:
+		case BCMA_HOSTTYPE_SOC:
+			core->dev.dma_mask = &core->dev.coherent_dma_mask;
+			core->dma_dev = &core->dev;
+			break;
 		case BCMA_HOSTTYPE_SDIO:
 			break;
 		}
@@ -140,6 +148,13 @@ int bcma_bus_register(struct bcma_bus *bus)
 		bcma_core_chipcommon_init(&bus->drv_cc);
 	}
 
+	/* Init MIPS core */
+	core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+	if (core) {
+		bus->drv_mips.core = core;
+		bcma_core_mips_init(&bus->drv_mips);
+	}
+
 	/* Init PCIE core */
 	core = bcma_find_core(bus, BCMA_CORE_PCIE);
 	if (core) {
@@ -169,6 +184,59 @@ void bcma_bus_unregister(struct bcma_bus *bus)
 	bcma_unregister_cores(bus);
 }
 
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+				   struct bcma_device *core_cc,
+				   struct bcma_device *core_mips)
+{
+	int err;
+	struct bcma_device *core;
+	struct bcma_device_id match;
+
+	bcma_init_bus(bus);
+
+	match.manuf = BCMA_MANUF_BCM;
+	match.id = BCMA_CORE_CHIPCOMMON;
+	match.class = BCMA_CL_SIM;
+	match.rev = BCMA_ANY_REV;
+
+	/* Scan for chip common core */
+	err = bcma_bus_scan_early(bus, &match, core_cc);
+	if (err) {
+		pr_err("Failed to scan for common core: %d\n", err);
+		return -1;
+	}
+
+	match.manuf = BCMA_MANUF_MIPS;
+	match.id = BCMA_CORE_MIPS_74K;
+	match.class = BCMA_CL_SIM;
+	match.rev = BCMA_ANY_REV;
+
+	/* Scan for mips core */
+	err = bcma_bus_scan_early(bus, &match, core_mips);
+	if (err) {
+		pr_err("Failed to scan for mips core: %d\n", err);
+		return -1;
+	}
+
+	/* Init CC core */
+	core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
+	if (core) {
+		bus->drv_cc.core = core;
+		bcma_core_chipcommon_init(&bus->drv_cc);
+	}
+
+	/* Init MIPS core */
+	core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+	if (core) {
+		bus->drv_mips.core = core;
+		bcma_core_mips_init(&bus->drv_mips);
+	}
+
+	pr_info("Early bus registered\n");
+
+	return 0;
+}
+
 int __bcma_driver_register(struct bcma_driver *drv, struct module *owner)
 {
 	drv->drv.name = drv->name;

+ 237 - 111
drivers/bcma/scan.c

@@ -200,18 +200,162 @@ static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 **eromptr,
 	return addrl;
 }
 
-int bcma_bus_scan(struct bcma_bus *bus)
+static struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus,
+						   u16 index)
 {
-	u32 erombase;
-	u32 __iomem *eromptr, *eromend;
+	struct bcma_device *core;
 
+	list_for_each_entry(core, &bus->cores, list) {
+		if (core->core_index == index)
+			return core;
+	}
+	return NULL;
+}
+
+static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
+			      struct bcma_device_id *match, int core_num,
+			      struct bcma_device *core)
+{
+	s32 tmp;
+	u8 i, j;
 	s32 cia, cib;
 	u8 ports[2], wrappers[2];
 
+	/* get CIs */
+	cia = bcma_erom_get_ci(bus, eromptr);
+	if (cia < 0) {
+		bcma_erom_push_ent(eromptr);
+		if (bcma_erom_is_end(bus, eromptr))
+			return -ESPIPE;
+		return -EILSEQ;
+	}
+	cib = bcma_erom_get_ci(bus, eromptr);
+	if (cib < 0)
+		return -EILSEQ;
+
+	/* parse CIs */
+	core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
+	core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
+	core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
+	ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
+	ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
+	wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
+	wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
+	core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
+
+	if (((core->id.manuf == BCMA_MANUF_ARM) &&
+	     (core->id.id == 0xFFF)) ||
+	    (ports[1] == 0)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	/* check if component is a core at all */
+	if (wrappers[0] + wrappers[1] == 0) {
+		/* we could save addrl of the router
+		if (cid == BCMA_CORE_OOB_ROUTER)
+		 */
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	if (bcma_erom_is_bridge(bus, eromptr)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	if (bcma_find_core_by_index(bus, core_num)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENODEV;
+	}
+
+	if (match && ((match->manuf != BCMA_ANY_MANUF &&
+	      match->manuf != core->id.manuf) ||
+	     (match->id != BCMA_ANY_ID && match->id != core->id.id) ||
+	     (match->rev != BCMA_ANY_REV && match->rev != core->id.rev) ||
+	     (match->class != BCMA_ANY_CLASS && match->class != core->id.class)
+	    )) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENODEV;
+	}
+
+	/* get & parse master ports */
+	for (i = 0; i < ports[0]; i++) {
+		u32 mst_port_d = bcma_erom_get_mst_port(bus, eromptr);
+		if (mst_port_d < 0)
+			return -EILSEQ;
+	}
+
+	/* get & parse slave ports */
+	for (i = 0; i < ports[1]; i++) {
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_SLAVE, i);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: slave port %d "
+				 * "has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (i == 0 && j == 0)
+					core->addr = tmp;
+			}
+		}
+	}
+
+	/* get & parse master wrappers */
+	for (i = 0; i < wrappers[0]; i++) {
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_MWRAP, i);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: master wrapper %d "
+				 * "has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (i == 0 && j == 0)
+					core->wrap = tmp;
+			}
+		}
+	}
+
+	/* get & parse slave wrappers */
+	for (i = 0; i < wrappers[1]; i++) {
+		u8 hack = (ports[1] == 1) ? 0 : 1;
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_SWRAP, i + hack);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: master wrapper %d "
+				 * has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (wrappers[0] == 0 && !i && !j)
+					core->wrap = tmp;
+			}
+		}
+	}
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		core->io_addr = ioremap_nocache(core->addr, BCMA_CORE_SIZE);
+		if (!core->io_addr)
+			return -ENOMEM;
+		core->io_wrap = ioremap_nocache(core->wrap, BCMA_CORE_SIZE);
+		if (!core->io_wrap) {
+			iounmap(core->io_addr);
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+void bcma_init_bus(struct bcma_bus *bus)
+{
 	s32 tmp;
-	u8 i, j;
 
-	int err;
+	if (bus->init_done)
+		return;
 
 	INIT_LIST_HEAD(&bus->cores);
 	bus->nr_cores = 0;
@@ -222,9 +366,27 @@ int bcma_bus_scan(struct bcma_bus *bus)
 	bus->chipinfo.id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT;
 	bus->chipinfo.rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT;
 	bus->chipinfo.pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
+	bus->init_done = true;
+}
+
+int bcma_bus_scan(struct bcma_bus *bus)
+{
+	u32 erombase;
+	u32 __iomem *eromptr, *eromend;
+
+	int err, core_num = 0;
+
+	bcma_init_bus(bus);
 
 	erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
-	eromptr = bus->mmio;
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+		if (!eromptr)
+			return -ENOMEM;
+	} else {
+		eromptr = bus->mmio;
+	}
+
 	eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
 
 	bcma_scan_switch_core(bus, erombase);
@@ -236,125 +398,89 @@ int bcma_bus_scan(struct bcma_bus *bus)
 		INIT_LIST_HEAD(&core->list);
 		core->bus = bus;
 
-		/* get CIs */
-		cia = bcma_erom_get_ci(bus, &eromptr);
-		if (cia < 0) {
-			bcma_erom_push_ent(&eromptr);
-			if (bcma_erom_is_end(bus, &eromptr))
-				break;
-			err= -EILSEQ;
-			goto out;
-		}
-		cib = bcma_erom_get_ci(bus, &eromptr);
-		if (cib < 0) {
-			err= -EILSEQ;
-			goto out;
-		}
-
-		/* parse CIs */
-		core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
-		core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
-		core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
-		ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
-		ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
-		wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
-		wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
-		core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
-
-		if (((core->id.manuf == BCMA_MANUF_ARM) &&
-		     (core->id.id == 0xFFF)) ||
-		    (ports[1] == 0)) {
-			bcma_erom_skip_component(bus, &eromptr);
+		err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core);
+		if (err == -ENODEV) {
+			core_num++;
 			continue;
-		}
-
-		/* check if component is a core at all */
-		if (wrappers[0] + wrappers[1] == 0) {
-			/* we could save addrl of the router
-			if (cid == BCMA_CORE_OOB_ROUTER)
-			 */
-			bcma_erom_skip_component(bus, &eromptr);
+		} else if (err == -ENXIO)
 			continue;
-		}
+		else if (err == -ESPIPE)
+			break;
+		else if (err < 0)
+			return err;
 
-		if (bcma_erom_is_bridge(bus, &eromptr)) {
-			bcma_erom_skip_component(bus, &eromptr);
-			continue;
-		}
+		core->core_index = core_num++;
+		bus->nr_cores++;
 
-		/* get & parse master ports */
-		for (i = 0; i < ports[0]; i++) {
-			u32 mst_port_d = bcma_erom_get_mst_port(bus, &eromptr);
-			if (mst_port_d < 0) {
-				err= -EILSEQ;
-				goto out;
-			}
-		}
+		pr_info("Core %d found: %s "
+			"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
+			core->core_index, bcma_device_name(&core->id),
+			core->id.manuf, core->id.id, core->id.rev,
+			core->id.class);
 
-		/* get & parse slave ports */
-		for (i = 0; i < ports[1]; i++) {
-			for (j = 0; ; j++) {
-				tmp = bcma_erom_get_addr_desc(bus, &eromptr,
-					SCAN_ADDR_TYPE_SLAVE, i);
-				if (tmp < 0) {
-					/* no more entries for port _i_ */
-					/* pr_debug("erom: slave port %d "
-					 * "has %d descriptors\n", i, j); */
-					break;
-				} else {
-					if (i == 0 && j == 0)
-						core->addr = tmp;
-				}
-			}
-		}
+		list_add(&core->list, &bus->cores);
+	}
 
-		/* get & parse master wrappers */
-		for (i = 0; i < wrappers[0]; i++) {
-			for (j = 0; ; j++) {
-				tmp = bcma_erom_get_addr_desc(bus, &eromptr,
-					SCAN_ADDR_TYPE_MWRAP, i);
-				if (tmp < 0) {
-					/* no more entries for port _i_ */
-					/* pr_debug("erom: master wrapper %d "
-					 * "has %d descriptors\n", i, j); */
-					break;
-				} else {
-					if (i == 0 && j == 0)
-						core->wrap = tmp;
-				}
-			}
-		}
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+		iounmap(eromptr);
 
-		/* get & parse slave wrappers */
-		for (i = 0; i < wrappers[1]; i++) {
-			u8 hack = (ports[1] == 1) ? 0 : 1;
-			for (j = 0; ; j++) {
-				tmp = bcma_erom_get_addr_desc(bus, &eromptr,
-					SCAN_ADDR_TYPE_SWRAP, i + hack);
-				if (tmp < 0) {
-					/* no more entries for port _i_ */
-					/* pr_debug("erom: master wrapper %d "
-					 * has %d descriptors\n", i, j); */
-					break;
-				} else {
-					if (wrappers[0] == 0 && !i && !j)
-						core->wrap = tmp;
-				}
-			}
-		}
+	return 0;
+}
+
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+			       struct bcma_device_id *match,
+			       struct bcma_device *core)
+{
+	u32 erombase;
+	u32 __iomem *eromptr, *eromend;
+
+	int err = -ENODEV;
+	int core_num = 0;
+
+	erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+		if (!eromptr)
+			return -ENOMEM;
+	} else {
+		eromptr = bus->mmio;
+	}
+
+	eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
+
+	bcma_scan_switch_core(bus, erombase);
+
+	while (eromptr < eromend) {
+		memset(core, 0, sizeof(*core));
+		INIT_LIST_HEAD(&core->list);
+		core->bus = bus;
 
+		err = bcma_get_next_core(bus, &eromptr, match, core_num, core);
+		if (err == -ENODEV) {
+			core_num++;
+			continue;
+		} else if (err == -ENXIO)
+			continue;
+		else if (err == -ESPIPE)
+			break;
+		else if (err < 0)
+			return err;
+
+		core->core_index = core_num++;
+		bus->nr_cores++;
 		pr_info("Core %d found: %s "
 			"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
-			bus->nr_cores, bcma_device_name(&core->id),
+			core->core_index, bcma_device_name(&core->id),
 			core->id.manuf, core->id.id, core->id.rev,
 			core->id.class);
 
-		core->core_index = bus->nr_cores++;
 		list_add(&core->list, &bus->cores);
-		continue;
-out:
-		return err;
+		err = 0;
+		break;
 	}
 
-	return 0;
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+		iounmap(eromptr);
+
+	return err;
 }

+ 1 - 0
drivers/net/wireless/ath/Kconfig

@@ -25,5 +25,6 @@ config ATH_DEBUG
 source "drivers/net/wireless/ath/ath5k/Kconfig"
 source "drivers/net/wireless/ath/ath9k/Kconfig"
 source "drivers/net/wireless/ath/carl9170/Kconfig"
+source "drivers/net/wireless/ath/ath6kl/Kconfig"
 
 endif

+ 1 - 0
drivers/net/wireless/ath/Makefile

@@ -1,6 +1,7 @@
 obj-$(CONFIG_ATH5K)		+= ath5k/
 obj-$(CONFIG_ATH9K_HW)		+= ath9k/
 obj-$(CONFIG_CARL9170)		+= carl9170/
+obj-$(CONFIG_ATH6KL)		+= ath6kl/
 
 obj-$(CONFIG_ATH_COMMON)	+= ath.o
 

+ 2 - 2
drivers/net/wireless/ath/ath5k/ahb.c

@@ -169,7 +169,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
 			__set_bit(ATH_STAT_2G_DISABLED, ah->status);
 	}
 
-	ret = ath5k_init_softc(ah, &ath_ahb_bus_ops);
+	ret = ath5k_init_ah(ah, &ath_ahb_bus_ops);
 	if (ret != 0) {
 		dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
 		ret = -ENODEV;
@@ -214,7 +214,7 @@ static int ath_ahb_remove(struct platform_device *pdev)
 		__raw_writel(reg, (void __iomem *) AR5K_AR5312_ENABLE);
 	}
 
-	ath5k_deinit_softc(ah);
+	ath5k_deinit_ah(ah);
 	platform_set_drvdata(pdev, NULL);
 	ieee80211_free_hw(hw);
 

+ 0 - 1
drivers/net/wireless/ath/ath5k/ani.c

@@ -15,7 +15,6 @@
  */
 
 #include "ath5k.h"
-#include "base.h"
 #include "reg.h"
 #include "debug.h"
 #include "ani.h"

+ 4 - 0
drivers/net/wireless/ath/ath5k/ani.h

@@ -16,6 +16,10 @@
 #ifndef ANI_H
 #define ANI_H
 
+#include "../ath.h"
+
+enum ath5k_phy_error_code;
+
 /* these thresholds are relative to the ATH5K_ANI_LISTEN_PERIOD */
 #define ATH5K_ANI_LISTEN_PERIOD		100
 #define ATH5K_ANI_OFDM_TRIG_HIGH	500

+ 4 - 94
drivers/net/wireless/ath/ath5k/ath5k.h

@@ -131,13 +131,6 @@
 #define AR5K_REG_DISABLE_BITS(ah, _reg, _flags)			\
 	ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) & ~(_flags), _reg)
 
-/* Access to PHY registers */
-#define AR5K_PHY_READ(ah, _reg)					\
-	ath5k_hw_reg_read(ah, (ah)->ah_phy + ((_reg) << 2))
-
-#define AR5K_PHY_WRITE(ah, _reg, _val)					\
-	ath5k_hw_reg_write(ah, _val, (ah)->ah_phy + ((_reg) << 2))
-
 /* Access QCU registers per queue */
 #define AR5K_REG_READ_Q(ah, _reg, _queue)				\
 	(ath5k_hw_reg_read(ah, _reg) & (1 << _queue))			\
@@ -166,7 +159,6 @@
 #define AR5K_TUNE_DMA_BEACON_RESP		2
 #define AR5K_TUNE_SW_BEACON_RESP		10
 #define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF	0
-#define AR5K_TUNE_RADAR_ALERT			false
 #define AR5K_TUNE_MIN_TX_FIFO_THRES		1
 #define AR5K_TUNE_MAX_TX_FIFO_THRES	((IEEE80211_MAX_FRAME_LEN / 64) + 1)
 #define AR5K_TUNE_REGISTER_TIMEOUT		20000
@@ -295,17 +287,6 @@ enum ath5k_radio {
  * Common silicon revision/version values
  */
 
-enum ath5k_srev_type {
-	AR5K_VERSION_MAC,
-	AR5K_VERSION_RAD,
-};
-
-struct ath5k_srev_name {
-	const char		*sr_name;
-	enum ath5k_srev_type	sr_type;
-	u_int			sr_val;
-};
-
 #define AR5K_SREV_UNKNOWN	0xffff
 
 #define AR5K_SREV_AR5210	0x00 /* Crete */
@@ -424,7 +405,6 @@ enum ath5k_driver_mode {
 	AR5K_MODE_11A		=	0,
 	AR5K_MODE_11B		=	1,
 	AR5K_MODE_11G		=	2,
-	AR5K_MODE_XR		=	0,
 	AR5K_MODE_MAX		=	3
 };
 
@@ -694,33 +674,6 @@ struct ath5k_gain {
 #define AR5K_SLOT_TIME_20	880
 #define AR5K_SLOT_TIME_MAX	0xffff
 
-/* channel_flags */
-#define	CHANNEL_CW_INT	0x0008	/* Contention Window interference detected */
-#define	CHANNEL_CCK	0x0020	/* CCK channel */
-#define	CHANNEL_OFDM	0x0040	/* OFDM channel */
-#define	CHANNEL_2GHZ	0x0080	/* 2GHz channel. */
-#define	CHANNEL_5GHZ	0x0100	/* 5GHz channel */
-#define	CHANNEL_PASSIVE	0x0200	/* Only passive scan allowed */
-#define	CHANNEL_DYN	0x0400	/* Dynamic CCK-OFDM channel (for g operation) */
-#define	CHANNEL_XR	0x0800	/* XR channel */
-
-#define	CHANNEL_A	(CHANNEL_5GHZ | CHANNEL_OFDM)
-#define	CHANNEL_B	(CHANNEL_2GHZ | CHANNEL_CCK)
-#define	CHANNEL_G	(CHANNEL_2GHZ | CHANNEL_OFDM)
-#define	CHANNEL_X	(CHANNEL_5GHZ | CHANNEL_OFDM | CHANNEL_XR)
-
-#define	CHANNEL_ALL	(CHANNEL_OFDM | CHANNEL_CCK | \
-			 CHANNEL_2GHZ | CHANNEL_5GHZ)
-
-#define CHANNEL_MODES		CHANNEL_ALL
-
-/*
- * Used internally for ath5k_hw_reset_tx_queue().
- * Also see struct struct ieee80211_channel.
- */
-#define IS_CHAN_XR(_c)	((_c->hw_value & CHANNEL_XR) != 0)
-#define IS_CHAN_B(_c)	((_c->hw_value & CHANNEL_B) != 0)
-
 /*
  * The following structure is used to map 2GHz channels to
  * 5GHz Atheros channels.
@@ -977,7 +930,7 @@ enum ath5k_power_mode {
 struct ath5k_capabilities {
 	/*
 	 * Supported PHY modes
-	 * (ie. CHANNEL_A, CHANNEL_B, ...)
+	 * (ie. AR5K_MODE_11A, AR5K_MODE_11B, ...)
 	 */
 	DECLARE_BITMAP(cap_mode, AR5K_MODE_MAX);
 
@@ -1013,16 +966,6 @@ struct ath5k_nfcal_hist {
 	s16 nfval[ATH5K_NF_CAL_HIST_MAX];	/* last few noise floors */
 };
 
-/**
- * struct avg_val - Helper structure for average calculation
- * @avg: contains the actual average value
- * @avg_weight: is used internally during calculation to prevent rounding errors
- */
-struct ath5k_avg_val {
-	int avg;
-	int avg_weight;
-};
-
 #define ATH5K_LED_MAX_NAME_LEN 31
 
 /*
@@ -1148,7 +1091,6 @@ struct ath5k_hw {
 	bool			rx_pending;	/* rx tasklet pending */
 	bool			tx_pending;	/* tx tasklet pending */
 
-	u8			lladdr[ETH_ALEN];
 	u8			bssidmask[ETH_ALEN];
 
 	unsigned int		led_pin,	/* GPIO pin for driving LED */
@@ -1156,7 +1098,6 @@ struct ath5k_hw {
 
 	struct work_struct	reset_work;	/* deferred chip reset */
 
-	unsigned int		rxbufsize;	/* rx size based on mtu */
 	struct list_head	rxbuf;		/* receive buffer */
 	spinlock_t		rxbuflock;
 	u32			*rxlink;	/* link ptr in last RX desc */
@@ -1208,10 +1149,8 @@ struct ath5k_hw {
 
 	enum ath5k_version	ah_version;
 	enum ath5k_radio	ah_radio;
-	u32			ah_phy;
 	u32			ah_mac_srev;
 	u16			ah_mac_version;
-	u16			ah_mac_revision;
 	u16			ah_phy_revision;
 	u16			ah_radio_5ghz_revision;
 	u16			ah_radio_2ghz_revision;
@@ -1279,12 +1218,6 @@ struct ath5k_hw {
 		bool		txp_setup;
 	} ah_txpower;
 
-	struct {
-		bool		r_enabled;
-		int		r_last_alert;
-		struct ieee80211_channel r_last_channel;
-	} ah_radar;
-
 	struct ath5k_nfcal_hist ah_nfcal_hist;
 
 	/* average beacon RSSI in our BSS (used by ANI) */
@@ -1327,36 +1260,13 @@ struct ath_bus_ops {
 extern const struct ieee80211_ops ath5k_hw_ops;
 
 /* Initialization and detach functions */
-int ath5k_init_softc(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops);
-void ath5k_deinit_softc(struct ath5k_hw *ah);
 int ath5k_hw_init(struct ath5k_hw *ah);
 void ath5k_hw_deinit(struct ath5k_hw *ah);
 
 int ath5k_sysfs_register(struct ath5k_hw *ah);
 void ath5k_sysfs_unregister(struct ath5k_hw *ah);
 
-/* base.c */
-struct ath5k_buf;
-struct ath5k_txq;
-
-void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable);
-bool ath5k_any_vif_assoc(struct ath5k_hw *ah);
-void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-		    struct ath5k_txq *txq);
-int ath5k_start(struct ieee80211_hw *hw);
-void ath5k_stop(struct ieee80211_hw *hw);
-void ath5k_mode_setup(struct ath5k_hw *ah, struct ieee80211_vif *vif);
-void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
-					struct ieee80211_vif *vif);
-int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
-void ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf);
-int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
-void ath5k_beacon_config(struct ath5k_hw *ah);
-void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
-void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
-
 /*Chip id helper functions */
-const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
 int ath5k_hw_read_srev(struct ath5k_hw *ah);
 
 /* LED functions */
@@ -1367,7 +1277,7 @@ void ath5k_unregister_leds(struct ath5k_hw *ah);
 
 
 /* Reset Functions */
-int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial);
+int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel);
 int ath5k_hw_on_hold(struct ath5k_hw *ah);
 int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
 	   struct ieee80211_channel *channel, bool fast, bool skip_pcu);
@@ -1487,13 +1397,13 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel);
 
 /* PHY functions */
 /* Misc PHY functions */
-u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan);
+u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band);
 int ath5k_hw_phy_disable(struct ath5k_hw *ah);
 /* Gain_F optimization */
 enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah);
 int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah);
 /* PHY/RF channel functions */
-bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags);
+bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel);
 /* PHY calibration */
 void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah);
 int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,

+ 7 - 11
drivers/net/wireless/ath/ath5k/attach.c

@@ -25,7 +25,6 @@
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
 
 /**
  * ath5k_hw_post - Power On Self Test helper function
@@ -95,7 +94,7 @@ static int ath5k_hw_post(struct ath5k_hw *ah)
 /**
  * ath5k_hw_init - Check if hw is supported and init the needed structs
  *
- * @ah: The &struct ath5k_hw we got from the driver's init_softc function
+ * @ah: The &struct ath5k_hw associated with the device
  *
  * Check if the device is supported, perform a POST and initialize the needed
  * structs. Returns -ENOMEM if we don't have memory for the needed structs,
@@ -114,7 +113,6 @@ int ath5k_hw_init(struct ath5k_hw *ah)
 	/*
 	 * HW information
 	 */
-	ah->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT;
 	ah->ah_bwmode = AR5K_BWMODE_DEFAULT;
 	ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
 	ah->ah_imr = 0;
@@ -137,9 +135,8 @@ int ath5k_hw_init(struct ath5k_hw *ah)
 	else
 		ah->ah_version = AR5K_AR5212;
 
-	/* Get the MAC revision */
+	/* Get the MAC version */
 	ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
-	ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV);
 
 	/* Fill the ath5k_hw struct with the needed functions */
 	ret = ath5k_hw_init_desc_functions(ah);
@@ -147,7 +144,7 @@ int ath5k_hw_init(struct ath5k_hw *ah)
 		goto err;
 
 	/* Bring device out of sleep and reset its units */
-	ret = ath5k_hw_nic_wakeup(ah, 0, true);
+	ret = ath5k_hw_nic_wakeup(ah, NULL);
 	if (ret)
 		goto err;
 
@@ -155,8 +152,7 @@ int ath5k_hw_init(struct ath5k_hw *ah)
 	ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) &
 			0xffffffff;
 	ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah,
-			CHANNEL_5GHZ);
-	ah->ah_phy = AR5K_PHY(0);
+			IEEE80211_BAND_5GHZ);
 
 	/* Try to identify radio chip based on its srev */
 	switch (ah->ah_radio_5ghz_revision & 0xf0) {
@@ -164,14 +160,14 @@ int ath5k_hw_init(struct ath5k_hw *ah)
 		ah->ah_radio = AR5K_RF5111;
 		ah->ah_single_chip = false;
 		ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
-							CHANNEL_2GHZ);
+							IEEE80211_BAND_2GHZ);
 		break;
 	case AR5K_SREV_RAD_5112:
 	case AR5K_SREV_RAD_2112:
 		ah->ah_radio = AR5K_RF5112;
 		ah->ah_single_chip = false;
 		ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
-							CHANNEL_2GHZ);
+							IEEE80211_BAND_2GHZ);
 		break;
 	case AR5K_SREV_RAD_2413:
 		ah->ah_radio = AR5K_RF2413;
@@ -208,7 +204,7 @@ int ath5k_hw_init(struct ath5k_hw *ah)
 			ah->ah_radio = AR5K_RF5111;
 			ah->ah_single_chip = false;
 			ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
-								CHANNEL_2GHZ);
+							IEEE80211_BAND_2GHZ);
 		} else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) ||
 			   ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) ||
 			   ah->ah_phy_revision == AR5K_SREV_PHY_2425) {

+ 12 - 19
drivers/net/wireless/ath/ath5k/base.c

@@ -52,6 +52,7 @@
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/etherdevice.h>
+#include <linux/nl80211.h>
 
 #include <net/ieee80211_radiotap.h>
 
@@ -61,6 +62,8 @@
 #include "reg.h"
 #include "debug.h"
 #include "ani.h"
+#include "ath5k.h"
+#include "../regd.h"
 
 #define CREATE_TRACE_POINTS
 #include "trace.h"
@@ -272,20 +275,18 @@ static unsigned int
 ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
 		unsigned int mode, unsigned int max)
 {
-	unsigned int count, size, chfreq, freq, ch;
+	unsigned int count, size, freq, ch;
 	enum ieee80211_band band;
 
 	switch (mode) {
 	case AR5K_MODE_11A:
 		/* 1..220, but 2GHz frequencies are filtered by check_channel */
 		size = 220;
-		chfreq = CHANNEL_5GHZ;
 		band = IEEE80211_BAND_5GHZ;
 		break;
 	case AR5K_MODE_11B:
 	case AR5K_MODE_11G:
 		size = 26;
-		chfreq = CHANNEL_2GHZ;
 		band = IEEE80211_BAND_2GHZ;
 		break;
 	default:
@@ -300,26 +301,19 @@ ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels,
 		if (freq == 0) /* mapping failed - not a standard channel */
 			continue;
 
+		/* Write channel info, needed for ath5k_channel_ok() */
+		channels[count].center_freq = freq;
+		channels[count].band = band;
+		channels[count].hw_value = mode;
+
 		/* Check if channel is supported by the chipset */
-		if (!ath5k_channel_ok(ah, freq, chfreq))
+		if (!ath5k_channel_ok(ah, &channels[count]))
 			continue;
 
 		if (!modparam_all_channels &&
 		    !ath5k_is_standard_channel(ch, band))
 			continue;
 
-		/* Write channel info and increment counter */
-		channels[count].center_freq = freq;
-		channels[count].band = band;
-		switch (mode) {
-		case AR5K_MODE_11A:
-		case AR5K_MODE_11G:
-			channels[count].hw_value = chfreq | CHANNEL_OFDM;
-			break;
-		case AR5K_MODE_11B:
-			channels[count].hw_value = CHANNEL_B;
-		}
-
 		count++;
 	}
 
@@ -2349,7 +2343,7 @@ ath5k_tx_complete_poll_work(struct work_struct *work)
 \*************************/
 
 int __devinit
-ath5k_init_softc(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
+ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
 {
 	struct ieee80211_hw *hw = ah->hw;
 	struct ath_common *common;
@@ -2867,7 +2861,6 @@ ath5k_init(struct ieee80211_hw *hw)
 	}
 
 	SET_IEEE80211_PERM_ADDR(hw, mac);
-	memcpy(&ah->lladdr, mac, ETH_ALEN);
 	/* All MAC address bits matter for ACKs */
 	ath5k_update_bssid_mask_and_opmode(ah, NULL);
 
@@ -2903,7 +2896,7 @@ err:
 }
 
 void
-ath5k_deinit_softc(struct ath5k_hw *ah)
+ath5k_deinit_ah(struct ath5k_hw *ah)
 {
 	struct ieee80211_hw *hw = ah->hw;
 

+ 42 - 13
drivers/net/wireless/ath/ath5k/base.h

@@ -38,19 +38,27 @@
 /*
  * Definitions for the Atheros Wireless LAN controller driver.
  */
-#ifndef _DEV_ATH_ATHVAR_H
-#define _DEV_ATH_ATHVAR_H
+#ifndef _DEV_ATH5K_BASE_H
+#define _DEV_ATH5K_BASE_H
 
-#include <linux/interrupt.h>
-#include <linux/list.h>
-#include <linux/wireless.h>
-#include <linux/if_ether.h>
-#include <linux/rfkill.h>
-#include <linux/workqueue.h>
+struct ieee80211_vif;
+struct ieee80211_hw;
+struct ath5k_hw;
+struct ath5k_txq;
+struct ieee80211_channel;
+struct ath_bus_ops;
+enum nl80211_iftype;
 
-#include "ath5k.h"
-#include "../regd.h"
-#include "../ath.h"
+enum ath5k_srev_type {
+	AR5K_VERSION_MAC,
+	AR5K_VERSION_RAD,
+};
+
+struct ath5k_srev_name {
+	const char		*sr_name;
+	enum ath5k_srev_type	sr_type;
+	u_int			sr_val;
+};
 
 struct ath5k_buf {
 	struct list_head	list;
@@ -65,7 +73,6 @@ struct ath5k_vif {
 	enum nl80211_iftype	opmode;
 	int			bslot;
 	struct ath5k_buf	*bbuf; /* beacon buffer */
-	u8			lladdr[ETH_ALEN];
 };
 
 struct ath5k_vif_iter_data {
@@ -78,8 +85,30 @@ struct ath5k_vif_iter_data {
 	enum nl80211_iftype opmode;
 	int n_stas;
 };
+
 void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif);
+bool ath5k_any_vif_assoc(struct ath5k_hw *ah);
+
+int ath5k_start(struct ieee80211_hw *hw);
+void ath5k_stop(struct ieee80211_hw *hw);
+
+void ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf);
+int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+void ath5k_beacon_config(struct ath5k_hw *ah);
+void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable);
+
+void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
+					struct ieee80211_vif *vif);
+int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
+void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
+void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
+void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
+		    struct ath5k_txq *txq);
+
+const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
 
+int ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops);
+void ath5k_deinit_ah(struct ath5k_hw *ah);
 
 /* Check whether BSSID mask is supported */
 #define ath5k_hw_hasbssidmask(_ah) (ah->ah_version == AR5K_AR5212)
@@ -87,4 +116,4 @@ void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif);
 /* Check whether virtual EOL is supported */
 #define ath5k_hw_hasveol(_ah) (ah->ah_version != AR5K_AR5210)
 
-#endif
+#endif	/* _DEV_ATH5K_BASE_H */

+ 1 - 1
drivers/net/wireless/ath/ath5k/caps.c

@@ -24,7 +24,7 @@
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
+#include "../regd.h"
 
 /*
  * Fill the capabilities struct

+ 6 - 9
drivers/net/wireless/ath/ath5k/debug.c

@@ -58,19 +58,18 @@
  * THE POSSIBILITY OF SUCH DAMAGES.
  */
 
-#include "base.h"
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/list.h>
 #include "debug.h"
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
 
 static unsigned int ath5k_debug;
 module_param_named(debug, ath5k_debug, uint, 0);
 
 
-#ifdef CONFIG_ATH5K_DEBUG
-
-#include <linux/seq_file.h>
-#include "reg.h"
-#include "ani.h"
-
 static int ath5k_debugfs_open(struct inode *inode, struct file *file)
 {
 	file->private_data = inode->i_private;
@@ -1031,5 +1030,3 @@ ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf)
 		td->tx_stat.tx_status_0, td->tx_stat.tx_status_1,
 		done ? ' ' : (ts.ts_status == 0) ? '*' : '!');
 }
-
-#endif /* ifdef CONFIG_ATH5K_DEBUG */

+ 0 - 1
drivers/net/wireless/ath/ath5k/desc.c

@@ -24,7 +24,6 @@
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
 
 
 /************************\

+ 0 - 1
drivers/net/wireless/ath/ath5k/dma.c

@@ -35,7 +35,6 @@
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
 
 
 /*********\

+ 4 - 6
drivers/net/wireless/ath/ath5k/eeprom.c

@@ -26,7 +26,6 @@
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
 
 
 /******************\
@@ -1780,13 +1779,12 @@ ath5k_eeprom_detach(struct ath5k_hw *ah)
 int
 ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel)
 {
-	switch (channel->hw_value & CHANNEL_MODES) {
-	case CHANNEL_A:
-	case CHANNEL_XR:
+	switch (channel->hw_value) {
+	case AR5K_MODE_11A:
 		return AR5K_EEPROM_MODE_11A;
-	case CHANNEL_G:
+	case AR5K_MODE_11G:
 		return AR5K_EEPROM_MODE_11G;
-	case CHANNEL_B:
+	case AR5K_MODE_11B:
 		return AR5K_EEPROM_MODE_11B;
 	default:
 		return -1;

+ 0 - 1
drivers/net/wireless/ath/ath5k/gpio.c

@@ -23,7 +23,6 @@
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
 
 /*
  * Set led state

+ 0 - 1
drivers/net/wireless/ath/ath5k/initvals.c

@@ -22,7 +22,6 @@
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
 
 /*
  * Mode-independent initial register writes

+ 0 - 1
drivers/net/wireless/ath/ath5k/led.c

@@ -41,7 +41,6 @@
 
 #include <linux/pci.h>
 #include "ath5k.h"
-#include "base.h"
 
 #define ATH_SDEVICE(subv, subd) \
 	.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \

+ 2 - 3
drivers/net/wireless/ath/ath5k/mac80211-ops.c

@@ -41,8 +41,10 @@
  *
  */
 
+#include <net/mac80211.h>
 #include <asm/unaligned.h>
 
+#include "ath5k.h"
 #include "base.h"
 #include "reg.h"
 
@@ -137,11 +139,8 @@ ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 	/* Any MAC address is fine, all others are included through the
 	 * filter.
 	 */
-	memcpy(&ah->lladdr, vif->addr, ETH_ALEN);
 	ath5k_hw_set_lladdr(ah, vif->addr);
 
-	memcpy(&avf->lladdr, vif->addr, ETH_ALEN);
-
 	ath5k_update_bssid_mask_and_opmode(ah, vif);
 	ret = 0;
 end:

+ 2 - 2
drivers/net/wireless/ath/ath5k/pci.c

@@ -261,7 +261,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
 	ah->iobase = mem; /* So we can unmap it on detach */
 
 	/* Initialize */
-	ret = ath5k_init_softc(ah, &ath_pci_bus_ops);
+	ret = ath5k_init_ah(ah, &ath_pci_bus_ops);
 	if (ret)
 		goto err_free;
 
@@ -287,7 +287,7 @@ ath5k_pci_remove(struct pci_dev *pdev)
 	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
 	struct ath5k_hw *ah = hw->priv;
 
-	ath5k_deinit_softc(ah);
+	ath5k_deinit_ah(ah);
 	pci_iounmap(pdev, ah->iobase);
 	pci_release_region(pdev, 0);
 	pci_disable_device(pdev);

+ 2 - 3
drivers/net/wireless/ath/ath5k/pcu.c

@@ -29,7 +29,6 @@
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
 
 /*
  * AR5212+ can use higher rates for ack transmission
@@ -152,7 +151,7 @@ unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah)
 	case AR5K_BWMODE_DEFAULT:
 	default:
 		slot_time = AR5K_INIT_SLOT_TIME_DEFAULT;
-		if ((channel->hw_value & CHANNEL_CCK) && !ah->ah_short_slot)
+		if ((channel->hw_value == AR5K_MODE_11B) && !ah->ah_short_slot)
 			slot_time = AR5K_INIT_SLOT_TIME_B;
 		break;
 	}
@@ -183,7 +182,7 @@ unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah)
 	case AR5K_BWMODE_DEFAULT:
 		sifs = AR5K_INIT_SIFS_DEFAULT_BG;
 	default:
-		if (channel->hw_value & CHANNEL_5GHZ)
+		if (channel->band == IEEE80211_BAND_5GHZ)
 			sifs = AR5K_INIT_SIFS_DEFAULT_A;
 		break;
 	}

+ 50 - 41
drivers/net/wireless/ath/ath5k/phy.c

@@ -26,9 +26,9 @@
 
 #include "ath5k.h"
 #include "reg.h"
-#include "base.h"
 #include "rfbuffer.h"
 #include "rfgain.h"
+#include "../regd.h"
 
 
 /******************\
@@ -38,7 +38,7 @@
 /*
  * Get the PHY Chip revision
  */
-u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan)
+u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band)
 {
 	unsigned int i;
 	u32 srev;
@@ -47,11 +47,11 @@ u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan)
 	/*
 	 * Set the radio chip access register
 	 */
-	switch (chan) {
-	case CHANNEL_2GHZ:
+	switch (band) {
+	case IEEE80211_BAND_2GHZ:
 		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0));
 		break;
-	case CHANNEL_5GHZ:
+	case IEEE80211_BAND_5GHZ:
 		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
 		break;
 	default:
@@ -84,14 +84,16 @@ u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan)
 /*
  * Check if a channel is supported
  */
-bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags)
+bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel)
 {
+	u16 freq = channel->center_freq;
+
 	/* Check if the channel is in our supported range */
-	if (flags & CHANNEL_2GHZ) {
+	if (channel->band == IEEE80211_BAND_2GHZ) {
 		if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) &&
 		    (freq <= ah->ah_capabilities.cap_range.range_2ghz_max))
 			return true;
-	} else if (flags & CHANNEL_5GHZ)
+	} else if (channel->band == IEEE80211_BAND_5GHZ)
 		if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) &&
 		    (freq <= ah->ah_capabilities.cap_range.range_5ghz_max))
 			return true;
@@ -224,7 +226,7 @@ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
 		ds_coef_exp, ds_coef_man, clock;
 
 	BUG_ON(!(ah->ah_version == AR5K_AR5212) ||
-		!(channel->hw_value & CHANNEL_OFDM));
+		(channel->hw_value == AR5K_MODE_11B));
 
 	/* Get coefficient
 	 * ALGO: coef = (5 * clock / carrier_freq) / 2
@@ -298,7 +300,7 @@ static void ath5k_hw_wait_for_synth(struct ath5k_hw *ah,
 		u32 delay;
 		delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) &
 			AR5K_PHY_RX_DELAY_M;
-		delay = (channel->hw_value & CHANNEL_CCK) ?
+		delay = (channel->hw_value == AR5K_MODE_11B) ?
 			((delay << 2) / 22) : (delay / 10);
 		if (ah->ah_bwmode == AR5K_BWMODE_10MHZ)
 			delay = delay << 1;
@@ -798,9 +800,9 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
 	}
 
 	/* Set Output and Driver bias current (OB/DB) */
-	if (channel->hw_value & CHANNEL_2GHZ) {
+	if (channel->band == IEEE80211_BAND_2GHZ) {
 
-		if (channel->hw_value & CHANNEL_CCK)
+		if (channel->hw_value == AR5K_MODE_11B)
 			ee_mode = AR5K_EEPROM_MODE_11B;
 		else
 			ee_mode = AR5K_EEPROM_MODE_11G;
@@ -825,7 +827,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
 						AR5K_RF_DB_2GHZ, true);
 
 	/* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */
-	} else if ((channel->hw_value & CHANNEL_5GHZ) ||
+	} else if ((channel->band == IEEE80211_BAND_5GHZ) ||
 			(ah->ah_radio == AR5K_RF5111)) {
 
 		/* For 11a, Turbo and XR we need to choose
@@ -857,7 +859,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
 	if (ah->ah_radio == AR5K_RF5111) {
 
 		/* Set gain_F settings according to current step */
-		if (channel->hw_value & CHANNEL_OFDM) {
+		if (channel->hw_value != AR5K_MODE_11B) {
 
 			AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL,
 					AR5K_PHY_FRAME_CTL_TX_CLIP,
@@ -914,7 +916,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
 	if (ah->ah_radio == AR5K_RF5112) {
 
 		/* Set gain_F settings according to current step */
-		if (channel->hw_value & CHANNEL_OFDM) {
+		if (channel->hw_value != AR5K_MODE_11B) {
 
 			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0],
 						AR5K_RF_MIXGAIN_OVR, true);
@@ -1026,7 +1028,7 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
 	}
 
 	if (ah->ah_radio == AR5K_RF5413 &&
-	channel->hw_value & CHANNEL_2GHZ) {
+	channel->band == IEEE80211_BAND_2GHZ) {
 
 		ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE,
 									true);
@@ -1138,7 +1140,7 @@ static int ath5k_hw_rf5111_channel(struct ath5k_hw *ah,
 	 */
 	data0 = data1 = 0;
 
-	if (channel->hw_value & CHANNEL_2GHZ) {
+	if (channel->band == IEEE80211_BAND_2GHZ) {
 		/* Map 2GHz channel to 5GHz Atheros channel ID */
 		ret = ath5k_hw_rf5111_chan2athchan(
 			ieee80211_frequency_to_channel(channel->center_freq),
@@ -1265,10 +1267,9 @@ static int ath5k_hw_channel(struct ath5k_hw *ah,
 	int ret;
 	/*
 	 * Check bounds supported by the PHY (we don't care about regulatory
-	 * restrictions at this point). Note: hw_value already has the band
-	 * (CHANNEL_2GHZ, or CHANNEL_5GHZ) so we inform ath5k_channel_ok()
-	 * of the band by that */
-	if (!ath5k_channel_ok(ah, channel->center_freq, channel->hw_value)) {
+	 * restrictions at this point).
+	 */
+	if (!ath5k_channel_ok(ah, channel)) {
 		ATH5K_ERR(ah,
 			"channel frequency (%u MHz) out of supported "
 			"band range\n",
@@ -1614,7 +1615,7 @@ int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
 	ret = ath5k_hw_rf511x_iq_calibrate(ah);
 
 	if ((ah->ah_radio == AR5K_RF5111 || ah->ah_radio == AR5K_RF5112) &&
-	    (channel->hw_value & CHANNEL_OFDM))
+	    (channel->hw_value != AR5K_MODE_11B))
 		ath5k_hw_request_rfgain_probe(ah);
 
 	return ret;
@@ -1641,7 +1642,7 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
 	/* Convert current frequency to fbin value (the same way channels
 	 * are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale
 	 * up by 2 so we can compare it later */
-	if (channel->hw_value & CHANNEL_2GHZ) {
+	if (channel->band == IEEE80211_BAND_2GHZ) {
 		chan_fbin = (channel->center_freq - 2300) * 10;
 		freq_band = AR5K_EEPROM_BAND_2GHZ;
 	} else {
@@ -1703,7 +1704,7 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
 			spur_freq_sigma_delta = (spur_delta_phase >> 10);
 			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4;
 		default:
-			if (channel->hw_value == CHANNEL_A) {
+			if (channel->band == IEEE80211_BAND_5GHZ) {
 				/* Both sample_freq and chip_freq are 40MHz */
 				spur_delta_phase = (spur_offset << 17) / 25;
 				spur_freq_sigma_delta =
@@ -2226,15 +2227,20 @@ ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah,
 	idx_l = 0;
 	idx_r = 0;
 
-	if (!(channel->hw_value & CHANNEL_OFDM)) {
+	switch (channel->hw_value) {
+	case AR5K_EEPROM_MODE_11A:
+		pcinfo = ee->ee_pwr_cal_a;
+		mode = AR5K_EEPROM_MODE_11A;
+		break;
+	case AR5K_EEPROM_MODE_11B:
 		pcinfo = ee->ee_pwr_cal_b;
 		mode = AR5K_EEPROM_MODE_11B;
-	} else if (channel->hw_value & CHANNEL_2GHZ) {
+		break;
+	case AR5K_EEPROM_MODE_11G:
+	default:
 		pcinfo = ee->ee_pwr_cal_g;
 		mode = AR5K_EEPROM_MODE_11G;
-	} else {
-		pcinfo = ee->ee_pwr_cal_a;
-		mode = AR5K_EEPROM_MODE_11A;
+		break;
 	}
 	max = ee->ee_n_piers[mode] - 1;
 
@@ -2303,15 +2309,20 @@ ath5k_get_rate_pcal_data(struct ath5k_hw *ah,
 	idx_l = 0;
 	idx_r = 0;
 
-	if (!(channel->hw_value & CHANNEL_OFDM)) {
+	switch (channel->hw_value) {
+	case AR5K_MODE_11A:
+		rpinfo = ee->ee_rate_tpwr_a;
+		mode = AR5K_EEPROM_MODE_11A;
+		break;
+	case AR5K_MODE_11B:
 		rpinfo = ee->ee_rate_tpwr_b;
 		mode = AR5K_EEPROM_MODE_11B;
-	} else if (channel->hw_value & CHANNEL_2GHZ) {
+		break;
+	case AR5K_MODE_11G:
+	default:
 		rpinfo = ee->ee_rate_tpwr_g;
 		mode = AR5K_EEPROM_MODE_11G;
-	} else {
-		rpinfo = ee->ee_rate_tpwr_a;
-		mode = AR5K_EEPROM_MODE_11A;
+		break;
 	}
 	max = ee->ee_rate_target_pwr_num[mode] - 1;
 
@@ -2392,24 +2403,22 @@ ath5k_get_max_ctl_power(struct ath5k_hw *ah,
 
 	ctl_mode = ath_regd_get_band_ctl(regulatory, channel->band);
 
-	switch (channel->hw_value & CHANNEL_MODES) {
-	case CHANNEL_A:
+	switch (channel->hw_value) {
+	case AR5K_MODE_11A:
 		if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
 			ctl_mode |= AR5K_CTL_TURBO;
 		else
 			ctl_mode |= AR5K_CTL_11A;
 		break;
-	case CHANNEL_G:
+	case AR5K_MODE_11G:
 		if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
 			ctl_mode |= AR5K_CTL_TURBOG;
 		else
 			ctl_mode |= AR5K_CTL_11G;
 		break;
-	case CHANNEL_B:
+	case AR5K_MODE_11B:
 		ctl_mode |= AR5K_CTL_11B;
 		break;
-	case CHANNEL_XR:
-		/* Fall through */
 	default:
 		return;
 	}
@@ -3292,7 +3301,7 @@ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
 
 	/* Write OFDM timings on 5212*/
 	if (ah->ah_version == AR5K_AR5212 &&
-		channel->hw_value & CHANNEL_OFDM) {
+		channel->hw_value != AR5K_MODE_11B) {
 
 		ret = ath5k_hw_write_ofdm_timings(ah, channel);
 		if (ret)

+ 1 - 9
drivers/net/wireless/ath/ath5k/qcu.c

@@ -23,7 +23,6 @@ Queue Control Unit, DFS Control Unit Functions
 #include "ath5k.h"
 #include "reg.h"
 #include "debug.h"
-#include "base.h"
 
 
 /******************\
@@ -185,13 +184,6 @@ int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
 		case AR5K_TX_QUEUE_CAB:
 			queue = AR5K_TX_QUEUE_ID_CAB;
 			break;
-		case AR5K_TX_QUEUE_XR_DATA:
-			if (ah->ah_version != AR5K_AR5212)
-				ATH5K_ERR(ah,
-					"XR data queues only supported in"
-					" 5212!\n");
-			queue = AR5K_TX_QUEUE_ID_XR_DATA;
-			break;
 		default:
 			return -EINVAL;
 		}
@@ -544,7 +536,7 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
 	 *
 	 * Also we have different lowest rate for 802.11a
 	 */
-	if (channel->hw_value & CHANNEL_5GHZ)
+	if (channel->band == IEEE80211_BAND_5GHZ)
 		rate = &ah->sbands[IEEE80211_BAND_5GHZ].bitrates[0];
 	else
 		rate = &ah->sbands[IEEE80211_BAND_2GHZ].bitrates[0];

+ 30 - 49
drivers/net/wireless/ath/ath5k/reset.c

@@ -30,7 +30,6 @@
 #include <linux/platform_device.h>
 #include "ath5k.h"
 #include "reg.h"
-#include "base.h"
 #include "debug.h"
 
 
@@ -102,12 +101,18 @@ static void ath5k_hw_init_core_clock(struct ath5k_hw *ah)
 	/*
 	 * Set core clock frequency
 	 */
-	if (channel->hw_value & CHANNEL_5GHZ)
-		clock = 40; /* 802.11a */
-	else if (channel->hw_value & CHANNEL_CCK)
-		clock = 22; /* 802.11b */
-	else
-		clock = 44; /* 802.11g */
+	switch (channel->hw_value) {
+	case AR5K_MODE_11A:
+		clock = 40;
+		break;
+	case AR5K_MODE_11B:
+		clock = 22;
+		break;
+	case AR5K_MODE_11G:
+	default:
+		clock = 44;
+		break;
+	}
 
 	/* Use clock multiplier for non-default
 	 * bwmode */
@@ -581,8 +586,9 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
 
 /*
  * Bring up MAC + PHY Chips and program PLL
+ * Channel is NULL for the initial wakeup.
  */
-int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
+int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel)
 {
 	struct pci_dev *pdev = ah->pdev;
 	u32 turbo, mode, clock, bus_flags;
@@ -592,7 +598,7 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
 	mode = 0;
 	clock = 0;
 
-	if ((ath5k_get_bus_type(ah) != ATH_AHB) || !initial) {
+	if ((ath5k_get_bus_type(ah) != ATH_AHB) || channel) {
 		/* Wakeup the device */
 		ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
 		if (ret) {
@@ -652,7 +658,7 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
 
 	/* On initialization skip PLL programming since we don't have
 	 * a channel / mode set yet */
-	if (initial)
+	if (!channel)
 		return 0;
 
 	if (ah->ah_version != AR5K_AR5210) {
@@ -668,13 +674,13 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
 			clock = AR5K_PHY_PLL_RF5111;		/*Zero*/
 		}
 
-		if (flags & CHANNEL_2GHZ) {
+		if (channel->band == IEEE80211_BAND_2GHZ) {
 			mode |= AR5K_PHY_MODE_FREQ_2GHZ;
 			clock |= AR5K_PHY_PLL_44MHZ;
 
-			if (flags & CHANNEL_CCK) {
+			if (channel->hw_value == AR5K_MODE_11B) {
 				mode |= AR5K_PHY_MODE_MOD_CCK;
-			} else if (flags & CHANNEL_OFDM) {
+			} else {
 				/* XXX Dynamic OFDM/CCK is not supported by the
 				 * AR5211 so we set MOD_OFDM for plain g (no
 				 * CCK headers) operation. We need to test
@@ -686,27 +692,16 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
 					mode |= AR5K_PHY_MODE_MOD_OFDM;
 				else
 					mode |= AR5K_PHY_MODE_MOD_DYN;
-			} else {
-				ATH5K_ERR(ah,
-					"invalid radio modulation mode\n");
-				return -EINVAL;
 			}
-		} else if (flags & CHANNEL_5GHZ) {
-			mode |= AR5K_PHY_MODE_FREQ_5GHZ;
+		} else if (channel->band == IEEE80211_BAND_5GHZ) {
+			mode |= (AR5K_PHY_MODE_FREQ_5GHZ |
+				 AR5K_PHY_MODE_MOD_OFDM);
 
 			/* Different PLL setting for 5413 */
 			if (ah->ah_radio == AR5K_RF5413)
 				clock = AR5K_PHY_PLL_40MHZ_5413;
 			else
 				clock |= AR5K_PHY_PLL_40MHZ;
-
-			if (flags & CHANNEL_OFDM)
-				mode |= AR5K_PHY_MODE_MOD_OFDM;
-			else {
-				ATH5K_ERR(ah,
-					"invalid radio modulation mode\n");
-				return -EINVAL;
-			}
 		} else {
 			ATH5K_ERR(ah, "invalid radio frequency mode\n");
 			return -EINVAL;
@@ -822,7 +817,7 @@ static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
 		u32 data;
 		ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD,
 				AR5K_PHY_CCKTXCTL);
-		if (channel->hw_value & CHANNEL_5GHZ)
+		if (channel->band == IEEE80211_BAND_5GHZ)
 			data = 0xffb81020;
 		else
 			data = 0xffb80d20;
@@ -905,7 +900,7 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
 	/* Set CCK to OFDM power delta on tx power
 	 * adjustment register */
 	if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
-		if (channel->hw_value == CHANNEL_G)
+		if (channel->hw_value == AR5K_MODE_11G)
 			ath5k_hw_reg_write(ah,
 			AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1),
 				AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) |
@@ -1084,37 +1079,23 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
 		ret = 0;
 	}
 
-	switch (channel->hw_value & CHANNEL_MODES) {
-	case CHANNEL_A:
-		mode = AR5K_MODE_11A;
+	mode = channel->hw_value;
+	switch (mode) {
+	case AR5K_MODE_11A:
 		break;
-	case CHANNEL_G:
-
+	case AR5K_MODE_11G:
 		if (ah->ah_version <= AR5K_AR5211) {
 			ATH5K_ERR(ah,
 				"G mode not available on 5210/5211");
 			return -EINVAL;
 		}
-
-		mode = AR5K_MODE_11G;
 		break;
-	case CHANNEL_B:
-
+	case AR5K_MODE_11B:
 		if (ah->ah_version < AR5K_AR5211) {
 			ATH5K_ERR(ah,
 				"B mode not available on 5210");
 			return -EINVAL;
 		}
-
-		mode = AR5K_MODE_11B;
-		break;
-	case CHANNEL_XR:
-		if (ah->ah_version == AR5K_AR5211) {
-			ATH5K_ERR(ah,
-				"XR mode not available on 5211");
-			return -EINVAL;
-		}
-		mode = AR5K_MODE_XR;
 		break;
 	default:
 		ATH5K_ERR(ah,
@@ -1200,7 +1181,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
 	}
 
 	/* Wakeup the device */
-	ret = ath5k_hw_nic_wakeup(ah, channel->hw_value, false);
+	ret = ath5k_hw_nic_wakeup(ah, channel);
 	if (ret)
 		return ret;
 

+ 1 - 1
drivers/net/wireless/ath/ath5k/rfkill.c

@@ -33,7 +33,7 @@
  * THE POSSIBILITY OF SUCH DAMAGES.
  */
 
-#include "base.h"
+#include "ath5k.h"
 
 
 static inline void ath5k_rfkill_disable(struct ath5k_hw *ah)

+ 0 - 1
drivers/net/wireless/ath/ath5k/sysfs.c

@@ -1,7 +1,6 @@
 #include <linux/device.h>
 #include <linux/pci.h>
 
-#include "base.h"
 #include "ath5k.h"
 #include "reg.h"
 

+ 2 - 1
drivers/net/wireless/ath/ath5k/trace.h

@@ -2,7 +2,6 @@
 #define __TRACE_ATH5K_H
 
 #include <linux/tracepoint.h>
-#include "base.h"
 
 #ifndef CONFIG_ATH5K_TRACER
 #undef TRACE_EVENT
@@ -11,6 +10,8 @@ static inline void trace_ ## name(proto) {}
 #endif
 
 struct sk_buff;
+struct ath5k_txq;
+struct ath5k_tx_status;
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM ath5k

+ 15 - 0
drivers/net/wireless/ath/ath6kl/Kconfig

@@ -0,0 +1,15 @@
+config ATH6KL
+	tristate "Atheros ath6kl support"
+	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.
+
+config ATH6KL_DEBUG
+	bool "Atheros ath6kl debugging"
+	depends on ATH6KL
+	---help---
+	  Enables debug support

+ 35 - 0
drivers/net/wireless/ath/ath6kl/Makefile

@@ -0,0 +1,35 @@
+#------------------------------------------------------------------------------
+# Copyright (c) 2004-2010 Atheros Communications Inc.
+# All rights reserved.
+#
+#
+#
+# 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.
+#
+#
+#
+# Author(s): ="Atheros"
+#------------------------------------------------------------------------------
+
+obj-$(CONFIG_ATH6KL) := ath6kl.o
+ath6kl-y += debug.o
+ath6kl-y += htc_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 += node.o
+ath6kl-y += sdio.o

+ 692 - 0
drivers/net/wireless/ath/ath6kl/bmi.c

@@ -0,0 +1,692 @@
+/*
+ * Copyright (c) 2004-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 "core.h"
+#include "hif-ops.h"
+#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, bool need_timeout)
+{
+	unsigned long timeout;
+	u32 rx_word = 0;
+	int ret = 0;
+
+	timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
+	while ((!need_timeout || 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, bool want_timeout)
+{
+	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, want_timeout);
+		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;
+	u32 cid = BMI_DONE;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n");
+		return 0;
+	}
+
+	ar->bmi.done_sent = true;
+
+	ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
+	if (ret) {
+		ath6kl_err("Unable to send bmi done: %d\n", ret);
+		return ret;
+	}
+
+	ath6kl_bmi_cleanup(ar);
+
+	return 0;
+}
+
+int ath6kl_bmi_get_target_info(struct ath6kl *ar,
+			       struct ath6kl_bmi_target_info *targ_info)
+{
+	int ret;
+	u32 cid = BMI_GET_TARGET_INFO;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	ret = ath6kl_bmi_send_buf(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), true);
+	if (ret) {
+		ath6kl_err("Unable to recv target info: %d\n", ret);
+		return ret;
+	}
+
+	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,
+				   (u8 *)&targ_info->byte_count,
+				   sizeof(targ_info->byte_count),
+				   true);
+		if (ret) {
+			ath6kl_err("unable to read target info byte count: %d\n",
+				   ret);
+			return ret;
+		}
+
+		/*
+		 * The target's targ_info doesn't match the host's targ_info.
+		 * We need to do some backwards compatibility to make this work.
+		 */
+		if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
+			WARN_ON(1);
+			return -EINVAL;
+		}
+
+		/* Read the remainder of the targ_info */
+		ret = ath6kl_bmi_recv_buf(ar,
+				   ((u8 *)targ_info) +
+				   sizeof(targ_info->byte_count),
+				   sizeof(*targ_info) -
+				   sizeof(targ_info->byte_count),
+				   true);
+
+		if (ret) {
+			ath6kl_err("Unable to read target info (%d bytes): %d\n",
+				   targ_info->byte_count, ret);
+			return ret;
+		}
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
+		targ_info->version, targ_info->type);
+
+	return 0;
+}
+
+int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
+{
+	u32 cid = BMI_READ_MEMORY;
+	int ret;
+	u32 offset;
+	u32 len_remain, rx_len;
+	u16 size;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len);
+	if (size > MAX_BMI_CMDBUF_SZ) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	memset(ar->bmi.cmd_buf, 0, size);
+
+	ath6kl_dbg(ATH6KL_DBG_BMI,
+		   "bmi read memory: device: addr: 0x%x, len: %d\n",
+		   addr, len);
+
+	len_remain = len;
+
+	while (len_remain) {
+		rx_len = (len_remain < BMI_DATASZ_MAX) ?
+					len_remain : BMI_DATASZ_MAX;
+		offset = 0;
+		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+		offset += sizeof(cid);
+		memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+		offset += sizeof(addr);
+		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);
+		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, true);
+		if (ret) {
+			ath6kl_err("Unable to read from the device: %d\n",
+				   ret);
+			return ret;
+		}
+		memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
+		len_remain -= rx_len; addr += rx_len;
+	}
+
+	return 0;
+}
+
+int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
+{
+	u32 cid = BMI_WRITE_MEMORY;
+	int ret;
+	u32 offset;
+	u32 len_remain, tx_len;
+	const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
+	u8 aligned_buf[BMI_DATASZ_MAX];
+	u8 *src;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header);
+
+	ath6kl_dbg(ATH6KL_DBG_BMI,
+		  "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
+
+	len_remain = len;
+	while (len_remain) {
+		src = &buf[len - len_remain];
+
+		if (len_remain < (BMI_DATASZ_MAX - header)) {
+			if (len_remain & 3) {
+				/* align it with 4 bytes */
+				len_remain = len_remain +
+					     (4 - (len_remain & 3));
+				memcpy(aligned_buf, src, len_remain);
+				src = aligned_buf;
+			}
+			tx_len = len_remain;
+		} else {
+			tx_len = (BMI_DATASZ_MAX - header);
+		}
+
+		offset = 0;
+		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+		offset += sizeof(cid);
+		memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+		offset += sizeof(addr);
+		memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
+		offset += sizeof(tx_len);
+		memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
+		offset += tx_len;
+
+		ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+		if (ret) {
+			ath6kl_err("Unable to write to the device: %d\n",
+				   ret);
+			return ret;
+		}
+		len_remain -= tx_len; addr += tx_len;
+	}
+
+	return 0;
+}
+
+int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
+{
+	u32 cid = BMI_EXECUTE;
+	int ret;
+	u32 offset;
+	u16 size;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	size = sizeof(cid) + sizeof(addr) + sizeof(param);
+	if (size > MAX_BMI_CMDBUF_SZ) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	memset(ar->bmi.cmd_buf, 0, size);
+
+	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n",
+		   addr, *param);
+
+	offset = 0;
+	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+	offset += sizeof(cid);
+	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+	offset += sizeof(addr);
+	memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
+	offset += sizeof(*param);
+
+	ret = ath6kl_bmi_send_buf(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), false);
+	if (ret) {
+		ath6kl_err("Unable to read from the device: %d\n", ret);
+		return ret;
+	}
+
+	memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
+
+	return 0;
+}
+
+int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
+{
+	u32 cid = BMI_SET_APP_START;
+	int ret;
+	u32 offset;
+	u16 size;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	size = sizeof(cid) + sizeof(addr);
+	if (size > MAX_BMI_CMDBUF_SZ) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	memset(ar->bmi.cmd_buf, 0, size);
+
+	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr);
+
+	offset = 0;
+	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+	offset += sizeof(cid);
+	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+	offset += sizeof(addr);
+
+	ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+	if (ret) {
+		ath6kl_err("Unable to write to the device: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
+{
+	u32 cid = BMI_READ_SOC_REGISTER;
+	int ret;
+	u32 offset;
+	u16 size;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	size = sizeof(cid) + sizeof(addr);
+	if (size > MAX_BMI_CMDBUF_SZ) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	memset(ar->bmi.cmd_buf, 0, size);
+
+	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr);
+
+	offset = 0;
+	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+	offset += sizeof(cid);
+	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+	offset += sizeof(addr);
+
+	ret = ath6kl_bmi_send_buf(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), true);
+	if (ret) {
+		ath6kl_err("Unable to read from the device: %d\n", ret);
+		return ret;
+	}
+	memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
+
+	return 0;
+}
+
+int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
+{
+	u32 cid = BMI_WRITE_SOC_REGISTER;
+	int ret;
+	u32 offset;
+	u16 size;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	size = sizeof(cid) + sizeof(addr) + sizeof(param);
+	if (size > MAX_BMI_CMDBUF_SZ) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	memset(ar->bmi.cmd_buf, 0, size);
+
+	ath6kl_dbg(ATH6KL_DBG_BMI,
+		   "bmi write SOC reg: addr: 0x%x, param: %d\n",
+		    addr, param);
+
+	offset = 0;
+	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+	offset += sizeof(cid);
+	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+	offset += sizeof(addr);
+	memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
+	offset += sizeof(param);
+
+	ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+	if (ret) {
+		ath6kl_err("Unable to write to the device: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
+{
+	u32 cid = BMI_LZ_DATA;
+	int ret;
+	u32 offset;
+	u32 len_remain, tx_len;
+	const u32 header = sizeof(cid) + sizeof(len);
+	u16 size;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	size = BMI_DATASZ_MAX + header;
+	if (size > MAX_BMI_CMDBUF_SZ) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	memset(ar->bmi.cmd_buf, 0, size);
+
+	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n",
+		   len);
+
+	len_remain = len;
+	while (len_remain) {
+		tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ?
+			  len_remain : (BMI_DATASZ_MAX - header);
+
+		offset = 0;
+		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+		offset += sizeof(cid);
+		memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
+		offset += sizeof(tx_len);
+		memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
+			tx_len);
+		offset += tx_len;
+
+		ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+		if (ret) {
+			ath6kl_err("Unable to write to the device: %d\n",
+				   ret);
+			return ret;
+		}
+
+		len_remain -= tx_len;
+	}
+
+	return 0;
+}
+
+int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
+{
+	u32 cid = BMI_LZ_STREAM_START;
+	int ret;
+	u32 offset;
+	u16 size;
+
+	if (ar->bmi.done_sent) {
+		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
+		return -EACCES;
+	}
+
+	size = sizeof(cid) + sizeof(addr);
+	if (size > MAX_BMI_CMDBUF_SZ) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	memset(ar->bmi.cmd_buf, 0, size);
+
+	ath6kl_dbg(ATH6KL_DBG_BMI,
+		   "bmi LZ stream start: addr: 0x%x)\n",
+		    addr);
+
+	offset = 0;
+	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
+	offset += sizeof(cid);
+	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
+	offset += sizeof(addr);
+
+	ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+	if (ret) {
+		ath6kl_err("Unable to start LZ stream to the device: %d\n",
+			   ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
+{
+	int ret;
+	u32 last_word = 0;
+	u32 last_word_offset = len & ~0x3;
+	u32 unaligned_bytes = len & 0x3;
+
+	ret = ath6kl_bmi_lz_stream_start(ar, addr);
+	if (ret)
+		return ret;
+
+	if (unaligned_bytes) {
+		/* copy the last word into a zero padded buffer */
+		memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
+	}
+
+	ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset);
+	if (ret)
+		return ret;
+
+	if (unaligned_bytes)
+		ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4);
+
+	if (!ret) {
+		/* Close compressed stream and open a new (fake) one.
+		 * This serves mainly to flush Target caches. */
+		ret = ath6kl_bmi_lz_stream_start(ar, 0x00);
+	}
+	return ret;
+}
+
+int ath6kl_bmi_init(struct ath6kl *ar)
+{
+	ar->bmi.cmd_buf = kzalloc(MAX_BMI_CMDBUF_SZ, GFP_ATOMIC);
+
+	if (!ar->bmi.cmd_buf)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void ath6kl_bmi_cleanup(struct ath6kl *ar)
+{
+	kfree(ar->bmi.cmd_buf);
+	ar->bmi.cmd_buf = NULL;
+}

+ 250 - 0
drivers/net/wireless/ath/ath6kl/bmi.h

@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2004-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.
+ */
+
+#ifndef BMI_H
+#define BMI_H
+
+/*
+ * Bootloader Messaging Interface (BMI)
+ *
+ * BMI is a very simple messaging interface used during initialization
+ * to read memory, write memory, execute code, and to define an
+ * application entry PC.
+ *
+ * It is used to download an application to ATH6KL, to provide
+ * patches to code that is already resident on ATH6KL, and generally
+ * to examine and modify state.  The Host has an opportunity to use
+ * BMI only once during bootup.  Once the Host issues a BMI_DONE
+ * command, this opportunity ends.
+ *
+ * The Host writes BMI requests to mailbox0, and reads BMI responses
+ * from mailbox0.   BMI requests all begin with a command
+ * (see below for specific commands), and are followed by
+ * command-specific data.
+ *
+ * Flow control:
+ * The Host can only issue a command once the Target gives it a
+ * "BMI Command Credit", using ATH6KL Counter #4.  As soon as the
+ * Target has completed a command, it issues another BMI Command
+ * Credit (so the Host can issue the next command).
+ *
+ * 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
+
+#define BMI_DONE                            1
+/*
+ * Semantics: Host is done using BMI
+ * Request format:
+ *    u32 command (BMI_DONE)
+ * Response format: none
+ */
+
+#define BMI_READ_MEMORY                     2
+/*
+ * Semantics: Host reads ATH6KL memory
+ * Request format:
+ *    u32 command (BMI_READ_MEMORY)
+ *    u32 address
+ *    u32 length, at most BMI_DATASZ_MAX
+ * Response format:
+ *    u8 data[length]
+ */
+
+#define BMI_WRITE_MEMORY                    3
+/*
+ * Semantics: Host writes ATH6KL memory
+ * Request format:
+ *    u32 command (BMI_WRITE_MEMORY)
+ *    u32 address
+ *    u32 length, at most BMI_DATASZ_MAX
+ *    u8 data[length]
+ * Response format: none
+ */
+
+#define BMI_EXECUTE                         4
+/*
+ * Semantics: Causes ATH6KL to execute code
+ * Request format:
+ *    u32 command (BMI_EXECUTE)
+ *    u32 address
+ *    u32 parameter
+ * Response format:
+ *    u32 return value
+ */
+
+#define BMI_SET_APP_START                   5
+/*
+ * Semantics: Set Target application starting address
+ * Request format:
+ *    u32 command (BMI_SET_APP_START)
+ *    u32 address
+ * Response format: none
+ */
+
+#define BMI_READ_SOC_REGISTER               6
+/*
+ * Semantics: Read a 32-bit Target SOC register.
+ * Request format:
+ *    u32 command (BMI_READ_REGISTER)
+ *    u32 address
+ * Response format:
+ *    u32 value
+ */
+
+#define BMI_WRITE_SOC_REGISTER              7
+/*
+ * Semantics: Write a 32-bit Target SOC register.
+ * Request format:
+ *    u32 command (BMI_WRITE_REGISTER)
+ *    u32 address
+ *    u32 value
+ *
+ * Response format: none
+ */
+
+#define BMI_GET_TARGET_ID                  8
+#define BMI_GET_TARGET_INFO                8
+/*
+ * Semantics: Fetch the 4-byte Target information
+ * Request format:
+ *    u32 command (BMI_GET_TARGET_ID/INFO)
+ * Response format1 (old firmware):
+ *    u32 TargetVersionID
+ * Response format2 (newer firmware):
+ *    u32 TARGET_VERSION_SENTINAL
+ *    struct bmi_target_info;
+ */
+
+#define TARGET_VERSION_SENTINAL 0xffffffff
+#define TARGET_TYPE_AR6003	3
+
+#define BMI_ROMPATCH_INSTALL               9
+/*
+ * Semantics: Install a ROM Patch.
+ * Request format:
+ *    u32 command (BMI_ROMPATCH_INSTALL)
+ *    u32 Target ROM Address
+ *    u32 Target RAM Address or Value (depending on Target Type)
+ *    u32 Size, in bytes
+ *    u32 Activate? 1-->activate;
+ *                            0-->install but do not activate
+ * Response format:
+ *    u32 PatchID
+ */
+
+#define BMI_ROMPATCH_UNINSTALL             10
+/*
+ * Semantics: Uninstall a previously-installed ROM Patch,
+ * automatically deactivating, if necessary.
+ * Request format:
+ *    u32 command (BMI_ROMPATCH_UNINSTALL)
+ *    u32 PatchID
+ *
+ * Response format: none
+ */
+
+#define BMI_ROMPATCH_ACTIVATE              11
+/*
+ * Semantics: Activate a list of previously-installed ROM Patches.
+ * Request format:
+ *    u32 command (BMI_ROMPATCH_ACTIVATE)
+ *    u32 rompatch_count
+ *    u32 PatchID[rompatch_count]
+ *
+ * Response format: none
+ */
+
+#define BMI_ROMPATCH_DEACTIVATE            12
+/*
+ * Semantics: Deactivate a list of active ROM Patches.
+ * Request format:
+ *    u32 command (BMI_ROMPATCH_DEACTIVATE)
+ *    u32 rompatch_count
+ *    u32 PatchID[rompatch_count]
+ *
+ * Response format: none
+ */
+
+
+#define BMI_LZ_STREAM_START                13
+/*
+ * Semantics: Begin an LZ-compressed stream of input
+ * which is to be uncompressed by the Target to an
+ * output buffer at address.  The output buffer must
+ * be sufficiently large to hold the uncompressed
+ * output from the compressed input stream.  This BMI
+ * command should be followed by a series of 1 or more
+ * BMI_LZ_DATA commands.
+ *    u32 command (BMI_LZ_STREAM_START)
+ *    u32 address
+ * Note: Not supported on all versions of ROM firmware.
+ */
+
+#define BMI_LZ_DATA                        14
+/*
+ * Semantics: Host writes ATH6KL memory with LZ-compressed
+ * data which is uncompressed by the Target.  This command
+ * must be preceded by a BMI_LZ_STREAM_START command. A series
+ * of BMI_LZ_DATA commands are considered part of a single
+ * input stream until another BMI_LZ_STREAM_START is issued.
+ * Request format:
+ *    u32 command (BMI_LZ_DATA)
+ *    u32 length (of compressed data),
+ *                  at most BMI_DATASZ_MAX
+ *    u8 CompressedData[length]
+ * Response format: none
+ * Note: Not supported on all versions of ROM firmware.
+ */
+
+#define BMI_COMMUNICATION_TIMEOUT       1000 /* in msec */
+
+struct ath6kl;
+struct ath6kl_bmi_target_info {
+	__le32 byte_count;   /* size of this structure */
+	__le32 version;      /* target version id */
+	__le32 type;         /* target type */
+} __packed;
+
+int ath6kl_bmi_init(struct ath6kl *ar);
+void ath6kl_bmi_cleanup(struct ath6kl *ar);
+int ath6kl_bmi_done(struct ath6kl *ar);
+int ath6kl_bmi_get_target_info(struct ath6kl *ar,
+			       struct ath6kl_bmi_target_info *targ_info);
+int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len);
+int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len);
+int ath6kl_bmi_execute(struct ath6kl *ar,
+		       u32 addr, u32 *param);
+int ath6kl_bmi_set_app_start(struct ath6kl *ar,
+			     u32 addr);
+int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param);
+int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param);
+int ath6kl_bmi_lz_data(struct ath6kl *ar,
+		       u8 *buf, u32 len);
+int ath6kl_bmi_lz_stream_start(struct ath6kl *ar,
+			       u32 addr);
+int ath6kl_bmi_fast_download(struct ath6kl *ar,
+			     u32 addr, u8 *buf, u32 len);
+#endif

+ 1538 - 0
drivers/net/wireless/ath/ath6kl/cfg80211.c

@@ -0,0 +1,1538 @@
+/*
+ * Copyright (c) 2004-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 "core.h"
+#include "cfg80211.h"
+#include "debug.h"
+
+#define RATETAB_ENT(_rate, _rateid, _flags) {   \
+	.bitrate    = (_rate),                  \
+	.flags      = (_flags),                 \
+	.hw_value   = (_rateid),                \
+}
+
+#define CHAN2G(_channel, _freq, _flags) {   \
+	.band           = IEEE80211_BAND_2GHZ,  \
+	.hw_value       = (_channel),           \
+	.center_freq    = (_freq),              \
+	.flags          = (_flags),             \
+	.max_antenna_gain   = 0,                \
+	.max_power      = 30,                   \
+}
+
+#define CHAN5G(_channel, _flags) {		    \
+	.band           = IEEE80211_BAND_5GHZ,      \
+	.hw_value       = (_channel),               \
+	.center_freq    = 5000 + (5 * (_channel)),  \
+	.flags          = (_flags),                 \
+	.max_antenna_gain   = 0,                    \
+	.max_power      = 30,                       \
+}
+
+static struct ieee80211_rate ath6kl_rates[] = {
+	RATETAB_ENT(10, 0x1, 0),
+	RATETAB_ENT(20, 0x2, 0),
+	RATETAB_ENT(55, 0x4, 0),
+	RATETAB_ENT(110, 0x8, 0),
+	RATETAB_ENT(60, 0x10, 0),
+	RATETAB_ENT(90, 0x20, 0),
+	RATETAB_ENT(120, 0x40, 0),
+	RATETAB_ENT(180, 0x80, 0),
+	RATETAB_ENT(240, 0x100, 0),
+	RATETAB_ENT(360, 0x200, 0),
+	RATETAB_ENT(480, 0x400, 0),
+	RATETAB_ENT(540, 0x800, 0),
+};
+
+#define ath6kl_a_rates     (ath6kl_rates + 4)
+#define ath6kl_a_rates_size    8
+#define ath6kl_g_rates     (ath6kl_rates + 0)
+#define ath6kl_g_rates_size    12
+
+static struct ieee80211_channel ath6kl_2ghz_channels[] = {
+	CHAN2G(1, 2412, 0),
+	CHAN2G(2, 2417, 0),
+	CHAN2G(3, 2422, 0),
+	CHAN2G(4, 2427, 0),
+	CHAN2G(5, 2432, 0),
+	CHAN2G(6, 2437, 0),
+	CHAN2G(7, 2442, 0),
+	CHAN2G(8, 2447, 0),
+	CHAN2G(9, 2452, 0),
+	CHAN2G(10, 2457, 0),
+	CHAN2G(11, 2462, 0),
+	CHAN2G(12, 2467, 0),
+	CHAN2G(13, 2472, 0),
+	CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
+	CHAN5G(34, 0), CHAN5G(36, 0),
+	CHAN5G(38, 0), CHAN5G(40, 0),
+	CHAN5G(42, 0), CHAN5G(44, 0),
+	CHAN5G(46, 0), CHAN5G(48, 0),
+	CHAN5G(52, 0), CHAN5G(56, 0),
+	CHAN5G(60, 0), CHAN5G(64, 0),
+	CHAN5G(100, 0), CHAN5G(104, 0),
+	CHAN5G(108, 0), CHAN5G(112, 0),
+	CHAN5G(116, 0), CHAN5G(120, 0),
+	CHAN5G(124, 0), CHAN5G(128, 0),
+	CHAN5G(132, 0), CHAN5G(136, 0),
+	CHAN5G(140, 0), CHAN5G(149, 0),
+	CHAN5G(153, 0), CHAN5G(157, 0),
+	CHAN5G(161, 0), CHAN5G(165, 0),
+	CHAN5G(184, 0), CHAN5G(188, 0),
+	CHAN5G(192, 0), CHAN5G(196, 0),
+	CHAN5G(200, 0), CHAN5G(204, 0),
+	CHAN5G(208, 0), CHAN5G(212, 0),
+	CHAN5G(216, 0),
+};
+
+static struct ieee80211_supported_band ath6kl_band_2ghz = {
+	.n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
+	.channels = ath6kl_2ghz_channels,
+	.n_bitrates = ath6kl_g_rates_size,
+	.bitrates = ath6kl_g_rates,
+};
+
+static struct ieee80211_supported_band ath6kl_band_5ghz = {
+	.n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
+	.channels = ath6kl_5ghz_a_channels,
+	.n_bitrates = ath6kl_a_rates_size,
+	.bitrates = ath6kl_a_rates,
+};
+
+static int ath6kl_set_wpa_version(struct ath6kl *ar,
+				  enum nl80211_wpa_versions wpa_version)
+{
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
+
+	if (!wpa_version) {
+		ar->auth_mode = NONE_AUTH;
+	} else if (wpa_version & NL80211_WPA_VERSION_2) {
+		ar->auth_mode = WPA2_AUTH;
+	} else if (wpa_version & NL80211_WPA_VERSION_1) {
+		ar->auth_mode = WPA_AUTH;
+	} else {
+		ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static int ath6kl_set_auth_type(struct ath6kl *ar,
+				enum nl80211_auth_type auth_type)
+{
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
+
+	switch (auth_type) {
+	case NL80211_AUTHTYPE_OPEN_SYSTEM:
+		ar->dot11_auth_mode = OPEN_AUTH;
+		break;
+	case NL80211_AUTHTYPE_SHARED_KEY:
+		ar->dot11_auth_mode = SHARED_AUTH;
+		break;
+	case NL80211_AUTHTYPE_NETWORK_EAP:
+		ar->dot11_auth_mode = LEAP_AUTH;
+		break;
+
+	case NL80211_AUTHTYPE_AUTOMATIC:
+		ar->dot11_auth_mode = OPEN_AUTH;
+		ar->auto_auth_stage = AUTH_OPEN_IN_PROGRESS;
+		break;
+
+	default:
+		ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast)
+{
+	u8 *ar_cipher = ucast ? &ar->prwise_crypto : &ar->grp_crypto;
+	u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len : &ar->grp_crpto_len;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
+		   __func__, cipher, ucast);
+
+	switch (cipher) {
+	case 0:
+		/* our own hack to use value 0 as no crypto used */
+		*ar_cipher = NONE_CRYPT;
+		*ar_cipher_len = 0;
+		break;
+	case WLAN_CIPHER_SUITE_WEP40:
+		*ar_cipher = WEP_CRYPT;
+		*ar_cipher_len = 5;
+		break;
+	case WLAN_CIPHER_SUITE_WEP104:
+		*ar_cipher = WEP_CRYPT;
+		*ar_cipher_len = 13;
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		*ar_cipher = TKIP_CRYPT;
+		*ar_cipher_len = 0;
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		*ar_cipher = AES_CRYPT;
+		*ar_cipher_len = 0;
+		break;
+	default:
+		ath6kl_err("cipher 0x%x not supported\n", cipher);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static void ath6kl_set_key_mgmt(struct ath6kl *ar, u32 key_mgmt)
+{
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
+
+	if (key_mgmt == WLAN_AKM_SUITE_PSK) {
+		if (ar->auth_mode == WPA_AUTH)
+			ar->auth_mode = WPA_PSK_AUTH;
+		else if (ar->auth_mode == WPA2_AUTH)
+			ar->auth_mode = WPA2_PSK_AUTH;
+	} else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
+		ar->auth_mode = NONE_AUTH;
+	}
+}
+
+static bool ath6kl_cfg80211_ready(struct ath6kl *ar)
+{
+	if (!test_bit(WMI_READY, &ar->flag)) {
+		ath6kl_err("wmi is not ready\n");
+		return false;
+	}
+
+	if (!test_bit(WLAN_ENABLED, &ar->flag)) {
+		ath6kl_err("wlan disabled\n");
+		return false;
+	}
+
+	return true;
+}
+
+static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+				   struct cfg80211_connect_params *sme)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	int status;
+
+	ar->sme_state = SME_CONNECTING;
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
+		ath6kl_err("destroy in progress\n");
+		return -EBUSY;
+	}
+
+	if (test_bit(SKIP_SCAN, &ar->flag) &&
+	    ((sme->channel && sme->channel->center_freq == 0) ||
+	     (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
+		ath6kl_err("SkipScan: channel or bssid invalid\n");
+		return -EINVAL;
+	}
+
+	if (down_interruptible(&ar->sem)) {
+		ath6kl_err("busy, couldn't get access\n");
+		return -ERESTARTSYS;
+	}
+
+	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
+		ath6kl_err("busy, destroy in progress\n");
+		up(&ar->sem);
+		return -EBUSY;
+	}
+
+	if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
+		/*
+		 * sleep until the command queue drains
+		 */
+		wait_event_interruptible_timeout(ar->event_wq,
+			ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
+			WMI_TIMEOUT);
+		if (signal_pending(current)) {
+			ath6kl_err("cmd queue drain timeout\n");
+			up(&ar->sem);
+			return -EINTR;
+		}
+	}
+
+	if (test_bit(CONNECTED, &ar->flag) &&
+	    ar->ssid_len == sme->ssid_len &&
+	    !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) {
+		ar->reconnect_flag = true;
+		status = ath6kl_wmi_reconnect_cmd(ar->wmi, ar->req_bssid,
+						  ar->ch_hint);
+
+		up(&ar->sem);
+		if (status) {
+			ath6kl_err("wmi_reconnect_cmd failed\n");
+			return -EIO;
+		}
+		return 0;
+	} else if (ar->ssid_len == sme->ssid_len &&
+		   !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) {
+		ath6kl_disconnect(ar);
+	}
+
+	memset(ar->ssid, 0, sizeof(ar->ssid));
+	ar->ssid_len = sme->ssid_len;
+	memcpy(ar->ssid, sme->ssid, sme->ssid_len);
+
+	if (sme->channel)
+		ar->ch_hint = sme->channel->center_freq;
+
+	memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+	if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
+		memcpy(ar->req_bssid, sme->bssid, sizeof(ar->req_bssid));
+
+	ath6kl_set_wpa_version(ar, sme->crypto.wpa_versions);
+
+	status = ath6kl_set_auth_type(ar, sme->auth_type);
+	if (status) {
+		up(&ar->sem);
+		return status;
+	}
+
+	if (sme->crypto.n_ciphers_pairwise)
+		ath6kl_set_cipher(ar, sme->crypto.ciphers_pairwise[0], true);
+	else
+		ath6kl_set_cipher(ar, 0, true);
+
+	ath6kl_set_cipher(ar, sme->crypto.cipher_group, false);
+
+	if (sme->crypto.n_akm_suites)
+		ath6kl_set_key_mgmt(ar, sme->crypto.akm_suites[0]);
+
+	if ((sme->key_len) &&
+	    (ar->auth_mode == NONE_AUTH) && (ar->prwise_crypto == WEP_CRYPT)) {
+		struct ath6kl_key *key = NULL;
+
+		if (sme->key_idx < WMI_MIN_KEY_INDEX ||
+		    sme->key_idx > WMI_MAX_KEY_INDEX) {
+			ath6kl_err("key index %d out of bounds\n",
+				   sme->key_idx);
+			up(&ar->sem);
+			return -ENOENT;
+		}
+
+		key = &ar->keys[sme->key_idx];
+		key->key_len = sme->key_len;
+		memcpy(key->key, sme->key, key->key_len);
+		key->cipher = ar->prwise_crypto;
+		ar->def_txkey_index = sme->key_idx;
+
+		ath6kl_wmi_addkey_cmd(ar->wmi, sme->key_idx,
+				      ar->prwise_crypto,
+				      GROUP_USAGE | TX_USAGE,
+				      key->key_len,
+				      NULL,
+				      key->key, KEY_OP_INIT_VAL, NULL,
+				      NO_SYNC_WMIFLAG);
+	}
+
+	if (!ar->usr_bss_filter) {
+		if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) {
+			ath6kl_err("couldn't set bss filtering\n");
+			up(&ar->sem);
+			return -EIO;
+		}
+	}
+
+	ar->nw_type = ar->next_mode;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+		   "%s: connect called with authmode %d dot11 auth %d"
+		   " PW crypto %d PW crypto len %d GRP crypto %d"
+		   " GRP crypto len %d channel hint %u\n",
+		   __func__,
+		   ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
+		   ar->prwise_crypto_len, ar->grp_crypto,
+		   ar->grp_crpto_len, ar->ch_hint);
+
+	ar->reconnect_flag = 0;
+	status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
+					ar->dot11_auth_mode, ar->auth_mode,
+					ar->prwise_crypto,
+					ar->prwise_crypto_len,
+					ar->grp_crypto, ar->grp_crpto_len,
+					ar->ssid_len, ar->ssid,
+					ar->req_bssid, ar->ch_hint,
+					ar->connect_ctrl_flags);
+
+	up(&ar->sem);
+
+	if (status == -EINVAL) {
+		memset(ar->ssid, 0, sizeof(ar->ssid));
+		ar->ssid_len = 0;
+		ath6kl_err("invalid request\n");
+		return -ENOENT;
+	} else if (status) {
+		ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
+		return -EIO;
+	}
+
+	if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
+	    ((ar->auth_mode == WPA_PSK_AUTH)
+	     || (ar->auth_mode == WPA2_PSK_AUTH))) {
+		mod_timer(&ar->disconnect_timer,
+			  jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
+	}
+
+	ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
+	set_bit(CONNECT_PEND, &ar->flag);
+
+	return 0;
+}
+
+void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
+				   u8 *bssid, u16 listen_intvl,
+				   u16 beacon_intvl,
+				   enum network_type nw_type,
+				   u8 beacon_ie_len, u8 assoc_req_len,
+				   u8 assoc_resp_len, u8 *assoc_info)
+{
+	u16 size = 0;
+	u16 capability = 0;
+	struct cfg80211_bss *bss = NULL;
+	struct ieee80211_mgmt *mgmt = NULL;
+	struct ieee80211_channel *ibss_ch = NULL;
+	s32 signal = 50 * 100;
+	u8 ie_buf_len = 0;
+	unsigned char ie_buf[256];
+	unsigned char *ptr_ie_buf = ie_buf;
+	unsigned char *ieeemgmtbuf = NULL;
+	u8 source_mac[ETH_ALEN];
+	u16 capa_mask;
+	u16 capa_val;
+
+	/* 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 = assoc_info + beacon_ie_len + assoc_req_ie_offset;
+	u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
+	    assoc_resp_ie_offset;
+
+	assoc_req_len -= assoc_req_ie_offset;
+	assoc_resp_len -= assoc_resp_ie_offset;
+
+	ar->auto_auth_stage = AUTH_IDLE;
+
+	if (nw_type & ADHOC_NETWORK) {
+		if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
+			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+				   "%s: ath6k not in ibss mode\n", __func__);
+			return;
+		}
+	}
+
+	if (nw_type & INFRA_NETWORK) {
+		if (ar->wdev->iftype != NL80211_IFTYPE_STATION) {
+			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+				   "%s: ath6k not in station mode\n", __func__);
+			return;
+		}
+	}
+
+	if (nw_type & ADHOC_NETWORK) {
+		capa_mask = WLAN_CAPABILITY_IBSS;
+		capa_val = WLAN_CAPABILITY_IBSS;
+	} else {
+		capa_mask = WLAN_CAPABILITY_ESS;
+		capa_val = WLAN_CAPABILITY_ESS;
+	}
+
+	/* Before informing the join/connect event, make sure that
+	 * bss entry is present in scan list, if it not present
+	 * construct and insert into scan list, otherwise that
+	 * event will be dropped on the way by cfg80211, due to
+	 * this keys will not be plumbed in case of WEP and
+	 * application will not be aware of join/connect status. */
+	bss = cfg80211_get_bss(ar->wdev->wiphy, NULL, bssid,
+			       ar->wdev->ssid, ar->wdev->ssid_len,
+			       capa_mask, capa_val);
+
+	/*
+	 * Earlier we were updating the cfg about bss by making a beacon frame
+	 * only if the entry for bss is not there. This can have some issue if
+	 * ROAM event is generated and a heavy traffic is ongoing. The ROAM
+	 * event is handled through a work queue and by the time it really gets
+	 * handled, BSS would have been aged out. So it is better to update the
+	 * cfg about BSS irrespective of its entry being present right now or
+	 * not.
+	 */
+
+	if (nw_type & ADHOC_NETWORK) {
+		/* construct 802.11 mgmt beacon */
+		if (ptr_ie_buf) {
+			*ptr_ie_buf++ = WLAN_EID_SSID;
+			*ptr_ie_buf++ = ar->ssid_len;
+			memcpy(ptr_ie_buf, ar->ssid, ar->ssid_len);
+			ptr_ie_buf += ar->ssid_len;
+
+			*ptr_ie_buf++ = WLAN_EID_IBSS_PARAMS;
+			*ptr_ie_buf++ = 2;	/* length */
+			*ptr_ie_buf++ = 0;	/* ATIM window */
+			*ptr_ie_buf++ = 0;	/* ATIM window */
+
+			/* TODO: update ibss params and include supported rates,
+			 * DS param set, extened support rates, wmm. */
+
+			ie_buf_len = ptr_ie_buf - ie_buf;
+		}
+
+		capability |= WLAN_CAPABILITY_IBSS;
+
+		if (ar->prwise_crypto == WEP_CRYPT)
+			capability |= WLAN_CAPABILITY_PRIVACY;
+
+		memcpy(source_mac, ar->net_dev->dev_addr, ETH_ALEN);
+		ptr_ie_buf = ie_buf;
+	} else {
+		capability = *(u16 *) (&assoc_info[beacon_ie_len]);
+		memcpy(source_mac, bssid, ETH_ALEN);
+		ptr_ie_buf = assoc_req_ie;
+		ie_buf_len = assoc_req_len;
+	}
+
+	size = offsetof(struct ieee80211_mgmt, u)
+	+ sizeof(mgmt->u.beacon)
+	+ ie_buf_len;
+
+	ieeemgmtbuf = kzalloc(size, GFP_ATOMIC);
+	if (!ieeemgmtbuf) {
+		ath6kl_err("ieee mgmt buf alloc error\n");
+		cfg80211_put_bss(bss);
+		return;
+	}
+
+	mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf;
+	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+					  IEEE80211_STYPE_BEACON);
+	memset(mgmt->da, 0xff, ETH_ALEN);	/* broadcast addr */
+	memcpy(mgmt->sa, source_mac, ETH_ALEN);
+	memcpy(mgmt->bssid, bssid, ETH_ALEN);
+	mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_intvl);
+	mgmt->u.beacon.capab_info = cpu_to_le16(capability);
+	memcpy(mgmt->u.beacon.variable, ptr_ie_buf, ie_buf_len);
+
+	ibss_ch = ieee80211_get_channel(ar->wdev->wiphy, (int)channel);
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+		   "%s: inform bss with bssid %pM channel %d beacon_intvl %d capability 0x%x\n",
+		   __func__, mgmt->bssid, ibss_ch->hw_value,
+		   beacon_intvl, capability);
+
+	bss = cfg80211_inform_bss_frame(ar->wdev->wiphy,
+					ibss_ch, mgmt,
+					size, signal, GFP_KERNEL);
+	kfree(ieeemgmtbuf);
+	cfg80211_put_bss(bss);
+
+	if (nw_type & ADHOC_NETWORK) {
+		cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
+		return;
+	}
+
+	if (ar->sme_state == SME_CONNECTING) {
+		/* inform connect result to cfg80211 */
+		ar->sme_state = SME_CONNECTED;
+		cfg80211_connect_result(ar->net_dev, bssid,
+					assoc_req_ie, assoc_req_len,
+					assoc_resp_ie, assoc_resp_len,
+					WLAN_STATUS_SUCCESS, GFP_KERNEL);
+	} else if (ar->sme_state == SME_CONNECTED) {
+		/* inform roam event to cfg80211 */
+		cfg80211_roamed(ar->net_dev, ibss_ch, bssid,
+				assoc_req_ie, assoc_req_len,
+				assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
+	}
+}
+
+static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
+				      struct net_device *dev, u16 reason_code)
+{
+	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
+		   reason_code);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
+		ath6kl_err("busy, destroy in progress\n");
+		return -EBUSY;
+	}
+
+	if (down_interruptible(&ar->sem)) {
+		ath6kl_err("busy, couldn't get access\n");
+		return -ERESTARTSYS;
+	}
+
+	ar->reconnect_flag = 0;
+	ath6kl_disconnect(ar);
+	memset(ar->ssid, 0, sizeof(ar->ssid));
+	ar->ssid_len = 0;
+
+	if (!test_bit(SKIP_SCAN, &ar->flag))
+		memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+
+	up(&ar->sem);
+
+	return 0;
+}
+
+void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
+				      u8 *bssid, u8 assoc_resp_len,
+				      u8 *assoc_info, u16 proto_reason)
+{
+	struct ath6kl_key *key = NULL;
+	u16 status;
+
+	if (ar->scan_req) {
+		cfg80211_scan_done(ar->scan_req, true);
+		ar->scan_req = NULL;
+	}
+
+	if (ar->nw_type & ADHOC_NETWORK) {
+		if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
+			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+				   "%s: ath6k not in ibss mode\n", __func__);
+			return;
+		}
+		memset(bssid, 0, ETH_ALEN);
+		cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
+		return;
+	}
+
+	if (ar->nw_type & INFRA_NETWORK) {
+		if (ar->wdev->iftype != NL80211_IFTYPE_STATION) {
+			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+				   "%s: ath6k not in station mode\n", __func__);
+			return;
+		}
+	}
+
+	if (!test_bit(CONNECT_PEND, &ar->flag)) {
+		if (reason != DISCONNECT_CMD)
+			ath6kl_wmi_disconnect_cmd(ar->wmi);
+
+		return;
+	}
+
+	if (reason == NO_NETWORK_AVAIL) {
+		/* connect cmd failed */
+		ath6kl_wmi_disconnect_cmd(ar->wmi);
+		return;
+	}
+
+	if (reason != DISCONNECT_CMD)
+		return;
+
+	if (!ar->auto_auth_stage) {
+		clear_bit(CONNECT_PEND, &ar->flag);
+
+		if (ar->sme_state == SME_CONNECTING) {
+			cfg80211_connect_result(ar->net_dev,
+						bssid, NULL, 0,
+						NULL, 0,
+						WLAN_STATUS_UNSPECIFIED_FAILURE,
+						GFP_KERNEL);
+		} else {
+			cfg80211_disconnected(ar->net_dev, reason,
+					      NULL, 0, GFP_KERNEL);
+		}
+
+		ar->sme_state = SME_DISCONNECTED;
+		return;
+	}
+
+	if (ar->dot11_auth_mode != OPEN_AUTH)
+		return;
+
+	/*
+	 * If the current auth algorithm is open, try shared and
+	 * make autoAuthStage idle. We do not make it leap for now
+	 * being.
+	 */
+	key = &ar->keys[ar->def_txkey_index];
+	if (down_interruptible(&ar->sem)) {
+		ath6kl_err("busy, couldn't get access\n");
+		return;
+	}
+
+	ar->dot11_auth_mode = SHARED_AUTH;
+	ar->auto_auth_stage = AUTH_IDLE;
+
+	ath6kl_wmi_addkey_cmd(ar->wmi,
+			      ar->def_txkey_index,
+			      ar->prwise_crypto,
+			      GROUP_USAGE | TX_USAGE,
+			      key->key_len, NULL,
+			      key->key,
+			      KEY_OP_INIT_VAL, NULL,
+			      NO_SYNC_WMIFLAG);
+
+	status = ath6kl_wmi_connect_cmd(ar->wmi,
+					ar->nw_type,
+					ar->dot11_auth_mode,
+					ar->auth_mode,
+					ar->prwise_crypto,
+					ar->prwise_crypto_len,
+					ar->grp_crypto,
+					ar->grp_crpto_len,
+					ar->ssid_len,
+					ar->ssid,
+					ar->req_bssid,
+					ar->ch_hint,
+					ar->connect_ctrl_flags);
+	up(&ar->sem);
+}
+
+static inline bool is_ch_11a(u16 ch)
+{
+	return (!((ch >= 2412) && (ch <= 2484)));
+}
+
+/* struct ath6kl_node_table::nt_nodelock is locked when calling this */
+void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni)
+{
+	u16 size;
+	unsigned char *ieeemgmtbuf = NULL;
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_channel *channel;
+	struct ieee80211_supported_band *band;
+	struct ath6kl_common_ie *cie;
+	s32 signal;
+	int freq;
+
+	cie = &ni->ni_cie;
+
+	if (is_ch_11a(cie->ie_chan))
+		band = wiphy->bands[IEEE80211_BAND_5GHZ]; /* 11a */
+	else if ((cie->ie_erp) || (cie->ie_xrates))
+		band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11g */
+	else
+		band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11b */
+
+	size = ni->ni_framelen + offsetof(struct ieee80211_mgmt, u);
+	ieeemgmtbuf = kmalloc(size, GFP_ATOMIC);
+	if (!ieeemgmtbuf) {
+		ath6kl_err("ieee mgmt buf alloc error\n");
+		return;
+	}
+
+	/*
+	 * TODO: Update target to include 802.11 mac header while sending
+	 * bss info. Target removes 802.11 mac header while sending the bss
+	 * info to host, cfg80211 needs it, for time being just filling the
+	 * da, sa and bssid fields alone.
+	 */
+	mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf;
+	memset(mgmt->da, 0xff, ETH_ALEN);	/*broadcast addr */
+	memcpy(mgmt->sa, ni->ni_macaddr, ETH_ALEN);
+	memcpy(mgmt->bssid, ni->ni_macaddr, ETH_ALEN);
+	memcpy(ieeemgmtbuf + offsetof(struct ieee80211_mgmt, u),
+	       ni->ni_buf, ni->ni_framelen);
+
+	freq = cie->ie_chan;
+	channel = ieee80211_get_channel(wiphy, freq);
+	signal = ni->ni_snr * 100;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+		   "%s: bssid %pM ch %d freq %d size %d\n", __func__,
+		   mgmt->bssid, channel->hw_value, freq, size);
+	cfg80211_inform_bss_frame(wiphy, channel, mgmt,
+				  size, signal, GFP_ATOMIC);
+
+	kfree(ieeemgmtbuf);
+}
+
+static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+				struct cfg80211_scan_request *request)
+{
+	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+	int ret = 0;
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (!ar->usr_bss_filter) {
+		if (ath6kl_wmi_bssfilter_cmd(ar->wmi,
+					     (test_bit(CONNECTED, &ar->flag) ?
+					     ALL_BUT_BSS_FILTER :
+					     ALL_BSS_FILTER), 0) != 0) {
+			ath6kl_err("couldn't set bss filtering\n");
+			return -EIO;
+		}
+	}
+
+	if (request->n_ssids && request->ssids[0].ssid_len) {
+		u8 i;
+
+		if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
+			request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
+
+		for (i = 0; i < request->n_ssids; i++)
+			ath6kl_wmi_probedssid_cmd(ar->wmi, i + 1,
+						  SPECIFIC_SSID_FLAG,
+						  request->ssids[i].ssid_len,
+						  request->ssids[i].ssid);
+	}
+
+	if (ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0,
+				     false, 0, 0, 0, NULL) != 0) {
+		ath6kl_err("wmi_startscan_cmd failed\n");
+		ret = -EIO;
+	}
+
+	ar->scan_req = request;
+
+	return ret;
+}
+
+void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status)
+{
+	int i;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status %d\n", __func__, status);
+
+	if (!ar->scan_req)
+		return;
+
+	if ((status == -ECANCELED) || (status == -EBUSY)) {
+		cfg80211_scan_done(ar->scan_req, true);
+		goto out;
+	}
+
+	/* Translate data to cfg80211 mgmt format */
+	wlan_iterate_nodes(&ar->scan_table, ar->wdev->wiphy);
+
+	cfg80211_scan_done(ar->scan_req, false);
+
+	if (ar->scan_req->n_ssids && ar->scan_req->ssids[0].ssid_len) {
+		for (i = 0; i < ar->scan_req->n_ssids; i++) {
+			ath6kl_wmi_probedssid_cmd(ar->wmi, i + 1,
+						  DISABLE_SSID_FLAG,
+						  0, NULL);
+		}
+	}
+
+out:
+	ar->scan_req = NULL;
+}
+
+static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+				   u8 key_index, bool pairwise,
+				   const u8 *mac_addr,
+				   struct key_params *params)
+{
+	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+	struct ath6kl_key *key = NULL;
+	u8 key_usage;
+	u8 key_type;
+	int status = 0;
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+			   "%s: key index %d out of bounds\n", __func__,
+			   key_index);
+		return -ENOENT;
+	}
+
+	key = &ar->keys[key_index];
+	memset(key, 0, sizeof(struct ath6kl_key));
+
+	if (pairwise)
+		key_usage = PAIRWISE_USAGE;
+	else
+		key_usage = GROUP_USAGE;
+
+	if (params) {
+		if (params->key_len > WLAN_MAX_KEY_LEN ||
+		    params->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;
+		memcpy(key->seq, params->seq, key->seq_len);
+		key->cipher = params->cipher;
+	}
+
+	switch (key->cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		key_type = WEP_CRYPT;
+		break;
+
+	case WLAN_CIPHER_SUITE_TKIP:
+		key_type = TKIP_CRYPT;
+		break;
+
+	case WLAN_CIPHER_SUITE_CCMP:
+		key_type = AES_CRYPT;
+		break;
+
+	default:
+		return -ENOTSUPP;
+	}
+
+	if (((ar->auth_mode == WPA_PSK_AUTH)
+	     || (ar->auth_mode == WPA2_PSK_AUTH))
+	    && (key_usage & GROUP_USAGE))
+		del_timer(&ar->disconnect_timer);
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+		   "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
+		   __func__, key_index, key->key_len, key_type,
+		   key_usage, key->seq_len);
+
+	ar->def_txkey_index = key_index;
+	status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
+				       key_type, key_usage, key->key_len,
+				       key->seq, key->key, KEY_OP_INIT_VAL,
+				       (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
+
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+				   u8 key_index, bool pairwise,
+				   const u8 *mac_addr)
+{
+	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+			   "%s: key index %d out of bounds\n", __func__,
+			   key_index);
+		return -ENOENT;
+	}
+
+	if (!ar->keys[key_index].key_len) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+			   "%s: index %d is empty\n", __func__, key_index);
+		return 0;
+	}
+
+	ar->keys[key_index].key_len = 0;
+
+	return ath6kl_wmi_deletekey_cmd(ar->wmi, key_index);
+}
+
+static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
+				   u8 key_index, bool pairwise,
+				   const u8 *mac_addr, void *cookie,
+				   void (*callback) (void *cookie,
+						     struct key_params *))
+{
+	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+	struct ath6kl_key *key = NULL;
+	struct key_params params;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+			   "%s: key index %d out of bounds\n", __func__,
+			   key_index);
+		return -ENOENT;
+	}
+
+	key = &ar->keys[key_index];
+	memset(&params, 0, sizeof(params));
+	params.cipher = key->cipher;
+	params.key_len = key->key_len;
+	params.seq_len = key->seq_len;
+	params.seq = key->seq;
+	params.key = key->key;
+
+	callback(cookie, &params);
+
+	return key->key_len ? 0 : -ENOENT;
+}
+
+static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
+					   struct net_device *ndev,
+					   u8 key_index, bool unicast,
+					   bool multicast)
+{
+	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+	struct ath6kl_key *key = NULL;
+	int status = 0;
+	u8 key_usage;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+			   "%s: key index %d out of bounds\n",
+			   __func__, key_index);
+		return -ENOENT;
+	}
+
+	if (!ar->keys[key_index].key_len) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
+			   __func__, key_index);
+		return -EINVAL;
+	}
+
+	ar->def_txkey_index = key_index;
+	key = &ar->keys[ar->def_txkey_index];
+	key_usage = GROUP_USAGE;
+	if (ar->prwise_crypto == WEP_CRYPT)
+		key_usage |= TX_USAGE;
+
+	status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
+				       ar->prwise_crypto, key_usage,
+				       key->key_len, key->seq, key->key,
+				       KEY_OP_INIT_VAL, NULL,
+				       SYNC_BOTH_WMIFLAG);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl *ar, u8 keyid,
+				       bool ismcast)
+{
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+		   "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
+
+	cfg80211_michael_mic_failure(ar->net_dev, ar->bssid,
+				     (ismcast ? NL80211_KEYTYPE_GROUP :
+				      NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
+				     GFP_KERNEL);
+}
+
+static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
+	int ret;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
+		   changed);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+		ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
+		if (ret != 0) {
+			ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * The type nl80211_tx_power_setting replaces the following
+ * data type from 2.6.36 onwards
+*/
+static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
+				       enum nl80211_tx_power_setting type,
+				       int dbm)
+{
+	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
+	u8 ath6kl_dbm;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
+		   type, dbm);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	switch (type) {
+	case NL80211_TX_POWER_AUTOMATIC:
+		return 0;
+	case NL80211_TX_POWER_LIMITED:
+		ar->tx_pwr = ath6kl_dbm = dbm;
+		break;
+	default:
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
+			   __func__, type);
+		return -EOPNOTSUPP;
+	}
+
+	ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, ath6kl_dbm);
+
+	return 0;
+}
+
+static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
+{
+	struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (test_bit(CONNECTED, &ar->flag)) {
+		ar->tx_pwr = 0;
+
+		if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi) != 0) {
+			ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
+			return -EIO;
+		}
+
+		wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
+						 5 * HZ);
+
+		if (signal_pending(current)) {
+			ath6kl_err("target did not respond\n");
+			return -EINTR;
+		}
+	}
+
+	*dbm = ar->tx_pwr;
+	return 0;
+}
+
+static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+					  struct net_device *dev,
+					  bool pmgmt, int timeout)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	struct wmi_power_mode_cmd mode;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
+		   __func__, pmgmt, timeout);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	if (pmgmt) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
+		mode.pwr_mode = REC_POWER;
+	} else {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
+		mode.pwr_mode = MAX_PERF_POWER;
+	}
+
+	if (ath6kl_wmi_powermode_cmd(ar->wmi, mode.pwr_mode) != 0) {
+		ath6kl_err("wmi_powermode_cmd failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
+					struct net_device *ndev,
+					enum nl80211_iftype type, u32 *flags,
+					struct vif_params *params)
+{
+	struct ath6kl *ar = ath6kl_priv(ndev);
+	struct wireless_dev *wdev = ar->wdev;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	switch (type) {
+	case NL80211_IFTYPE_STATION:
+		ar->next_mode = INFRA_NETWORK;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		ar->next_mode = ADHOC_NETWORK;
+		break;
+	default:
+		ath6kl_err("invalid interface type %u\n", type);
+		return -EOPNOTSUPP;
+	}
+
+	wdev->iftype = type;
+
+	return 0;
+}
+
+static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
+				     struct net_device *dev,
+				     struct cfg80211_ibss_params *ibss_param)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	int status;
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	ar->ssid_len = ibss_param->ssid_len;
+	memcpy(ar->ssid, ibss_param->ssid, ar->ssid_len);
+
+	if (ibss_param->channel)
+		ar->ch_hint = ibss_param->channel->center_freq;
+
+	if (ibss_param->channel_fixed) {
+		/*
+		 * TODO: channel_fixed: The channel should be fixed, do not
+		 * search for IBSSs to join on other channels. Target
+		 * firmware does not support this feature, needs to be
+		 * updated.
+		 */
+		return -EOPNOTSUPP;
+	}
+
+	memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+	if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
+		memcpy(ar->req_bssid, ibss_param->bssid, sizeof(ar->req_bssid));
+
+	ath6kl_set_wpa_version(ar, 0);
+
+	status = ath6kl_set_auth_type(ar, NL80211_AUTHTYPE_OPEN_SYSTEM);
+	if (status)
+		return status;
+
+	if (ibss_param->privacy) {
+		ath6kl_set_cipher(ar, WLAN_CIPHER_SUITE_WEP40, true);
+		ath6kl_set_cipher(ar, WLAN_CIPHER_SUITE_WEP40, false);
+	} else {
+		ath6kl_set_cipher(ar, 0, true);
+		ath6kl_set_cipher(ar, 0, false);
+	}
+
+	ar->nw_type = ar->next_mode;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+		   "%s: connect called with authmode %d dot11 auth %d"
+		   " PW crypto %d PW crypto len %d GRP crypto %d"
+		   " GRP crypto len %d channel hint %u\n",
+		   __func__,
+		   ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
+		   ar->prwise_crypto_len, ar->grp_crypto,
+		   ar->grp_crpto_len, ar->ch_hint);
+
+	status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
+					ar->dot11_auth_mode, ar->auth_mode,
+					ar->prwise_crypto,
+					ar->prwise_crypto_len,
+					ar->grp_crypto, ar->grp_crpto_len,
+					ar->ssid_len, ar->ssid,
+					ar->req_bssid, ar->ch_hint,
+					ar->connect_ctrl_flags);
+	set_bit(CONNECT_PEND, &ar->flag);
+
+	return 0;
+}
+
+static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
+				      struct net_device *dev)
+{
+	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
+
+	if (!ath6kl_cfg80211_ready(ar))
+		return -EIO;
+
+	ath6kl_disconnect(ar);
+	memset(ar->ssid, 0, sizeof(ar->ssid));
+	ar->ssid_len = 0;
+
+	return 0;
+}
+
+static const u32 cipher_suites[] = {
+	WLAN_CIPHER_SUITE_WEP40,
+	WLAN_CIPHER_SUITE_WEP104,
+	WLAN_CIPHER_SUITE_TKIP,
+	WLAN_CIPHER_SUITE_CCMP,
+};
+
+static bool is_rate_legacy(s32 rate)
+{
+	static const s32 legacy[] = { 1000, 2000, 5500, 11000,
+		6000, 9000, 12000, 18000, 24000,
+		36000, 48000, 54000
+	};
+	u8 i;
+
+	for (i = 0; i < ARRAY_SIZE(legacy); i++)
+		if (rate == legacy[i])
+			return true;
+
+	return false;
+}
+
+static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
+{
+	static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
+		52000, 58500, 65000, 72200
+	};
+	u8 i;
+
+	for (i = 0; i < ARRAY_SIZE(ht20); i++) {
+		if (rate == ht20[i]) {
+			if (i == ARRAY_SIZE(ht20) - 1)
+				/* last rate uses sgi */
+				*sgi = true;
+			else
+				*sgi = false;
+
+			*mcs = i;
+			return true;
+		}
+	}
+	return false;
+}
+
+static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
+{
+	static const s32 ht40[] = { 13500, 27000, 40500, 54000,
+		81000, 108000, 121500, 135000,
+		150000
+	};
+	u8 i;
+
+	for (i = 0; i < ARRAY_SIZE(ht40); i++) {
+		if (rate == ht40[i]) {
+			if (i == ARRAY_SIZE(ht40) - 1)
+				/* last rate uses sgi */
+				*sgi = true;
+			else
+				*sgi = false;
+
+			*mcs = i;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
+			      u8 *mac, struct station_info *sinfo)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	long left;
+	bool sgi;
+	s32 rate;
+	int ret;
+	u8 mcs;
+
+	if (memcmp(mac, ar->bssid, ETH_ALEN) != 0)
+		return -ENOENT;
+
+	if (down_interruptible(&ar->sem))
+		return -EBUSY;
+
+	set_bit(STATS_UPDATE_PEND, &ar->flag);
+
+	ret = ath6kl_wmi_get_stats_cmd(ar->wmi);
+
+	if (ret != 0) {
+		up(&ar->sem);
+		return -EIO;
+	}
+
+	left = wait_event_interruptible_timeout(ar->event_wq,
+						!test_bit(STATS_UPDATE_PEND,
+							  &ar->flag),
+						WMI_TIMEOUT);
+
+	up(&ar->sem);
+
+	if (left == 0)
+		return -ETIMEDOUT;
+	else if (left < 0)
+		return left;
+
+	if (ar->target_stats.rx_byte) {
+		sinfo->rx_bytes = ar->target_stats.rx_byte;
+		sinfo->filled |= STATION_INFO_RX_BYTES;
+		sinfo->rx_packets = ar->target_stats.rx_pkt;
+		sinfo->filled |= STATION_INFO_RX_PACKETS;
+	}
+
+	if (ar->target_stats.tx_byte) {
+		sinfo->tx_bytes = ar->target_stats.tx_byte;
+		sinfo->filled |= STATION_INFO_TX_BYTES;
+		sinfo->tx_packets = ar->target_stats.tx_pkt;
+		sinfo->filled |= STATION_INFO_TX_PACKETS;
+	}
+
+	sinfo->signal = ar->target_stats.cs_rssi;
+	sinfo->filled |= STATION_INFO_SIGNAL;
+
+	rate = ar->target_stats.tx_ucast_rate;
+
+	if (is_rate_legacy(rate)) {
+		sinfo->txrate.legacy = rate / 100;
+	} else if (is_rate_ht20(rate, &mcs, &sgi)) {
+		if (sgi) {
+			sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+			sinfo->txrate.mcs = mcs - 1;
+		} else {
+			sinfo->txrate.mcs = mcs;
+		}
+
+		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+	} else if (is_rate_ht40(rate, &mcs, &sgi)) {
+		if (sgi) {
+			sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+			sinfo->txrate.mcs = mcs - 1;
+		} else {
+			sinfo->txrate.mcs = mcs;
+		}
+
+		sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+	} else {
+		ath6kl_warn("invalid rate: %d\n", rate);
+		return 0;
+	}
+
+	sinfo->filled |= STATION_INFO_TX_BITRATE;
+
+	return 0;
+}
+
+static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+			    struct cfg80211_pmksa *pmksa)
+{
+	struct ath6kl *ar = ath6kl_priv(netdev);
+	return ath6kl_wmi_setpmkid_cmd(ar->wmi, pmksa->bssid,
+				       pmksa->pmkid, true);
+}
+
+static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+			    struct cfg80211_pmksa *pmksa)
+{
+	struct ath6kl *ar = ath6kl_priv(netdev);
+	return ath6kl_wmi_setpmkid_cmd(ar->wmi, pmksa->bssid,
+				       pmksa->pmkid, false);
+}
+
+static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
+{
+	struct ath6kl *ar = ath6kl_priv(netdev);
+	if (test_bit(CONNECTED, &ar->flag))
+		return ath6kl_wmi_setpmkid_cmd(ar->wmi, ar->bssid, NULL, false);
+	return 0;
+}
+
+static struct cfg80211_ops ath6kl_cfg80211_ops = {
+	.change_virtual_intf = ath6kl_cfg80211_change_iface,
+	.scan = ath6kl_cfg80211_scan,
+	.connect = ath6kl_cfg80211_connect,
+	.disconnect = ath6kl_cfg80211_disconnect,
+	.add_key = ath6kl_cfg80211_add_key,
+	.get_key = ath6kl_cfg80211_get_key,
+	.del_key = ath6kl_cfg80211_del_key,
+	.set_default_key = ath6kl_cfg80211_set_default_key,
+	.set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
+	.set_tx_power = ath6kl_cfg80211_set_txpower,
+	.get_tx_power = ath6kl_cfg80211_get_txpower,
+	.set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
+	.join_ibss = ath6kl_cfg80211_join_ibss,
+	.leave_ibss = ath6kl_cfg80211_leave_ibss,
+	.get_station = ath6kl_get_station,
+	.set_pmksa = ath6kl_set_pmksa,
+	.del_pmksa = ath6kl_del_pmksa,
+	.flush_pmksa = ath6kl_flush_pmksa,
+};
+
+struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
+{
+	int ret = 0;
+	struct wireless_dev *wdev;
+
+	wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+	if (!wdev) {
+		ath6kl_err("couldn't allocate wireless device\n");
+		return NULL;
+	}
+
+	/* create a new wiphy for use with cfg80211 */
+	wdev->wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
+	if (!wdev->wiphy) {
+		ath6kl_err("couldn't allocate wiphy device\n");
+		kfree(wdev);
+		return NULL;
+	}
+
+	/* set device pointer for wiphy */
+	set_wiphy_dev(wdev->wiphy, dev);
+
+	wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+	    BIT(NL80211_IFTYPE_ADHOC);
+	/* max num of ssids that can be probed during scanning */
+	wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
+	wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
+	wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
+	wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+	wdev->wiphy->cipher_suites = cipher_suites;
+	wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+	ret = wiphy_register(wdev->wiphy);
+	if (ret < 0) {
+		ath6kl_err("couldn't register wiphy device\n");
+		wiphy_free(wdev->wiphy);
+		kfree(wdev);
+		return NULL;
+	}
+
+	return wdev;
+}
+
+void ath6kl_cfg80211_deinit(struct ath6kl *ar)
+{
+	struct wireless_dev *wdev = ar->wdev;
+
+	if (ar->scan_req) {
+		cfg80211_scan_done(ar->scan_req, true);
+		ar->scan_req = NULL;
+	}
+
+	if (!wdev)
+		return;
+
+	wiphy_unregister(wdev->wiphy);
+	wiphy_free(wdev->wiphy);
+	kfree(wdev);
+}

+ 39 - 0
drivers/net/wireless/ath/ath6kl/cfg80211.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef ATH6KL_CFG80211_H
+#define ATH6KL_CFG80211_H
+
+struct wireless_dev *ath6kl_cfg80211_init(struct device *dev);
+void ath6kl_cfg80211_deinit(struct ath6kl *ar);
+
+void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status);
+
+void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
+				   u8 *bssid, u16 listen_intvl,
+				   u16 beacon_intvl,
+				   enum network_type nw_type,
+				   u8 beacon_ie_len, u8 assoc_req_len,
+				   u8 assoc_resp_len, u8 *assoc_info);
+
+void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
+				      u8 *bssid, u8 assoc_resp_len,
+				      u8 *assoc_info, u16 proto_reason);
+
+void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl *ar, u8 keyid,
+				     bool ismcast);
+
+#endif /* ATH6KL_CFG80211_H */

+ 180 - 0
drivers/net/wireless/ath/ath6kl/common.h

@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2010-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.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <linux/netdevice.h>
+
+#define ATH6KL_MAX_IE			256
+
+extern int ath6kl_printk(const char *level, const char *fmt, ...);
+
+#define A_CACHE_LINE_PAD            128
+
+/*
+ * Reflects the version of binary interface exposed by ATH6KL target
+ * firmware. Needs to be incremented by 1 for any change in the firmware
+ * that requires upgrade of the driver on the host side for the change to
+ * work correctly
+ */
+#define ATH6KL_ABI_VERSION        1
+
+#define SIGNAL_QUALITY_METRICS_NUM_MAX    2
+
+enum {
+	SIGNAL_QUALITY_METRICS_SNR = 0,
+	SIGNAL_QUALITY_METRICS_RSSI,
+	SIGNAL_QUALITY_METRICS_ALL,
+};
+
+/*
+ * Data Path
+ */
+
+#define WMI_MAX_TX_DATA_FRAME_LENGTH	      \
+	(1500 + sizeof(struct wmi_data_hdr) + \
+	 sizeof(struct ethhdr) +      \
+	 sizeof(struct ath6kl_llc_snap_hdr))
+
+/* An AMSDU frame */ /* The MAX AMSDU length of AR6003 is 3839 */
+#define WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH    \
+	(3840 + sizeof(struct wmi_data_hdr) + \
+	 sizeof(struct ethhdr) +      \
+	 sizeof(struct ath6kl_llc_snap_hdr))
+
+#define EPPING_ALIGNMENT_PAD			       \
+	(((sizeof(struct htc_frame_hdr) + 3) & (~0x3)) \
+	 - sizeof(struct htc_frame_hdr))
+
+struct ath6kl_llc_snap_hdr {
+	u8 dsap;
+	u8 ssap;
+	u8 cntl;
+	u8 org_code[3];
+	__be16 eth_type;
+} __packed;
+
+enum crypto_type {
+	NONE_CRYPT          = 0x01,
+	WEP_CRYPT           = 0x02,
+	TKIP_CRYPT          = 0x04,
+	AES_CRYPT           = 0x08,
+};
+
+#define ATH6KL_NODE_HASHSIZE 32
+/* simple hash is enough for variation of macaddr */
+#define ATH6KL_NODE_HASH(addr)   \
+	(((const u8 *)(addr))[ETH_ALEN - 1] % \
+	 ATH6KL_NODE_HASHSIZE)
+
+/*
+ * Table of ath6kl_node instances.  Each ieee80211com
+ * has at least one for holding the scan candidates.
+ * When operating as an access point or in ibss mode there
+ * is a second table for associated stations or neighbors.
+ */
+struct ath6kl_node_table {
+	spinlock_t nt_nodelock;	/* on node table */
+	struct bss *nt_node_first;	/* information of all nodes */
+	struct bss *nt_node_last;	/* information of all nodes */
+	struct bss *nt_hash[ATH6KL_NODE_HASHSIZE];
+	const char *nt_name;	/* for debugging */
+	u32 nt_node_age;		/* node aging time */
+};
+
+#define WLAN_NODE_INACT_TIMEOUT_MSEC    120000
+#define WLAN_NODE_INACT_CNT		4
+
+struct ath6kl_common_ie {
+	u16 ie_chan;
+	u8 *ie_tstamp;
+	u8 *ie_ssid;
+	u8 *ie_rates;
+	u8 *ie_xrates;
+	u8 *ie_country;
+	u8 *ie_wpa;
+	u8 *ie_rsn;
+	u8 *ie_wmm;
+	u8 *ie_ath;
+	u16 ie_capInfo;
+	u16 ie_beaconInt;
+	u8 *ie_tim;
+	u8 *ie_chswitch;
+	u8 ie_erp;
+	u8 *ie_wsc;
+	u8 *ie_htcap;
+	u8 *ie_htop;
+};
+
+struct bss {
+	u8 ni_macaddr[ETH_ALEN];
+	u8 ni_snr;
+	s16 ni_rssi;
+	struct bss *ni_list_next;
+	struct bss *ni_list_prev;
+	struct bss *ni_hash_next;
+	struct bss *ni_hash_prev;
+	struct ath6kl_common_ie ni_cie;
+	u8 *ni_buf;
+	u16 ni_framelen;
+	struct ath6kl_node_table *ni_table;
+	u32 ni_refcnt;
+
+	u32 ni_tstamp;
+	u32 ni_actcnt;
+};
+
+struct htc_endpoint_credit_dist;
+struct ath6kl;
+enum htc_credit_dist_reason;
+struct htc_credit_state_info;
+
+struct bss *wlan_node_alloc(int wh_size);
+void wlan_node_free(struct bss *ni);
+void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni,
+		     const u8 *mac_addr);
+struct bss *wlan_find_node(struct ath6kl_node_table *nt,
+			   const u8 *mac_addr);
+void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni);
+void wlan_free_allnodes(struct ath6kl_node_table *nt);
+void wlan_iterate_nodes(struct ath6kl_node_table *nt, void *arg);
+
+void wlan_node_table_init(struct ath6kl_node_table *nt);
+void wlan_node_table_cleanup(struct ath6kl_node_table *nt);
+
+void wlan_refresh_inactive_nodes(struct ath6kl *ar);
+
+struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 *ssid,
+				  u32 ssid_len, bool is_wpa2, bool match_ssid);
+
+void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni);
+
+int ath6k_setup_credit_dist(void *htc_handle,
+			    struct htc_credit_state_info *cred_info);
+void ath6k_credit_distribute(struct htc_credit_state_info *cred_inf,
+			     struct list_head *epdist_list,
+			     enum htc_credit_dist_reason reason);
+void ath6k_credit_init(struct htc_credit_state_info *cred_inf,
+		       struct list_head *ep_list,
+		       int tot_credits);
+void ath6k_seek_credits(struct htc_credit_state_info *cred_inf,
+			struct htc_endpoint_credit_dist *ep_dist);
+struct ath6kl *ath6kl_core_alloc(struct device *sdev);
+int ath6kl_core_init(struct ath6kl *ar);
+int ath6kl_unavail_ev(struct ath6kl *ar);
+struct sk_buff *ath6kl_buf_alloc(int size);
+#endif /* COMMON_H */

+ 544 - 0
drivers/net/wireless/ath/ath6kl/core.h

@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2010-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.
+ */
+
+#ifndef CORE_H
+#define CORE_H
+
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <net/cfg80211.h>
+#include "htc.h"
+#include "wmi.h"
+#include "bmi.h"
+
+#define MAX_ATH6KL                        1
+#define ATH6KL_MAX_RX_BUFFERS             16
+#define ATH6KL_BUFFER_SIZE                1664
+#define ATH6KL_MAX_AMSDU_RX_BUFFERS       4
+#define ATH6KL_AMSDU_REFILL_THRESHOLD     3
+#define ATH6KL_AMSDU_BUFFER_SIZE     (WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH + 128)
+#define MAX_MSDU_SUBFRAME_PAYLOAD_LEN	1508
+#define MIN_MSDU_SUBFRAME_PAYLOAD_LEN	46
+
+#define USER_SAVEDKEYS_STAT_INIT     0
+#define USER_SAVEDKEYS_STAT_RUN      1
+
+#define ATH6KL_TX_TIMEOUT      10
+#define ATH6KL_MAX_ENDPOINTS   4
+#define MAX_NODE_NUM           15
+
+/* MAX_HI_COOKIE_NUM are reserved for high priority traffic */
+#define MAX_DEF_COOKIE_NUM                180
+#define MAX_HI_COOKIE_NUM                 18	/* 10% of MAX_COOKIE_NUM */
+#define MAX_COOKIE_NUM                 (MAX_DEF_COOKIE_NUM + MAX_HI_COOKIE_NUM)
+
+#define MAX_DEFAULT_SEND_QUEUE_DEPTH      (MAX_DEF_COOKIE_NUM / WMM_NUM_AC)
+
+#define DISCON_TIMER_INTVAL               10000  /* in msec */
+#define A_DEFAULT_LISTEN_INTERVAL         100
+#define A_MAX_WOW_LISTEN_INTERVAL         1000
+
+/* AR6003 1.0 definitions */
+#define AR6003_REV1_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_PATCH_FILE              "ath6k/AR6003/hw2.0/data.patch.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"
+
+/* 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_PATCH_FILE            "ath6k/AR6003/hw2.1.1/data.patch.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"
+
+/* Per STA data, used in AP mode */
+#define STA_PS_AWAKE		BIT(0)
+#define	STA_PS_SLEEP		BIT(1)
+#define	STA_PS_POLLED		BIT(2)
+
+/* HTC TX packet tagging definitions */
+#define ATH6KL_CONTROL_PKT_TAG    HTC_TX_PACKET_TAG_USER_DEFINED
+#define ATH6KL_DATA_PKT_TAG       (ATH6KL_CONTROL_PKT_TAG + 1)
+
+#define AR6003_CUST_DATA_SIZE 16
+
+#define AGGR_WIN_IDX(x, y)          ((x) % (y))
+#define AGGR_INCR_IDX(x, y)         AGGR_WIN_IDX(((x) + 1), (y))
+#define AGGR_DCRM_IDX(x, y)         AGGR_WIN_IDX(((x) - 1), (y))
+#define ATH6KL_MAX_SEQ_NO		0xFFF
+#define ATH6KL_NEXT_SEQ_NO(x)		(((x) + 1) & ATH6KL_MAX_SEQ_NO)
+
+#define NUM_OF_TIDS         8
+#define AGGR_SZ_DEFAULT     8
+
+#define AGGR_WIN_SZ_MIN     2
+#define AGGR_WIN_SZ_MAX     8
+
+#define TID_WINDOW_SZ(_x)   ((_x) << 1)
+
+#define AGGR_NUM_OF_FREE_NETBUFS    16
+
+#define AGGR_RX_TIMEOUT     400	/* in ms */
+
+#define WMI_TIMEOUT (2 * HZ)
+
+#define MBOX_YIELD_LIMIT 99
+
+/* configuration lags */
+/*
+ * ATH6KL_CONF_IGNORE_ERP_BARKER: Ignore the barker premable in
+ * ERP IE of beacon to determine the short premable support when
+ * sending (Re)Assoc req.
+ * ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN: Don't send the power
+ * module state transition failure events which happen during
+ * scan, to the host.
+ */
+#define ATH6KL_CONF_IGNORE_ERP_BARKER		BIT(0)
+#define ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN  BIT(1)
+#define ATH6KL_CONF_ENABLE_11N			BIT(2)
+#define ATH6KL_CONF_ENABLE_TX_BURST		BIT(3)
+
+enum wlan_low_pwr_state {
+	WLAN_POWER_STATE_ON,
+	WLAN_POWER_STATE_CUT_PWR,
+	WLAN_POWER_STATE_DEEP_SLEEP,
+	WLAN_POWER_STATE_WOW
+};
+
+enum sme_state {
+	SME_DISCONNECTED,
+	SME_CONNECTING,
+	SME_CONNECTED
+};
+
+struct skb_hold_q {
+	struct sk_buff *skb;
+	bool is_amsdu;
+	u16 seq_no;
+};
+
+struct rxtid {
+	bool aggr;
+	bool progress;
+	bool timer_mon;
+	u16 win_sz;
+	u16 seq_next;
+	u32 hold_q_sz;
+	struct skb_hold_q *hold_q;
+	struct sk_buff_head q;
+	spinlock_t lock;
+};
+
+struct rxtid_stats {
+	u32 num_into_aggr;
+	u32 num_dups;
+	u32 num_oow;
+	u32 num_mpdu;
+	u32 num_amsdu;
+	u32 num_delivered;
+	u32 num_timeouts;
+	u32 num_hole;
+	u32 num_bar;
+};
+
+struct aggr_info {
+	u8 aggr_sz;
+	u8 timer_scheduled;
+	struct timer_list timer;
+	struct net_device *dev;
+	struct rxtid rx_tid[NUM_OF_TIDS];
+	struct sk_buff_head free_q;
+	struct rxtid_stats stat[NUM_OF_TIDS];
+};
+
+struct ath6kl_wep_key {
+	u8 key_index;
+	u8 key_len;
+	u8 key[64];
+};
+
+#define ATH6KL_KEY_SEQ_LEN 8
+
+struct ath6kl_key {
+	u8 key[WLAN_MAX_KEY_LEN];
+	u8 key_len;
+	u8 seq[ATH6KL_KEY_SEQ_LEN];
+	u8 seq_len;
+	u32 cipher;
+};
+
+struct ath6kl_node_mapping {
+	u8 mac_addr[ETH_ALEN];
+	u8 ep_id;
+	u8 tx_pend;
+};
+
+struct ath6kl_cookie {
+	struct sk_buff *skb;
+	u32 map_no;
+	struct htc_packet htc_pkt;
+	struct ath6kl_cookie *arc_list_next;
+};
+
+struct ath6kl_sta {
+	u16 sta_flags;
+	u8 mac[ETH_ALEN];
+	u8 aid;
+	u8 keymgmt;
+	u8 ucipher;
+	u8 auth;
+	u8 wpa_ie[ATH6KL_MAX_IE];
+	struct sk_buff_head psq;
+	spinlock_t psq_lock;
+};
+
+struct ath6kl_version {
+	u32 target_ver;
+	u32 wlan_ver;
+	u32 abi_ver;
+};
+
+struct ath6kl_bmi {
+	u32 cmd_credits;
+	bool done_sent;
+	u8 *cmd_buf;
+};
+
+struct target_stats {
+	u64 tx_pkt;
+	u64 tx_byte;
+	u64 tx_ucast_pkt;
+	u64 tx_ucast_byte;
+	u64 tx_mcast_pkt;
+	u64 tx_mcast_byte;
+	u64 tx_bcast_pkt;
+	u64 tx_bcast_byte;
+	u64 tx_rts_success_cnt;
+	u64 tx_pkt_per_ac[4];
+
+	u64 tx_err;
+	u64 tx_fail_cnt;
+	u64 tx_retry_cnt;
+	u64 tx_mult_retry_cnt;
+	u64 tx_rts_fail_cnt;
+
+	u64 rx_pkt;
+	u64 rx_byte;
+	u64 rx_ucast_pkt;
+	u64 rx_ucast_byte;
+	u64 rx_mcast_pkt;
+	u64 rx_mcast_byte;
+	u64 rx_bcast_pkt;
+	u64 rx_bcast_byte;
+	u64 rx_frgment_pkt;
+
+	u64 rx_err;
+	u64 rx_crc_err;
+	u64 rx_key_cache_miss;
+	u64 rx_decrypt_err;
+	u64 rx_dupl_frame;
+
+	u64 tkip_local_mic_fail;
+	u64 tkip_cnter_measures_invoked;
+	u64 tkip_replays;
+	u64 tkip_fmt_err;
+	u64 ccmp_fmt_err;
+	u64 ccmp_replays;
+
+	u64 pwr_save_fail_cnt;
+
+	u64 cs_bmiss_cnt;
+	u64 cs_low_rssi_cnt;
+	u64 cs_connect_cnt;
+	u64 cs_discon_cnt;
+
+	s32 tx_ucast_rate;
+	s32 rx_ucast_rate;
+
+	u32 lq_val;
+
+	u32 wow_pkt_dropped;
+	u16 wow_evt_discarded;
+
+	s16 noise_floor_calib;
+	s16 cs_rssi;
+	s16 cs_ave_beacon_rssi;
+	u8 cs_ave_beacon_snr;
+	u8 cs_last_roam_msec;
+	u8 cs_snr;
+
+	u8 wow_host_pkt_wakeups;
+	u8 wow_host_evt_wakeups;
+
+	u32 arp_received;
+	u32 arp_matched;
+	u32 arp_replied;
+};
+
+struct ath6kl_mbox_info {
+	u32 htc_addr;
+	u32 htc_ext_addr;
+	u32 htc_ext_sz;
+
+	u32 block_size;
+
+	u32 gmbox_addr;
+
+	u32 gmbox_sz;
+};
+
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP.  For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+
+#define ATH6KL_KEYBUF_SIZE 16
+#define ATH6KL_MICBUF_SIZE (8+8)	/* space for both tx and rx */
+
+#define ATH6KL_KEY_XMIT  0x01
+#define ATH6KL_KEY_RECV  0x02
+#define ATH6KL_KEY_DEFAULT   0x80	/* default xmit key */
+
+/*
+ * WPA/RSN get/set key request.  Specify the key/cipher
+ * type and whether the key is to be used for sending and/or
+ * receiving.  The key index should be set only when working
+ * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
+ * Otherwise a unicast/pairwise key is specified by the bssid
+ * (on a station) or mac address (on an ap).  They key length
+ * must include any MIC key data; otherwise it should be no
+ * more than ATH6KL_KEYBUF_SIZE.
+ */
+struct ath6kl_req_key {
+	u8 ik_type;	/* key/cipher type */
+	u8 ik_pad;
+	u16 ik_keyix;	/* key index */
+	u8 ik_keylen;	/* key length in bytes */
+	u8 ik_flags;
+	u8 ik_macaddr[ETH_ALEN];
+	u64 ik_keyrsc;	/* key receive sequence counter */
+	u64 ik_keytsc;	/* key transmit sequence counter */
+	u8 ik_keydata[ATH6KL_KEYBUF_SIZE + ATH6KL_MICBUF_SIZE];
+};
+
+/* Flag info */
+#define WMI_ENABLED	0
+#define WMI_READY	1
+#define CONNECTED	2
+#define STATS_UPDATE_PEND 3
+#define CONNECT_PEND	  4
+#define WMM_ENABLED	  5
+#define NETQ_STOPPED	  6
+#define WMI_CTRL_EP_FULL  7
+#define DTIM_EXPIRED	  8
+#define DESTROY_IN_PROGRESS  9
+#define NETDEV_REGISTERED    10
+#define SKIP_SCAN	     11
+#define WLAN_ENABLED	     12
+
+struct ath6kl {
+	struct device *dev;
+	struct net_device *net_dev;
+	struct ath6kl_bmi bmi;
+	const struct ath6kl_hif_ops *hif_ops;
+	struct wmi *wmi;
+	int tx_pending[ENDPOINT_MAX];
+	int total_tx_data_pend;
+	struct htc_target *htc_target;
+	void *hif_priv;
+	spinlock_t lock;
+	struct semaphore sem;
+	int ssid_len;
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	u8 next_mode;
+	u8 nw_type;
+	u8 dot11_auth_mode;
+	u8 auth_mode;
+	u8 prwise_crypto;
+	u8 prwise_crypto_len;
+	u8 grp_crypto;
+	u8 grp_crpto_len;
+	u8 def_txkey_index;
+	struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
+	u8 bssid[ETH_ALEN];
+	u8 req_bssid[ETH_ALEN];
+	u16 ch_hint;
+	u16 bss_ch;
+	u16 listen_intvl_b;
+	u16 listen_intvl_t;
+	struct ath6kl_version version;
+	u32 target_type;
+	u8 tx_pwr;
+	struct net_device_stats net_stats;
+	struct target_stats target_stats;
+	struct ath6kl_node_mapping node_map[MAX_NODE_NUM];
+	u8 ibss_ps_enable;
+	u8 node_num;
+	u8 next_ep_id;
+	struct ath6kl_cookie *cookie_list;
+	u32 cookie_count;
+	enum htc_endpoint_id ac2ep_map[WMM_NUM_AC];
+	bool ac_stream_active[WMM_NUM_AC];
+	u8 ac_stream_pri_map[WMM_NUM_AC];
+	u8 hiac_stream_active_pri;
+	u8 ep2ac_map[ENDPOINT_MAX];
+	enum htc_endpoint_id ctrl_ep;
+	struct htc_credit_state_info credit_state_info;
+	u32 connect_ctrl_flags;
+	u32 user_key_ctrl;
+	u8 usr_bss_filter;
+	struct ath6kl_sta sta_list[AP_MAX_NUM_STA];
+	u8 sta_list_index;
+	struct ath6kl_req_key ap_mode_bkey;
+	struct sk_buff_head mcastpsq;
+	spinlock_t mcastpsq_lock;
+	u8 intra_bss;
+	struct aggr_info *aggr_cntxt;
+	struct wmi_ap_mode_stat ap_stats;
+	u8 ap_country_code[3];
+	struct list_head amsdu_rx_buffer_queue;
+	struct timer_list disconnect_timer;
+	u8 rx_meta_ver;
+	struct wireless_dev *wdev;
+	struct cfg80211_scan_request *scan_req;
+	struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1];
+	enum sme_state sme_state;
+	enum wlan_low_pwr_state wlan_pwr_state;
+	struct wmi_scan_params_cmd sc_params;
+#define AR_MCAST_FILTER_MAC_ADDR_SIZE  4
+	u8 auto_auth_stage;
+
+	u16 conf_flags;
+	wait_queue_head_t event_wq;
+	struct ath6kl_mbox_info mbox_info;
+
+	struct ath6kl_cookie cookie_mem[MAX_COOKIE_NUM];
+	int reconnect_flag;
+	unsigned long flag;
+
+	u8 *fw_board;
+	size_t fw_board_len;
+
+	u8 *fw_otp;
+	size_t fw_otp_len;
+
+	u8 *fw;
+	size_t fw_len;
+
+	u8 *fw_patch;
+	size_t fw_patch_len;
+
+	struct workqueue_struct *ath6kl_wq;
+
+	struct ath6kl_node_table scan_table;
+};
+
+static inline void *ath6kl_priv(struct net_device *dev)
+{
+	return wdev_priv(dev->ieee80211_ptr);
+}
+
+static inline void ath6kl_deposit_credit_to_ep(struct htc_credit_state_info
+					       *cred_info,
+					       struct htc_endpoint_credit_dist
+					       *ep_dist, int credits)
+{
+	ep_dist->credits += credits;
+	ep_dist->cred_assngd += credits;
+	cred_info->cur_free_credits -= credits;
+}
+
+void ath6kl_destroy(struct net_device *dev, unsigned int unregister);
+int ath6kl_configure_target(struct ath6kl *ar);
+void ath6kl_detect_error(unsigned long ptr);
+void disconnect_timer_handler(unsigned long ptr);
+void init_netdev(struct net_device *dev);
+void ath6kl_cookie_init(struct ath6kl *ar);
+void ath6kl_cookie_cleanup(struct ath6kl *ar);
+void ath6kl_rx(struct htc_target *target, struct htc_packet *packet);
+void ath6kl_tx_complete(void *context, struct list_head *packet_queue);
+enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
+					       struct htc_packet *packet);
+void ath6kl_stop_txrx(struct ath6kl *ar);
+void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar);
+int ath6kl_access_datadiag(struct ath6kl *ar, u32 address,
+			   u8 *data, u32 length, bool read);
+int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data);
+void ath6kl_init_profile_info(struct ath6kl *ar);
+void ath6kl_tx_data_cleanup(struct ath6kl *ar);
+void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
+			  bool get_dbglogs);
+
+struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar);
+void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie);
+int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev);
+
+struct aggr_info *aggr_init(struct net_device *dev);
+void ath6kl_rx_refill(struct htc_target *target,
+		      enum htc_endpoint_id endpoint);
+void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count);
+struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target,
+					    enum htc_endpoint_id endpoint,
+					    int len);
+void aggr_module_destroy(struct aggr_info *aggr_info);
+void aggr_reset_state(struct aggr_info *aggr_info);
+
+struct ath6kl_sta *ath6kl_find_sta(struct ath6kl *ar, u8 * node_addr);
+struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid);
+
+void ath6kl_ready_event(void *devt, u8 * datap, u32 sw_ver, u32 abi_ver);
+int ath6kl_control_tx(void *devt, struct sk_buff *skb,
+		      enum htc_endpoint_id eid);
+void ath6kl_connect_event(struct ath6kl *ar, u16 channel,
+			  u8 *bssid, u16 listen_int,
+			  u16 beacon_int, enum network_type net_type,
+			  u8 beacon_ie_len, u8 assoc_req_len,
+			  u8 assoc_resp_len, u8 *assoc_info);
+void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason,
+			     u8 *bssid, u8 assoc_resp_len,
+			     u8 *assoc_info, u16 prot_reason_status);
+void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast);
+void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr);
+void ath6kl_scan_complete_evt(struct ath6kl *ar, int status);
+void ath6kl_tgt_stats_event(struct ath6kl *ar, u8 *ptr, u32 len);
+void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active);
+enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac);
+
+void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid);
+
+void ath6kl_dtimexpiry_event(struct ath6kl *ar);
+void ath6kl_disconnect(struct ath6kl *ar);
+void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid);
+void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no,
+			     u8 win_sz);
+void ath6kl_wakeup_event(void *dev);
+void ath6kl_target_failure(struct ath6kl *ar);
+
+void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni);
+#endif /* CORE_H */

+ 150 - 0
drivers/net/wireless/ath/ath6kl/debug.c

@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2004-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 "core.h"
+#include "debug.h"
+
+int ath6kl_printk(const char *level, const char *fmt, ...)
+{
+	struct va_format vaf;
+	va_list args;
+	int rtn;
+
+	va_start(args, fmt);
+
+	vaf.fmt = fmt;
+	vaf.va = &args;
+
+	rtn = printk("%sath6kl: %pV", level, &vaf);
+
+	va_end(args);
+
+	return rtn;
+}
+
+#ifdef CONFIG_ATH6KL_DEBUG
+void ath6kl_dump_registers(struct ath6kl_device *dev,
+			   struct ath6kl_irq_proc_registers *irq_proc_reg,
+			   struct ath6kl_irq_enable_reg *irq_enable_reg)
+{
+
+	ath6kl_dbg(ATH6KL_DBG_ANY, ("<------- Register Table -------->\n"));
+
+	if (irq_proc_reg != NULL) {
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			"Host Int status:           0x%x\n",
+			irq_proc_reg->host_int_status);
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			   "CPU Int status:            0x%x\n",
+			irq_proc_reg->cpu_int_status);
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			   "Error Int status:          0x%x\n",
+			irq_proc_reg->error_int_status);
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			   "Counter Int status:        0x%x\n",
+			irq_proc_reg->counter_int_status);
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			   "Mbox Frame:                0x%x\n",
+			irq_proc_reg->mbox_frame);
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			   "Rx Lookahead Valid:        0x%x\n",
+			irq_proc_reg->rx_lkahd_valid);
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			   "Rx Lookahead 0:            0x%x\n",
+			irq_proc_reg->rx_lkahd[0]);
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			   "Rx Lookahead 1:            0x%x\n",
+			irq_proc_reg->rx_lkahd[1]);
+
+		if (dev->ar->mbox_info.gmbox_addr != 0) {
+			/*
+			 * If the target supports GMBOX hardware, dump some
+			 * additional state.
+			 */
+			ath6kl_dbg(ATH6KL_DBG_ANY,
+				"GMBOX Host Int status 2:   0x%x\n",
+				irq_proc_reg->host_int_status2);
+			ath6kl_dbg(ATH6KL_DBG_ANY,
+				"GMBOX RX Avail:            0x%x\n",
+				irq_proc_reg->gmbox_rx_avail);
+			ath6kl_dbg(ATH6KL_DBG_ANY,
+				"GMBOX lookahead alias 0:   0x%x\n",
+				irq_proc_reg->rx_gmbox_lkahd_alias[0]);
+			ath6kl_dbg(ATH6KL_DBG_ANY,
+				"GMBOX lookahead alias 1:   0x%x\n",
+				irq_proc_reg->rx_gmbox_lkahd_alias[1]);
+		}
+
+	}
+
+	if (irq_enable_reg != NULL) {
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			"Int status Enable:         0x%x\n",
+			irq_enable_reg->int_status_en);
+		ath6kl_dbg(ATH6KL_DBG_ANY, "Counter Int status Enable: 0x%x\n",
+			irq_enable_reg->cntr_int_status_en);
+	}
+	ath6kl_dbg(ATH6KL_DBG_ANY, "<------------------------------->\n");
+}
+
+static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist)
+{
+	ath6kl_dbg(ATH6KL_DBG_ANY,
+		   "--- endpoint: %d  svc_id: 0x%X ---\n",
+		   ep_dist->endpoint, ep_dist->svc_id);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " dist_flags     : 0x%X\n",
+		   ep_dist->dist_flags);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_norm      : %d\n",
+		   ep_dist->cred_norm);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_min       : %d\n",
+		   ep_dist->cred_min);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " credits        : %d\n",
+		   ep_dist->credits);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_assngd    : %d\n",
+		   ep_dist->cred_assngd);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " seek_cred      : %d\n",
+		   ep_dist->seek_cred);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_sz        : %d\n",
+		   ep_dist->cred_sz);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_per_msg   : %d\n",
+		   ep_dist->cred_per_msg);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " cred_to_dist   : %d\n",
+		   ep_dist->cred_to_dist);
+	ath6kl_dbg(ATH6KL_DBG_ANY, " txq_depth      : %d\n",
+		   get_queue_depth(&((struct htc_endpoint *)
+				     ep_dist->htc_rsvd)->txq));
+	ath6kl_dbg(ATH6KL_DBG_ANY,
+		   "----------------------------------\n");
+}
+
+void dump_cred_dist_stats(struct htc_target *target)
+{
+	struct htc_endpoint_credit_dist *ep_list;
+
+	if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_TRC))
+		return;
+
+	list_for_each_entry(ep_list, &target->cred_dist_list, list)
+		dump_cred_dist(ep_list);
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:%p dist:%p\n",
+		   target->cred_dist_cntxt, NULL);
+	ath6kl_dbg(ATH6KL_DBG_TRC, "credit distribution, total : %d, free : %d\n",
+		   target->cred_dist_cntxt->total_avail_credits,
+		   target->cred_dist_cntxt->cur_free_credits);
+}
+
+#endif

+ 105 - 0
drivers/net/wireless/ath/ath6kl/debug.h

@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include "htc_hif.h"
+
+enum ATH6K_DEBUG_MASK {
+	ATH6KL_DBG_WLAN_CONNECT = BIT(0),     /* wlan connect */
+	ATH6KL_DBG_WLAN_SCAN    = BIT(1),     /* wlan scan */
+	ATH6KL_DBG_WLAN_TX      = BIT(2),     /* wlan tx */
+	ATH6KL_DBG_WLAN_RX      = BIT(3),     /* wlan rx */
+	ATH6KL_DBG_BMI		= BIT(4),     /* bmi tracing */
+	ATH6KL_DBG_HTC_SEND     = BIT(5),     /* htc send */
+	ATH6KL_DBG_HTC_RECV     = BIT(6),     /* htc recv */
+	ATH6KL_DBG_IRQ		= BIT(7),     /* interrupt processing */
+	ATH6KL_DBG_PM           = BIT(8),     /* power management */
+	ATH6KL_DBG_WLAN_NODE    = BIT(9),     /* general wlan node tracing */
+	ATH6KL_DBG_WMI          = BIT(10),    /* wmi tracing */
+	ATH6KL_DBG_TRC	        = BIT(11),    /* generic func tracing */
+	ATH6KL_DBG_SCATTER	= BIT(12),    /* hif scatter tracing */
+	ATH6KL_DBG_WLAN_CFG     = BIT(13),    /* cfg80211 i/f file tracing */
+	ATH6KL_DBG_RAW_BYTES    = BIT(14),    /* dump tx/rx and wmi frames */
+	ATH6KL_DBG_AGGR		= BIT(15),    /* aggregation */
+	ATH6KL_DBG_ANY	        = 0xffffffff  /* enable all logs */
+};
+
+extern unsigned int debug_mask;
+extern int ath6kl_printk(const char *level, const char *fmt, ...)
+	__attribute__ ((format (printf, 2, 3)));
+
+#define ath6kl_info(fmt, ...)				\
+	ath6kl_printk(KERN_INFO, fmt, ##__VA_ARGS__)
+#define ath6kl_err(fmt, ...)					\
+	ath6kl_printk(KERN_ERR, fmt, ##__VA_ARGS__)
+#define ath6kl_warn(fmt, ...)					\
+	ath6kl_printk(KERN_WARNING, fmt, ##__VA_ARGS__)
+
+#define AR_DBG_LVL_CHECK(mask)	(debug_mask & mask)
+
+#ifdef CONFIG_ATH6KL_DEBUG
+#define ath6kl_dbg(mask, fmt, ...)					\
+	({								\
+	 int rtn;							\
+	 if (debug_mask & mask)						\
+		rtn = ath6kl_printk(KERN_DEBUG, fmt, ##__VA_ARGS__);	\
+	 else								\
+		rtn = 0;						\
+									\
+	 rtn;								\
+	 })
+
+static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
+				   const char *msg, const void *buf,
+				   size_t len)
+{
+	if (debug_mask & mask) {
+		ath6kl_dbg(mask, "%s\n", msg);
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
+	}
+}
+
+void ath6kl_dump_registers(struct ath6kl_device *dev,
+			   struct ath6kl_irq_proc_registers *irq_proc_reg,
+			   struct ath6kl_irq_enable_reg *irq_en_reg);
+void dump_cred_dist_stats(struct htc_target *target);
+#else
+static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
+			     const char *fmt, ...)
+{
+	return 0;
+}
+
+static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
+				   const char *msg, const void *buf,
+				   size_t len)
+{
+}
+
+static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
+		struct ath6kl_irq_proc_registers *irq_proc_reg,
+		struct ath6kl_irq_enable_reg *irq_en_reg)
+{
+
+}
+static inline void dump_cred_dist_stats(struct htc_target *target)
+{
+}
+#endif
+
+#endif

+ 72 - 0
drivers/net/wireless/ath/ath6kl/hif-ops.h

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2004-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.
+ */
+
+#ifndef HIF_OPS_H
+#define HIF_OPS_H
+
+#include "hif.h"
+
+static inline int hif_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
+				      u32 len, u32 request)
+{
+	return ar->hif_ops->read_write_sync(ar, addr, buf, len, request);
+}
+
+static inline int hif_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
+				  u32 length, u32 request,
+				  struct htc_packet *packet)
+{
+	return ar->hif_ops->write_async(ar, address, buffer, length,
+					request, packet);
+}
+static inline void ath6kl_hif_irq_enable(struct ath6kl *ar)
+{
+	return ar->hif_ops->irq_enable(ar);
+}
+
+static inline void ath6kl_hif_irq_disable(struct ath6kl *ar)
+{
+	return ar->hif_ops->irq_disable(ar);
+}
+
+static inline struct hif_scatter_req *hif_scatter_req_get(struct ath6kl *ar)
+{
+	return ar->hif_ops->scatter_req_get(ar);
+}
+
+static inline void hif_scatter_req_add(struct ath6kl *ar,
+				       struct hif_scatter_req *s_req)
+{
+	return ar->hif_ops->scatter_req_add(ar, s_req);
+}
+
+static inline int ath6kl_hif_enable_scatter(struct ath6kl *ar)
+{
+	return ar->hif_ops->enable_scatter(ar);
+}
+
+static inline int ath6kl_hif_scat_req_rw(struct ath6kl *ar,
+					 struct hif_scatter_req *scat_req)
+{
+	return ar->hif_ops->scat_req_rw(ar, scat_req);
+}
+
+static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar)
+{
+	return ar->hif_ops->cleanup_scatter(ar);
+}
+
+#endif

+ 207 - 0
drivers/net/wireless/ath/ath6kl/hif.h

@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2004-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.
+ */
+
+#ifndef HIF_H
+#define HIF_H
+
+#include "common.h"
+#include "core.h"
+
+#include <linux/scatterlist.h>
+
+#define BUS_REQUEST_MAX_NUM                64
+#define HIF_MBOX_BLOCK_SIZE                128
+#define HIF_MBOX0_BLOCK_SIZE               1
+
+#define HIF_DMA_BUFFER_SIZE (32 * 1024)
+#define CMD53_FIXED_ADDRESS 1
+#define CMD53_INCR_ADDRESS  2
+
+#define MAX_SCATTER_REQUESTS             4
+#define MAX_SCATTER_ENTRIES_PER_REQ      16
+#define MAX_SCATTER_REQ_TRANSFER_SIZE    (32 * 1024)
+
+#define MANUFACTURER_ID_AR6003_BASE        0x300
+    /* SDIO manufacturer ID and Codes */
+#define MANUFACTURER_ID_ATH6KL_BASE_MASK     0xFF00
+#define MANUFACTURER_CODE                  0x271	/* Atheros */
+
+/* Mailbox address in SDIO address space */
+#define HIF_MBOX_BASE_ADDR                 0x800
+#define HIF_MBOX_WIDTH                     0x800
+
+#define HIF_MBOX_END_ADDR  (HTC_MAILBOX_NUM_MAX * HIF_MBOX_WIDTH - 1)
+
+/* version 1 of the chip has only a 12K extended mbox range */
+#define HIF_MBOX0_EXT_BASE_ADDR  0x4000
+#define HIF_MBOX0_EXT_WIDTH      (12*1024)
+
+/* GMBOX addresses */
+#define HIF_GMBOX_BASE_ADDR                0x7000
+#define HIF_GMBOX_WIDTH                    0x4000
+
+/* interrupt mode register */
+#define CCCR_SDIO_IRQ_MODE_REG         0xF0
+
+/* mode to enable special 4-bit interrupt assertion without clock */
+#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ   (1 << 0)
+
+struct bus_request {
+	struct list_head list;
+
+	/* request data */
+	u32 address;
+
+	u8 *buffer;
+	u32 length;
+	u32 request;
+	struct htc_packet *packet;
+	int status;
+
+	/* this is a scatter request */
+	struct hif_scatter_req *scat_req;
+};
+
+/* direction of transfer (read/write) */
+#define HIF_READ                    0x00000001
+#define HIF_WRITE                   0x00000002
+#define HIF_DIR_MASK                (HIF_READ | HIF_WRITE)
+
+/*
+ *     emode - This indicates the whether the command is to be executed in a
+ *             blocking or non-blocking fashion (HIF_SYNCHRONOUS/
+ *             HIF_ASYNCHRONOUS). The read/write data paths in HTC have been
+ *             implemented using the asynchronous mode allowing the the bus
+ *             driver to indicate the completion of operation through the
+ *             registered callback routine. The requirement primarily comes
+ *             from the contexts these operations get called from (a driver's
+ *             transmit context or the ISR context in case of receive).
+ *             Support for both of these modes is essential.
+ */
+#define HIF_SYNCHRONOUS             0x00000010
+#define HIF_ASYNCHRONOUS            0x00000020
+#define HIF_EMODE_MASK              (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS)
+
+/*
+ *     dmode - An interface may support different kinds of commands based on
+ *             the tradeoff between the amount of data it can carry and the
+ *             setup time. Byte and Block modes are supported (HIF_BYTE_BASIS/
+ *             HIF_BLOCK_BASIS). In case of latter, the data is rounded off
+ *             to the nearest block size by padding. The size of the block is
+ *             configurable at compile time using the HIF_BLOCK_SIZE and is
+ *             negotiated with the target during initialization after the
+ *             ATH6KL interrupts are enabled.
+ */
+#define HIF_BYTE_BASIS              0x00000040
+#define HIF_BLOCK_BASIS             0x00000080
+#define HIF_DMODE_MASK              (HIF_BYTE_BASIS | HIF_BLOCK_BASIS)
+
+/*
+ *     amode - This indicates if the address has to be incremented on ATH6KL
+ *             after every read/write operation (HIF?FIXED_ADDRESS/
+ *             HIF_INCREMENTAL_ADDRESS).
+ */
+#define HIF_FIXED_ADDRESS           0x00000100
+#define HIF_INCREMENTAL_ADDRESS     0x00000200
+#define HIF_AMODE_MASK		  (HIF_FIXED_ADDRESS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_ASYNC_BYTE_INC					\
+	(HIF_WRITE | HIF_ASYNCHRONOUS |				\
+	 HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_ASYNC_BLOCK_INC					\
+	(HIF_WRITE | HIF_ASYNCHRONOUS |				\
+	 HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_SYNC_BYTE_FIX					\
+	(HIF_WRITE | HIF_SYNCHRONOUS |				\
+	 HIF_BYTE_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_WR_SYNC_BYTE_INC					\
+	(HIF_WRITE | HIF_SYNCHRONOUS |				\
+	 HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_SYNC_BLOCK_INC					\
+	(HIF_WRITE | HIF_SYNCHRONOUS |				\
+	 HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_RD_SYNC_BYTE_INC						\
+	(HIF_READ | HIF_SYNCHRONOUS |					\
+	 HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_RD_SYNC_BYTE_FIX						\
+	(HIF_READ | HIF_SYNCHRONOUS |					\
+	 HIF_BYTE_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_RD_ASYNC_BLOCK_FIX						\
+	(HIF_READ | HIF_ASYNCHRONOUS |					\
+	 HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_RD_SYNC_BLOCK_FIX						\
+	(HIF_READ | HIF_SYNCHRONOUS |					\
+	 HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS)
+
+struct hif_scatter_item {
+	u8 *buf;
+	int len;
+	struct htc_packet *packet;
+};
+
+struct hif_scatter_req {
+	struct list_head list;
+	/* address for the read/write operation */
+	u32 addr;
+
+	/* request flags */
+	u32 req;
+
+	/* total length of entire transfer */
+	u32 len;
+
+	bool virt_scat;
+
+	void (*complete) (struct htc_target *, struct hif_scatter_req *);
+	int status;
+	int scat_entries;
+
+	struct bus_request *busrequest;
+	struct scatterlist *sgentries;
+
+	/* bounce buffer for upper layers to copy to/from */
+	u8 *virt_dma_buf;
+
+	struct hif_scatter_item scat_list[1];
+};
+
+struct ath6kl_hif_ops {
+	int (*read_write_sync)(struct ath6kl *ar, u32 addr, u8 *buf,
+			       u32 len, u32 request);
+	int (*write_async)(struct ath6kl *ar, u32 address, u8 *buffer,
+			   u32 length, u32 request, struct htc_packet *packet);
+
+	void (*irq_enable)(struct ath6kl *ar);
+	void (*irq_disable)(struct ath6kl *ar);
+
+	struct hif_scatter_req *(*scatter_req_get)(struct ath6kl *ar);
+	void (*scatter_req_add)(struct ath6kl *ar,
+				struct hif_scatter_req *s_req);
+	int (*enable_scatter)(struct ath6kl *ar);
+	int (*scat_req_rw) (struct ath6kl *ar,
+			    struct hif_scatter_req *scat_req);
+	void (*cleanup_scatter)(struct ath6kl *ar);
+};
+
+#endif

+ 2457 - 0
drivers/net/wireless/ath/ath6kl/htc.c

@@ -0,0 +1,2457 @@
+/*
+ * 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 "core.h"
+#include "htc_hif.h"
+#include "debug.h"
+#include "hif-ops.h"
+#include <asm/unaligned.h>
+
+#define CALC_TXRX_PADDED_LEN(dev, len)  (__ALIGN_MASK((len), (dev)->block_mask))
+
+static void htc_prep_send_pkt(struct htc_packet *packet, u8 flags, int ctrl0,
+			      int ctrl1)
+{
+	struct htc_frame_hdr *hdr;
+
+	packet->buf -= HTC_HDR_LENGTH;
+	hdr =  (struct htc_frame_hdr *)packet->buf;
+
+	/* Endianess? */
+	put_unaligned((u16)packet->act_len, &hdr->payld_len);
+	hdr->flags = flags;
+	hdr->eid = packet->endpoint;
+	hdr->ctrl[0] = ctrl0;
+	hdr->ctrl[1] = ctrl1;
+}
+
+static void htc_reclaim_txctrl_buf(struct htc_target *target,
+				   struct htc_packet *pkt)
+{
+	spin_lock_bh(&target->htc_lock);
+	list_add_tail(&pkt->list, &target->free_ctrl_txbuf);
+	spin_unlock_bh(&target->htc_lock);
+}
+
+static struct htc_packet *htc_get_control_buf(struct htc_target *target,
+					      bool tx)
+{
+	struct htc_packet *packet = NULL;
+	struct list_head *buf_list;
+
+	buf_list = tx ? &target->free_ctrl_txbuf : &target->free_ctrl_rxbuf;
+
+	spin_lock_bh(&target->htc_lock);
+
+	if (list_empty(buf_list)) {
+		spin_unlock_bh(&target->htc_lock);
+		return NULL;
+	}
+
+	packet = list_first_entry(buf_list, struct htc_packet, list);
+	list_del(&packet->list);
+	spin_unlock_bh(&target->htc_lock);
+
+	if (tx)
+		packet->buf = packet->buf_start + HTC_HDR_LENGTH;
+
+	return packet;
+}
+
+static void htc_tx_comp_update(struct htc_target *target,
+			       struct htc_endpoint *endpoint,
+			       struct htc_packet *packet)
+{
+	packet->completion = NULL;
+	packet->buf += HTC_HDR_LENGTH;
+
+	if (!packet->status)
+		return;
+
+	ath6kl_err("req failed (status:%d, ep:%d, len:%d creds:%d)\n",
+		   packet->status, packet->endpoint, packet->act_len,
+		   packet->info.tx.cred_used);
+
+	/* on failure to submit, reclaim credits for this packet */
+	spin_lock_bh(&target->tx_lock);
+	endpoint->cred_dist.cred_to_dist +=
+				packet->info.tx.cred_used;
+	endpoint->cred_dist.txq_depth = get_queue_depth(&endpoint->txq);
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+		   target->cred_dist_cntxt, &target->cred_dist_list);
+
+	ath6k_credit_distribute(target->cred_dist_cntxt,
+				&target->cred_dist_list,
+				HTC_CREDIT_DIST_SEND_COMPLETE);
+
+	spin_unlock_bh(&target->tx_lock);
+}
+
+static void htc_tx_complete(struct htc_endpoint *endpoint,
+			    struct list_head *txq)
+{
+	if (list_empty(txq))
+		return;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+		   "send complete ep %d, (%d pkts)\n",
+		   endpoint->eid, get_queue_depth(txq));
+
+	ath6kl_tx_complete(endpoint->target->dev->ar, txq);
+}
+
+static void htc_tx_comp_handler(struct htc_target *target,
+				struct htc_packet *packet)
+{
+	struct htc_endpoint *endpoint = &target->endpoint[packet->endpoint];
+	struct list_head container;
+
+	htc_tx_comp_update(target, endpoint, packet);
+	INIT_LIST_HEAD(&container);
+	list_add_tail(&packet->list, &container);
+	/* do completion */
+	htc_tx_complete(endpoint, &container);
+}
+
+static void htc_async_tx_scat_complete(struct htc_target *target,
+				       struct hif_scatter_req *scat_req)
+{
+	struct htc_endpoint *endpoint;
+	struct htc_packet *packet;
+	struct list_head tx_compq;
+	int i;
+
+	INIT_LIST_HEAD(&tx_compq);
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+		"htc_async_tx_scat_complete  total len: %d  entries: %d\n",
+		scat_req->len, scat_req->scat_entries);
+
+	if (scat_req->status)
+		ath6kl_err("send scatter req failed: %d\n", scat_req->status);
+
+	packet = scat_req->scat_list[0].packet;
+	endpoint = &target->endpoint[packet->endpoint];
+
+	/* walk through the scatter list and process */
+	for (i = 0; i < scat_req->scat_entries; i++) {
+		packet = scat_req->scat_list[i].packet;
+		if (!packet) {
+			WARN_ON(1);
+			return;
+		}
+
+		packet->status = scat_req->status;
+		htc_tx_comp_update(target, endpoint, packet);
+		list_add_tail(&packet->list, &tx_compq);
+	}
+
+	/* free scatter request */
+	hif_scatter_req_add(target->dev->ar, scat_req);
+
+	/* complete all packets */
+	htc_tx_complete(endpoint, &tx_compq);
+}
+
+static int htc_issue_send(struct htc_target *target, struct htc_packet *packet)
+{
+	int status;
+	bool sync = false;
+	u32 padded_len, send_len;
+
+	if (!packet->completion)
+		sync = true;
+
+	send_len = packet->act_len + HTC_HDR_LENGTH;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "%s: transmit len : %d (%s)\n",
+		   __func__, send_len, sync ? "sync" : "async");
+
+	padded_len = CALC_TXRX_PADDED_LEN(target, send_len);
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+		"DevSendPacket, padded len: %d mbox:0x%X (mode:%s)\n",
+		padded_len,
+		target->dev->ar->mbox_info.htc_addr,
+		sync ? "sync" : "async");
+
+	if (sync) {
+		status = hif_read_write_sync(target->dev->ar,
+				target->dev->ar->mbox_info.htc_addr,
+				 packet->buf, padded_len,
+				 HIF_WR_SYNC_BLOCK_INC);
+
+		packet->status = status;
+		 packet->buf += HTC_HDR_LENGTH;
+	} else
+		status = hif_write_async(target->dev->ar,
+				target->dev->ar->mbox_info.htc_addr,
+				packet->buf, padded_len,
+				HIF_WR_ASYNC_BLOCK_INC, packet);
+
+	return status;
+}
+
+static int htc_check_credits(struct htc_target *target,
+			     struct htc_endpoint *ep, u8 *flags,
+			     enum htc_endpoint_id eid, unsigned int len,
+			     int *req_cred)
+{
+
+	*req_cred = (len > target->tgt_cred_sz) ?
+		     DIV_ROUND_UP(len, target->tgt_cred_sz) : 1;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "creds required:%d got:%d\n",
+		   *req_cred, ep->cred_dist.credits);
+
+	if (ep->cred_dist.credits < *req_cred) {
+		if (eid == ENDPOINT_0)
+			return -EINVAL;
+
+		/* Seek more credits */
+		ep->cred_dist.seek_cred = *req_cred - ep->cred_dist.credits;
+
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+			   target->cred_dist_cntxt, &ep->cred_dist);
+
+		ath6k_seek_credits(target->cred_dist_cntxt, &ep->cred_dist);
+
+		ep->cred_dist.seek_cred = 0;
+
+		if (ep->cred_dist.credits < *req_cred) {
+			ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+				   "not enough credits for ep %d - leaving packet in queue\n",
+				   eid);
+			return -EINVAL;
+		}
+	}
+
+	ep->cred_dist.credits -= *req_cred;
+	ep->ep_st.cred_cosumd += *req_cred;
+
+	 /* When we are getting low on credits, ask for more */
+	if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) {
+		ep->cred_dist.seek_cred =
+		ep->cred_dist.cred_per_msg - ep->cred_dist.credits;
+
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+			   target->cred_dist_cntxt, &ep->cred_dist);
+
+		ath6k_seek_credits(target->cred_dist_cntxt, &ep->cred_dist);
+
+		/* see if we were successful in getting more */
+		if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) {
+			/* tell the target we need credits ASAP! */
+			*flags |= HTC_FLAGS_NEED_CREDIT_UPDATE;
+			ep->ep_st.cred_low_indicate += 1;
+			ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "host needs credits\n");
+		}
+	}
+
+	return 0;
+}
+
+static void htc_tx_pkts_get(struct htc_target *target,
+			    struct htc_endpoint *endpoint,
+			    struct list_head *queue)
+{
+	int req_cred;
+	u8 flags;
+	struct htc_packet *packet;
+	unsigned int len;
+
+	while (true) {
+
+		flags = 0;
+
+		if (list_empty(&endpoint->txq))
+			break;
+		packet = list_first_entry(&endpoint->txq, struct htc_packet,
+					  list);
+
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+			"got head pkt:0x%p , queue depth: %d\n",
+			packet, get_queue_depth(&endpoint->txq));
+
+		len = CALC_TXRX_PADDED_LEN(target,
+					   packet->act_len + HTC_HDR_LENGTH);
+
+		if (htc_check_credits(target, endpoint, &flags,
+				      packet->endpoint, len, &req_cred))
+			break;
+
+		/* now we can fully move onto caller's queue */
+		packet = list_first_entry(&endpoint->txq, struct htc_packet,
+					  list);
+		list_move_tail(&packet->list, queue);
+
+		/* save the number of credits this packet consumed */
+		packet->info.tx.cred_used = req_cred;
+
+		/* all TX packets are handled asynchronously */
+		packet->completion = htc_tx_comp_handler;
+		packet->context = target;
+		endpoint->ep_st.tx_issued += 1;
+
+		/* save send flags */
+		packet->info.tx.flags = flags;
+		packet->info.tx.seqno = endpoint->seqno;
+		endpoint->seqno++;
+	}
+}
+
+/* See if the padded tx length falls on a credit boundary */
+static int htc_get_credit_padding(unsigned int cred_sz, int *len,
+				  struct htc_endpoint *ep)
+{
+	int rem_cred, cred_pad;
+
+	rem_cred = *len % cred_sz;
+
+	/* No padding needed */
+	if  (!rem_cred)
+		return 0;
+
+	if (!(ep->conn_flags & HTC_FLGS_TX_BNDL_PAD_EN))
+		return -1;
+
+	/*
+	 * The transfer consumes a "partial" credit, this
+	 * packet cannot be bundled unless we add
+	 * additional "dummy" padding (max 255 bytes) to
+	 * consume the entire credit.
+	 */
+	cred_pad = *len < cred_sz ? (cred_sz - *len) : rem_cred;
+
+	if ((cred_pad > 0) && (cred_pad <= 255))
+		*len += cred_pad;
+	else
+		/* The amount of padding is too large, send as non-bundled */
+		return -1;
+
+	return cred_pad;
+}
+
+static int htc_setup_send_scat_list(struct htc_target *target,
+				    struct htc_endpoint *endpoint,
+				    struct hif_scatter_req *scat_req,
+				    int n_scat,
+				    struct list_head *queue)
+{
+	struct htc_packet *packet;
+	int i, len, rem_scat, cred_pad;
+	int status = 0;
+
+	rem_scat = target->max_tx_bndl_sz;
+
+	for (i = 0; i < n_scat; i++) {
+		scat_req->scat_list[i].packet = NULL;
+
+		if (list_empty(queue))
+			break;
+
+		packet = list_first_entry(queue, struct htc_packet, list);
+		len = CALC_TXRX_PADDED_LEN(target,
+					   packet->act_len + HTC_HDR_LENGTH);
+
+		cred_pad = htc_get_credit_padding(target->tgt_cred_sz,
+						  &len, endpoint);
+		if (cred_pad < 0) {
+			status = -EINVAL;
+			break;
+		}
+
+		if (rem_scat < len) {
+			/* exceeds what we can transfer */
+			status = -ENOSPC;
+			break;
+		}
+
+		rem_scat -= len;
+		/* now remove it from the queue */
+		packet = list_first_entry(queue, struct htc_packet, list);
+		list_del(&packet->list);
+
+		scat_req->scat_list[i].packet = packet;
+		/* prepare packet and flag message as part of a send bundle */
+		htc_prep_send_pkt(packet,
+				packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE,
+				cred_pad, packet->info.tx.seqno);
+		scat_req->scat_list[i].buf = packet->buf;
+		scat_req->scat_list[i].len = len;
+
+		scat_req->len += len;
+		scat_req->scat_entries++;
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+			   "%d, adding pkt : 0x%p len:%d (remaining space:%d)\n",
+			   i, packet, len, rem_scat);
+	}
+
+	/* Roll back scatter setup in case of any failure */
+	if (status || (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
+		for (i = scat_req->scat_entries - 1; i >= 0; i--) {
+			packet = scat_req->scat_list[i].packet;
+			if (packet) {
+				packet->buf += HTC_HDR_LENGTH;
+				list_add(&packet->list, queue);
+			}
+		}
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * htc_issue_send_bundle: drain a queue and send as bundles
+ * this function may return without fully draining the queue
+ * when
+ *
+ *    1. scatter resources are exhausted
+ *    2. a message that will consume a partial credit will stop the
+ *    bundling process early
+ *    3. we drop below the minimum number of messages for a bundle
+ */
+static void htc_issue_send_bundle(struct htc_endpoint *endpoint,
+				  struct list_head *queue,
+				  int *sent_bundle, int *n_bundle_pkts)
+{
+	struct htc_target *target = endpoint->target;
+	struct hif_scatter_req *scat_req = NULL;
+	int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0;
+
+	while (true) {
+		n_scat = get_queue_depth(queue);
+		n_scat = min(n_scat, target->msg_per_bndl_max);
+
+		if (n_scat < HTC_MIN_HTC_MSGS_TO_BUNDLE)
+			/* not enough to bundle */
+			break;
+
+		scat_req = hif_scatter_req_get(target->dev->ar);
+
+		if (!scat_req) {
+			/* no scatter resources  */
+			ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+				"no more scatter resources\n");
+			break;
+		}
+
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "pkts to scatter: %d\n",
+			   n_scat);
+
+		scat_req->len = 0;
+		scat_req->scat_entries = 0;
+
+		if (htc_setup_send_scat_list(target, endpoint, scat_req,
+					     n_scat, queue)) {
+			hif_scatter_req_add(target->dev->ar, scat_req);
+			break;
+		}
+
+		/* send path is always asynchronous */
+		scat_req->complete = htc_async_tx_scat_complete;
+		n_sent_bundle++;
+		tot_pkts_bundle += scat_req->scat_entries;
+
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+			   "send scatter total bytes: %d , entries: %d\n",
+			   scat_req->len, scat_req->scat_entries);
+		ath6kldev_submit_scat_req(target->dev, scat_req, false);
+	}
+
+	*sent_bundle = n_sent_bundle;
+	*n_bundle_pkts = tot_pkts_bundle;
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "htc_issue_send_bundle (sent:%d)\n",
+		   n_sent_bundle);
+
+	return;
+}
+
+static void htc_tx_from_ep_txq(struct htc_target *target,
+			       struct htc_endpoint *endpoint)
+{
+	struct list_head txq;
+	struct htc_packet *packet;
+	int bundle_sent;
+	int n_pkts_bundle;
+
+	spin_lock_bh(&target->tx_lock);
+
+	endpoint->tx_proc_cnt++;
+	if (endpoint->tx_proc_cnt > 1) {
+		endpoint->tx_proc_cnt--;
+		spin_unlock_bh(&target->tx_lock);
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "htc_try_send (busy)\n");
+		return;
+	}
+
+	/*
+	 * drain the endpoint TX queue for transmission as long
+	 * as we have enough credits.
+	 */
+	INIT_LIST_HEAD(&txq);
+
+	while (true) {
+
+		if (list_empty(&endpoint->txq))
+			break;
+
+		htc_tx_pkts_get(target, endpoint, &txq);
+
+		if (list_empty(&txq))
+			break;
+
+		spin_unlock_bh(&target->tx_lock);
+
+		bundle_sent = 0;
+		n_pkts_bundle = 0;
+
+		while (true) {
+			/* try to send a bundle on each pass */
+			if ((target->tx_bndl_enable) &&
+			    (get_queue_depth(&txq) >=
+			    HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
+				int temp1 = 0, temp2 = 0;
+
+				htc_issue_send_bundle(endpoint, &txq,
+						      &temp1, &temp2);
+				bundle_sent += temp1;
+				n_pkts_bundle += temp2;
+			}
+
+			if (list_empty(&txq))
+				break;
+
+			packet = list_first_entry(&txq, struct htc_packet,
+						  list);
+			list_del(&packet->list);
+
+			htc_prep_send_pkt(packet, packet->info.tx.flags,
+					  0, packet->info.tx.seqno);
+			htc_issue_send(target, packet);
+		}
+
+		spin_lock_bh(&target->tx_lock);
+
+		endpoint->ep_st.tx_bundles += bundle_sent;
+		endpoint->ep_st.tx_pkt_bundled += n_pkts_bundle;
+	}
+
+	endpoint->tx_proc_cnt = 0;
+	spin_unlock_bh(&target->tx_lock);
+}
+
+static bool htc_try_send(struct htc_target *target,
+			 struct htc_endpoint *endpoint,
+			 struct htc_packet *tx_pkt)
+{
+	struct htc_ep_callbacks ep_cb;
+	int txq_depth;
+	bool overflow = false;
+
+	ep_cb = endpoint->ep_cb;
+
+	spin_lock_bh(&target->tx_lock);
+	txq_depth = get_queue_depth(&endpoint->txq);
+	spin_unlock_bh(&target->tx_lock);
+
+	if (txq_depth >= endpoint->max_txq_depth)
+		overflow = true;
+
+	if (overflow)
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+			   "ep %d, tx queue will overflow :%d , tx depth:%d, max:%d\n",
+			   endpoint->eid, overflow, txq_depth,
+			   endpoint->max_txq_depth);
+
+	if (overflow && ep_cb.tx_full) {
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+			   "indicating overflowed tx packet: 0x%p\n", tx_pkt);
+
+		if (ep_cb.tx_full(endpoint->target, tx_pkt) ==
+		    HTC_SEND_FULL_DROP) {
+			endpoint->ep_st.tx_dropped += 1;
+			return false;
+		}
+	}
+
+	spin_lock_bh(&target->tx_lock);
+	list_add_tail(&tx_pkt->list, &endpoint->txq);
+	spin_unlock_bh(&target->tx_lock);
+
+	htc_tx_from_ep_txq(target, endpoint);
+
+	return true;
+}
+
+static void htc_chk_ep_txq(struct htc_target *target)
+{
+	struct htc_endpoint *endpoint;
+	struct htc_endpoint_credit_dist *cred_dist;
+
+	/*
+	 * Run through the credit distribution list to see if there are
+	 * packets queued. NOTE: no locks need to be taken since the
+	 * distribution list is not dynamic (cannot be re-ordered) and we
+	 * are not modifying any state.
+	 */
+	list_for_each_entry(cred_dist, &target->cred_dist_list, list) {
+		endpoint = (struct htc_endpoint *)cred_dist->htc_rsvd;
+
+		spin_lock_bh(&target->tx_lock);
+		if (!list_empty(&endpoint->txq)) {
+			ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+				   "ep %d has %d credits and %d packets in tx queue\n",
+				   cred_dist->endpoint,
+				   endpoint->cred_dist.credits,
+				   get_queue_depth(&endpoint->txq));
+			spin_unlock_bh(&target->tx_lock);
+			/*
+			 * Try to start the stalled queue, this list is
+			 * ordered by priority. If there are credits
+			 * available the highest priority queue will get a
+			 * chance to reclaim credits from lower priority
+			 * ones.
+			 */
+			htc_tx_from_ep_txq(target, endpoint);
+			spin_lock_bh(&target->tx_lock);
+		}
+		spin_unlock_bh(&target->tx_lock);
+	}
+}
+
+static int htc_setup_tx_complete(struct htc_target *target)
+{
+	struct htc_packet *send_pkt = NULL;
+	int status;
+
+	send_pkt = htc_get_control_buf(target, true);
+
+	if (!send_pkt)
+		return -ENOMEM;
+
+	if (target->htc_tgt_ver >= HTC_VERSION_2P1) {
+		struct htc_setup_comp_ext_msg *setup_comp_ext;
+		u32 flags = 0;
+
+		setup_comp_ext =
+		    (struct htc_setup_comp_ext_msg *)send_pkt->buf;
+		memset(setup_comp_ext, 0, sizeof(*setup_comp_ext));
+		setup_comp_ext->msg_id =
+			cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID);
+
+		if (target->msg_per_bndl_max > 0) {
+			/* Indicate HTC bundling to the target */
+			flags |= HTC_SETUP_COMP_FLG_RX_BNDL_EN;
+			setup_comp_ext->msg_per_rxbndl =
+						target->msg_per_bndl_max;
+		}
+
+		memcpy(&setup_comp_ext->flags, &flags,
+		       sizeof(setup_comp_ext->flags));
+		set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp_ext,
+				       sizeof(struct htc_setup_comp_ext_msg),
+				       ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
+
+	} else {
+		struct htc_setup_comp_msg *setup_comp;
+		setup_comp = (struct htc_setup_comp_msg *)send_pkt->buf;
+		memset(setup_comp, 0, sizeof(struct htc_setup_comp_msg));
+		setup_comp->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_ID);
+		set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp,
+				       sizeof(struct htc_setup_comp_msg),
+				       ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
+	}
+
+	/* we want synchronous operation */
+	send_pkt->completion = NULL;
+	htc_prep_send_pkt(send_pkt, 0, 0, 0);
+	status = htc_issue_send(target, send_pkt);
+
+	if (send_pkt != NULL)
+		htc_reclaim_txctrl_buf(target, send_pkt);
+
+	return status;
+}
+
+void ath6kl_htc_set_credit_dist(struct htc_target *target,
+				struct htc_credit_state_info *cred_dist_cntxt,
+				u16 srvc_pri_order[], int list_len)
+{
+	struct htc_endpoint *endpoint;
+	int i, ep;
+
+	target->cred_dist_cntxt = cred_dist_cntxt;
+
+	list_add_tail(&target->endpoint[ENDPOINT_0].cred_dist.list,
+		      &target->cred_dist_list);
+
+	for (i = 0; i < list_len; i++) {
+		for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) {
+			endpoint = &target->endpoint[ep];
+			if (endpoint->svc_id == srvc_pri_order[i]) {
+				list_add_tail(&endpoint->cred_dist.list,
+					      &target->cred_dist_list);
+				break;
+			}
+		}
+		if (ep >= ENDPOINT_MAX) {
+			WARN_ON(1);
+			return;
+		}
+	}
+}
+
+int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet)
+{
+	struct htc_endpoint *endpoint;
+	struct list_head queue;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+		   "htc_tx: ep id: %d, buf: 0x%p, len: %d\n",
+		   packet->endpoint, packet->buf, packet->act_len);
+
+	if (packet->endpoint >= ENDPOINT_MAX) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	endpoint = &target->endpoint[packet->endpoint];
+
+	if (!htc_try_send(target, endpoint, packet)) {
+		packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ?
+				 -ECANCELED : -ENOSPC;
+		INIT_LIST_HEAD(&queue);
+		list_add(&packet->list, &queue);
+		htc_tx_complete(endpoint, &queue);
+	}
+
+	return 0;
+}
+
+/* flush endpoint TX queue */
+void ath6kl_htc_flush_txep(struct htc_target *target,
+			   enum htc_endpoint_id eid, u16 tag)
+{
+	struct htc_packet *packet, *tmp_pkt;
+	struct list_head discard_q, container;
+	struct htc_endpoint *endpoint = &target->endpoint[eid];
+
+	if (!endpoint->svc_id) {
+		WARN_ON(1);
+		return;
+	}
+
+	/* initialize the discard queue */
+	INIT_LIST_HEAD(&discard_q);
+
+	spin_lock_bh(&target->tx_lock);
+
+	list_for_each_entry_safe(packet, tmp_pkt, &endpoint->txq, list) {
+		if ((tag == HTC_TX_PACKET_TAG_ALL) ||
+		    (tag == packet->info.tx.tag))
+			list_move_tail(&packet->list, &discard_q);
+	}
+
+	spin_unlock_bh(&target->tx_lock);
+
+	list_for_each_entry_safe(packet, tmp_pkt, &discard_q, list) {
+		packet->status = -ECANCELED;
+		list_del(&packet->list);
+		ath6kl_dbg(ATH6KL_DBG_TRC,
+			"flushing tx pkt:0x%p, len:%d, ep:%d tag:0x%X\n",
+			packet, packet->act_len,
+			packet->endpoint, packet->info.tx.tag);
+
+		INIT_LIST_HEAD(&container);
+		list_add_tail(&packet->list, &container);
+		htc_tx_complete(endpoint, &container);
+	}
+
+}
+
+static void ath6kl_htc_flush_txep_all(struct htc_target *target)
+{
+	struct htc_endpoint *endpoint;
+	int i;
+
+	dump_cred_dist_stats(target);
+
+	for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+		endpoint = &target->endpoint[i];
+		if (endpoint->svc_id == 0)
+			/* not in use.. */
+			continue;
+		ath6kl_htc_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL);
+	}
+}
+
+void ath6kl_htc_indicate_activity_change(struct htc_target *target,
+					 enum htc_endpoint_id eid, bool active)
+{
+	struct htc_endpoint *endpoint = &target->endpoint[eid];
+	bool dist = false;
+
+	if (endpoint->svc_id == 0) {
+		WARN_ON(1);
+		return;
+	}
+
+	spin_lock_bh(&target->tx_lock);
+
+	if (active) {
+		if (!(endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE)) {
+			endpoint->cred_dist.dist_flags |= HTC_EP_ACTIVE;
+			dist = true;
+		}
+	} else {
+		if (endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE) {
+			endpoint->cred_dist.dist_flags &= ~HTC_EP_ACTIVE;
+			dist = true;
+		}
+	}
+
+	if (dist) {
+		endpoint->cred_dist.txq_depth =
+			get_queue_depth(&endpoint->txq);
+
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+			   target->cred_dist_cntxt, &target->cred_dist_list);
+
+		ath6k_credit_distribute(target->cred_dist_cntxt,
+					&target->cred_dist_list,
+					HTC_CREDIT_DIST_ACTIVITY_CHANGE);
+	}
+
+	spin_unlock_bh(&target->tx_lock);
+
+	if (dist && !active)
+		htc_chk_ep_txq(target);
+}
+
+/* HTC Rx */
+
+static inline void htc_update_rx_stats(struct htc_endpoint *endpoint,
+				       int n_look_ahds)
+{
+	endpoint->ep_st.rx_pkts++;
+	if (n_look_ahds == 1)
+		endpoint->ep_st.rx_lkahds++;
+	else if (n_look_ahds > 1)
+		endpoint->ep_st.rx_bundle_lkahd++;
+}
+
+static inline bool htc_valid_rx_frame_len(struct htc_target *target,
+					  enum htc_endpoint_id eid, int len)
+{
+	return (eid == target->dev->ar->ctrl_ep) ?
+		len <= ATH6KL_BUFFER_SIZE : len <= ATH6KL_AMSDU_BUFFER_SIZE;
+}
+
+static int htc_add_rxbuf(struct htc_target *target, struct htc_packet *packet)
+{
+	struct list_head queue;
+
+	INIT_LIST_HEAD(&queue);
+	list_add_tail(&packet->list, &queue);
+	return ath6kl_htc_add_rxbuf_multiple(target, &queue);
+}
+
+static void htc_reclaim_rxbuf(struct htc_target *target,
+			      struct htc_packet *packet,
+			      struct htc_endpoint *ep)
+{
+	if (packet->info.rx.rx_flags & HTC_RX_PKT_NO_RECYCLE) {
+		htc_rxpkt_reset(packet);
+		packet->status = -ECANCELED;
+		ep->ep_cb.rx(ep->target, packet);
+	} else {
+		htc_rxpkt_reset(packet);
+		htc_add_rxbuf((void *)(target), packet);
+	}
+}
+
+static void reclaim_rx_ctrl_buf(struct htc_target *target,
+				struct htc_packet *packet)
+{
+	spin_lock_bh(&target->htc_lock);
+	list_add_tail(&packet->list, &target->free_ctrl_rxbuf);
+	spin_unlock_bh(&target->htc_lock);
+}
+
+static int dev_rx_pkt(struct htc_target *target, struct htc_packet *packet,
+		      u32 rx_len)
+{
+	struct ath6kl_device *dev = target->dev;
+	u32 padded_len;
+	int status;
+
+	padded_len = CALC_TXRX_PADDED_LEN(target, rx_len);
+
+	if (padded_len > packet->buf_len) {
+		ath6kl_err("not enough receive space for packet - padlen:%d recvlen:%d bufferlen:%d\n",
+			   padded_len, rx_len, packet->buf_len);
+		return -ENOMEM;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+		   "dev_rx_pkt (0x%p : hdr:0x%X) padded len: %d mbox:0x%X (mode:%s)\n",
+		   packet, packet->info.rx.exp_hdr,
+		   padded_len, dev->ar->mbox_info.htc_addr, "sync");
+
+	status = hif_read_write_sync(dev->ar,
+				     dev->ar->mbox_info.htc_addr,
+				     packet->buf, padded_len,
+				     HIF_RD_SYNC_BLOCK_FIX);
+
+	packet->status = status;
+
+	return status;
+}
+
+/*
+ * optimization for recv packets, we can indicate a
+ * "hint" that there are more  single-packets to fetch
+ * on this endpoint.
+ */
+static void set_rxpkt_indication_flag(u32 lk_ahd,
+				      struct htc_endpoint *endpoint,
+				      struct htc_packet *packet)
+{
+	struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)&lk_ahd;
+
+	if (htc_hdr->eid == packet->endpoint) {
+		if (!list_empty(&endpoint->rx_bufq))
+			packet->info.rx.indicat_flags |=
+					HTC_RX_FLAGS_INDICATE_MORE_PKTS;
+	}
+}
+
+static void chk_rx_water_mark(struct htc_endpoint *endpoint)
+{
+	struct htc_ep_callbacks ep_cb = endpoint->ep_cb;
+
+	if (ep_cb.rx_refill_thresh > 0) {
+		spin_lock_bh(&endpoint->target->rx_lock);
+		if (get_queue_depth(&endpoint->rx_bufq)
+		    < ep_cb.rx_refill_thresh) {
+			spin_unlock_bh(&endpoint->target->rx_lock);
+			ep_cb.rx_refill(endpoint->target, endpoint->eid);
+			return;
+		}
+		spin_unlock_bh(&endpoint->target->rx_lock);
+	}
+}
+
+/* This function is called with rx_lock held */
+static int htc_setup_rxpkts(struct htc_target *target, struct htc_endpoint *ep,
+			    u32 *lk_ahds, struct list_head *queue, int n_msg)
+{
+	struct htc_packet *packet;
+	/* FIXME: type of lk_ahds can't be right */
+	struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)lk_ahds;
+	struct htc_ep_callbacks ep_cb;
+	int status = 0, j, full_len;
+	bool no_recycle;
+
+	full_len = CALC_TXRX_PADDED_LEN(target,
+					le16_to_cpu(htc_hdr->payld_len) +
+					sizeof(*htc_hdr));
+
+	if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) {
+		ath6kl_warn("Rx buffer requested with invalid length\n");
+		return -EINVAL;
+	}
+
+	ep_cb = ep->ep_cb;
+	for (j = 0; j < n_msg; j++) {
+
+		/*
+		 * Reset flag, any packets allocated using the
+		 * rx_alloc() API cannot be recycled on
+		 * cleanup,they must be explicitly returned.
+		 */
+		no_recycle = false;
+
+		if (ep_cb.rx_allocthresh &&
+		    (full_len > ep_cb.rx_alloc_thresh)) {
+			ep->ep_st.rx_alloc_thresh_hit += 1;
+			ep->ep_st.rxalloc_thresh_byte +=
+				le16_to_cpu(htc_hdr->payld_len);
+
+			spin_unlock_bh(&target->rx_lock);
+			no_recycle = true;
+
+			packet = ep_cb.rx_allocthresh(ep->target, ep->eid,
+						      full_len);
+			spin_lock_bh(&target->rx_lock);
+		} else {
+			/* refill handler is being used */
+			if (list_empty(&ep->rx_bufq)) {
+				if (ep_cb.rx_refill) {
+					spin_unlock_bh(&target->rx_lock);
+					ep_cb.rx_refill(ep->target, ep->eid);
+					spin_lock_bh(&target->rx_lock);
+				}
+			}
+
+			if (list_empty(&ep->rx_bufq))
+				packet = NULL;
+			else {
+				packet = list_first_entry(&ep->rx_bufq,
+						struct htc_packet, list);
+				list_del(&packet->list);
+			}
+		}
+
+		if (!packet) {
+			target->rx_st_flags |= HTC_RECV_WAIT_BUFFERS;
+			target->ep_waiting = ep->eid;
+			return -ENOSPC;
+		}
+
+		/* clear flags */
+		packet->info.rx.rx_flags = 0;
+		packet->info.rx.indicat_flags = 0;
+		packet->status = 0;
+
+		if (no_recycle)
+			/*
+			 * flag that these packets cannot be
+			 * recycled, they have to be returned to
+			 * the user
+			 */
+			packet->info.rx.rx_flags |= HTC_RX_PKT_NO_RECYCLE;
+
+		/* Caller needs to free this upon any failure */
+		list_add_tail(&packet->list, queue);
+
+		if (target->htc_flags & HTC_OP_STATE_STOPPING) {
+			status = -ECANCELED;
+			break;
+		}
+
+		if (j) {
+			packet->info.rx.rx_flags |= HTC_RX_PKT_REFRESH_HDR;
+			packet->info.rx.exp_hdr = 0xFFFFFFFF;
+		} else
+			/* set expected look ahead */
+			packet->info.rx.exp_hdr = *lk_ahds;
+
+		packet->act_len = le16_to_cpu(htc_hdr->payld_len) +
+			HTC_HDR_LENGTH;
+	}
+
+	return status;
+}
+
+static int alloc_and_prep_rxpkts(struct htc_target *target,
+				 u32 lk_ahds[], int msg,
+				 struct htc_endpoint *endpoint,
+				 struct list_head *queue)
+{
+	int status = 0;
+	struct htc_packet *packet, *tmp_pkt;
+	struct htc_frame_hdr *htc_hdr;
+	int i, n_msg;
+
+	spin_lock_bh(&target->rx_lock);
+
+	for (i = 0; i < msg; i++) {
+
+		htc_hdr = (struct htc_frame_hdr *)&lk_ahds[i];
+
+		if (htc_hdr->eid >= ENDPOINT_MAX) {
+			ath6kl_err("invalid ep in look-ahead: %d\n",
+				   htc_hdr->eid);
+			status = -ENOMEM;
+			break;
+		}
+
+		if (htc_hdr->eid != endpoint->eid) {
+			ath6kl_err("invalid ep in look-ahead: %d should be : %d (index:%d)\n",
+				   htc_hdr->eid, endpoint->eid, i);
+			status = -ENOMEM;
+			break;
+		}
+
+		if (le16_to_cpu(htc_hdr->payld_len) > HTC_MAX_PAYLOAD_LENGTH) {
+			ath6kl_err("payload len %d exceeds max htc : %d !\n",
+				   htc_hdr->payld_len,
+				   (u32) HTC_MAX_PAYLOAD_LENGTH);
+			status = -ENOMEM;
+			break;
+		}
+
+		if (endpoint->svc_id == 0) {
+			ath6kl_err("ep %d is not connected !\n", htc_hdr->eid);
+			status = -ENOMEM;
+			break;
+		}
+
+		if (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) {
+			/*
+			 * HTC header indicates that every packet to follow
+			 * has the same padded length so that it can be
+			 * optimally fetched as a full bundle.
+			 */
+			n_msg = (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) >>
+				HTC_FLG_RX_BNDL_CNT_S;
+
+			/* the count doesn't include the starter frame */
+			n_msg++;
+			if (n_msg > target->msg_per_bndl_max) {
+				status = -ENOMEM;
+				break;
+			}
+
+			endpoint->ep_st.rx_bundle_from_hdr += 1;
+			ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+				   "htc hdr indicates :%d msg can be fetched as a bundle\n",
+				   n_msg);
+		} else
+			/* HTC header only indicates 1 message to fetch */
+			n_msg = 1;
+
+		/* Setup packet buffers for each message */
+		status = htc_setup_rxpkts(target, endpoint, &lk_ahds[i], queue,
+					  n_msg);
+
+		/*
+		 * This is due to unavailabilty of buffers to rx entire data.
+		 * Return no error so that free buffers from queue can be used
+		 * to receive partial data.
+		 */
+		if (status == -ENOSPC) {
+			spin_unlock_bh(&target->rx_lock);
+			return 0;
+		}
+
+		if (status)
+			break;
+	}
+
+	spin_unlock_bh(&target->rx_lock);
+
+	if (status) {
+		list_for_each_entry_safe(packet, tmp_pkt, queue, list) {
+			list_del(&packet->list);
+			htc_reclaim_rxbuf(target, packet,
+					  &target->endpoint[packet->endpoint]);
+		}
+	}
+
+	return status;
+}
+
+static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets)
+{
+	if (packets->endpoint != ENDPOINT_0) {
+		WARN_ON(1);
+		return;
+	}
+
+	if (packets->status == -ECANCELED) {
+		reclaim_rx_ctrl_buf(context, packets);
+		return;
+	}
+
+	if (packets->act_len > 0) {
+		ath6kl_err("htc_ctrl_rx, got message with len:%zu\n",
+			packets->act_len + HTC_HDR_LENGTH);
+
+		ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
+			     "Unexpected ENDPOINT 0 Message",
+			     packets->buf - HTC_HDR_LENGTH,
+			     packets->act_len + HTC_HDR_LENGTH);
+	}
+
+	htc_reclaim_rxbuf(context, packets, &context->endpoint[0]);
+}
+
+static void htc_proc_cred_rpt(struct htc_target *target,
+			      struct htc_credit_report *rpt,
+			      int n_entries,
+			      enum htc_endpoint_id from_ep)
+{
+	struct htc_endpoint *endpoint;
+	int tot_credits = 0, i;
+	bool dist = false;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+		   "htc_proc_cred_rpt, credit report entries:%d\n", n_entries);
+
+	spin_lock_bh(&target->tx_lock);
+
+	for (i = 0; i < n_entries; i++, rpt++) {
+		if (rpt->eid >= ENDPOINT_MAX) {
+			WARN_ON(1);
+			spin_unlock_bh(&target->tx_lock);
+			return;
+		}
+
+		endpoint = &target->endpoint[rpt->eid];
+
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND, " ep %d got %d credits\n",
+			rpt->eid, rpt->credits);
+
+		endpoint->ep_st.tx_cred_rpt += 1;
+		endpoint->ep_st.cred_retnd += rpt->credits;
+
+		if (from_ep == rpt->eid) {
+			/*
+			 * This credit report arrived on the same endpoint
+			 * indicating it arrived in an RX packet.
+			 */
+			endpoint->ep_st.cred_from_rx += rpt->credits;
+			endpoint->ep_st.cred_rpt_from_rx += 1;
+		} else if (from_ep == ENDPOINT_0) {
+			/* credit arrived on endpoint 0 as a NULL message */
+			endpoint->ep_st.cred_from_ep0 += rpt->credits;
+			endpoint->ep_st.cred_rpt_ep0 += 1;
+		} else {
+			endpoint->ep_st.cred_from_other += rpt->credits;
+			endpoint->ep_st.cred_rpt_from_other += 1;
+		}
+
+		if (rpt->eid == ENDPOINT_0)
+			/* always give endpoint 0 credits back */
+			endpoint->cred_dist.credits += rpt->credits;
+		else {
+			endpoint->cred_dist.cred_to_dist += rpt->credits;
+			dist = true;
+		}
+
+		/*
+		 * Refresh tx depth for distribution function that will
+		 * recover these credits NOTE: this is only valid when
+		 * there are credits to recover!
+		 */
+		endpoint->cred_dist.txq_depth =
+			get_queue_depth(&endpoint->txq);
+
+		tot_credits += rpt->credits;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
+		   "report indicated %d credits to distribute\n",
+		   tot_credits);
+
+	if (dist) {
+		/*
+		 * This was a credit return based on a completed send
+		 * operations note, this is done with the lock held
+		 */
+		ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "ctxt:0x%p dist:0x%p\n",
+			   target->cred_dist_cntxt, &target->cred_dist_list);
+
+		ath6k_credit_distribute(target->cred_dist_cntxt,
+					&target->cred_dist_list,
+					HTC_CREDIT_DIST_SEND_COMPLETE);
+	}
+
+	spin_unlock_bh(&target->tx_lock);
+
+	if (tot_credits)
+		htc_chk_ep_txq(target);
+}
+
+static int htc_parse_trailer(struct htc_target *target,
+			     struct htc_record_hdr *record,
+			     u8 *record_buf, u32 *next_lk_ahds,
+			     enum htc_endpoint_id endpoint,
+			     int *n_lk_ahds)
+{
+	struct htc_bundle_lkahd_rpt *bundle_lkahd_rpt;
+	struct htc_lookahead_report *lk_ahd;
+	int len;
+
+	switch (record->rec_id) {
+	case HTC_RECORD_CREDITS:
+		len = record->len / sizeof(struct htc_credit_report);
+		if (!len) {
+			WARN_ON(1);
+			return -EINVAL;
+		}
+
+		htc_proc_cred_rpt(target,
+				  (struct htc_credit_report *) record_buf,
+				  len, endpoint);
+		break;
+	case HTC_RECORD_LOOKAHEAD:
+		len = record->len / sizeof(*lk_ahd);
+		if (!len) {
+			WARN_ON(1);
+			return -EINVAL;
+		}
+
+		lk_ahd = (struct htc_lookahead_report *) record_buf;
+		if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF))
+		    && next_lk_ahds) {
+
+			ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+				   "lk_ahd report found (pre valid:0x%X, post valid:0x%X)\n",
+				   lk_ahd->pre_valid, lk_ahd->post_valid);
+
+			/* look ahead bytes are valid, copy them over */
+			memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4);
+
+			ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Next Look Ahead",
+					next_lk_ahds, 4);
+
+			*n_lk_ahds = 1;
+		}
+		break;
+	case HTC_RECORD_LOOKAHEAD_BUNDLE:
+		len = record->len / sizeof(*bundle_lkahd_rpt);
+		if (!len || (len > HTC_HOST_MAX_MSG_PER_BUNDLE)) {
+			WARN_ON(1);
+			return -EINVAL;
+		}
+
+		if (next_lk_ahds) {
+			int i;
+
+			bundle_lkahd_rpt =
+				(struct htc_bundle_lkahd_rpt *) record_buf;
+
+			ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Bundle lk_ahd",
+					record_buf, record->len);
+
+			for (i = 0; i < len; i++) {
+				memcpy((u8 *)&next_lk_ahds[i],
+				       bundle_lkahd_rpt->lk_ahd, 4);
+				bundle_lkahd_rpt++;
+			}
+
+			*n_lk_ahds = i;
+		}
+		break;
+	default:
+		ath6kl_err("unhandled record: id:%d len:%d\n",
+			   record->rec_id, record->len);
+		break;
+	}
+
+	return 0;
+
+}
+
+static int htc_proc_trailer(struct htc_target *target,
+			    u8 *buf, int len, u32 *next_lk_ahds,
+			    int *n_lk_ahds, enum htc_endpoint_id endpoint)
+{
+	struct htc_record_hdr *record;
+	int orig_len;
+	int status;
+	u8 *record_buf;
+	u8 *orig_buf;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "+htc_proc_trailer (len:%d)\n", len);
+
+	ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Recv Trailer", buf, len);
+
+	orig_buf = buf;
+	orig_len = len;
+	status = 0;
+
+	while (len > 0) {
+
+		if (len < sizeof(struct htc_record_hdr)) {
+			status = -ENOMEM;
+			break;
+		}
+		/* these are byte aligned structs */
+		record = (struct htc_record_hdr *) buf;
+		len -= sizeof(struct htc_record_hdr);
+		buf += sizeof(struct htc_record_hdr);
+
+		if (record->len > len) {
+			ath6kl_err("invalid record len: %d (id:%d) buf has: %d bytes left\n",
+				   record->len, record->rec_id, len);
+			status = -ENOMEM;
+			break;
+		}
+		record_buf = buf;
+
+		status = htc_parse_trailer(target, record, record_buf,
+					   next_lk_ahds, endpoint, n_lk_ahds);
+
+		if (status)
+			break;
+
+		/* advance buffer past this record for next time around */
+		buf += record->len;
+		len -= record->len;
+	}
+
+	if (status)
+		ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD Recv Trailer",
+				orig_buf, orig_len);
+
+	return status;
+}
+
+static int htc_proc_rxhdr(struct htc_target *target,
+			  struct htc_packet *packet,
+			  u32 *next_lkahds, int *n_lkahds)
+{
+	int status = 0;
+	u16 payload_len;
+	u32 lk_ahd;
+	struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)packet->buf;
+
+	if (n_lkahds != NULL)
+		*n_lkahds = 0;
+
+	ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", packet->buf,
+			packet->act_len);
+
+	/*
+	 * NOTE: we cannot assume the alignment of buf, so we use the safe
+	 * macros to retrieve 16 bit fields.
+	 */
+	payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len));
+
+	memcpy((u8 *)&lk_ahd, packet->buf, sizeof(lk_ahd));
+
+	if (packet->info.rx.rx_flags & HTC_RX_PKT_REFRESH_HDR) {
+		/*
+		 * Refresh the expected header and the actual length as it
+		 * was unknown when this packet was grabbed as part of the
+		 * bundle.
+		 */
+		packet->info.rx.exp_hdr = lk_ahd;
+		packet->act_len = payload_len + HTC_HDR_LENGTH;
+
+		/* validate the actual header that was refreshed  */
+		if (packet->act_len > packet->buf_len) {
+			ath6kl_err("refreshed hdr payload len (%d) in bundled recv is invalid (hdr: 0x%X)\n",
+				   payload_len, lk_ahd);
+			/*
+			 * Limit this to max buffer just to print out some
+			 * of the buffer.
+			 */
+			packet->act_len = min(packet->act_len, packet->buf_len);
+			status = -ENOMEM;
+			goto fail_rx;
+		}
+
+		if (packet->endpoint != htc_hdr->eid) {
+			ath6kl_err("refreshed hdr ep (%d) does not match expected ep (%d)\n",
+				   htc_hdr->eid, packet->endpoint);
+			status = -ENOMEM;
+			goto fail_rx;
+		}
+	}
+
+	if (lk_ahd != packet->info.rx.exp_hdr) {
+		ath6kl_err("htc_proc_rxhdr, lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n",
+			   packet, packet->info.rx.rx_flags);
+		ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Expected Message lk_ahd",
+				&packet->info.rx.exp_hdr, 4);
+		ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Current Frame Header",
+				(u8 *)&lk_ahd, sizeof(lk_ahd));
+		status = -ENOMEM;
+		goto fail_rx;
+	}
+
+	if (htc_hdr->flags & HTC_FLG_RX_TRAILER) {
+		if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) ||
+		    htc_hdr->ctrl[0] > payload_len) {
+			ath6kl_err("htc_proc_rxhdr, invalid hdr (payload len should be :%d, CB[0] is:%d)\n",
+				   payload_len, htc_hdr->ctrl[0]);
+			status = -ENOMEM;
+			goto fail_rx;
+		}
+
+		if (packet->info.rx.rx_flags & HTC_RX_PKT_IGNORE_LOOKAHEAD) {
+			next_lkahds = NULL;
+			n_lkahds = NULL;
+		}
+
+		status = htc_proc_trailer(target, packet->buf + HTC_HDR_LENGTH
+					  + payload_len - htc_hdr->ctrl[0],
+					  htc_hdr->ctrl[0], next_lkahds,
+					   n_lkahds, packet->endpoint);
+
+		if (status)
+			goto fail_rx;
+
+		packet->act_len -= htc_hdr->ctrl[0];
+	}
+
+	packet->buf += HTC_HDR_LENGTH;
+	packet->act_len -= HTC_HDR_LENGTH;
+
+fail_rx:
+	if (status)
+		ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD HTC Recv PKT",
+				packet->buf,
+				packet->act_len < 256 ? packet->act_len : 256);
+	else {
+		if (packet->act_len > 0)
+			ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
+					"HTC - Application Msg",
+					packet->buf, packet->act_len);
+	}
+
+	return status;
+}
+
+static void do_rx_completion(struct htc_endpoint *endpoint,
+			     struct htc_packet *packet)
+{
+		ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+			   "htc calling ep %d recv callback on packet 0x%p\n",
+			   endpoint->eid, packet);
+		endpoint->ep_cb.rx(endpoint->target, packet);
+}
+
+static int htc_issue_rxpkt_bundle(struct htc_target *target,
+				  struct list_head *rxq,
+				  struct list_head *sync_compq,
+				  int *n_pkt_fetched, bool part_bundle)
+{
+	struct hif_scatter_req *scat_req;
+	struct htc_packet *packet;
+	int rem_space = target->max_rx_bndl_sz;
+	int n_scat_pkt, status = 0, i, len;
+
+	n_scat_pkt = get_queue_depth(rxq);
+	n_scat_pkt = min(n_scat_pkt, target->msg_per_bndl_max);
+
+	if ((get_queue_depth(rxq) - n_scat_pkt) > 0) {
+		/*
+		 * We were forced to split this bundle receive operation
+		 * all packets in this partial bundle must have their
+		 * lookaheads ignored.
+		 */
+		part_bundle = true;
+
+		/*
+		 * This would only happen if the target ignored our max
+		 * bundle limit.
+		 */
+		ath6kl_warn("htc_issue_rxpkt_bundle : partial bundle detected num:%d , %d\n",
+			    get_queue_depth(rxq), n_scat_pkt);
+	}
+
+	len = 0;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+		"htc_issue_rxpkt_bundle (numpackets: %d , actual : %d)\n",
+		get_queue_depth(rxq), n_scat_pkt);
+
+	scat_req = hif_scatter_req_get(target->dev->ar);
+
+	if (scat_req == NULL)
+		goto fail_rx_pkt;
+
+	for (i = 0; i < n_scat_pkt; i++) {
+		int pad_len;
+
+		packet = list_first_entry(rxq, struct htc_packet, list);
+		list_del(&packet->list);
+
+		pad_len = CALC_TXRX_PADDED_LEN(target,
+						   packet->act_len);
+
+		if ((rem_space - pad_len) < 0) {
+			list_add(&packet->list, rxq);
+			break;
+		}
+
+		rem_space -= pad_len;
+
+		if (part_bundle || (i < (n_scat_pkt - 1)))
+			/*
+			 * Packet 0..n-1 cannot be checked for look-aheads
+			 * since we are fetching a bundle the last packet
+			 * however can have it's lookahead used
+			 */
+			packet->info.rx.rx_flags |=
+			    HTC_RX_PKT_IGNORE_LOOKAHEAD;
+
+		/* NOTE: 1 HTC packet per scatter entry */
+		scat_req->scat_list[i].buf = packet->buf;
+		scat_req->scat_list[i].len = pad_len;
+
+		packet->info.rx.rx_flags |= HTC_RX_PKT_PART_OF_BUNDLE;
+
+		list_add_tail(&packet->list, sync_compq);
+
+		WARN_ON(!scat_req->scat_list[i].len);
+		len += scat_req->scat_list[i].len;
+	}
+
+	scat_req->len = len;
+	scat_req->scat_entries = i;
+
+	status = ath6kldev_submit_scat_req(target->dev, scat_req, true);
+
+	if (!status)
+		*n_pkt_fetched = i;
+
+	/* free scatter request */
+	hif_scatter_req_add(target->dev->ar, scat_req);
+
+fail_rx_pkt:
+
+	return status;
+}
+
+static int htc_proc_fetched_rxpkts(struct htc_target *target,
+				   struct list_head *comp_pktq, u32 lk_ahds[],
+				   int *n_lk_ahd)
+{
+	struct htc_packet *packet, *tmp_pkt;
+	struct htc_endpoint *ep;
+	int status = 0;
+
+	list_for_each_entry_safe(packet, tmp_pkt, comp_pktq, list) {
+		list_del(&packet->list);
+		ep = &target->endpoint[packet->endpoint];
+
+		/* process header for each of the recv packet */
+		status = htc_proc_rxhdr(target, packet, lk_ahds, n_lk_ahd);
+		if (status)
+			return status;
+
+		if (list_empty(comp_pktq)) {
+			/*
+			 * Last packet's more packet flag is set
+			 * based on the lookahead.
+			 */
+			if (*n_lk_ahd > 0)
+				set_rxpkt_indication_flag(lk_ahds[0],
+							  ep, packet);
+		} else
+			/*
+			 * Packets in a bundle automatically have
+			 * this flag set.
+			 */
+			packet->info.rx.indicat_flags |=
+				HTC_RX_FLAGS_INDICATE_MORE_PKTS;
+
+		htc_update_rx_stats(ep, *n_lk_ahd);
+
+		if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE)
+			ep->ep_st.rx_bundl += 1;
+
+		do_rx_completion(ep, packet);
+	}
+
+	return status;
+}
+
+static int htc_fetch_rxpkts(struct htc_target *target,
+			    struct list_head *rx_pktq,
+			    struct list_head *comp_pktq)
+{
+	int fetched_pkts;
+	bool part_bundle = false;
+	int status = 0;
+
+	/* now go fetch the list of HTC packets */
+	while (!list_empty(rx_pktq)) {
+		fetched_pkts = 0;
+
+		if (target->rx_bndl_enable && (get_queue_depth(rx_pktq) > 1)) {
+			/*
+			 * There are enough packets to attempt a
+			 * bundle transfer and recv bundling is
+			 * allowed.
+			 */
+			status = htc_issue_rxpkt_bundle(target, rx_pktq,
+							comp_pktq,
+							&fetched_pkts,
+							part_bundle);
+			if (status)
+				return status;
+
+			if (!list_empty(rx_pktq))
+				part_bundle = true;
+		}
+
+		if (!fetched_pkts) {
+			struct htc_packet *packet;
+
+			packet = list_first_entry(rx_pktq, struct htc_packet,
+						   list);
+
+			list_del(&packet->list);
+
+			/* fully synchronous */
+			packet->completion = NULL;
+
+			if (!list_empty(rx_pktq))
+				/*
+				 * look_aheads in all packet
+				 * except the last one in the
+				 * bundle must be ignored
+				 */
+				packet->info.rx.rx_flags |=
+					HTC_RX_PKT_IGNORE_LOOKAHEAD;
+
+			/* go fetch the packet */
+			status = dev_rx_pkt(target, packet, packet->act_len);
+			if (status)
+				return status;
+
+			list_add_tail(&packet->list, comp_pktq);
+		}
+	}
+
+	return status;
+}
+
+int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
+				     u32 msg_look_ahead[], int *num_pkts)
+{
+	struct htc_packet *packets, *tmp_pkt;
+	struct htc_endpoint *endpoint;
+	struct list_head rx_pktq, comp_pktq;
+	int status = 0;
+	u32 look_aheads[HTC_HOST_MAX_MSG_PER_BUNDLE];
+	int num_look_ahead = 1;
+	enum htc_endpoint_id id;
+	int n_fetched = 0;
+
+	*num_pkts = 0;
+
+	/*
+	 * On first entry copy the look_aheads into our temp array for
+	 * processing
+	 */
+	memcpy(look_aheads, msg_look_ahead, sizeof(look_aheads));
+
+	while (true) {
+
+		/*
+		 * First lookahead sets the expected endpoint IDs for all
+		 * packets in a bundle.
+		 */
+		id = ((struct htc_frame_hdr *)&look_aheads[0])->eid;
+		endpoint = &target->endpoint[id];
+
+		if (id >= ENDPOINT_MAX) {
+			ath6kl_err("MsgPend, invalid endpoint in look-ahead: %d\n",
+				   id);
+			status = -ENOMEM;
+			break;
+		}
+
+		INIT_LIST_HEAD(&rx_pktq);
+		INIT_LIST_HEAD(&comp_pktq);
+
+		/*
+		 * Try to allocate as many HTC RX packets indicated by the
+		 * look_aheads.
+		 */
+		status = alloc_and_prep_rxpkts(target, look_aheads,
+					       num_look_ahead, endpoint,
+					       &rx_pktq);
+		if (status)
+			break;
+
+		if (get_queue_depth(&rx_pktq) >= 2)
+			/*
+			 * A recv bundle was detected, force IRQ status
+			 * re-check again
+			 */
+			target->chk_irq_status_cnt = 1;
+
+		n_fetched += get_queue_depth(&rx_pktq);
+
+		num_look_ahead = 0;
+
+		status = htc_fetch_rxpkts(target, &rx_pktq, &comp_pktq);
+
+		if (!status)
+			chk_rx_water_mark(endpoint);
+
+		/* Process fetched packets */
+		status = htc_proc_fetched_rxpkts(target, &comp_pktq,
+						 look_aheads, &num_look_ahead);
+
+		if (!num_look_ahead || status)
+			break;
+
+		/*
+		 * For SYNCH processing, if we get here, we are running
+		 * through the loop again due to a detected lookahead. Set
+		 * flag that we should re-check IRQ status registers again
+		 * before leaving IRQ processing, this can net better
+		 * performance in high throughput situations.
+		 */
+		target->chk_irq_status_cnt = 1;
+	}
+
+	if (status) {
+		ath6kl_err("failed to get pending recv messages: %d\n",
+			   status);
+		/*
+		 * Cleanup any packets we allocated but didn't use to
+		 * actually fetch any packets.
+		 */
+		list_for_each_entry_safe(packets, tmp_pkt, &rx_pktq, list) {
+			list_del(&packets->list);
+			htc_reclaim_rxbuf(target, packets,
+					&target->endpoint[packets->endpoint]);
+		}
+
+		/* cleanup any packets in sync completion queue */
+		list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) {
+			list_del(&packets->list);
+			htc_reclaim_rxbuf(target, packets,
+					  &target->endpoint[packets->endpoint]);
+		}
+
+		if (target->htc_flags & HTC_OP_STATE_STOPPING) {
+			ath6kl_warn("host is going to stop blocking receiver for htc_stop\n");
+			ath6kldev_rx_control(target->dev, false);
+		}
+	}
+
+	/*
+	 * Before leaving, check to see if host ran out of buffers and
+	 * needs to stop the receiver.
+	 */
+	if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) {
+		ath6kl_warn("host has no rx buffers blocking receiver to prevent overrun\n");
+		ath6kldev_rx_control(target->dev, false);
+	}
+	*num_pkts = n_fetched;
+
+	return status;
+}
+
+/*
+ * Synchronously wait for a control message from the target,
+ * This function is used at initialization time ONLY.  At init messages
+ * on ENDPOINT 0 are expected.
+ */
+static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target)
+{
+	struct htc_packet *packet = NULL;
+	struct htc_frame_hdr *htc_hdr;
+	u32 look_ahead;
+
+	if (ath6kldev_poll_mboxmsg_rx(target->dev, &look_ahead,
+			       HTC_TARGET_RESPONSE_TIMEOUT))
+		return NULL;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+		"htc_wait_for_ctrl_msg: look_ahead : 0x%X\n", look_ahead);
+
+	htc_hdr = (struct htc_frame_hdr *)&look_ahead;
+
+	if (htc_hdr->eid != ENDPOINT_0)
+		return NULL;
+
+	packet = htc_get_control_buf(target, false);
+
+	if (!packet)
+		return NULL;
+
+	packet->info.rx.rx_flags = 0;
+	packet->info.rx.exp_hdr = look_ahead;
+	packet->act_len = le16_to_cpu(htc_hdr->payld_len) + HTC_HDR_LENGTH;
+
+	if (packet->act_len > packet->buf_len)
+		goto fail_ctrl_rx;
+
+	/* we want synchronous operation */
+	packet->completion = NULL;
+
+	/* get the message from the device, this will block */
+	if (dev_rx_pkt(target, packet, packet->act_len))
+		goto fail_ctrl_rx;
+
+	/* process receive header */
+	packet->status = htc_proc_rxhdr(target, packet, NULL, NULL);
+
+	if (packet->status) {
+		ath6kl_err("htc_wait_for_ctrl_msg, htc_proc_rxhdr failed (status = %d)\n",
+			   packet->status);
+		goto fail_ctrl_rx;
+	}
+
+	return packet;
+
+fail_ctrl_rx:
+	if (packet != NULL) {
+		htc_rxpkt_reset(packet);
+		reclaim_rx_ctrl_buf(target, packet);
+	}
+
+	return NULL;
+}
+
+int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
+				  struct list_head *pkt_queue)
+{
+	struct htc_endpoint *endpoint;
+	struct htc_packet *first_pkt;
+	bool rx_unblock = false;
+	int status = 0, depth;
+
+	if (list_empty(pkt_queue))
+		return -ENOMEM;
+
+	first_pkt = list_first_entry(pkt_queue, struct htc_packet, list);
+
+	if (first_pkt->endpoint >= ENDPOINT_MAX)
+		return status;
+
+	depth = get_queue_depth(pkt_queue);
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+		"htc_add_rxbuf_multiple: ep id: %d, cnt:%d, len: %d\n",
+		first_pkt->endpoint, depth, first_pkt->buf_len);
+
+	endpoint = &target->endpoint[first_pkt->endpoint];
+
+	if (target->htc_flags & HTC_OP_STATE_STOPPING) {
+		struct htc_packet *packet, *tmp_pkt;
+
+		/* walk through queue and mark each one canceled */
+		list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) {
+			packet->status = -ECANCELED;
+			list_del(&packet->list);
+			do_rx_completion(endpoint, packet);
+		}
+
+		return status;
+	}
+
+	spin_lock_bh(&target->rx_lock);
+
+	list_splice_tail_init(pkt_queue, &endpoint->rx_bufq);
+
+	/* check if we are blocked waiting for a new buffer */
+	if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) {
+		if (target->ep_waiting == first_pkt->endpoint) {
+			ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+				"receiver was blocked on ep:%d, unblocking.\n",
+				target->ep_waiting);
+			target->rx_st_flags &= ~HTC_RECV_WAIT_BUFFERS;
+			target->ep_waiting = ENDPOINT_MAX;
+			rx_unblock = true;
+		}
+	}
+
+	spin_unlock_bh(&target->rx_lock);
+
+	if (rx_unblock && !(target->htc_flags & HTC_OP_STATE_STOPPING))
+		/* TODO : implement a buffer threshold count? */
+		ath6kldev_rx_control(target->dev, true);
+
+	return status;
+}
+
+void ath6kl_htc_flush_rx_buf(struct htc_target *target)
+{
+	struct htc_endpoint *endpoint;
+	struct htc_packet *packet, *tmp_pkt;
+	int i;
+
+	for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+		endpoint = &target->endpoint[i];
+		if (!endpoint->svc_id)
+			/* not in use.. */
+			continue;
+
+		spin_lock_bh(&target->rx_lock);
+		list_for_each_entry_safe(packet, tmp_pkt,
+					 &endpoint->rx_bufq, list) {
+			list_del(&packet->list);
+			spin_unlock_bh(&target->rx_lock);
+			ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+				   "flushing rx pkt:0x%p, len:%d, ep:%d\n",
+				   packet, packet->buf_len,
+				   packet->endpoint);
+			dev_kfree_skb(packet->pkt_cntxt);
+			spin_lock_bh(&target->rx_lock);
+		}
+		spin_unlock_bh(&target->rx_lock);
+	}
+}
+
+int ath6kl_htc_conn_service(struct htc_target *target,
+			    struct htc_service_connect_req *conn_req,
+			    struct htc_service_connect_resp *conn_resp)
+{
+	struct htc_packet *rx_pkt = NULL;
+	struct htc_packet *tx_pkt = NULL;
+	struct htc_conn_service_resp *resp_msg;
+	struct htc_conn_service_msg *conn_msg;
+	struct htc_endpoint *endpoint;
+	enum htc_endpoint_id assigned_ep = ENDPOINT_MAX;
+	unsigned int max_msg_sz = 0;
+	int status = 0;
+
+	ath6kl_dbg(ATH6KL_DBG_TRC,
+		   "htc_conn_service, target:0x%p service id:0x%X\n",
+		   target, conn_req->svc_id);
+
+	if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) {
+		/* special case for pseudo control service */
+		assigned_ep = ENDPOINT_0;
+		max_msg_sz = HTC_MAX_CTRL_MSG_LEN;
+	} else {
+		/* allocate a packet to send to the target */
+		tx_pkt = htc_get_control_buf(target, true);
+
+		if (!tx_pkt)
+			return -ENOMEM;
+
+		conn_msg = (struct htc_conn_service_msg *)tx_pkt->buf;
+		memset(conn_msg, 0, sizeof(*conn_msg));
+		conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID);
+		conn_msg->svc_id = cpu_to_le16(conn_req->svc_id);
+		conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags);
+
+		set_htc_pkt_info(tx_pkt, NULL, (u8 *) conn_msg,
+				 sizeof(*conn_msg) + conn_msg->svc_meta_len,
+				 ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
+
+		/* we want synchronous operation */
+		tx_pkt->completion = NULL;
+		htc_prep_send_pkt(tx_pkt, 0, 0, 0);
+		status = htc_issue_send(target, tx_pkt);
+
+		if (status)
+			goto fail_tx;
+
+		/* wait for response */
+		rx_pkt = htc_wait_for_ctrl_msg(target);
+
+		if (!rx_pkt) {
+			status = -ENOMEM;
+			goto fail_tx;
+		}
+
+		resp_msg = (struct htc_conn_service_resp *)rx_pkt->buf;
+
+		if ((le16_to_cpu(resp_msg->msg_id) != HTC_MSG_CONN_SVC_RESP_ID)
+		    || (rx_pkt->act_len < sizeof(*resp_msg))) {
+			status = -ENOMEM;
+			goto fail_tx;
+		}
+
+		conn_resp->resp_code = resp_msg->status;
+		/* check response status */
+		if (resp_msg->status != HTC_SERVICE_SUCCESS) {
+			ath6kl_err("target failed service 0x%X connect request (status:%d)\n",
+				   resp_msg->svc_id, resp_msg->status);
+			status = -ENOMEM;
+			goto fail_tx;
+		}
+
+		assigned_ep = (enum htc_endpoint_id)resp_msg->eid;
+		max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz);
+	}
+
+	if (assigned_ep >= ENDPOINT_MAX || !max_msg_sz) {
+		status = -ENOMEM;
+		goto fail_tx;
+	}
+
+	endpoint = &target->endpoint[assigned_ep];
+	endpoint->eid = assigned_ep;
+	if (endpoint->svc_id) {
+		status = -ENOMEM;
+		goto fail_tx;
+	}
+
+	/* return assigned endpoint to caller */
+	conn_resp->endpoint = assigned_ep;
+	conn_resp->len_max = max_msg_sz;
+
+	/* setup the endpoint */
+
+	/* this marks the endpoint in use */
+	endpoint->svc_id = conn_req->svc_id;
+
+	endpoint->max_txq_depth = conn_req->max_txq_depth;
+	endpoint->len_max = max_msg_sz;
+	endpoint->ep_cb = conn_req->ep_cb;
+	endpoint->cred_dist.svc_id = conn_req->svc_id;
+	endpoint->cred_dist.htc_rsvd = endpoint;
+	endpoint->cred_dist.endpoint = assigned_ep;
+	endpoint->cred_dist.cred_sz = target->tgt_cred_sz;
+
+	if (conn_req->max_rxmsg_sz) {
+		/*
+		 * Override cred_per_msg calculation, this optimizes
+		 * the credit-low indications since the host will actually
+		 * issue smaller messages in the Send path.
+		 */
+		if (conn_req->max_rxmsg_sz > max_msg_sz) {
+			status = -ENOMEM;
+			goto fail_tx;
+		}
+		endpoint->cred_dist.cred_per_msg =
+		    conn_req->max_rxmsg_sz / target->tgt_cred_sz;
+	} else
+		endpoint->cred_dist.cred_per_msg =
+		    max_msg_sz / target->tgt_cred_sz;
+
+	if (!endpoint->cred_dist.cred_per_msg)
+		endpoint->cred_dist.cred_per_msg = 1;
+
+	/* save local connection flags */
+	endpoint->conn_flags = conn_req->flags;
+
+fail_tx:
+	if (tx_pkt)
+		htc_reclaim_txctrl_buf(target, tx_pkt);
+
+	if (rx_pkt) {
+		htc_rxpkt_reset(rx_pkt);
+		reclaim_rx_ctrl_buf(target, rx_pkt);
+	}
+
+	return status;
+}
+
+static void reset_ep_state(struct htc_target *target)
+{
+	struct htc_endpoint *endpoint;
+	int i;
+
+	for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+		endpoint = &target->endpoint[i];
+		memset(&endpoint->cred_dist, 0, sizeof(endpoint->cred_dist));
+		endpoint->svc_id = 0;
+		endpoint->len_max = 0;
+		endpoint->max_txq_depth = 0;
+		memset(&endpoint->ep_st, 0,
+		       sizeof(endpoint->ep_st));
+		INIT_LIST_HEAD(&endpoint->rx_bufq);
+		INIT_LIST_HEAD(&endpoint->txq);
+		endpoint->target = target;
+	}
+
+	/* reset distribution list */
+	INIT_LIST_HEAD(&target->cred_dist_list);
+}
+
+int ath6kl_htc_get_rxbuf_num(struct htc_target *target,
+			     enum htc_endpoint_id endpoint)
+{
+	int num;
+
+	spin_lock_bh(&target->rx_lock);
+	num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq));
+	spin_unlock_bh(&target->rx_lock);
+	return num;
+}
+
+static void htc_setup_msg_bndl(struct htc_target *target)
+{
+	/* limit what HTC can handle */
+	target->msg_per_bndl_max = min(HTC_HOST_MAX_MSG_PER_BUNDLE,
+				       target->msg_per_bndl_max);
+
+	if (ath6kl_hif_enable_scatter(target->dev->ar)) {
+		target->msg_per_bndl_max = 0;
+		return;
+	}
+
+	/* limit bundle what the device layer can handle */
+	target->msg_per_bndl_max = min(target->max_scat_entries,
+				       target->msg_per_bndl_max);
+
+	ath6kl_dbg(ATH6KL_DBG_TRC,
+		   "htc bundling allowed. max msg per htc bundle: %d\n",
+		   target->msg_per_bndl_max);
+
+	/* Max rx bundle size is limited by the max tx bundle size */
+	target->max_rx_bndl_sz = target->max_xfer_szper_scatreq;
+	/* Max tx bundle size if limited by the extended mbox address range */
+	target->max_tx_bndl_sz = min(HIF_MBOX0_EXT_WIDTH,
+				     target->max_xfer_szper_scatreq);
+
+	ath6kl_dbg(ATH6KL_DBG_ANY, "max recv: %d max send: %d\n",
+		   target->max_rx_bndl_sz, target->max_tx_bndl_sz);
+
+	if (target->max_tx_bndl_sz)
+		target->tx_bndl_enable = true;
+
+	if (target->max_rx_bndl_sz)
+		target->rx_bndl_enable = true;
+
+	if ((target->tgt_cred_sz % target->block_sz) != 0) {
+		ath6kl_warn("credit size: %d is not block aligned! Disabling send bundling\n",
+			    target->tgt_cred_sz);
+
+		/*
+		 * Disallow send bundling since the credit size is
+		 * not aligned to a block size the I/O block
+		 * padding will spill into the next credit buffer
+		 * which is fatal.
+		 */
+		target->tx_bndl_enable = false;
+	}
+}
+
+int ath6kl_htc_wait_target(struct htc_target *target)
+{
+	struct htc_packet *packet = NULL;
+	struct htc_ready_ext_msg *rdy_msg;
+	struct htc_service_connect_req connect;
+	struct htc_service_connect_resp resp;
+	int status;
+
+	/* we should be getting 1 control message that the target is ready */
+	packet = htc_wait_for_ctrl_msg(target);
+
+	if (!packet)
+		return -ENOMEM;
+
+	/* we controlled the buffer creation so it's properly aligned */
+	rdy_msg = (struct htc_ready_ext_msg *)packet->buf;
+
+	if ((le16_to_cpu(rdy_msg->ver2_0_info.msg_id) != HTC_MSG_READY_ID) ||
+	    (packet->act_len < sizeof(struct htc_ready_msg))) {
+		status = -ENOMEM;
+		goto fail_wait_target;
+	}
+
+	if (!rdy_msg->ver2_0_info.cred_cnt || !rdy_msg->ver2_0_info.cred_sz) {
+		status = -ENOMEM;
+		goto fail_wait_target;
+	}
+
+	target->tgt_creds = le16_to_cpu(rdy_msg->ver2_0_info.cred_cnt);
+	target->tgt_cred_sz = le16_to_cpu(rdy_msg->ver2_0_info.cred_sz);
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+		   "target ready: credits: %d credit size: %d\n",
+		   target->tgt_creds, target->tgt_cred_sz);
+
+	/* check if this is an extended ready message */
+	if (packet->act_len >= sizeof(struct htc_ready_ext_msg)) {
+		/* this is an extended message */
+		target->htc_tgt_ver = rdy_msg->htc_ver;
+		target->msg_per_bndl_max = rdy_msg->msg_per_htc_bndl;
+	} else {
+		/* legacy */
+		target->htc_tgt_ver = HTC_VERSION_2P0;
+		target->msg_per_bndl_max = 0;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "using htc protocol version : %s (%d)\n",
+		  (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1",
+		  target->htc_tgt_ver);
+
+	if (target->msg_per_bndl_max > 0)
+		htc_setup_msg_bndl(target);
+
+	/* setup our pseudo HTC control endpoint connection */
+	memset(&connect, 0, sizeof(connect));
+	memset(&resp, 0, sizeof(resp));
+	connect.ep_cb.rx = htc_ctrl_rx;
+	connect.ep_cb.rx_refill = NULL;
+	connect.ep_cb.tx_full = NULL;
+	connect.max_txq_depth = NUM_CONTROL_BUFFERS;
+	connect.svc_id = HTC_CTRL_RSVD_SVC;
+
+	/* connect fake service */
+	status = ath6kl_htc_conn_service((void *)target, &connect, &resp);
+
+	if (status)
+		ath6kl_hif_cleanup_scatter(target->dev->ar);
+
+fail_wait_target:
+	if (packet) {
+		htc_rxpkt_reset(packet);
+		reclaim_rx_ctrl_buf(target, packet);
+	}
+
+	return status;
+}
+
+/*
+ * Start HTC, enable interrupts and let the target know
+ * host has finished setup.
+ */
+int ath6kl_htc_start(struct htc_target *target)
+{
+	struct htc_packet *packet;
+	int status;
+
+	/* Disable interrupts at the chip level */
+	ath6kldev_disable_intrs(target->dev);
+
+	target->htc_flags = 0;
+	target->rx_st_flags = 0;
+
+	/* Push control receive buffers into htc control endpoint */
+	while ((packet = htc_get_control_buf(target, false)) != NULL) {
+		status = htc_add_rxbuf(target, packet);
+		if (status)
+			return status;
+	}
+
+	/* NOTE: the first entry in the distribution list is ENDPOINT_0 */
+	ath6k_credit_init(target->cred_dist_cntxt, &target->cred_dist_list,
+			  target->tgt_creds);
+
+	dump_cred_dist_stats(target);
+
+	/* Indicate to the target of the setup completion */
+	status = htc_setup_tx_complete(target);
+
+	if (status)
+		return status;
+
+	/* unmask interrupts */
+	status = ath6kldev_unmask_intrs(target->dev);
+
+	if (status)
+		ath6kl_htc_stop(target);
+
+	return status;
+}
+
+/* htc_stop: stop interrupt reception, and flush all queued buffers */
+void ath6kl_htc_stop(struct htc_target *target)
+{
+	spin_lock_bh(&target->htc_lock);
+	target->htc_flags |= HTC_OP_STATE_STOPPING;
+	spin_unlock_bh(&target->htc_lock);
+
+	/*
+	 * Masking interrupts is a synchronous operation, when this
+	 * function returns all pending HIF I/O has completed, we can
+	 * safely flush the queues.
+	 */
+	ath6kldev_mask_intrs(target->dev);
+
+	ath6kl_htc_flush_txep_all(target);
+
+	ath6kl_htc_flush_rx_buf(target);
+
+	reset_ep_state(target);
+}
+
+void *ath6kl_htc_create(struct ath6kl *ar)
+{
+	struct htc_target *target = NULL;
+	struct htc_packet *packet;
+	int status = 0, i = 0;
+	u32 block_size, ctrl_bufsz;
+
+	target = kzalloc(sizeof(*target), GFP_KERNEL);
+	if (!target) {
+		ath6kl_err("unable to allocate memory\n");
+		return NULL;
+	}
+
+	target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL);
+	if (!target->dev) {
+		ath6kl_err("unable to allocate memory\n");
+		status = -ENOMEM;
+		goto fail_create_htc;
+	}
+
+	spin_lock_init(&target->htc_lock);
+	spin_lock_init(&target->rx_lock);
+	spin_lock_init(&target->tx_lock);
+
+	INIT_LIST_HEAD(&target->free_ctrl_txbuf);
+	INIT_LIST_HEAD(&target->free_ctrl_rxbuf);
+	INIT_LIST_HEAD(&target->cred_dist_list);
+
+	target->dev->ar = ar;
+	target->dev->htc_cnxt = target;
+	target->ep_waiting = ENDPOINT_MAX;
+
+	reset_ep_state(target);
+
+	status = ath6kldev_setup(target->dev);
+
+	if (status)
+		goto fail_create_htc;
+
+	block_size = ar->mbox_info.block_size;
+
+	ctrl_bufsz = (block_size > HTC_MAX_CTRL_MSG_LEN) ?
+		      (block_size + HTC_HDR_LENGTH) :
+		      (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH);
+
+	for (i = 0; i < NUM_CONTROL_BUFFERS; i++) {
+		packet = kzalloc(sizeof(*packet), GFP_KERNEL);
+		if (!packet)
+			break;
+
+		packet->buf_start = kzalloc(ctrl_bufsz, GFP_KERNEL);
+		if (!packet->buf_start) {
+			kfree(packet);
+			break;
+		}
+
+		packet->buf_len = ctrl_bufsz;
+		if (i < NUM_CONTROL_RX_BUFFERS) {
+			packet->act_len = 0;
+			packet->buf = packet->buf_start;
+			packet->endpoint = ENDPOINT_0;
+			list_add_tail(&packet->list, &target->free_ctrl_rxbuf);
+		} else
+			list_add_tail(&packet->list, &target->free_ctrl_txbuf);
+	}
+
+fail_create_htc:
+	if (i != NUM_CONTROL_BUFFERS || status) {
+		if (target) {
+			ath6kl_htc_cleanup(target);
+			target = NULL;
+		}
+	}
+
+	return target;
+}
+
+/* cleanup the HTC instance */
+void ath6kl_htc_cleanup(struct htc_target *target)
+{
+	struct htc_packet *packet, *tmp_packet;
+
+	ath6kl_hif_cleanup_scatter(target->dev->ar);
+
+	list_for_each_entry_safe(packet, tmp_packet,
+			&target->free_ctrl_txbuf, list) {
+		list_del(&packet->list);
+		kfree(packet->buf_start);
+		kfree(packet);
+	}
+
+	list_for_each_entry_safe(packet, tmp_packet,
+			&target->free_ctrl_rxbuf, list) {
+		list_del(&packet->list);
+		kfree(packet->buf_start);
+		kfree(packet);
+	}
+
+	kfree(target->dev);
+	kfree(target);
+}

+ 607 - 0
drivers/net/wireless/ath/ath6kl/htc.h

@@ -0,0 +1,607 @@
+/*
+ * Copyright (c) 2004-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.
+ */
+
+#ifndef HTC_H
+#define HTC_H
+
+#include "common.h"
+
+/* frame header flags */
+
+/* send direction */
+#define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0)
+#define HTC_FLAGS_SEND_BUNDLE        (1 << 1)
+
+/* receive direction */
+#define HTC_FLG_RX_UNUSED        (1 << 0)
+#define HTC_FLG_RX_TRAILER       (1 << 1)
+/* Bundle count maske and shift */
+#define HTC_FLG_RX_BNDL_CNT	 (0xF0)
+#define HTC_FLG_RX_BNDL_CNT_S	 4
+
+#define HTC_HDR_LENGTH  (sizeof(struct htc_frame_hdr))
+#define HTC_MAX_PAYLOAD_LENGTH   (4096 - sizeof(struct htc_frame_hdr))
+
+/* HTC control message IDs */
+
+#define HTC_MSG_READY_ID		1
+#define HTC_MSG_CONN_SVC_ID		2
+#define HTC_MSG_CONN_SVC_RESP_ID	3
+#define HTC_MSG_SETUP_COMPLETE_ID	4
+#define HTC_MSG_SETUP_COMPLETE_EX_ID	5
+
+#define HTC_MAX_CTRL_MSG_LEN  256
+
+#define HTC_VERSION_2P0  0x00
+#define HTC_VERSION_2P1  0x01
+
+#define HTC_SERVICE_META_DATA_MAX_LENGTH 128
+
+#define HTC_CONN_FLGS_THRESH_LVL_QUAT		0x0
+#define HTC_CONN_FLGS_THRESH_LVL_HALF		0x1
+#define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT	0x2
+#define HTC_CONN_FLGS_REDUCE_CRED_DRIB		0x4
+#define HTC_CONN_FLGS_THRESH_MASK		0x3
+
+/* connect response status codes */
+#define HTC_SERVICE_SUCCESS      0
+#define HTC_SERVICE_NOT_FOUND    1
+#define HTC_SERVICE_FAILED       2
+
+/* no resources (i.e. no more endpoints) */
+#define HTC_SERVICE_NO_RESOURCES 3
+
+/* specific service is not allowing any more endpoints */
+#define HTC_SERVICE_NO_MORE_EP   4
+
+/* report record IDs */
+#define HTC_RECORD_NULL             0
+#define HTC_RECORD_CREDITS          1
+#define HTC_RECORD_LOOKAHEAD        2
+#define HTC_RECORD_LOOKAHEAD_BUNDLE 3
+
+#define HTC_SETUP_COMP_FLG_RX_BNDL_EN     (1 << 0)
+
+#define MAKE_SERVICE_ID(group, index) \
+	(int)(((int)group << 8) | (int)(index))
+
+/* NOTE: service ID of 0x0000 is reserved and should never be used */
+#define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1)
+#define WMI_CONTROL_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0)
+#define WMI_DATA_BE_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1)
+#define WMI_DATA_BK_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2)
+#define WMI_DATA_VI_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3)
+#define WMI_DATA_VO_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4)
+#define WMI_MAX_SERVICES  5
+
+/* reserved and used to flush ALL packets */
+#define HTC_TX_PACKET_TAG_ALL          0
+#define HTC_SERVICE_TX_PACKET_TAG      1
+#define HTC_TX_PACKET_TAG_USER_DEFINED (HTC_SERVICE_TX_PACKET_TAG + 9)
+
+/* more packets on this endpoint are being fetched */
+#define HTC_RX_FLAGS_INDICATE_MORE_PKTS  (1 << 0)
+
+/* TODO.. for BMI */
+#define ENDPOINT1 0
+/* TODO -remove me, but we have to fix BMI first */
+#define HTC_MAILBOX_NUM_MAX    4
+
+/* enable send bundle padding for this endpoint */
+#define HTC_FLGS_TX_BNDL_PAD_EN	 (1 << 0)
+#define HTC_EP_ACTIVE                            ((u32) (1u << 31))
+
+/* HTC operational parameters */
+#define HTC_TARGET_RESPONSE_TIMEOUT        2000	/* in ms */
+#define HTC_TARGET_DEBUG_INTR_MASK         0x01
+#define HTC_TARGET_CREDIT_INTR_MASK        0xF0
+
+#define HTC_HOST_MAX_MSG_PER_BUNDLE        8
+#define HTC_MIN_HTC_MSGS_TO_BUNDLE         2
+
+/* packet flags */
+
+#define HTC_RX_PKT_IGNORE_LOOKAHEAD      (1 << 0)
+#define HTC_RX_PKT_REFRESH_HDR           (1 << 1)
+#define HTC_RX_PKT_PART_OF_BUNDLE        (1 << 2)
+#define HTC_RX_PKT_NO_RECYCLE            (1 << 3)
+
+#define NUM_CONTROL_BUFFERS     8
+#define NUM_CONTROL_TX_BUFFERS  2
+#define NUM_CONTROL_RX_BUFFERS  (NUM_CONTROL_BUFFERS - NUM_CONTROL_TX_BUFFERS)
+
+#define HTC_RECV_WAIT_BUFFERS        (1 << 0)
+#define HTC_OP_STATE_STOPPING        (1 << 0)
+
+/*
+ * The frame header length and message formats defined herein were selected
+ * to accommodate optimal alignment for target processing. This reduces
+ * code size and improves performance. Any changes to the header length may
+ * alter the alignment and cause exceptions on the target. When adding to
+ * the messagestructures insure that fields are properly aligned.
+ */
+
+/* HTC frame header
+ *
+ * NOTE: do not remove or re-arrange the fields, these are minimally
+ * required to take advantage of 4-byte lookaheads in some hardware
+ * implementations.
+ */
+struct htc_frame_hdr {
+	u8 eid;
+	u8 flags;
+
+	/* length of data (including trailer) that follows the header */
+	__le16 payld_len;
+
+	/* end of 4-byte lookahead */
+
+	u8 ctrl[2];
+} __packed;
+
+/* HTC ready message */
+struct htc_ready_msg {
+	__le16 msg_id;
+	__le16 cred_cnt;
+	__le16 cred_sz;
+	u8 max_ep;
+	u8 pad;
+} __packed;
+
+/* extended HTC ready message */
+struct htc_ready_ext_msg {
+	struct htc_ready_msg ver2_0_info;
+	u8 htc_ver;
+	u8 msg_per_htc_bndl;
+} __packed;
+
+/* connect service */
+struct htc_conn_service_msg {
+	__le16 msg_id;
+	__le16 svc_id;
+	__le16 conn_flags;
+	u8 svc_meta_len;
+	u8 pad;
+} __packed;
+
+/* connect response */
+struct htc_conn_service_resp {
+	__le16 msg_id;
+	__le16 svc_id;
+	u8 status;
+	u8 eid;
+	__le16 max_msg_sz;
+	u8 svc_meta_len;
+	u8 pad;
+} __packed;
+
+struct htc_setup_comp_msg {
+	__le16 msg_id;
+} __packed;
+
+/* extended setup completion message */
+struct htc_setup_comp_ext_msg {
+	__le16 msg_id;
+	__le32 flags;
+	u8 msg_per_rxbndl;
+	u8 Rsvd[3];
+} __packed;
+
+struct htc_record_hdr {
+	u8 rec_id;
+	u8 len;
+} __packed;
+
+struct htc_credit_report {
+	u8 eid;
+	u8 credits;
+} __packed;
+
+/*
+ * NOTE: The lk_ahd array is guarded by a pre_valid
+ * and Post Valid guard bytes. The pre_valid bytes must
+ * equal the inverse of the post_valid byte.
+ */
+struct htc_lookahead_report {
+	u8 pre_valid;
+	u8 lk_ahd[4];
+	u8 post_valid;
+} __packed;
+
+struct htc_bundle_lkahd_rpt {
+	u8 lk_ahd[4];
+} __packed;
+
+/* Current service IDs */
+
+enum htc_service_grp_ids {
+	RSVD_SERVICE_GROUP = 0,
+	WMI_SERVICE_GROUP = 1,
+
+	HTC_TEST_GROUP = 254,
+	HTC_SERVICE_GROUP_LAST = 255
+};
+
+/* ------ endpoint IDS ------ */
+
+enum htc_endpoint_id {
+	ENDPOINT_UNUSED = -1,
+	ENDPOINT_0 = 0,
+	ENDPOINT_1 = 1,
+	ENDPOINT_2 = 2,
+	ENDPOINT_3,
+	ENDPOINT_4,
+	ENDPOINT_5,
+	ENDPOINT_6,
+	ENDPOINT_7,
+	ENDPOINT_8,
+	ENDPOINT_MAX,
+};
+
+struct htc_tx_packet_info {
+	u16 tag;
+	int cred_used;
+	u8 flags;
+	int seqno;
+};
+
+struct htc_rx_packet_info {
+	u32 exp_hdr;
+	u32 rx_flags;
+	u32 indicat_flags;
+};
+
+struct htc_target;
+
+/* wrapper around endpoint-specific packets */
+struct htc_packet {
+	struct list_head list;
+
+	/* caller's per packet specific context */
+	void *pkt_cntxt;
+
+	/*
+	 * the true buffer start , the caller can store the real
+	 * buffer start here.  In receive callbacks, the HTC layer
+	 * sets buf to the start of the payload past the header.
+	 * This field allows the caller to reset buf when it recycles
+	 * receive packets back to HTC.
+	 */
+	u8 *buf_start;
+
+	/*
+	 * Pointer to the start of the buffer. In the transmit
+	 * direction this points to the start of the payload. In the
+	 * receive direction, however, the buffer when queued up
+	 * points to the start of the HTC header but when returned
+	 * to the caller points to the start of the payload
+	 */
+	u8 *buf;
+	u32 buf_len;
+
+	/* actual length of payload */
+	u32 act_len;
+
+	/* endpoint that this packet was sent/recv'd from */
+	enum htc_endpoint_id endpoint;
+
+	/* completion status */
+
+	int status;
+	union {
+		struct htc_tx_packet_info tx;
+		struct htc_rx_packet_info rx;
+	} info;
+
+	void (*completion) (struct htc_target *, struct htc_packet *);
+	struct htc_target *context;
+};
+
+enum htc_send_full_action {
+	HTC_SEND_FULL_KEEP = 0,
+	HTC_SEND_FULL_DROP = 1,
+};
+
+struct htc_ep_callbacks {
+	void (*rx) (struct htc_target *, struct htc_packet *);
+	void (*rx_refill) (struct htc_target *, enum htc_endpoint_id endpoint);
+	enum htc_send_full_action (*tx_full) (struct htc_target *,
+					      struct htc_packet *);
+	struct htc_packet *(*rx_allocthresh) (struct htc_target *,
+					      enum htc_endpoint_id, int);
+	int rx_alloc_thresh;
+	int rx_refill_thresh;
+};
+
+/* service connection information */
+struct htc_service_connect_req {
+	u16 svc_id;
+	u16 conn_flags;
+	struct htc_ep_callbacks ep_cb;
+	int max_txq_depth;
+	u32 flags;
+	unsigned int max_rxmsg_sz;
+};
+
+/* service connection response information */
+struct htc_service_connect_resp {
+	u8 buf_len;
+	u8 act_len;
+	enum htc_endpoint_id endpoint;
+	unsigned int len_max;
+	u8 resp_code;
+};
+
+/* endpoint distributionstructure */
+struct htc_endpoint_credit_dist {
+	struct list_head list;
+
+	/* Service ID (set by HTC) */
+	u16 svc_id;
+
+	/* endpoint for this distributionstruct (set by HTC) */
+	enum htc_endpoint_id endpoint;
+
+	u32 dist_flags;
+
+	/*
+	 * credits for normal operation, anything above this
+	 * indicates the endpoint is over-subscribed.
+	 */
+	int cred_norm;
+
+	/* floor for credit distribution */
+	int cred_min;
+
+	int cred_assngd;
+
+	/* current credits available */
+	int credits;
+
+	/*
+	 * pending credits to distribute on this endpoint, this
+	 * is set by HTC when credit reports arrive.  The credit
+	 * distribution functions sets this to zero when it distributes
+	 * the credits.
+	 */
+	int cred_to_dist;
+
+	/*
+	 * the number of credits that the current pending TX packet needs
+	 * to transmit. This is set by HTC when endpoint needs credits in
+	 * order to transmit.
+	 */
+	int seek_cred;
+
+	/* size in bytes of each credit */
+	int cred_sz;
+
+	/* credits required for a maximum sized messages */
+	int cred_per_msg;
+
+	/* reserved for HTC use */
+	void *htc_rsvd;
+
+	/*
+	 * current depth of TX queue , i.e. messages waiting for credits
+	 * This field is valid only when HTC_CREDIT_DIST_ACTIVITY_CHANGE
+	 * or HTC_CREDIT_DIST_SEND_COMPLETE is indicated on an endpoint
+	 * that has non-zero credits to recover.
+	 */
+	int txq_depth;
+};
+
+/*
+ * credit distibution code that is passed into the distrbution function,
+ * there are mandatory and optional codes that must be handled
+ */
+enum htc_credit_dist_reason {
+	HTC_CREDIT_DIST_SEND_COMPLETE = 0,
+	HTC_CREDIT_DIST_ACTIVITY_CHANGE = 1,
+	HTC_CREDIT_DIST_SEEK_CREDITS,
+};
+
+struct htc_credit_state_info {
+	int total_avail_credits;
+	int cur_free_credits;
+	struct list_head lowestpri_ep_dist;
+};
+
+/* endpoint statistics */
+struct htc_endpoint_stats {
+	/*
+	 * number of times the host set the credit-low flag in a send
+	 * message on this endpoint
+	 */
+	u32 cred_low_indicate;
+
+	u32 tx_issued;
+	u32 tx_pkt_bundled;
+	u32 tx_bundles;
+	u32 tx_dropped;
+
+	/* running count of total credit reports received for this endpoint */
+	u32 tx_cred_rpt;
+
+	/* credit reports received from this endpoint's RX packets */
+	u32 cred_rpt_from_rx;
+
+	/* credit reports received from RX packets of other endpoints */
+	u32 cred_rpt_from_other;
+
+	/* credit reports received from endpoint 0 RX packets */
+	u32 cred_rpt_ep0;
+
+	/* count of credits received via Rx packets on this endpoint */
+	u32 cred_from_rx;
+
+	/* count of credits received via another endpoint */
+	u32 cred_from_other;
+
+	/* count of credits received via another endpoint */
+	u32 cred_from_ep0;
+
+	/* count of consummed credits */
+	u32 cred_cosumd;
+
+	/* count of credits returned */
+	u32 cred_retnd;
+
+	u32 rx_pkts;
+
+	/* count of lookahead records found in Rx msg */
+	u32 rx_lkahds;
+
+	/* count of recv packets received in a bundle */
+	u32 rx_bundl;
+
+	/* count of number of bundled lookaheads */
+	u32 rx_bundle_lkahd;
+
+	/* count of the number of bundle indications from the HTC header */
+	u32 rx_bundle_from_hdr;
+
+	/* the number of times the recv allocation threshold was hit */
+	u32 rx_alloc_thresh_hit;
+
+	/* total number of bytes */
+	u32 rxalloc_thresh_byte;
+};
+
+struct htc_endpoint {
+	enum htc_endpoint_id eid;
+	u16 svc_id;
+	struct list_head txq;
+	struct list_head rx_bufq;
+	struct htc_endpoint_credit_dist cred_dist;
+	struct htc_ep_callbacks ep_cb;
+	int max_txq_depth;
+	int len_max;
+	int tx_proc_cnt;
+	int rx_proc_cnt;
+	struct htc_target *target;
+	u8 seqno;
+	u32 conn_flags;
+	struct htc_endpoint_stats ep_st;
+};
+
+struct htc_control_buffer {
+	struct htc_packet packet;
+	u8 *buf;
+};
+
+struct ath6kl_device;
+
+/* our HTC target state */
+struct htc_target {
+	struct htc_endpoint endpoint[ENDPOINT_MAX];
+	struct list_head cred_dist_list;
+	struct list_head free_ctrl_txbuf;
+	struct list_head free_ctrl_rxbuf;
+	struct htc_credit_state_info *cred_dist_cntxt;
+	int tgt_creds;
+	unsigned int tgt_cred_sz;
+	spinlock_t htc_lock;
+	spinlock_t rx_lock;
+	spinlock_t tx_lock;
+	struct ath6kl_device *dev;
+	u32 htc_flags;
+	u32 rx_st_flags;
+	enum htc_endpoint_id ep_waiting;
+	u8 htc_tgt_ver;
+
+	/* max messages per bundle for HTC */
+	int msg_per_bndl_max;
+
+	bool tx_bndl_enable;
+	int rx_bndl_enable;
+	int max_rx_bndl_sz;
+	int max_tx_bndl_sz;
+
+	u32 block_sz;
+	u32 block_mask;
+
+	int max_scat_entries;
+	int max_xfer_szper_scatreq;
+
+	int chk_irq_status_cnt;
+};
+
+void *ath6kl_htc_create(struct ath6kl *ar);
+void ath6kl_htc_set_credit_dist(struct htc_target *target,
+				struct htc_credit_state_info *cred_info,
+				u16 svc_pri_order[], int len);
+int ath6kl_htc_wait_target(struct htc_target *target);
+int ath6kl_htc_start(struct htc_target *target);
+int ath6kl_htc_conn_service(struct htc_target *target,
+			    struct htc_service_connect_req *req,
+			    struct htc_service_connect_resp *resp);
+int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet);
+void ath6kl_htc_stop(struct htc_target *target);
+void ath6kl_htc_cleanup(struct htc_target *target);
+void ath6kl_htc_flush_txep(struct htc_target *target,
+			   enum htc_endpoint_id endpoint, u16 tag);
+void ath6kl_htc_flush_rx_buf(struct htc_target *target);
+void ath6kl_htc_indicate_activity_change(struct htc_target *target,
+					 enum htc_endpoint_id endpoint,
+					 bool active);
+int ath6kl_htc_get_rxbuf_num(struct htc_target *target,
+			     enum htc_endpoint_id endpoint);
+int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
+				  struct list_head *pktq);
+int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
+				     u32 msg_look_ahead[], int *n_pkts);
+
+static inline void set_htc_pkt_info(struct htc_packet *packet, void *context,
+				    u8 *buf, unsigned int len,
+				    enum htc_endpoint_id eid, u16 tag)
+{
+	packet->pkt_cntxt = context;
+	packet->buf = buf;
+	packet->act_len = len;
+	packet->endpoint = eid;
+	packet->info.tx.tag = tag;
+}
+
+static inline void htc_rxpkt_reset(struct htc_packet *packet)
+{
+	packet->buf = packet->buf_start;
+	packet->act_len = 0;
+}
+
+static inline void set_htc_rxpkt_info(struct htc_packet *packet, void *context,
+				      u8 *buf, unsigned long len,
+				      enum htc_endpoint_id eid)
+{
+	packet->pkt_cntxt = context;
+	packet->buf = buf;
+	packet->buf_start = buf;
+	packet->buf_len = len;
+	packet->endpoint = eid;
+}
+
+static inline int get_queue_depth(struct list_head *queue)
+{
+	struct list_head *tmp_list;
+	int depth = 0;
+
+	list_for_each(tmp_list, queue)
+	    depth++;
+
+	return depth;
+}
+
+#endif

+ 641 - 0
drivers/net/wireless/ath/ath6kl/htc_hif.c

@@ -0,0 +1,641 @@
+/*
+ * 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 "core.h"
+#include "target.h"
+#include "hif-ops.h"
+#include "htc_hif.h"
+#include "debug.h"
+
+#define MAILBOX_FOR_BLOCK_SIZE          1
+
+#define ATH6KL_TIME_QUANTUM	10  /* in ms */
+
+static int ath6kldev_cp_scat_dma_buf(struct hif_scatter_req *req, bool from_dma)
+{
+	u8 *buf;
+	int i;
+
+	buf = req->virt_dma_buf;
+
+	for (i = 0; i < req->scat_entries; i++) {
+
+		if (from_dma)
+			memcpy(req->scat_list[i].buf, buf,
+			       req->scat_list[i].len);
+		else
+			memcpy(buf, req->scat_list[i].buf,
+			       req->scat_list[i].len);
+
+		buf += req->scat_list[i].len;
+	}
+
+	return 0;
+}
+
+int ath6kldev_rw_comp_handler(void *context, int status)
+{
+	struct htc_packet *packet = context;
+
+	ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
+		   "ath6kldev_rw_comp_handler (pkt:0x%p , status: %d\n",
+		   packet, status);
+
+	packet->status = status;
+	packet->completion(packet->context, packet);
+
+	return 0;
+}
+
+static int ath6kldev_proc_dbg_intr(struct ath6kl_device *dev)
+{
+	u32 dummy;
+	int status;
+
+	ath6kl_err("target debug interrupt\n");
+
+	ath6kl_target_failure(dev->ar);
+
+	/*
+	 * read counter to clear the interrupt, the debug error interrupt is
+	 * counter 0.
+	 */
+	status = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
+				     (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
+	if (status)
+		WARN_ON(1);
+
+	return status;
+}
+
+/* mailbox recv message polling */
+int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
+			      int timeout)
+{
+	struct ath6kl_irq_proc_registers *rg;
+	int status = 0, i;
+	u8 htc_mbox = 1 << HTC_MAILBOX;
+
+	for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) {
+		/* this is the standard HIF way, load the reg table */
+		status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
+					     (u8 *) &dev->irq_proc_reg,
+					     sizeof(dev->irq_proc_reg),
+					     HIF_RD_SYNC_BYTE_INC);
+
+		if (status) {
+			ath6kl_err("failed to read reg table\n");
+			return status;
+		}
+
+		/* check for MBOX data and valid lookahead */
+		if (dev->irq_proc_reg.host_int_status & htc_mbox) {
+			if (dev->irq_proc_reg.rx_lkahd_valid &
+			    htc_mbox) {
+				/*
+				 * Mailbox has a message and the look ahead
+				 * is valid.
+				 */
+				rg = &dev->irq_proc_reg;
+				*lk_ahd =
+					le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
+				break;
+			}
+		}
+
+		/* delay a little  */
+		mdelay(ATH6KL_TIME_QUANTUM);
+		ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "retry mbox poll : %d\n", i);
+	}
+
+	if (i == 0) {
+		ath6kl_err("timeout waiting for recv message\n");
+		status = -ETIME;
+		/* check if the target asserted */
+		if (dev->irq_proc_reg.counter_int_status &
+		    ATH6KL_TARGET_DEBUG_INTR_MASK)
+			/*
+			 * Target failure handler will be called in case of
+			 * an assert.
+			 */
+			ath6kldev_proc_dbg_intr(dev);
+	}
+
+	return status;
+}
+
+/*
+ * Disable packet reception (used in case the host runs out of buffers)
+ * using the interrupt enable registers through the host I/F
+ */
+int ath6kldev_rx_control(struct ath6kl_device *dev, bool enable_rx)
+{
+	struct ath6kl_irq_enable_reg regs;
+	int status = 0;
+
+	/* take the lock to protect interrupt enable shadows */
+	spin_lock_bh(&dev->lock);
+
+	if (enable_rx)
+		dev->irq_en_reg.int_status_en |=
+			SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
+	else
+		dev->irq_en_reg.int_status_en &=
+		    ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
+
+	memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
+
+	spin_unlock_bh(&dev->lock);
+
+	status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
+				     &regs.int_status_en,
+				     sizeof(struct ath6kl_irq_enable_reg),
+				     HIF_WR_SYNC_BYTE_INC);
+
+	return status;
+}
+
+int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
+			      struct hif_scatter_req *scat_req, bool read)
+{
+	int status = 0;
+
+	if (read) {
+		scat_req->req = HIF_RD_SYNC_BLOCK_FIX;
+		scat_req->addr = dev->ar->mbox_info.htc_addr;
+	} else {
+		scat_req->req = HIF_WR_ASYNC_BLOCK_INC;
+
+		scat_req->addr =
+			(scat_req->len > HIF_MBOX_WIDTH) ?
+			dev->ar->mbox_info.htc_ext_addr :
+			dev->ar->mbox_info.htc_addr;
+	}
+
+	ath6kl_dbg((ATH6KL_DBG_HTC_RECV | ATH6KL_DBG_HTC_SEND),
+		   "ath6kldev_submit_scat_req, entries: %d, total len: %d mbox:0x%X (mode: %s : %s)\n",
+		   scat_req->scat_entries, scat_req->len,
+		   scat_req->addr, !read ? "async" : "sync",
+		   (read) ? "rd" : "wr");
+
+	if (!read && scat_req->virt_scat) {
+		status = ath6kldev_cp_scat_dma_buf(scat_req, false);
+		if (status) {
+			scat_req->status = status;
+			scat_req->complete(dev->ar->htc_target, scat_req);
+			return 0;
+		}
+	}
+
+	status = ath6kl_hif_scat_req_rw(dev->ar, scat_req);
+
+	if (read) {
+		/* in sync mode, we can touch the scatter request */
+		scat_req->status = status;
+		if (!status && scat_req->virt_scat)
+			scat_req->status =
+				ath6kldev_cp_scat_dma_buf(scat_req, true);
+	}
+
+	return status;
+}
+
+static int ath6kldev_proc_counter_intr(struct ath6kl_device *dev)
+{
+	u8 counter_int_status;
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n");
+
+	counter_int_status = dev->irq_proc_reg.counter_int_status &
+			     dev->irq_en_reg.cntr_int_status_en;
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ,
+		"valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
+		counter_int_status);
+
+	/*
+	 * NOTE: other modules like GMBOX may use the counter interrupt for
+	 * credit flow control on other counters, we only need to check for
+	 * the debug assertion counter interrupt.
+	 */
+	if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK)
+		return ath6kldev_proc_dbg_intr(dev);
+
+	return 0;
+}
+
+static int ath6kldev_proc_err_intr(struct ath6kl_device *dev)
+{
+	int status;
+	u8 error_int_status;
+	u8 reg_buf[4];
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n");
+
+	error_int_status = dev->irq_proc_reg.error_int_status & 0x0F;
+	if (!error_int_status) {
+		WARN_ON(1);
+		return -EIO;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ,
+		   "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
+		   error_int_status);
+
+	if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status))
+		ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n");
+
+	if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status))
+		ath6kl_err("rx underflow\n");
+
+	if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status))
+		ath6kl_err("tx overflow\n");
+
+	/* Clear the interrupt */
+	dev->irq_proc_reg.error_int_status &= ~error_int_status;
+
+	/* set W1C value to clear the interrupt, this hits the register first */
+	reg_buf[0] = error_int_status;
+	reg_buf[1] = 0;
+	reg_buf[2] = 0;
+	reg_buf[3] = 0;
+
+	status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS,
+				     reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
+
+	if (status)
+		WARN_ON(1);
+
+	return status;
+}
+
+static int ath6kldev_proc_cpu_intr(struct ath6kl_device *dev)
+{
+	int status;
+	u8 cpu_int_status;
+	u8 reg_buf[4];
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n");
+
+	cpu_int_status = dev->irq_proc_reg.cpu_int_status &
+			 dev->irq_en_reg.cpu_int_status_en;
+	if (!cpu_int_status) {
+		WARN_ON(1);
+		return -EIO;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ,
+		"valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
+		cpu_int_status);
+
+	/* Clear the interrupt */
+	dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status;
+
+	/*
+	 * Set up the register transfer buffer to hit the register 4 times ,
+	 * this is done to make the access 4-byte aligned to mitigate issues
+	 * with host bus interconnects that restrict bus transfer lengths to
+	 * be a multiple of 4-bytes.
+	 */
+
+	/* set W1C value to clear the interrupt, this hits the register first */
+	reg_buf[0] = cpu_int_status;
+	/* the remaining are set to zero which have no-effect  */
+	reg_buf[1] = 0;
+	reg_buf[2] = 0;
+	reg_buf[3] = 0;
+
+	status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS,
+				     reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
+
+	if (status)
+		WARN_ON(1);
+
+	return status;
+}
+
+/* process pending interrupts synchronously */
+static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
+{
+	struct ath6kl_irq_proc_registers *rg;
+	int status = 0;
+	u8 host_int_status = 0;
+	u32 lk_ahd = 0;
+	u8 htc_mbox = 1 << HTC_MAILBOX;
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev);
+
+	/*
+	 * NOTE: HIF implementation guarantees that the context of this
+	 * call allows us to perform SYNCHRONOUS I/O, that is we can block,
+	 * sleep or call any API that can block or switch thread/task
+	 * contexts. This is a fully schedulable context.
+	 */
+
+	/*
+	 * Process pending intr only when int_status_en is clear, it may
+	 * result in unnecessary bus transaction otherwise. Target may be
+	 * unresponsive at the time.
+	 */
+	if (dev->irq_en_reg.int_status_en) {
+		/*
+		 * Read the first 28 bytes of the HTC register table. This
+		 * will yield us the value of different int status
+		 * registers and the lookahead registers.
+		 *
+		 *    length = sizeof(int_status) + sizeof(cpu_int_status)
+		 *             + sizeof(error_int_status) +
+		 *             sizeof(counter_int_status) +
+		 *             sizeof(mbox_frame) + sizeof(rx_lkahd_valid)
+		 *             + sizeof(hole) + sizeof(rx_lkahd) +
+		 *             sizeof(int_status_en) +
+		 *             sizeof(cpu_int_status_en) +
+		 *             sizeof(err_int_status_en) +
+		 *             sizeof(cntr_int_status_en);
+		 */
+		status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
+					     (u8 *) &dev->irq_proc_reg,
+					     sizeof(dev->irq_proc_reg),
+					     HIF_RD_SYNC_BYTE_INC);
+		if (status)
+			goto out;
+
+		if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ))
+			ath6kl_dump_registers(dev, &dev->irq_proc_reg,
+					 &dev->irq_en_reg);
+
+		/* Update only those registers that are enabled */
+		host_int_status = dev->irq_proc_reg.host_int_status &
+				  dev->irq_en_reg.int_status_en;
+
+		/* Look at mbox status */
+		if (host_int_status & htc_mbox) {
+			/*
+			 * Mask out pending mbox value, we use "lookAhead as
+			 * the real flag for mbox processing.
+			 */
+			host_int_status &= ~htc_mbox;
+			if (dev->irq_proc_reg.rx_lkahd_valid &
+			    htc_mbox) {
+				rg = &dev->irq_proc_reg;
+				lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
+				if (!lk_ahd)
+					ath6kl_err("lookAhead is zero!\n");
+			}
+		}
+	}
+
+	if (!host_int_status && !lk_ahd) {
+		*done = true;
+		goto out;
+	}
+
+	if (lk_ahd) {
+		int fetched = 0;
+
+		ath6kl_dbg(ATH6KL_DBG_IRQ,
+			   "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd);
+		/*
+		 * Mailbox Interrupt, the HTC layer may issue async
+		 * requests to empty the mailbox. When emptying the recv
+		 * mailbox we use the async handler above called from the
+		 * completion routine of the callers read request. This can
+		 * improve performance by reducing context switching when
+		 * we rapidly pull packets.
+		 */
+		status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt,
+							  &lk_ahd, &fetched);
+		if (status)
+			goto out;
+
+		if (!fetched)
+			/*
+			 * HTC could not pull any messages out due to lack
+			 * of resources.
+			 */
+			dev->htc_cnxt->chk_irq_status_cnt = 0;
+	}
+
+	/* now handle the rest of them */
+	ath6kl_dbg(ATH6KL_DBG_IRQ,
+		   "valid interrupt source(s) for other interrupts: 0x%x\n",
+		   host_int_status);
+
+	if (MS(HOST_INT_STATUS_CPU, host_int_status)) {
+		/* CPU Interrupt */
+		status = ath6kldev_proc_cpu_intr(dev);
+		if (status)
+			goto out;
+	}
+
+	if (MS(HOST_INT_STATUS_ERROR, host_int_status)) {
+		/* Error Interrupt */
+		status = ath6kldev_proc_err_intr(dev);
+		if (status)
+			goto out;
+	}
+
+	if (MS(HOST_INT_STATUS_COUNTER, host_int_status))
+		/* Counter Interrupt */
+		status = ath6kldev_proc_counter_intr(dev);
+
+out:
+	/*
+	 * An optimization to bypass reading the IRQ status registers
+	 * unecessarily which can re-wake the target, if upper layers
+	 * determine that we are in a low-throughput mode, we can rely on
+	 * taking another interrupt rather than re-checking the status
+	 * registers which can re-wake the target.
+	 *
+	 * NOTE : for host interfaces that makes use of detecting pending
+	 * mbox messages at hif can not use this optimization due to
+	 * possible side effects, SPI requires the host to drain all
+	 * messages from the mailbox before exiting the ISR routine.
+	 */
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ,
+		   "bypassing irq status re-check, forcing done\n");
+
+	if (!dev->htc_cnxt->chk_irq_status_cnt)
+		*done = true;
+
+	ath6kl_dbg(ATH6KL_DBG_IRQ,
+		   "proc_pending_irqs: (done:%d, status=%d\n", *done, status);
+
+	return status;
+}
+
+/* interrupt handler, kicks off all interrupt processing */
+int ath6kldev_intr_bh_handler(struct ath6kl *ar)
+{
+	struct ath6kl_device *dev = ar->htc_target->dev;
+	int status = 0;
+	bool done = false;
+
+	/*
+	 * Reset counter used to flag a re-scan of IRQ status registers on
+	 * the target.
+	 */
+	dev->htc_cnxt->chk_irq_status_cnt = 0;
+
+	/*
+	 * IRQ processing is synchronous, interrupt status registers can be
+	 * re-read.
+	 */
+	while (!done) {
+		status = proc_pending_irqs(dev, &done);
+		if (status)
+			break;
+	}
+
+	return status;
+}
+
+static int ath6kldev_enable_intrs(struct ath6kl_device *dev)
+{
+	struct ath6kl_irq_enable_reg regs;
+	int status;
+
+	spin_lock_bh(&dev->lock);
+
+	/* Enable all but ATH6KL CPU interrupts */
+	dev->irq_en_reg.int_status_en =
+			SM(INT_STATUS_ENABLE_ERROR, 0x01) |
+			SM(INT_STATUS_ENABLE_CPU, 0x01) |
+			SM(INT_STATUS_ENABLE_COUNTER, 0x01);
+
+	/*
+	 * NOTE: There are some cases where HIF can do detection of
+	 * pending mbox messages which is disabled now.
+	 */
+	dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
+
+	/* Set up the CPU Interrupt status Register */
+	dev->irq_en_reg.cpu_int_status_en = 0;
+
+	/* Set up the Error Interrupt status Register */
+	dev->irq_en_reg.err_int_status_en =
+		SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) |
+		SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1);
+
+	/*
+	 * Enable Counter interrupt status register to get fatal errors for
+	 * debugging.
+	 */
+	dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT,
+						ATH6KL_TARGET_DEBUG_INTR_MASK);
+	memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
+
+	spin_unlock_bh(&dev->lock);
+
+	status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
+				     &regs.int_status_en, sizeof(regs),
+				     HIF_WR_SYNC_BYTE_INC);
+
+	if (status)
+		ath6kl_err("failed to update interrupt ctl reg err: %d\n",
+			   status);
+
+	return status;
+}
+
+int ath6kldev_disable_intrs(struct ath6kl_device *dev)
+{
+	struct ath6kl_irq_enable_reg regs;
+
+	spin_lock_bh(&dev->lock);
+	/* Disable all interrupts */
+	dev->irq_en_reg.int_status_en = 0;
+	dev->irq_en_reg.cpu_int_status_en = 0;
+	dev->irq_en_reg.err_int_status_en = 0;
+	dev->irq_en_reg.cntr_int_status_en = 0;
+	memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
+	spin_unlock_bh(&dev->lock);
+
+	return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
+				   &regs.int_status_en, sizeof(regs),
+				   HIF_WR_SYNC_BYTE_INC);
+}
+
+/* enable device interrupts */
+int ath6kldev_unmask_intrs(struct ath6kl_device *dev)
+{
+	int status = 0;
+
+	/*
+	 * Make sure interrupt are disabled before unmasking at the HIF
+	 * layer. The rationale here is that between device insertion
+	 * (where we clear the interrupts the first time) and when HTC
+	 * is finally ready to handle interrupts, other software can perform
+	 * target "soft" resets. The ATH6KL interrupt enables reset back to an
+	 * "enabled" state when this happens.
+	 */
+	ath6kldev_disable_intrs(dev);
+
+	/* unmask the host controller interrupts */
+	ath6kl_hif_irq_enable(dev->ar);
+	status = ath6kldev_enable_intrs(dev);
+
+	return status;
+}
+
+/* disable all device interrupts */
+int ath6kldev_mask_intrs(struct ath6kl_device *dev)
+{
+	/*
+	 * Mask the interrupt at the HIF layer to avoid any stray interrupt
+	 * taken while we zero out our shadow registers in
+	 * ath6kldev_disable_intrs().
+	 */
+	ath6kl_hif_irq_disable(dev->ar);
+
+	return ath6kldev_disable_intrs(dev);
+}
+
+int ath6kldev_setup(struct ath6kl_device *dev)
+{
+	int status = 0;
+
+	spin_lock_init(&dev->lock);
+
+	/*
+	 * NOTE: we actually get the block size of a mailbox other than 0,
+	 * for SDIO the block size on mailbox 0 is artificially set to 1.
+	 * So we use the block size that is set for the other 3 mailboxes.
+	 */
+	dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size;
+
+	/* must be a power of 2 */
+	if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) {
+		WARN_ON(1);
+		goto fail_setup;
+	}
+
+	/* assemble mask, used for padding to a block */
+	dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1;
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "block size: %d, mbox addr:0x%X\n",
+		   dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr);
+
+	ath6kl_dbg(ATH6KL_DBG_TRC,
+		   "hif interrupt processing is sync only\n");
+
+	status = ath6kldev_disable_intrs(dev);
+
+fail_setup:
+	return status;
+
+}

+ 92 - 0
drivers/net/wireless/ath/ath6kl/htc_hif.h

@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef HTC_HIF_H
+#define HTC_HIF_H
+
+#include "htc.h"
+#include "hif.h"
+
+#define ATH6KL_MAILBOXES	4
+
+/* HTC runs over mailbox 0 */
+#define HTC_MAILBOX	0
+
+#define ATH6KL_TARGET_DEBUG_INTR_MASK     0x01
+
+#define OTHER_INTS_ENABLED		(INT_STATUS_ENABLE_ERROR_MASK |	\
+					INT_STATUS_ENABLE_CPU_MASK   |	\
+					INT_STATUS_ENABLE_COUNTER_MASK)
+
+#define ATH6KL_REG_IO_BUFFER_SIZE			32
+#define ATH6KL_MAX_REG_IO_BUFFERS			8
+#define ATH6KL_SCATTER_ENTRIES_PER_REQ            16
+#define ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER      (16 * 1024)
+#define ATH6KL_SCATTER_REQS                       4
+
+#ifndef A_CACHE_LINE_PAD
+#define A_CACHE_LINE_PAD                        128
+#endif
+#define ATH6KL_MIN_SCATTER_ENTRIES_PER_REQ        2
+#define ATH6KL_MIN_TRANSFER_SIZE_PER_SCATTER      (4 * 1024)
+
+struct ath6kl_irq_proc_registers {
+	u8 host_int_status;
+	u8 cpu_int_status;
+	u8 error_int_status;
+	u8 counter_int_status;
+	u8 mbox_frame;
+	u8 rx_lkahd_valid;
+	u8 host_int_status2;
+	u8 gmbox_rx_avail;
+	__le32 rx_lkahd[2];
+	__le32 rx_gmbox_lkahd_alias[2];
+} __packed;
+
+struct ath6kl_irq_enable_reg {
+	u8 int_status_en;
+	u8 cpu_int_status_en;
+	u8 err_int_status_en;
+	u8 cntr_int_status_en;
+} __packed;
+
+struct ath6kl_device {
+	spinlock_t lock;
+	u8 pad1[A_CACHE_LINE_PAD];
+	struct ath6kl_irq_proc_registers irq_proc_reg;
+	u8 pad2[A_CACHE_LINE_PAD];
+	struct ath6kl_irq_enable_reg irq_en_reg;
+	u8 pad3[A_CACHE_LINE_PAD];
+	struct htc_target *htc_cnxt;
+	struct ath6kl *ar;
+};
+
+int ath6kldev_setup(struct ath6kl_device *dev);
+int ath6kldev_unmask_intrs(struct ath6kl_device *dev);
+int ath6kldev_mask_intrs(struct ath6kl_device *dev);
+int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev,
+			      u32 *lk_ahd, int timeout);
+int ath6kldev_rx_control(struct ath6kl_device *dev, bool enable_rx);
+int ath6kldev_disable_intrs(struct ath6kl_device *dev);
+
+int ath6kldev_rw_comp_handler(void *context, int status);
+int ath6kldev_intr_bh_handler(struct ath6kl *ar);
+
+/* Scatter Function and Definitions */
+int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
+			    struct hif_scatter_req *scat_req, bool read);
+
+#endif /*ATH6KL_H_ */

+ 1303 - 0
drivers/net/wireless/ath/ath6kl/init.c

@@ -0,0 +1,1303 @@
+
+/*
+ * Copyright (c) 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/mmc/sdio_func.h>
+#include "core.h"
+#include "cfg80211.h"
+#include "target.h"
+#include "debug.h"
+#include "hif-ops.h"
+
+unsigned int debug_mask;
+
+module_param(debug_mask, uint, 0644);
+
+/*
+ * Include definitions here that can be used to tune the WLAN module
+ * behavior. Different customers can tune the behavior as per their needs,
+ * here.
+ */
+
+/*
+ * This configuration item enable/disable keepalive support.
+ * Keepalive support: In the absence of any data traffic to AP, null
+ * frames will be sent to the AP at periodic interval, to keep the association
+ * active. This configuration item defines the periodic interval.
+ * Use value of zero to disable keepalive support
+ * Default: 60 seconds
+ */
+#define WLAN_CONFIG_KEEP_ALIVE_INTERVAL 60
+
+/*
+ * This configuration item sets the value of disconnect timeout
+ * Firmware delays sending the disconnec event to the host for this
+ * timeout after is gets disconnected from the current AP.
+ * If the firmware successly roams within the disconnect timeout
+ * it sends a new connect event
+ */
+#define WLAN_CONFIG_DISCONNECT_TIMEOUT 10
+
+#define CONFIG_AR600x_DEBUG_UART_TX_PIN 8
+
+enum addr_type {
+	DATASET_PATCH_ADDR,
+	APP_LOAD_ADDR,
+	APP_START_OVERRIDE_ADDR,
+};
+
+#define ATH6KL_DATA_OFFSET    64
+struct sk_buff *ath6kl_buf_alloc(int size)
+{
+	struct sk_buff *skb;
+	u16 reserved;
+
+	/* Add chacheline space at front and back of buffer */
+	reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET +
+		   sizeof(struct htc_packet);
+	skb = dev_alloc_skb(size + reserved);
+
+	if (skb)
+		skb_reserve(skb, reserved - L1_CACHE_BYTES);
+	return skb;
+}
+
+void ath6kl_init_profile_info(struct ath6kl *ar)
+{
+	ar->ssid_len = 0;
+	memset(ar->ssid, 0, sizeof(ar->ssid));
+
+	ar->dot11_auth_mode = OPEN_AUTH;
+	ar->auth_mode = NONE_AUTH;
+	ar->prwise_crypto = NONE_CRYPT;
+	ar->prwise_crypto_len = 0;
+	ar->grp_crypto = NONE_CRYPT;
+	ar->grp_crpto_len = 0;
+	memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
+	memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+	memset(ar->bssid, 0, sizeof(ar->bssid));
+	ar->bss_ch = 0;
+	ar->nw_type = ar->next_mode = INFRA_NETWORK;
+}
+
+static u8 ath6kl_get_fw_iftype(struct ath6kl *ar)
+{
+	switch (ar->nw_type) {
+	case INFRA_NETWORK:
+		return HI_OPTION_FW_MODE_BSS_STA;
+	case ADHOC_NETWORK:
+		return HI_OPTION_FW_MODE_IBSS;
+	case AP_NETWORK:
+		return HI_OPTION_FW_MODE_AP;
+	default:
+		ath6kl_err("Unsupported interface type :%d\n", ar->nw_type);
+		return 0xff;
+	}
+}
+
+static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
+					  u32 item_offset)
+{
+	u32 addr = 0;
+
+	if (ar->target_type == TARGET_TYPE_AR6003)
+		addr = ATH6KL_HI_START_ADDR + item_offset;
+
+	return addr;
+}
+
+static int ath6kl_set_host_app_area(struct ath6kl *ar)
+{
+	u32 address, data;
+	struct host_app_area host_app_area;
+
+	/* Fetch the address of the host_app_area_s
+	 * instance in the host interest area */
+	address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest));
+	address = TARG_VTOP(address);
+
+	if (ath6kl_read_reg_diag(ar, &address, &data))
+		return -EIO;
+
+	address = TARG_VTOP(data);
+	host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION;
+	if (ath6kl_access_datadiag(ar, address,
+				(u8 *)&host_app_area,
+				sizeof(struct host_app_area), false))
+		return -EIO;
+
+	return 0;
+}
+
+static inline void set_ac2_ep_map(struct ath6kl *ar,
+				  u8 ac,
+				  enum htc_endpoint_id ep)
+{
+	ar->ac2ep_map[ac] = ep;
+	ar->ep2ac_map[ep] = ac;
+}
+
+/* connect to a service */
+static int ath6kl_connectservice(struct ath6kl *ar,
+				 struct htc_service_connect_req  *con_req,
+				 char *desc)
+{
+	int status;
+	struct htc_service_connect_resp response;
+
+	memset(&response, 0, sizeof(response));
+
+	status = ath6kl_htc_conn_service(ar->htc_target, con_req, &response);
+	if (status) {
+		ath6kl_err("failed to connect to %s service status:%d\n",
+			   desc, status);
+		return status;
+	}
+
+	switch (con_req->svc_id) {
+	case WMI_CONTROL_SVC:
+		if (test_bit(WMI_ENABLED, &ar->flag))
+			ath6kl_wmi_set_control_ep(ar->wmi, response.endpoint);
+		ar->ctrl_ep = response.endpoint;
+		break;
+	case WMI_DATA_BE_SVC:
+		set_ac2_ep_map(ar, WMM_AC_BE, response.endpoint);
+		break;
+	case WMI_DATA_BK_SVC:
+		set_ac2_ep_map(ar, WMM_AC_BK, response.endpoint);
+		break;
+	case WMI_DATA_VI_SVC:
+		set_ac2_ep_map(ar, WMM_AC_VI, response.endpoint);
+		break;
+	case WMI_DATA_VO_SVC:
+		set_ac2_ep_map(ar, WMM_AC_VO, response.endpoint);
+		break;
+	default:
+		ath6kl_err("service id is not mapped %d\n", con_req->svc_id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ath6kl_init_service_ep(struct ath6kl *ar)
+{
+	struct htc_service_connect_req connect;
+
+	memset(&connect, 0, sizeof(connect));
+
+	/* these fields are the same for all service endpoints */
+	connect.ep_cb.rx = ath6kl_rx;
+	connect.ep_cb.rx_refill = ath6kl_rx_refill;
+	connect.ep_cb.tx_full = ath6kl_tx_queue_full;
+
+	/*
+	 * Set the max queue depth so that our ath6kl_tx_queue_full handler
+	 * gets called.
+	*/
+	connect.max_txq_depth = MAX_DEFAULT_SEND_QUEUE_DEPTH;
+	connect.ep_cb.rx_refill_thresh = ATH6KL_MAX_RX_BUFFERS / 4;
+	if (!connect.ep_cb.rx_refill_thresh)
+		connect.ep_cb.rx_refill_thresh++;
+
+	/* connect to control service */
+	connect.svc_id = WMI_CONTROL_SVC;
+	if (ath6kl_connectservice(ar, &connect, "WMI CONTROL"))
+		return -EIO;
+
+	connect.flags |= HTC_FLGS_TX_BNDL_PAD_EN;
+
+	/*
+	 * Limit the HTC message size on the send path, although e can
+	 * receive A-MSDU frames of 4K, we will only send ethernet-sized
+	 * (802.3) frames on the send path.
+	 */
+	connect.max_rxmsg_sz = WMI_MAX_TX_DATA_FRAME_LENGTH;
+
+	/*
+	 * To reduce the amount of committed memory for larger A_MSDU
+	 * frames, use the recv-alloc threshold mechanism for larger
+	 * packets.
+	 */
+	connect.ep_cb.rx_alloc_thresh = ATH6KL_BUFFER_SIZE;
+	connect.ep_cb.rx_allocthresh = ath6kl_alloc_amsdu_rxbuf;
+
+	/*
+	 * For the remaining data services set the connection flag to
+	 * reduce dribbling, if configured to do so.
+	 */
+	connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB;
+	connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK;
+	connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF;
+
+	connect.svc_id = WMI_DATA_BE_SVC;
+
+	if (ath6kl_connectservice(ar, &connect, "WMI DATA BE"))
+		return -EIO;
+
+	/* connect to back-ground map this to WMI LOW_PRI */
+	connect.svc_id = WMI_DATA_BK_SVC;
+	if (ath6kl_connectservice(ar, &connect, "WMI DATA BK"))
+		return -EIO;
+
+	/* connect to Video service, map this to to HI PRI */
+	connect.svc_id = WMI_DATA_VI_SVC;
+	if (ath6kl_connectservice(ar, &connect, "WMI DATA VI"))
+		return -EIO;
+
+	/*
+	 * Connect to VO service, this is currently not mapped to a WMI
+	 * priority stream due to historical reasons. WMI originally
+	 * defined 3 priorities over 3 mailboxes We can change this when
+	 * WMI is reworked so that priorities are not dependent on
+	 * mailboxes.
+	 */
+	connect.svc_id = WMI_DATA_VO_SVC;
+	if (ath6kl_connectservice(ar, &connect, "WMI DATA VO"))
+		return -EIO;
+
+	return 0;
+}
+
+static void ath6kl_init_control_info(struct ath6kl *ar)
+{
+	u8 ctr;
+
+	clear_bit(WMI_ENABLED, &ar->flag);
+	ath6kl_init_profile_info(ar);
+	ar->def_txkey_index = 0;
+	memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
+	ar->ch_hint = 0;
+	ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
+	ar->listen_intvl_b = 0;
+	ar->tx_pwr = 0;
+	clear_bit(SKIP_SCAN, &ar->flag);
+	set_bit(WMM_ENABLED, &ar->flag);
+	ar->intra_bss = 1;
+	memset(&ar->sc_params, 0, sizeof(ar->sc_params));
+	ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT;
+	ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS;
+
+	memset((u8 *)ar->sta_list, 0,
+	       AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
+
+	spin_lock_init(&ar->mcastpsq_lock);
+
+	/* Init the PS queues */
+	for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
+		spin_lock_init(&ar->sta_list[ctr].psq_lock);
+		skb_queue_head_init(&ar->sta_list[ctr].psq);
+	}
+
+	skb_queue_head_init(&ar->mcastpsq);
+
+	memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
+}
+
+/*
+ * Set HTC/Mbox operational parameters, this can only be called when the
+ * target is in the BMI phase.
+ */
+static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val,
+				 u8 htc_ctrl_buf)
+{
+	int status;
+	u32 blk_size;
+
+	blk_size = ar->mbox_info.block_size;
+
+	if (htc_ctrl_buf)
+		blk_size |=  ((u32)htc_ctrl_buf) << 16;
+
+	/* set the host interest area for the block size */
+	status = ath6kl_bmi_write(ar,
+			ath6kl_get_hi_item_addr(ar,
+			HI_ITEM(hi_mbox_io_block_sz)),
+			(u8 *)&blk_size,
+			4);
+	if (status) {
+		ath6kl_err("bmi_write_memory for IO block size failed\n");
+		goto out;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "block size set: %d (target addr:0x%X)\n",
+		   blk_size,
+		   ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz)));
+
+	if (mbox_isr_yield_val) {
+		/* set the host interest area for the mbox ISR yield limit */
+		status = ath6kl_bmi_write(ar,
+				ath6kl_get_hi_item_addr(ar,
+				HI_ITEM(hi_mbox_isr_yield_limit)),
+				(u8 *)&mbox_isr_yield_val,
+				4);
+		if (status) {
+			ath6kl_err("bmi_write_memory for yield limit failed\n");
+			goto out;
+		}
+	}
+
+out:
+	return status;
+}
+
+#define REG_DUMP_COUNT_AR6003   60
+#define REGISTER_DUMP_LEN_MAX   60
+
+static void ath6kl_dump_target_assert_info(struct ath6kl *ar)
+{
+	u32 address;
+	u32 regdump_loc = 0;
+	int status;
+	u32 regdump_val[REGISTER_DUMP_LEN_MAX];
+	u32 i;
+
+	if (ar->target_type != TARGET_TYPE_AR6003)
+		return;
+
+	/* the reg dump pointer is copied to the host interest area */
+	address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state));
+	address = TARG_VTOP(address);
+
+	/* read RAM location through diagnostic window */
+	status = ath6kl_read_reg_diag(ar, &address, &regdump_loc);
+
+	if (status || !regdump_loc) {
+		ath6kl_err("failed to get ptr to register dump area\n");
+		return;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n",
+		regdump_loc);
+
+	regdump_loc = TARG_VTOP(regdump_loc);
+
+	/* fetch register dump data */
+	status = ath6kl_access_datadiag(ar,
+					regdump_loc,
+					(u8 *)&regdump_val[0],
+					REG_DUMP_COUNT_AR6003 * (sizeof(u32)),
+					true);
+
+	if (status) {
+		ath6kl_err("failed to get register dump\n");
+		return;
+	}
+	ath6kl_dbg(ATH6KL_DBG_TRC, "Register Dump:\n");
+
+	for (i = 0; i < REG_DUMP_COUNT_AR6003; i++)
+		ath6kl_dbg(ATH6KL_DBG_TRC, " %d :  0x%8.8X\n",
+			   i, regdump_val[i]);
+
+}
+
+void ath6kl_target_failure(struct ath6kl *ar)
+{
+	ath6kl_err("target asserted\n");
+
+	/* try dumping target assertion information (if any) */
+	ath6kl_dump_target_assert_info(ar);
+
+}
+
+static int ath6kl_target_config_wlan_params(struct ath6kl *ar)
+{
+	int status = 0;
+
+	/*
+	 * Configure the device for rx dot11 header rules. "0,0" are the
+	 * default values. Required if checksum offload is needed. Set
+	 * RxMetaVersion to 2.
+	 */
+	if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi,
+					       ar->rx_meta_ver, 0, 0)) {
+		ath6kl_err("unable to set the rx frame format\n");
+		status = -EIO;
+	}
+
+	if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN)
+		if ((ath6kl_wmi_pmparams_cmd(ar->wmi, 0, 1, 0, 0, 1,
+		     IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN)) != 0) {
+			ath6kl_err("unable to set power save fail event policy\n");
+			status = -EIO;
+		}
+
+	if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER))
+		if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, 0,
+		     WMI_DONOT_IGNORE_BARKER_IN_ERP)) != 0) {
+			ath6kl_err("unable to set barker preamble policy\n");
+			status = -EIO;
+		}
+
+	if (ath6kl_wmi_set_keepalive_cmd(ar->wmi,
+			WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) {
+		ath6kl_err("unable to set keep alive interval\n");
+		status = -EIO;
+	}
+
+	if (ath6kl_wmi_disctimeout_cmd(ar->wmi,
+			WLAN_CONFIG_DISCONNECT_TIMEOUT)) {
+		ath6kl_err("unable to set disconnect timeout\n");
+		status = -EIO;
+	}
+
+	if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST))
+		if (ath6kl_wmi_set_wmm_txop(ar->wmi, WMI_TXOP_DISABLED)) {
+			ath6kl_err("unable to set txop bursting\n");
+			status = -EIO;
+		}
+
+	return status;
+}
+
+int ath6kl_configure_target(struct ath6kl *ar)
+{
+	u32 param, ram_reserved_size;
+	u8 fw_iftype;
+
+	fw_iftype = ath6kl_get_fw_iftype(ar);
+	if (fw_iftype == 0xff)
+		return -EINVAL;
+
+	/* Tell target which HTC version it is used*/
+	param = HTC_PROTOCOL_VERSION;
+	if (ath6kl_bmi_write(ar,
+			     ath6kl_get_hi_item_addr(ar,
+			     HI_ITEM(hi_app_host_interest)),
+			     (u8 *)&param, 4) != 0) {
+		ath6kl_err("bmi_write_memory for htc version failed\n");
+		return -EIO;
+	}
+
+	/* set the firmware mode to STA/IBSS/AP */
+	param = 0;
+
+	if (ath6kl_bmi_read(ar,
+			    ath6kl_get_hi_item_addr(ar,
+			    HI_ITEM(hi_option_flag)),
+			    (u8 *)&param, 4) != 0) {
+		ath6kl_err("bmi_read_memory for setting fwmode failed\n");
+		return -EIO;
+	}
+
+	param |= (1 << HI_OPTION_NUM_DEV_SHIFT);
+	param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT);
+	param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
+	param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
+
+	if (ath6kl_bmi_write(ar,
+			     ath6kl_get_hi_item_addr(ar,
+			     HI_ITEM(hi_option_flag)),
+			     (u8 *)&param,
+			     4) != 0) {
+		ath6kl_err("bmi_write_memory for setting fwmode failed\n");
+		return -EIO;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "firmware mode set\n");
+
+	/*
+	 * Hardcode the address use for the extended board data
+	 * Ideally this should be pre-allocate by the OS at boot time
+	 * But since it is a new feature and board data is loaded
+	 * at init time, we have to workaround this from host.
+	 * It is difficult to patch the firmware boot code,
+	 * but possible in theory.
+	 */
+
+	if (ar->target_type == TARGET_TYPE_AR6003) {
+		if (ar->version.target_ver == AR6003_REV2_VERSION) {
+			param = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
+			ram_reserved_size =  AR6003_REV2_RAM_RESERVE_SIZE;
+		} else {
+			param = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
+			ram_reserved_size =  AR6003_REV3_RAM_RESERVE_SIZE;
+		}
+
+		if (ath6kl_bmi_write(ar,
+				     ath6kl_get_hi_item_addr(ar,
+				     HI_ITEM(hi_board_ext_data)),
+				     (u8 *)&param, 4) != 0) {
+			ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n");
+			return -EIO;
+		}
+		if (ath6kl_bmi_write(ar,
+				     ath6kl_get_hi_item_addr(ar,
+				     HI_ITEM(hi_end_ram_reserve_sz)),
+				     (u8 *)&ram_reserved_size, 4) != 0) {
+			ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n");
+			return -EIO;
+		}
+	}
+
+	/* set the block size for the target */
+	if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0))
+		/* use default number of control buffers */
+		return -EIO;
+
+	return 0;
+}
+
+struct ath6kl *ath6kl_core_alloc(struct device *sdev)
+{
+	struct net_device *dev;
+	struct ath6kl *ar;
+	struct wireless_dev *wdev;
+
+	wdev = ath6kl_cfg80211_init(sdev);
+	if (!wdev) {
+		ath6kl_err("ath6kl_cfg80211_init failed\n");
+		return NULL;
+	}
+
+	ar = wdev_priv(wdev);
+	ar->dev = sdev;
+	ar->wdev = wdev;
+	wdev->iftype = NL80211_IFTYPE_STATION;
+
+	dev = alloc_netdev(0, "wlan%d", ether_setup);
+	if (!dev) {
+		ath6kl_err("no memory for network device instance\n");
+		ath6kl_cfg80211_deinit(ar);
+		return NULL;
+	}
+
+	dev->ieee80211_ptr = wdev;
+	SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
+	wdev->netdev = dev;
+	ar->sme_state = SME_DISCONNECTED;
+	ar->auto_auth_stage = AUTH_IDLE;
+
+	init_netdev(dev);
+
+	ar->net_dev = dev;
+	set_bit(WLAN_ENABLED, &ar->flag);
+
+	ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
+
+	spin_lock_init(&ar->lock);
+
+	ath6kl_init_control_info(ar);
+	init_waitqueue_head(&ar->event_wq);
+	sema_init(&ar->sem, 1);
+	clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
+
+	INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
+
+	setup_timer(&ar->disconnect_timer, disconnect_timer_handler,
+		    (unsigned long) dev);
+
+	return ar;
+}
+
+int ath6kl_unavail_ev(struct ath6kl *ar)
+{
+	ath6kl_destroy(ar->net_dev, 1);
+
+	return 0;
+}
+
+/* firmware upload */
+static u32 ath6kl_get_load_address(u32 target_ver, enum addr_type type)
+{
+	WARN_ON(target_ver != AR6003_REV2_VERSION &&
+		target_ver != AR6003_REV3_VERSION);
+
+	switch (type) {
+	case DATASET_PATCH_ADDR:
+		return (target_ver == AR6003_REV2_VERSION) ?
+			AR6003_REV2_DATASET_PATCH_ADDRESS :
+			AR6003_REV3_DATASET_PATCH_ADDRESS;
+	case APP_LOAD_ADDR:
+		return (target_ver == AR6003_REV2_VERSION) ?
+			AR6003_REV2_APP_LOAD_ADDRESS :
+			0x1234;
+	case APP_START_OVERRIDE_ADDR:
+		return (target_ver == AR6003_REV2_VERSION) ?
+			AR6003_REV2_APP_START_OVERRIDE :
+			AR6003_REV3_APP_START_OVERRIDE;
+	default:
+		return 0;
+	}
+}
+
+static int ath6kl_get_fw(struct ath6kl *ar, const char *filename,
+			 u8 **fw, size_t *fw_len)
+{
+	const struct firmware *fw_entry;
+	int ret;
+
+	ret = request_firmware(&fw_entry, filename, ar->dev);
+	if (ret)
+		return ret;
+
+	*fw_len = fw_entry->size;
+	*fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
+
+	if (*fw == NULL)
+		ret = -ENOMEM;
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int ath6kl_fetch_board_file(struct ath6kl *ar)
+{
+	const char *filename;
+	int ret;
+
+	switch (ar->version.target_ver) {
+	case AR6003_REV2_VERSION:
+		filename = AR6003_REV2_BOARD_DATA_FILE;
+		break;
+	default:
+		filename = AR6003_REV3_BOARD_DATA_FILE;
+		break;
+	}
+
+	ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
+			    &ar->fw_board_len);
+	if (ret == 0) {
+		/* managed to get proper board file */
+		return 0;
+	}
+
+	/* there was no proper board file, try to use default instead */
+	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;
+	default:
+		filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE;
+		break;
+	}
+
+	ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
+			    &ar->fw_board_len);
+	if (ret) {
+		ath6kl_err("Failed to get default board file %s: %d\n",
+			   filename, ret);
+		return ret;
+	}
+
+	ath6kl_warn("WARNING! No proper board file was not found, instead using a default board file.\n");
+	ath6kl_warn("Most likely your hardware won't work as specified. Install correct board file!\n");
+
+	return 0;
+}
+
+
+static int ath6kl_upload_board_file(struct ath6kl *ar)
+{
+	u32 board_address, board_ext_address, param;
+	int ret;
+
+	if (ar->fw_board == NULL) {
+		ret = ath6kl_fetch_board_file(ar);
+		if (ret)
+			return ret;
+	}
+
+	/* Determine where in Target RAM to write Board Data */
+	ath6kl_bmi_read(ar,
+			ath6kl_get_hi_item_addr(ar,
+			HI_ITEM(hi_board_data)),
+			(u8 *) &board_address, 4);
+	ath6kl_dbg(ATH6KL_DBG_TRC, "board data download addr: 0x%x\n",
+		   board_address);
+
+	/* determine where in target ram to write extended board data */
+	ath6kl_bmi_read(ar,
+			ath6kl_get_hi_item_addr(ar,
+			HI_ITEM(hi_board_ext_data)),
+			(u8 *) &board_ext_address, 4);
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "board file download addr: 0x%x\n",
+		   board_ext_address);
+
+	if (board_ext_address == 0) {
+		ath6kl_err("Failed to get board file target address.\n");
+		return -EINVAL;
+	}
+
+	if (ar->fw_board_len == (AR6003_BOARD_DATA_SZ +
+				 AR6003_BOARD_EXT_DATA_SZ)) {
+		/* write extended board data */
+		ret = ath6kl_bmi_write(ar, board_ext_address,
+				       ar->fw_board + AR6003_BOARD_DATA_SZ,
+				       AR6003_BOARD_EXT_DATA_SZ);
+
+		if (ret) {
+			ath6kl_err("Failed to write extended board data: %d\n",
+				   ret);
+			return ret;
+		}
+
+		/* record that extended board data is initialized */
+		param = (AR6003_BOARD_EXT_DATA_SZ << 16) | 1;
+		ath6kl_bmi_write(ar,
+				 ath6kl_get_hi_item_addr(ar,
+				 HI_ITEM(hi_board_ext_data_config)),
+				 (unsigned char *) &param, 4);
+	}
+
+	if (ar->fw_board_len < AR6003_BOARD_DATA_SZ) {
+		ath6kl_err("Too small board file: %zu\n", ar->fw_board_len);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	ret = ath6kl_bmi_write(ar, board_address, ar->fw_board,
+			       AR6003_BOARD_DATA_SZ);
+
+	if (ret) {
+		ath6kl_err("Board file bmi write failed: %d\n", ret);
+		return ret;
+	}
+
+	/* record the fact that Board Data IS initialized */
+	param = 1;
+	ath6kl_bmi_write(ar,
+			 ath6kl_get_hi_item_addr(ar,
+			 HI_ITEM(hi_board_data_initialized)),
+			 (u8 *)&param, 4);
+
+	return ret;
+}
+
+static int ath6kl_upload_otp(struct ath6kl *ar)
+{
+	const char *filename;
+	u32 address, param;
+	int ret;
+
+	switch (ar->version.target_ver) {
+	case AR6003_REV2_VERSION:
+		filename = AR6003_REV2_OTP_FILE;
+		break;
+	default:
+		filename = AR6003_REV3_OTP_FILE;
+		break;
+	}
+
+	if (ar->fw_otp == NULL) {
+		ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
+				    &ar->fw_otp_len);
+		if (ret) {
+			ath6kl_err("Failed to get OTP file %s: %d\n",
+				   filename, ret);
+			return ret;
+		}
+	}
+
+	address = ath6kl_get_load_address(ar->version.target_ver,
+					  APP_LOAD_ADDR);
+
+	ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp,
+				       ar->fw_otp_len);
+	if (ret) {
+		ath6kl_err("Failed to upload OTP file: %d\n", ret);
+		return ret;
+	}
+
+	/* execute the OTP code */
+	param = 0;
+	address = ath6kl_get_load_address(ar->version.target_ver,
+					  APP_START_OVERRIDE_ADDR);
+	ath6kl_bmi_execute(ar, address, &param);
+
+	return ret;
+}
+
+static int ath6kl_upload_firmware(struct ath6kl *ar)
+{
+	const char *filename;
+	u32 address;
+	int ret;
+
+	switch (ar->version.target_ver) {
+	case AR6003_REV2_VERSION:
+		filename = AR6003_REV2_FIRMWARE_FILE;
+		break;
+	default:
+		filename = AR6003_REV3_FIRMWARE_FILE;
+		break;
+	}
+
+	if (ar->fw == NULL) {
+		ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
+		if (ret) {
+			ath6kl_err("Failed to get firmware file %s: %d\n",
+				   filename, ret);
+			return ret;
+		}
+	}
+
+	address = ath6kl_get_load_address(ar->version.target_ver,
+					  APP_LOAD_ADDR);
+
+	ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len);
+
+	if (ret) {
+		ath6kl_err("Failed to write firmware: %d\n", ret);
+		return ret;
+	}
+
+	/* Set starting address for firmware */
+	address = ath6kl_get_load_address(ar->version.target_ver,
+					  APP_START_OVERRIDE_ADDR);
+	ath6kl_bmi_set_app_start(ar, address);
+
+	return ret;
+}
+
+static int ath6kl_upload_patch(struct ath6kl *ar)
+{
+	const char *filename;
+	u32 address, param;
+	int ret;
+
+	switch (ar->version.target_ver) {
+	case AR6003_REV2_VERSION:
+		filename = AR6003_REV2_PATCH_FILE;
+		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;
+		}
+	}
+
+	address = ath6kl_get_load_address(ar->version.target_ver,
+					  DATASET_PATCH_ADDR);
+
+	ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len);
+	if (ret) {
+		ath6kl_err("Failed to write patch file: %d\n", ret);
+		return ret;
+	}
+
+	param = address;
+	ath6kl_bmi_write(ar,
+			 ath6kl_get_hi_item_addr(ar,
+			 HI_ITEM(hi_dset_list_head)),
+			 (unsigned char *) &param, 4);
+
+	return 0;
+}
+
+static int ath6kl_init_upload(struct ath6kl *ar)
+{
+	u32 param, options, sleep, address;
+	int status = 0;
+
+	if (ar->target_type != TARGET_TYPE_AR6003)
+		return -EINVAL;
+
+	/* temporarily disable system sleep */
+	address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS;
+	status = ath6kl_bmi_reg_read(ar, address, &param);
+	if (status)
+		return status;
+
+	options = param;
+
+	param |= ATH6KL_OPTION_SLEEP_DISABLE;
+	status = ath6kl_bmi_reg_write(ar, address, param);
+	if (status)
+		return status;
+
+	address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS;
+	status = ath6kl_bmi_reg_read(ar, address, &param);
+	if (status)
+		return status;
+
+	sleep = param;
+
+	param |= SM(SYSTEM_SLEEP_DISABLE, 1);
+	status = ath6kl_bmi_reg_write(ar, address, param);
+	if (status)
+		return status;
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "old options: %d, old sleep: %d\n",
+		   options, sleep);
+
+	/* program analog PLL register */
+	status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER,
+				      0xF9104001);
+	if (status)
+		return status;
+
+	/* Run at 80/88MHz by default */
+	param = SM(CPU_CLOCK_STANDARD, 1);
+
+	address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS;
+	status = ath6kl_bmi_reg_write(ar, address, param);
+	if (status)
+		return status;
+
+	param = 0;
+	address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS;
+	param = SM(LPO_CAL_ENABLE, 1);
+	status = ath6kl_bmi_reg_write(ar, address, param);
+	if (status)
+		return status;
+
+	/* WAR to avoid SDIO CRC err */
+	if (ar->version.target_ver == AR6003_REV2_VERSION) {
+		ath6kl_err("temporary war to avoid sdio crc error\n");
+
+		param = 0x20;
+
+		address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS;
+		status = ath6kl_bmi_reg_write(ar, address, param);
+		if (status)
+			return status;
+
+		address = GPIO_BASE_ADDRESS + GPIO_PIN11_ADDRESS;
+		status = ath6kl_bmi_reg_write(ar, address, param);
+		if (status)
+			return status;
+
+		address = GPIO_BASE_ADDRESS + GPIO_PIN12_ADDRESS;
+		status = ath6kl_bmi_reg_write(ar, address, param);
+		if (status)
+			return status;
+
+		address = GPIO_BASE_ADDRESS + GPIO_PIN13_ADDRESS;
+		status = ath6kl_bmi_reg_write(ar, address, param);
+		if (status)
+			return status;
+	}
+
+	/* write EEPROM data to Target RAM */
+	status = ath6kl_upload_board_file(ar);
+	if (status)
+		return status;
+
+	/* transfer One time Programmable data */
+	status = ath6kl_upload_otp(ar);
+	if (status)
+		return status;
+
+	/* Download Target firmware */
+	status = ath6kl_upload_firmware(ar);
+	if (status)
+		return status;
+
+	status = ath6kl_upload_patch(ar);
+	if (status)
+		return status;
+
+	/* Restore system sleep */
+	address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS;
+	status = ath6kl_bmi_reg_write(ar, address, sleep);
+	if (status)
+		return status;
+
+	address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS;
+	param = options | 0x20;
+	status = ath6kl_bmi_reg_write(ar, address, param);
+	if (status)
+		return status;
+
+	/* Configure GPIO AR6003 UART */
+	param = CONFIG_AR600x_DEBUG_UART_TX_PIN;
+	status = ath6kl_bmi_write(ar,
+				  ath6kl_get_hi_item_addr(ar,
+				  HI_ITEM(hi_dbg_uart_txpin)),
+				  (u8 *)&param, 4);
+
+	return status;
+}
+
+static int ath6kl_init(struct net_device *dev)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	int status = 0;
+	s32 timeleft;
+
+	if (!ar)
+		return -EIO;
+
+	/* Do we need to finish the BMI phase */
+	if (ath6kl_bmi_done(ar)) {
+		status = -EIO;
+		goto ath6kl_init_done;
+	}
+
+	/* Indicate that WMI is enabled (although not ready yet) */
+	set_bit(WMI_ENABLED, &ar->flag);
+	ar->wmi = ath6kl_wmi_init(ar);
+	if (!ar->wmi) {
+		ath6kl_err("failed to initialize wmi\n");
+		status = -EIO;
+		goto ath6kl_init_done;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi);
+
+	wlan_node_table_init(&ar->scan_table);
+
+	/*
+	 * The reason we have to wait for the target here is that the
+	 * driver layer has to init BMI in order to set the host block
+	 * size.
+	 */
+	if (ath6kl_htc_wait_target(ar->htc_target)) {
+		status = -EIO;
+		goto err_node_cleanup;
+	}
+
+	if (ath6kl_init_service_ep(ar)) {
+		status = -EIO;
+		goto err_cleanup_scatter;
+	}
+
+	/* setup access class priority mappings */
+	ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest  */
+	ar->ac_stream_pri_map[WMM_AC_BE] = 1;
+	ar->ac_stream_pri_map[WMM_AC_VI] = 2;
+	ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */
+
+	/* give our connected endpoints some buffers */
+	ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep);
+	ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]);
+
+	/* allocate some buffers that handle larger AMSDU frames */
+	ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS);
+
+	/* setup credit distribution */
+	ath6k_setup_credit_dist(ar->htc_target, &ar->credit_state_info);
+
+	ath6kl_cookie_init(ar);
+
+	/* start HTC */
+	status = ath6kl_htc_start(ar->htc_target);
+
+	if (status) {
+		ath6kl_cookie_cleanup(ar);
+		goto err_rxbuf_cleanup;
+	}
+
+	/* Wait for Wmi event to be ready */
+	timeleft = wait_event_interruptible_timeout(ar->event_wq,
+						    test_bit(WMI_READY,
+							     &ar->flag),
+						    WMI_TIMEOUT);
+
+	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);
+		status = -EIO;
+		goto err_htc_stop;
+	}
+
+	if (!timeleft || signal_pending(current)) {
+		ath6kl_err("wmi is not ready or wait was interrupted\n");
+		status = -EIO;
+		goto err_htc_stop;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__);
+
+	/* communicate the wmi protocol verision to the target */
+	if ((ath6kl_set_host_app_area(ar)) != 0)
+		ath6kl_err("unable to set the host app area\n");
+
+	ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER |
+			 ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST;
+
+	status = ath6kl_target_config_wlan_params(ar);
+	if (!status)
+		goto ath6kl_init_done;
+
+err_htc_stop:
+	ath6kl_htc_stop(ar->htc_target);
+err_rxbuf_cleanup:
+	ath6kl_htc_flush_rx_buf(ar->htc_target);
+	ath6kl_cleanup_amsdu_rxbufs(ar);
+err_cleanup_scatter:
+	ath6kl_hif_cleanup_scatter(ar);
+err_node_cleanup:
+	wlan_node_table_cleanup(&ar->scan_table);
+	ath6kl_wmi_shutdown(ar->wmi);
+	clear_bit(WMI_ENABLED, &ar->flag);
+	ar->wmi = NULL;
+
+ath6kl_init_done:
+	return status;
+}
+
+int ath6kl_core_init(struct ath6kl *ar)
+{
+	int ret = 0;
+	struct ath6kl_bmi_target_info targ_info;
+
+	ar->ath6kl_wq = create_singlethread_workqueue("ath6kl");
+	if (!ar->ath6kl_wq)
+		return -ENOMEM;
+
+	ret = ath6kl_bmi_init(ar);
+	if (ret)
+		goto err_wq;
+
+	ret = ath6kl_bmi_get_target_info(ar, &targ_info);
+	if (ret)
+		goto err_bmi_cleanup;
+
+	ar->version.target_ver = le32_to_cpu(targ_info.version);
+	ar->target_type = le32_to_cpu(targ_info.type);
+	ar->wdev->wiphy->hw_version = le32_to_cpu(targ_info.version);
+
+	ret = ath6kl_configure_target(ar);
+	if (ret)
+		goto err_bmi_cleanup;
+
+	ar->htc_target = ath6kl_htc_create(ar);
+
+	if (!ar->htc_target) {
+		ret = -ENOMEM;
+		goto err_bmi_cleanup;
+	}
+
+	ar->aggr_cntxt = aggr_init(ar->net_dev);
+	if (!ar->aggr_cntxt) {
+		ath6kl_err("failed to initialize aggr\n");
+		ret = -ENOMEM;
+		goto err_htc_cleanup;
+	}
+
+	ret = ath6kl_init_upload(ar);
+	if (ret)
+		goto err_htc_cleanup;
+
+	ret = ath6kl_init(ar->net_dev);
+	if (ret)
+		goto err_htc_cleanup;
+
+	/* This runs the init function if registered */
+	ret = register_netdev(ar->net_dev);
+	if (ret) {
+		ath6kl_err("register_netdev failed\n");
+		ath6kl_destroy(ar->net_dev, 0);
+		return ret;
+	}
+
+	set_bit(NETDEV_REGISTERED, &ar->flag);
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n",
+			__func__, ar->net_dev->name, ar->net_dev, ar);
+
+	return ret;
+
+err_htc_cleanup:
+	ath6kl_htc_cleanup(ar->htc_target);
+err_bmi_cleanup:
+	ath6kl_bmi_cleanup(ar);
+err_wq:
+	destroy_workqueue(ar->ath6kl_wq);
+	return ret;
+}
+
+void ath6kl_stop_txrx(struct ath6kl *ar)
+{
+	struct net_device *ndev = ar->net_dev;
+
+	if (!ndev)
+		return;
+
+	set_bit(DESTROY_IN_PROGRESS, &ar->flag);
+
+	if (down_interruptible(&ar->sem)) {
+		ath6kl_err("down_interruptible failed\n");
+		return;
+	}
+
+	if (ar->wlan_pwr_state != WLAN_POWER_STATE_CUT_PWR)
+		ath6kl_stop_endpoint(ndev, false, true);
+
+	clear_bit(WLAN_ENABLED, &ar->flag);
+}
+
+/*
+ * We need to differentiate between the surprise and planned removal of the
+ * device because of the following consideration:
+ *
+ * - In case of surprise removal, the hcd already frees up the pending
+ *   for the device and hence there is no need to unregister the function
+ *   driver inorder to get these requests. For planned removal, the function
+ *   driver has to explicitly unregister itself to have the hcd return all the
+ *   pending requests before the data structures for the devices are freed up.
+ *   Note that as per the current implementation, the function driver will
+ *   end up releasing all the devices since there is no API to selectively
+ *   release a particular device.
+ *
+ * - Certain commands issued to the target can be skipped for surprise
+ *   removal since they will anyway not go through.
+ */
+void ath6kl_destroy(struct net_device *dev, unsigned int unregister)
+{
+	struct ath6kl *ar;
+
+	if (!dev || !ath6kl_priv(dev)) {
+		ath6kl_err("failed to get device structure\n");
+		return;
+	}
+
+	ar = ath6kl_priv(dev);
+
+	destroy_workqueue(ar->ath6kl_wq);
+
+	if (ar->htc_target)
+		ath6kl_htc_cleanup(ar->htc_target);
+
+	aggr_module_destroy(ar->aggr_cntxt);
+
+	ath6kl_cookie_cleanup(ar);
+
+	ath6kl_cleanup_amsdu_rxbufs(ar);
+
+	ath6kl_bmi_cleanup(ar);
+
+	if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) {
+		unregister_netdev(dev);
+		clear_bit(NETDEV_REGISTERED, &ar->flag);
+	}
+
+	free_netdev(dev);
+
+	wlan_node_table_cleanup(&ar->scan_table);
+
+	kfree(ar->fw_board);
+	kfree(ar->fw_otp);
+	kfree(ar->fw);
+	kfree(ar->fw_patch);
+
+	ath6kl_cfg80211_deinit(ar);
+}

+ 1337 - 0
drivers/net/wireless/ath/ath6kl/main.c

@@ -0,0 +1,1337 @@
+/*
+ * Copyright (c) 2004-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 "core.h"
+#include "hif-ops.h"
+#include "cfg80211.h"
+#include "target.h"
+#include "debug.h"
+
+struct ath6kl_sta *ath6kl_find_sta(struct ath6kl *ar, u8 *node_addr)
+{
+	struct ath6kl_sta *conn = NULL;
+	u8 i, max_conn;
+
+	max_conn = (ar->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0;
+
+	for (i = 0; i < max_conn; i++) {
+		if (memcmp(node_addr, ar->sta_list[i].mac, ETH_ALEN) == 0) {
+			conn = &ar->sta_list[i];
+			break;
+		}
+	}
+
+	return conn;
+}
+
+struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid)
+{
+	struct ath6kl_sta *conn = NULL;
+	u8 ctr;
+
+	for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
+		if (ar->sta_list[ctr].aid == aid) {
+			conn = &ar->sta_list[ctr];
+			break;
+		}
+	}
+	return conn;
+}
+
+static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie,
+			u8 ielen, u8 keymgmt, u8 ucipher, u8 auth)
+{
+	struct ath6kl_sta *sta;
+	u8 free_slot;
+
+	free_slot = aid - 1;
+
+	sta = &ar->sta_list[free_slot];
+	memcpy(sta->mac, mac, ETH_ALEN);
+	memcpy(sta->wpa_ie, wpaie, ielen);
+	sta->aid = aid;
+	sta->keymgmt = keymgmt;
+	sta->ucipher = ucipher;
+	sta->auth = auth;
+
+	ar->sta_list_index = ar->sta_list_index | (1 << free_slot);
+	ar->ap_stats.sta[free_slot].aid = cpu_to_le32(aid);
+}
+
+static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
+{
+	struct ath6kl_sta *sta = &ar->sta_list[i];
+
+	/* empty the queued pkts in the PS queue if any */
+	spin_lock_bh(&sta->psq_lock);
+	skb_queue_purge(&sta->psq);
+	spin_unlock_bh(&sta->psq_lock);
+
+	memset(&ar->ap_stats.sta[sta->aid - 1], 0,
+	       sizeof(struct wmi_per_sta_stat));
+	memset(sta->mac, 0, ETH_ALEN);
+	memset(sta->wpa_ie, 0, ATH6KL_MAX_IE);
+	sta->aid = 0;
+	sta->sta_flags = 0;
+
+	ar->sta_list_index = ar->sta_list_index & ~(1 << i);
+
+}
+
+static u8 ath6kl_remove_sta(struct ath6kl *ar, u8 *mac, u16 reason)
+{
+	u8 i, removed = 0;
+
+	if (is_zero_ether_addr(mac))
+		return removed;
+
+	if (is_broadcast_ether_addr(mac)) {
+		ath6kl_dbg(ATH6KL_DBG_TRC, "deleting all station\n");
+
+		for (i = 0; i < AP_MAX_NUM_STA; i++) {
+			if (!is_zero_ether_addr(ar->sta_list[i].mac)) {
+				ath6kl_sta_cleanup(ar, i);
+				removed = 1;
+			}
+		}
+	} else {
+		for (i = 0; i < AP_MAX_NUM_STA; i++) {
+			if (memcmp(ar->sta_list[i].mac, mac, ETH_ALEN) == 0) {
+				ath6kl_dbg(ATH6KL_DBG_TRC,
+					   "deleting station %pM aid=%d reason=%d\n",
+					   mac, ar->sta_list[i].aid, reason);
+				ath6kl_sta_cleanup(ar, i);
+				removed = 1;
+				break;
+			}
+		}
+	}
+
+	return removed;
+}
+
+enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac)
+{
+	struct ath6kl *ar = devt;
+	return ar->ac2ep_map[ac];
+}
+
+struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar)
+{
+	struct ath6kl_cookie *cookie;
+
+	cookie = ar->cookie_list;
+	if (cookie != NULL) {
+		ar->cookie_list = cookie->arc_list_next;
+		ar->cookie_count--;
+	}
+
+	return cookie;
+}
+
+void ath6kl_cookie_init(struct ath6kl *ar)
+{
+	u32 i;
+
+	ar->cookie_list = NULL;
+	ar->cookie_count = 0;
+
+	memset(ar->cookie_mem, 0, sizeof(ar->cookie_mem));
+
+	for (i = 0; i < MAX_COOKIE_NUM; i++)
+		ath6kl_free_cookie(ar, &ar->cookie_mem[i]);
+}
+
+void ath6kl_cookie_cleanup(struct ath6kl *ar)
+{
+	ar->cookie_list = NULL;
+	ar->cookie_count = 0;
+}
+
+void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie)
+{
+	/* Insert first */
+
+	if (!ar || !cookie)
+		return;
+
+	cookie->arc_list_next = ar->cookie_list;
+	ar->cookie_list = cookie;
+	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;
+	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 = hif_read_write_sync(ar, reg_addr + i, 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
+	 */
+	status = hif_read_write_sync(ar, reg_addr, (u8 *)(&addr),
+				     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 ATH6KL through its diagnostic window. No cooperation from
+ * the Target is required for this.
+ */
+int ath6kl_read_reg_diag(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 = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data,
+				     sizeof(u32), HIF_RD_SYNC_BYTE_INC);
+	if (status) {
+		ath6kl_err("failed to read from window data addr\n");
+		return status;
+	}
+
+	return status;
+}
+
+
+/*
+ * Write to the ATH6KL through its diagnostic window. No cooperation from
+ * the Target is required for this.
+ */
+static int ath6kl_write_reg_diag(struct ath6kl *ar, u32 *address, u32 *data)
+{
+	int status;
+
+	/* set write data */
+	status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data,
+				     sizeof(u32), HIF_WR_SYNC_BYTE_INC);
+	if (status) {
+		ath6kl_err("failed to write 0x%x to window data addr\n", *data);
+		return status;
+	}
+
+	/* set window register, which starts the write cycle */
+	return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
+				      *address);
+}
+
+int ath6kl_access_datadiag(struct ath6kl *ar, u32 address,
+			   u8 *data, u32 length, bool read)
+{
+	u32 count;
+	int status = 0;
+
+	for (count = 0; count < length; count += 4, address += 4) {
+		if (read) {
+			status = ath6kl_read_reg_diag(ar, &address,
+						      (u32 *) &data[count]);
+			if (status)
+				break;
+		} else {
+			status = ath6kl_write_reg_diag(ar, &address,
+						       (u32 *) &data[count]);
+			if (status)
+				break;
+		}
+	}
+
+	return status;
+}
+
+static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
+				bool wait_fot_compltn, bool cold_reset)
+{
+	int status = 0;
+	u32 address;
+	u32 data;
+
+	if (target_type != TARGET_TYPE_AR6003)
+		return;
+
+	data = cold_reset ? RESET_CONTROL_COLD_RST : RESET_CONTROL_MBOX_RST;
+
+	address = RTC_BASE_ADDRESS;
+	status = ath6kl_write_reg_diag(ar, &address, &data);
+
+	if (status)
+		ath6kl_err("failed to reset target\n");
+}
+
+void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
+			  bool get_dbglogs)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+	bool discon_issued;
+
+	netif_stop_queue(dev);
+
+	/* disable the target and the interrupts associated with it */
+	if (test_bit(WMI_READY, &ar->flag)) {
+		discon_issued = (test_bit(CONNECTED, &ar->flag) ||
+				 test_bit(CONNECT_PEND, &ar->flag));
+		ath6kl_disconnect(ar);
+		if (!keep_profile)
+			ath6kl_init_profile_info(ar);
+
+		del_timer(&ar->disconnect_timer);
+
+		clear_bit(WMI_READY, &ar->flag);
+		ath6kl_wmi_shutdown(ar->wmi);
+		clear_bit(WMI_ENABLED, &ar->flag);
+		ar->wmi = NULL;
+
+		/*
+		 * After wmi_shudown all WMI events will be dropped. We
+		 * need to cleanup the buffers allocated in AP mode and
+		 * give disconnect notification to stack, which usually
+		 * happens in the disconnect_event. Simulate the disconnect
+		 * event by calling the function directly. Sometimes
+		 * disconnect_event will be received when the debug logs
+		 * are collected.
+		 */
+		if (discon_issued)
+			ath6kl_disconnect_event(ar, DISCONNECT_CMD,
+						(ar->nw_type & AP_NETWORK) ?
+						bcast_mac : ar->bssid,
+						0, NULL, 0);
+
+		ar->user_key_ctrl = 0;
+
+	} else {
+		ath6kl_dbg(ATH6KL_DBG_TRC,
+			   "%s: wmi is not ready 0x%p 0x%p\n",
+			   __func__, ar, ar->wmi);
+
+		/* Shut down WMI if we have started it */
+		if (test_bit(WMI_ENABLED, &ar->flag)) {
+			ath6kl_dbg(ATH6KL_DBG_TRC,
+				   "%s: shut down wmi\n", __func__);
+			ath6kl_wmi_shutdown(ar->wmi);
+			clear_bit(WMI_ENABLED, &ar->flag);
+			ar->wmi = NULL;
+		}
+	}
+
+	if (ar->htc_target) {
+		ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__);
+		ath6kl_htc_stop(ar->htc_target);
+	}
+
+	/*
+	 * Try to reset the device if we can. The driver may have been
+	 * configure NOT to reset the target during a debug session.
+	 */
+	ath6kl_dbg(ATH6KL_DBG_TRC,
+		   "attempting to reset target on instance destroy\n");
+	ath6kl_reset_device(ar, ar->target_type, true, true);
+}
+
+static void ath6kl_install_static_wep_keys(struct ath6kl *ar)
+{
+	u8 index;
+	u8 keyusage;
+
+	for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) {
+		if (ar->wep_key_list[index].key_len) {
+			keyusage = GROUP_USAGE;
+			if (index == ar->def_txkey_index)
+				keyusage |= TX_USAGE;
+
+			ath6kl_wmi_addkey_cmd(ar->wmi,
+					      index,
+					      WEP_CRYPT,
+					      keyusage,
+					      ar->wep_key_list[index].key_len,
+					      NULL,
+					      ar->wep_key_list[index].key,
+					      KEY_OP_INIT_VAL, NULL,
+					      NO_SYNC_WMIFLAG);
+		}
+	}
+}
+
+static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid,
+				   u16 listen_int, u16 beacon_int,
+				   u8 assoc_resp_len, u8 *assoc_info)
+{
+	struct net_device *dev = ar->net_dev;
+	struct station_info sinfo;
+	struct ath6kl_req_key *ik;
+	enum crypto_type keyType = NONE_CRYPT;
+
+	if (memcmp(dev->dev_addr, bssid, ETH_ALEN) == 0) {
+		ik = &ar->ap_mode_bkey;
+
+		switch (ar->auth_mode) {
+		case NONE_AUTH:
+			if (ar->prwise_crypto == WEP_CRYPT)
+				ath6kl_install_static_wep_keys(ar);
+			break;
+		case WPA_PSK_AUTH:
+		case WPA2_PSK_AUTH:
+		case (WPA_PSK_AUTH|WPA2_PSK_AUTH):
+			switch (ik->ik_type) {
+			case ATH6KL_CIPHER_TKIP:
+				keyType = TKIP_CRYPT;
+				break;
+			case ATH6KL_CIPHER_AES_CCM:
+				keyType = AES_CRYPT;
+				break;
+			default:
+				goto skip_key;
+			}
+			ath6kl_wmi_addkey_cmd(ar->wmi, ik->ik_keyix, keyType,
+					      GROUP_USAGE, ik->ik_keylen,
+					      (u8 *)&ik->ik_keyrsc,
+					      ik->ik_keydata,
+					      KEY_OP_INIT_VAL, ik->ik_macaddr,
+					      SYNC_BOTH_WMIFLAG);
+			break;
+		}
+skip_key:
+		set_bit(CONNECTED, &ar->flag);
+		return;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n",
+		   bssid, channel);
+
+	ath6kl_add_new_sta(ar, bssid, channel, assoc_info, assoc_resp_len,
+			   listen_int & 0xFF, beacon_int,
+			   (listen_int >> 8) & 0xFF);
+
+	/* send event to application */
+	memset(&sinfo, 0, sizeof(sinfo));
+
+	/* TODO: sinfo.generation */
+	/* TODO: need to deliver (Re)AssocReq IEs somehow.. change in
+	 * cfg80211 needed, e.g., by adding those into sinfo
+	 */
+	cfg80211_new_sta(ar->net_dev, bssid, &sinfo, GFP_KERNEL);
+
+	netif_wake_queue(ar->net_dev);
+
+	return;
+}
+
+/* Functions for Tx credit handling */
+void ath6k_credit_init(struct htc_credit_state_info *cred_info,
+		       struct list_head *ep_list,
+		       int tot_credits)
+{
+	struct htc_endpoint_credit_dist *cur_ep_dist;
+	int count;
+
+	cred_info->cur_free_credits = tot_credits;
+	cred_info->total_avail_credits = tot_credits;
+
+	list_for_each_entry(cur_ep_dist, ep_list, list) {
+		if (cur_ep_dist->endpoint == ENDPOINT_0)
+			continue;
+
+		cur_ep_dist->cred_min = cur_ep_dist->cred_per_msg;
+
+		if (tot_credits > 4)
+			if ((cur_ep_dist->svc_id == WMI_DATA_BK_SVC) ||
+			    (cur_ep_dist->svc_id == WMI_DATA_BE_SVC)) {
+				ath6kl_deposit_credit_to_ep(cred_info,
+						cur_ep_dist,
+						cur_ep_dist->cred_min);
+				cur_ep_dist->dist_flags |= HTC_EP_ACTIVE;
+			}
+
+		if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) {
+			ath6kl_deposit_credit_to_ep(cred_info, cur_ep_dist,
+						    cur_ep_dist->cred_min);
+			/*
+			 * Control service is always marked active, it
+			 * never goes inactive EVER.
+			 */
+			cur_ep_dist->dist_flags |= HTC_EP_ACTIVE;
+		} else if (cur_ep_dist->svc_id == WMI_DATA_BK_SVC)
+			/* this is the lowest priority data endpoint */
+			cred_info->lowestpri_ep_dist = cur_ep_dist->list;
+
+		/*
+		 * Streams have to be created (explicit | implicit) for all
+		 * kinds of traffic. BE endpoints are also inactive in the
+		 * beginning. When BE traffic starts it creates implicit
+		 * streams that redistributes credits.
+		 *
+		 * Note: all other endpoints have minimums set but are
+		 * initially given NO credits. credits will be distributed
+		 * as traffic activity demands
+		 */
+	}
+
+	WARN_ON(cred_info->cur_free_credits <= 0);
+
+	list_for_each_entry(cur_ep_dist, ep_list, list) {
+		if (cur_ep_dist->endpoint == ENDPOINT_0)
+			continue;
+
+		if (cur_ep_dist->svc_id == WMI_CONTROL_SVC)
+			cur_ep_dist->cred_norm = cur_ep_dist->cred_per_msg;
+		else {
+			/*
+			 * For the remaining data endpoints, we assume that
+			 * each cred_per_msg are the same. We use a simple
+			 * calculation here, we take the remaining credits
+			 * and determine how many max messages this can
+			 * cover and then set each endpoint's normal value
+			 * equal to 3/4 this amount.
+			 */
+			count = (cred_info->cur_free_credits /
+				 cur_ep_dist->cred_per_msg)
+				* cur_ep_dist->cred_per_msg;
+			count = (count * 3) >> 2;
+			count = max(count, cur_ep_dist->cred_per_msg);
+			cur_ep_dist->cred_norm = count;
+
+		}
+	}
+}
+
+/* initialize and setup credit distribution */
+int ath6k_setup_credit_dist(void *htc_handle,
+			    struct htc_credit_state_info *cred_info)
+{
+	u16 servicepriority[5];
+
+	memset(cred_info, 0, sizeof(struct htc_credit_state_info));
+
+	servicepriority[0] = WMI_CONTROL_SVC;  /* highest */
+	servicepriority[1] = WMI_DATA_VO_SVC;
+	servicepriority[2] = WMI_DATA_VI_SVC;
+	servicepriority[3] = WMI_DATA_BE_SVC;
+	servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */
+
+	/* set priority list */
+	ath6kl_htc_set_credit_dist(htc_handle, cred_info, servicepriority, 5);
+
+	return 0;
+}
+
+/* reduce an ep's credits back to a set limit */
+static void ath6k_reduce_credits(struct htc_credit_state_info *cred_info,
+				 struct htc_endpoint_credit_dist  *ep_dist,
+				 int limit)
+{
+	int credits;
+
+	ep_dist->cred_assngd = limit;
+
+	if (ep_dist->credits <= limit)
+		return;
+
+	credits = ep_dist->credits - limit;
+	ep_dist->credits -= credits;
+	cred_info->cur_free_credits += credits;
+}
+
+static void ath6k_credit_update(struct htc_credit_state_info *cred_info,
+				struct list_head *epdist_list)
+{
+	struct htc_endpoint_credit_dist *cur_dist_list;
+
+	list_for_each_entry(cur_dist_list, epdist_list, list) {
+		if (cur_dist_list->endpoint == ENDPOINT_0)
+			continue;
+
+		if (cur_dist_list->cred_to_dist > 0) {
+			cur_dist_list->credits +=
+					cur_dist_list->cred_to_dist;
+			cur_dist_list->cred_to_dist = 0;
+			if (cur_dist_list->credits >
+			    cur_dist_list->cred_assngd)
+				ath6k_reduce_credits(cred_info,
+						cur_dist_list,
+						cur_dist_list->cred_assngd);
+
+			if (cur_dist_list->credits >
+			    cur_dist_list->cred_norm)
+				ath6k_reduce_credits(cred_info, cur_dist_list,
+						     cur_dist_list->cred_norm);
+
+			if (!(cur_dist_list->dist_flags & HTC_EP_ACTIVE)) {
+				if (cur_dist_list->txq_depth == 0)
+					ath6k_reduce_credits(cred_info,
+							     cur_dist_list, 0);
+			}
+		}
+	}
+}
+
+/*
+ * HTC has an endpoint that needs credits, ep_dist is the endpoint in
+ * question.
+ */
+void ath6k_seek_credits(struct htc_credit_state_info *cred_info,
+			struct htc_endpoint_credit_dist *ep_dist)
+{
+	struct htc_endpoint_credit_dist *curdist_list;
+	int credits = 0;
+	int need;
+
+	if (ep_dist->svc_id == WMI_CONTROL_SVC)
+		goto out;
+
+	if ((ep_dist->svc_id == WMI_DATA_VI_SVC) ||
+	    (ep_dist->svc_id == WMI_DATA_VO_SVC))
+		if ((ep_dist->cred_assngd >= ep_dist->cred_norm))
+			goto out;
+
+	/*
+	 * For all other services, we follow a simple algorithm of:
+	 *
+	 * 1. checking the free pool for credits
+	 * 2. checking lower priority endpoints for credits to take
+	 */
+
+	credits = min(cred_info->cur_free_credits, ep_dist->seek_cred);
+
+	if (credits >= ep_dist->seek_cred)
+		goto out;
+
+	/*
+	 * We don't have enough in the free pool, try taking away from
+	 * lower priority services The rule for taking away credits:
+	 *
+	 *   1. Only take from lower priority endpoints
+	 *   2. Only take what is allocated above the minimum (never
+	 *      starve an endpoint completely)
+	 *   3. Only take what you need.
+	 */
+
+	list_for_each_entry_reverse(curdist_list,
+				    &cred_info->lowestpri_ep_dist,
+				    list) {
+		if (curdist_list == ep_dist)
+			break;
+
+		need = ep_dist->seek_cred - cred_info->cur_free_credits;
+
+		if ((curdist_list->cred_assngd - need) >=
+		     curdist_list->cred_min) {
+			/*
+			 * The current one has been allocated more than
+			 * it's minimum and it has enough credits assigned
+			 * above it's minimum to fulfill our need try to
+			 * take away just enough to fulfill our need.
+			 */
+			ath6k_reduce_credits(cred_info, curdist_list,
+					curdist_list->cred_assngd - need);
+
+			if (cred_info->cur_free_credits >=
+			    ep_dist->seek_cred)
+				break;
+		}
+
+		if (curdist_list->endpoint == ENDPOINT_0)
+			break;
+	}
+
+	credits = min(cred_info->cur_free_credits, ep_dist->seek_cred);
+
+out:
+	/* did we find some credits? */
+	if (credits)
+		ath6kl_deposit_credit_to_ep(cred_info, ep_dist, credits);
+
+	ep_dist->seek_cred = 0;
+}
+
+/* redistribute credits based on activity change */
+static void ath6k_redistribute_credits(struct htc_credit_state_info *info,
+				       struct list_head *ep_dist_list)
+{
+	struct htc_endpoint_credit_dist *curdist_list;
+
+	list_for_each_entry(curdist_list, ep_dist_list, list) {
+		if (curdist_list->endpoint == ENDPOINT_0)
+			continue;
+
+		if ((curdist_list->svc_id == WMI_DATA_BK_SVC)  ||
+		    (curdist_list->svc_id == WMI_DATA_BE_SVC))
+			curdist_list->dist_flags |= HTC_EP_ACTIVE;
+
+		if ((curdist_list->svc_id != WMI_CONTROL_SVC) &&
+		    !(curdist_list->dist_flags & HTC_EP_ACTIVE)) {
+			if (curdist_list->txq_depth == 0)
+				ath6k_reduce_credits(info,
+						curdist_list, 0);
+			else
+				ath6k_reduce_credits(info,
+						curdist_list,
+						curdist_list->cred_min);
+		}
+	}
+}
+
+/*
+ *
+ * This function is invoked whenever endpoints require credit
+ * distributions. A lock is held while this function is invoked, this
+ * function shall NOT block. The ep_dist_list is a list of distribution
+ * structures in prioritized order as defined by the call to the
+ * htc_set_credit_dist() api.
+ */
+void ath6k_credit_distribute(struct htc_credit_state_info *cred_info,
+			     struct list_head *ep_dist_list,
+			     enum htc_credit_dist_reason reason)
+{
+	switch (reason) {
+	case HTC_CREDIT_DIST_SEND_COMPLETE:
+		ath6k_credit_update(cred_info, ep_dist_list);
+		break;
+	case HTC_CREDIT_DIST_ACTIVITY_CHANGE:
+		ath6k_redistribute_credits(cred_info, ep_dist_list);
+		break;
+	default:
+		break;
+	}
+
+	WARN_ON(cred_info->cur_free_credits > cred_info->total_avail_credits);
+	WARN_ON(cred_info->cur_free_credits < 0);
+}
+
+void disconnect_timer_handler(unsigned long ptr)
+{
+	struct net_device *dev = (struct net_device *)ptr;
+	struct ath6kl *ar = ath6kl_priv(dev);
+
+	ath6kl_init_profile_info(ar);
+	ath6kl_disconnect(ar);
+}
+
+void ath6kl_disconnect(struct ath6kl *ar)
+{
+	if (test_bit(CONNECTED, &ar->flag) ||
+	    test_bit(CONNECT_PEND, &ar->flag)) {
+		ath6kl_wmi_disconnect_cmd(ar->wmi);
+		/*
+		 * Disconnect command is issued, clear the connect pending
+		 * flag. The connected flag will be cleared in
+		 * disconnect event notification.
+		 */
+		clear_bit(CONNECT_PEND, &ar->flag);
+	}
+}
+
+/* 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;
+	struct net_device *dev = ar->net_dev;
+
+	memcpy(dev->dev_addr, datap, ETH_ALEN);
+	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: mac addr = %pM\n",
+		   __func__, dev->dev_addr);
+
+	ar->version.wlan_ver = sw_ver;
+	ar->version.abi_ver = abi_ver;
+
+	snprintf(ar->wdev->wiphy->fw_version,
+		 sizeof(ar->wdev->wiphy->fw_version),
+		 "%u.%u.%u.%u",
+		 (ar->version.wlan_ver & 0xf0000000) >> 28,
+		 (ar->version.wlan_ver & 0x0f000000) >> 24,
+		 (ar->version.wlan_ver & 0x00ff0000) >> 16,
+		 (ar->version.wlan_ver & 0x0000ffff));
+
+	/* indicate to the waiting thread that the ready event was received */
+	set_bit(WMI_READY, &ar->flag);
+	wake_up(&ar->event_wq);
+
+	ath6kl_info("hw %s fw %s\n",
+		    get_hw_id_string(ar->wdev->wiphy->hw_version),
+		    ar->wdev->wiphy->fw_version);
+}
+
+void ath6kl_scan_complete_evt(struct ath6kl *ar, int status)
+{
+	ath6kl_cfg80211_scan_complete_event(ar, status);
+
+	if (!ar->usr_bss_filter)
+		ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_SCAN, "scan complete: %d\n", status);
+}
+
+void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid,
+			  u16 listen_int, u16 beacon_int,
+			  enum network_type net_type, u8 beacon_ie_len,
+			  u8 assoc_req_len, u8 assoc_resp_len,
+			  u8 *assoc_info)
+{
+	unsigned long flags;
+
+	if (ar->nw_type == AP_NETWORK) {
+		ath6kl_connect_ap_mode(ar, channel, bssid, listen_int,
+				       beacon_int, assoc_resp_len,
+				       assoc_info);
+		return;
+	}
+
+	ath6kl_cfg80211_connect_event(ar, channel, bssid,
+				      listen_int, beacon_int,
+				      net_type, beacon_ie_len,
+				      assoc_req_len, assoc_resp_len,
+				      assoc_info);
+
+	memcpy(ar->bssid, bssid, sizeof(ar->bssid));
+	ar->bss_ch = channel;
+
+	if ((ar->nw_type == INFRA_NETWORK))
+		ath6kl_wmi_listeninterval_cmd(ar->wmi, ar->listen_intvl_t,
+					      ar->listen_intvl_b);
+
+	netif_wake_queue(ar->net_dev);
+
+	/* Update connect & link status atomically */
+	spin_lock_irqsave(&ar->lock, flags);
+	set_bit(CONNECTED, &ar->flag);
+	clear_bit(CONNECT_PEND, &ar->flag);
+	netif_carrier_on(ar->net_dev);
+	spin_unlock_irqrestore(&ar->lock, flags);
+
+	aggr_reset_state(ar->aggr_cntxt);
+	ar->reconnect_flag = 0;
+
+	if ((ar->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable) {
+		memset(ar->node_map, 0, sizeof(ar->node_map));
+		ar->node_num = 0;
+		ar->next_ep_id = ENDPOINT_2;
+	}
+
+	if (!ar->usr_bss_filter)
+		ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+}
+
+void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast)
+{
+	struct ath6kl_sta *sta;
+	u8 tsc[6];
+	/*
+	 * For AP case, keyid will have aid of STA which sent pkt with
+	 * MIC error. Use this aid to get MAC & send it to hostapd.
+	 */
+	if (ar->nw_type == AP_NETWORK) {
+		sta = ath6kl_find_sta_by_aid(ar, (keyid >> 2));
+		if (!sta)
+			return;
+
+		ath6kl_dbg(ATH6KL_DBG_TRC,
+			   "ap tkip mic error received from aid=%d\n", keyid);
+
+		memset(tsc, 0, sizeof(tsc)); /* FIX: get correct TSC */
+		cfg80211_michael_mic_failure(ar->net_dev, sta->mac,
+					     NL80211_KEYTYPE_PAIRWISE, keyid,
+					     tsc, GFP_KERNEL);
+	} else
+		ath6kl_cfg80211_tkip_micerr_event(ar, keyid, ismcast);
+
+}
+
+static void ath6kl_update_target_stats(struct ath6kl *ar, u8 *ptr, u32 len)
+{
+	struct wmi_target_stats *tgt_stats =
+		(struct wmi_target_stats *) ptr;
+	struct target_stats *stats = &ar->target_stats;
+	struct tkip_ccmp_stats *ccmp_stats;
+	struct bss *conn_bss = NULL;
+	struct cserv_stats *c_stats;
+	u8 ac;
+
+	if (len < sizeof(*tgt_stats))
+		return;
+
+	/* update the RSSI of the connected bss */
+	if (test_bit(CONNECTED, &ar->flag)) {
+		conn_bss = ath6kl_wmi_find_node(ar->wmi, ar->bssid);
+		if (conn_bss) {
+			c_stats = &tgt_stats->cserv_stats;
+			conn_bss->ni_rssi =
+				a_sle16_to_cpu(c_stats->cs_ave_beacon_rssi);
+			conn_bss->ni_snr =
+				tgt_stats->cserv_stats.cs_ave_beacon_snr;
+			ath6kl_wmi_node_return(ar->wmi, conn_bss);
+		}
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n");
+
+	stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt);
+	stats->tx_byte += le32_to_cpu(tgt_stats->stats.tx.byte);
+	stats->tx_ucast_pkt += le32_to_cpu(tgt_stats->stats.tx.ucast_pkt);
+	stats->tx_ucast_byte += le32_to_cpu(tgt_stats->stats.tx.ucast_byte);
+	stats->tx_mcast_pkt += le32_to_cpu(tgt_stats->stats.tx.mcast_pkt);
+	stats->tx_mcast_byte += le32_to_cpu(tgt_stats->stats.tx.mcast_byte);
+	stats->tx_bcast_pkt  += le32_to_cpu(tgt_stats->stats.tx.bcast_pkt);
+	stats->tx_bcast_byte += le32_to_cpu(tgt_stats->stats.tx.bcast_byte);
+	stats->tx_rts_success_cnt +=
+		le32_to_cpu(tgt_stats->stats.tx.rts_success_cnt);
+
+	for (ac = 0; ac < WMM_NUM_AC; ac++)
+		stats->tx_pkt_per_ac[ac] +=
+			le32_to_cpu(tgt_stats->stats.tx.pkt_per_ac[ac]);
+
+	stats->tx_err += le32_to_cpu(tgt_stats->stats.tx.err);
+	stats->tx_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.fail_cnt);
+	stats->tx_retry_cnt += le32_to_cpu(tgt_stats->stats.tx.retry_cnt);
+	stats->tx_mult_retry_cnt +=
+		le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt);
+	stats->tx_rts_fail_cnt +=
+		le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt);
+	stats->tx_ucast_rate =
+	    ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate));
+
+	stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt);
+	stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte);
+	stats->rx_ucast_pkt += le32_to_cpu(tgt_stats->stats.rx.ucast_pkt);
+	stats->rx_ucast_byte += le32_to_cpu(tgt_stats->stats.rx.ucast_byte);
+	stats->rx_mcast_pkt += le32_to_cpu(tgt_stats->stats.rx.mcast_pkt);
+	stats->rx_mcast_byte += le32_to_cpu(tgt_stats->stats.rx.mcast_byte);
+	stats->rx_bcast_pkt += le32_to_cpu(tgt_stats->stats.rx.bcast_pkt);
+	stats->rx_bcast_byte += le32_to_cpu(tgt_stats->stats.rx.bcast_byte);
+	stats->rx_frgment_pkt += le32_to_cpu(tgt_stats->stats.rx.frgment_pkt);
+	stats->rx_err += le32_to_cpu(tgt_stats->stats.rx.err);
+	stats->rx_crc_err += le32_to_cpu(tgt_stats->stats.rx.crc_err);
+	stats->rx_key_cache_miss +=
+		le32_to_cpu(tgt_stats->stats.rx.key_cache_miss);
+	stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err);
+	stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame);
+	stats->rx_ucast_rate =
+	    ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate));
+
+	ccmp_stats = &tgt_stats->stats.tkip_ccmp_stats;
+
+	stats->tkip_local_mic_fail +=
+		le32_to_cpu(ccmp_stats->tkip_local_mic_fail);
+	stats->tkip_cnter_measures_invoked +=
+		le32_to_cpu(ccmp_stats->tkip_cnter_measures_invoked);
+	stats->tkip_fmt_err += le32_to_cpu(ccmp_stats->tkip_fmt_err);
+
+	stats->ccmp_fmt_err += le32_to_cpu(ccmp_stats->ccmp_fmt_err);
+	stats->ccmp_replays += le32_to_cpu(ccmp_stats->ccmp_replays);
+
+	stats->pwr_save_fail_cnt +=
+		le32_to_cpu(tgt_stats->pm_stats.pwr_save_failure_cnt);
+	stats->noise_floor_calib =
+		a_sle32_to_cpu(tgt_stats->noise_floor_calib);
+
+	stats->cs_bmiss_cnt +=
+		le32_to_cpu(tgt_stats->cserv_stats.cs_bmiss_cnt);
+	stats->cs_low_rssi_cnt +=
+		le32_to_cpu(tgt_stats->cserv_stats.cs_low_rssi_cnt);
+	stats->cs_connect_cnt +=
+		le16_to_cpu(tgt_stats->cserv_stats.cs_connect_cnt);
+	stats->cs_discon_cnt +=
+		le16_to_cpu(tgt_stats->cserv_stats.cs_discon_cnt);
+
+	stats->cs_ave_beacon_rssi =
+		a_sle16_to_cpu(tgt_stats->cserv_stats.cs_ave_beacon_rssi);
+
+	stats->cs_last_roam_msec =
+		tgt_stats->cserv_stats.cs_last_roam_msec;
+	stats->cs_snr = tgt_stats->cserv_stats.cs_snr;
+	stats->cs_rssi = a_sle16_to_cpu(tgt_stats->cserv_stats.cs_rssi);
+
+	stats->lq_val = le32_to_cpu(tgt_stats->lq_val);
+
+	stats->wow_pkt_dropped +=
+		le32_to_cpu(tgt_stats->wow_stats.wow_pkt_dropped);
+	stats->wow_host_pkt_wakeups +=
+		tgt_stats->wow_stats.wow_host_pkt_wakeups;
+	stats->wow_host_evt_wakeups +=
+		tgt_stats->wow_stats.wow_host_evt_wakeups;
+	stats->wow_evt_discarded +=
+		le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded);
+
+	if (test_bit(STATS_UPDATE_PEND, &ar->flag)) {
+		clear_bit(STATS_UPDATE_PEND, &ar->flag);
+		wake_up(&ar->event_wq);
+	}
+}
+
+static void ath6kl_add_le32(__le32 *var, __le32 val)
+{
+	*var = cpu_to_le32(le32_to_cpu(*var) + le32_to_cpu(val));
+}
+
+void ath6kl_tgt_stats_event(struct ath6kl *ar, u8 *ptr, u32 len)
+{
+	struct wmi_ap_mode_stat *p = (struct wmi_ap_mode_stat *) ptr;
+	struct wmi_ap_mode_stat *ap = &ar->ap_stats;
+	struct wmi_per_sta_stat *st_ap, *st_p;
+	u8 ac;
+
+	if (ar->nw_type == AP_NETWORK) {
+		if (len < sizeof(*p))
+			return;
+
+		for (ac = 0; ac < AP_MAX_NUM_STA; ac++) {
+			st_ap = &ap->sta[ac];
+			st_p = &p->sta[ac];
+
+			ath6kl_add_le32(&st_ap->tx_bytes, st_p->tx_bytes);
+			ath6kl_add_le32(&st_ap->tx_pkts, st_p->tx_pkts);
+			ath6kl_add_le32(&st_ap->tx_error, st_p->tx_error);
+			ath6kl_add_le32(&st_ap->tx_discard, st_p->tx_discard);
+			ath6kl_add_le32(&st_ap->rx_bytes, st_p->rx_bytes);
+			ath6kl_add_le32(&st_ap->rx_pkts, st_p->rx_pkts);
+			ath6kl_add_le32(&st_ap->rx_error, st_p->rx_error);
+			ath6kl_add_le32(&st_ap->rx_discard, st_p->rx_discard);
+		}
+
+	} else {
+		ath6kl_update_target_stats(ar, ptr, len);
+	}
+}
+
+void ath6kl_wakeup_event(void *dev)
+{
+	struct ath6kl *ar = (struct ath6kl *) dev;
+
+	wake_up(&ar->event_wq);
+}
+
+void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr)
+{
+	struct ath6kl *ar = (struct ath6kl *) devt;
+
+	ar->tx_pwr = tx_pwr;
+	wake_up(&ar->event_wq);
+}
+
+void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid)
+{
+	struct ath6kl_sta *conn;
+	struct sk_buff *skb;
+	bool psq_empty = false;
+
+	conn = ath6kl_find_sta_by_aid(ar, aid);
+
+	if (!conn)
+		return;
+	/*
+	 * Send out a packet queued on ps queue. When the ps queue
+	 * becomes empty update the PVB for this station.
+	 */
+	spin_lock_bh(&conn->psq_lock);
+	psq_empty  = skb_queue_empty(&conn->psq);
+	spin_unlock_bh(&conn->psq_lock);
+
+	if (psq_empty)
+		/* TODO: Send out a NULL data frame */
+		return;
+
+	spin_lock_bh(&conn->psq_lock);
+	skb = skb_dequeue(&conn->psq);
+	spin_unlock_bh(&conn->psq_lock);
+
+	conn->sta_flags |= STA_PS_POLLED;
+	ath6kl_data_tx(skb, ar->net_dev);
+	conn->sta_flags &= ~STA_PS_POLLED;
+
+	spin_lock_bh(&conn->psq_lock);
+	psq_empty  = skb_queue_empty(&conn->psq);
+	spin_unlock_bh(&conn->psq_lock);
+
+	if (psq_empty)
+		ath6kl_wmi_set_pvb_cmd(ar->wmi, conn->aid, 0);
+}
+
+void ath6kl_dtimexpiry_event(struct ath6kl *ar)
+{
+	bool mcastq_empty = false;
+	struct sk_buff *skb;
+
+	/*
+	 * If there are no associated STAs, ignore the DTIM expiry event.
+	 * There can be potential race conditions where the last associated
+	 * STA may disconnect & before the host could clear the 'Indicate
+	 * DTIM' request to the firmware, the firmware would have just
+	 * indicated a DTIM expiry event. The race is between 'clear DTIM
+	 * expiry cmd' going from the host to the firmware & the DTIM
+	 * expiry event happening from the firmware to the host.
+	 */
+	if (!ar->sta_list_index)
+		return;
+
+	spin_lock_bh(&ar->mcastpsq_lock);
+	mcastq_empty = skb_queue_empty(&ar->mcastpsq);
+	spin_unlock_bh(&ar->mcastpsq_lock);
+
+	if (mcastq_empty)
+		return;
+
+	/* set the STA flag to dtim_expired for the frame to go out */
+	set_bit(DTIM_EXPIRED, &ar->flag);
+
+	spin_lock_bh(&ar->mcastpsq_lock);
+	while ((skb = skb_dequeue(&ar->mcastpsq)) != NULL) {
+		spin_unlock_bh(&ar->mcastpsq_lock);
+
+		ath6kl_data_tx(skb, ar->net_dev);
+
+		spin_lock_bh(&ar->mcastpsq_lock);
+	}
+	spin_unlock_bh(&ar->mcastpsq_lock);
+
+	clear_bit(DTIM_EXPIRED, &ar->flag);
+
+	/* clear the LSB of the BitMapCtl field of the TIM IE */
+	ath6kl_wmi_set_pvb_cmd(ar->wmi, MCAST_AID, 0);
+}
+
+void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
+			     u8 assoc_resp_len, u8 *assoc_info,
+			     u16 prot_reason_status)
+{
+	struct bss *wmi_ssid_node = NULL;
+	unsigned long flags;
+
+	if (ar->nw_type == AP_NETWORK) {
+		if (!ath6kl_remove_sta(ar, bssid, prot_reason_status))
+			return;
+
+		/* if no more associated STAs, empty the mcast PS q */
+		if (ar->sta_list_index == 0) {
+			spin_lock_bh(&ar->mcastpsq_lock);
+			skb_queue_purge(&ar->mcastpsq);
+			spin_unlock_bh(&ar->mcastpsq_lock);
+
+			/* clear the LSB of the TIM IE's BitMapCtl field */
+			if (test_bit(WMI_READY, &ar->flag))
+				ath6kl_wmi_set_pvb_cmd(ar->wmi, MCAST_AID, 0);
+		}
+
+		if (!is_broadcast_ether_addr(bssid)) {
+			/* send event to application */
+			cfg80211_del_sta(ar->net_dev, bssid, GFP_KERNEL);
+		}
+
+		clear_bit(CONNECTED, &ar->flag);
+		return;
+	}
+
+	ath6kl_cfg80211_disconnect_event(ar, reason, bssid,
+				       assoc_resp_len, assoc_info,
+				       prot_reason_status);
+
+	aggr_reset_state(ar->aggr_cntxt);
+
+	del_timer(&ar->disconnect_timer);
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_CONNECT,
+		   "disconnect reason is %d\n", reason);
+
+	/*
+	 * If the event is due to disconnect cmd from the host, only they
+	 * the target would stop trying to connect. Under any other
+	 * condition, target would keep trying to connect.
+	 */
+	if (reason == DISCONNECT_CMD) {
+		if (!ar->usr_bss_filter && test_bit(WMI_READY, &ar->flag))
+			ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+	} else {
+		set_bit(CONNECT_PEND, &ar->flag);
+		if (((reason == ASSOC_FAILED) &&
+		    (prot_reason_status == 0x11)) ||
+		    ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0)
+		     && (ar->reconnect_flag == 1))) {
+			set_bit(CONNECTED, &ar->flag);
+			return;
+		}
+	}
+
+	if ((reason == NO_NETWORK_AVAIL) && test_bit(WMI_READY, &ar->flag))  {
+		ath6kl_wmi_node_free(ar->wmi, bssid);
+
+		/*
+		 * In case any other same SSID nodes are present remove it,
+		 * since those nodes also not available now.
+		 */
+		do {
+			/*
+			 * Find the nodes based on SSID and remove it
+			 *
+			 * Note: This case will not work out for
+			 * Hidden-SSID
+			 */
+			wmi_ssid_node = ath6kl_wmi_find_ssid_node(ar->wmi,
+								  ar->ssid,
+								  ar->ssid_len,
+								  false,
+								  true);
+
+			if (wmi_ssid_node)
+				ath6kl_wmi_node_free(ar->wmi,
+						     wmi_ssid_node->ni_macaddr);
+
+		} while (wmi_ssid_node);
+	}
+
+	/* update connect & link status atomically */
+	spin_lock_irqsave(&ar->lock, flags);
+	clear_bit(CONNECTED, &ar->flag);
+	netif_carrier_off(ar->net_dev);
+	spin_unlock_irqrestore(&ar->lock, flags);
+
+	if ((reason != CSERV_DISCONNECT) || (ar->reconnect_flag != 1))
+		ar->reconnect_flag = 0;
+
+	if (reason != CSERV_DISCONNECT)
+		ar->user_key_ctrl = 0;
+
+	netif_stop_queue(ar->net_dev);
+	memset(ar->bssid, 0, sizeof(ar->bssid));
+	ar->bss_ch = 0;
+
+	ath6kl_tx_data_cleanup(ar);
+}
+
+static int ath6kl_open(struct net_device *dev)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ar->lock, flags);
+
+	set_bit(WLAN_ENABLED, &ar->flag);
+
+	if (test_bit(CONNECTED, &ar->flag)) {
+		netif_carrier_on(dev);
+		netif_wake_queue(dev);
+	} else
+		netif_carrier_off(dev);
+
+	spin_unlock_irqrestore(&ar->lock, flags);
+
+	return 0;
+}
+
+static int ath6kl_close(struct net_device *dev)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+
+	netif_stop_queue(dev);
+
+	ath6kl_disconnect(ar);
+
+	if (test_bit(WMI_READY, &ar->flag)) {
+		if (ath6kl_wmi_scanparams_cmd(ar->wmi, 0xFFFF, 0, 0, 0, 0, 0, 0,
+					      0, 0, 0))
+			return -EIO;
+
+		clear_bit(WLAN_ENABLED, &ar->flag);
+	}
+
+	ath6kl_cfg80211_scan_complete_event(ar, -ECANCELED);
+
+	return 0;
+}
+
+static struct net_device_stats *ath6kl_get_stats(struct net_device *dev)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+
+	return &ar->net_stats;
+}
+
+static struct net_device_ops ath6kl_netdev_ops = {
+	.ndo_open               = ath6kl_open,
+	.ndo_stop               = ath6kl_close,
+	.ndo_start_xmit         = ath6kl_data_tx,
+	.ndo_get_stats          = ath6kl_get_stats,
+};
+
+void init_netdev(struct net_device *dev)
+{
+	dev->netdev_ops = &ath6kl_netdev_ops;
+	dev->watchdog_timeo = ATH6KL_TX_TIMEOUT;
+
+	dev->needed_headroom = ETH_HLEN;
+	dev->needed_headroom += sizeof(struct ath6kl_llc_snap_hdr) +
+				sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH
+				+ WMI_MAX_TX_META_SZ;
+
+	return;
+}

+ 234 - 0
drivers/net/wireless/ath/ath6kl/node.c

@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2004-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 "htc.h"
+#include "wmi.h"
+#include "debug.h"
+
+struct bss *wlan_node_alloc(int wh_size)
+{
+	struct bss *ni;
+
+	ni = kzalloc(sizeof(struct bss), GFP_ATOMIC);
+
+	if ((ni != NULL) && wh_size) {
+		ni->ni_buf = kmalloc(wh_size, GFP_ATOMIC);
+		if (ni->ni_buf == NULL) {
+			kfree(ni);
+			return NULL;
+		}
+	}
+
+	return ni;
+}
+
+void wlan_node_free(struct bss *ni)
+{
+	kfree(ni->ni_buf);
+	kfree(ni);
+}
+
+void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni,
+		     const u8 *mac_addr)
+{
+	int hash;
+
+	memcpy(ni->ni_macaddr, mac_addr, ETH_ALEN);
+	hash = ATH6KL_NODE_HASH(mac_addr);
+	ni->ni_refcnt = 1;
+
+	ni->ni_tstamp = jiffies_to_msecs(jiffies);
+	ni->ni_actcnt = WLAN_NODE_INACT_CNT;
+
+	spin_lock_bh(&nt->nt_nodelock);
+
+	/* insert at the end of the node list */
+	ni->ni_list_next = NULL;
+	ni->ni_list_prev = nt->nt_node_last;
+	if (nt->nt_node_last != NULL)
+		nt->nt_node_last->ni_list_next = ni;
+
+	nt->nt_node_last = ni;
+	if (nt->nt_node_first == NULL)
+		nt->nt_node_first = ni;
+
+	/* insert into the hash list */
+	ni->ni_hash_next = nt->nt_hash[hash];
+	if (ni->ni_hash_next != NULL)
+		nt->nt_hash[hash]->ni_hash_prev = ni;
+
+	ni->ni_hash_prev = NULL;
+	nt->nt_hash[hash] = ni;
+
+	spin_unlock_bh(&nt->nt_nodelock);
+}
+
+struct bss *wlan_find_node(struct ath6kl_node_table *nt,
+			   const u8 *mac_addr)
+{
+	struct bss *ni, *found_ni = NULL;
+	int hash;
+
+	spin_lock_bh(&nt->nt_nodelock);
+
+	hash = ATH6KL_NODE_HASH(mac_addr);
+	for (ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
+		if (memcmp(ni->ni_macaddr, mac_addr, ETH_ALEN) == 0) {
+			ni->ni_refcnt++;
+			found_ni = ni;
+			break;
+		}
+	}
+
+	spin_unlock_bh(&nt->nt_nodelock);
+
+	return found_ni;
+}
+
+void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni)
+{
+	int hash;
+
+	spin_lock_bh(&nt->nt_nodelock);
+
+	if (ni->ni_list_prev == NULL)
+		/* fix list head */
+		nt->nt_node_first = ni->ni_list_next;
+	else
+		ni->ni_list_prev->ni_list_next = ni->ni_list_next;
+
+	if (ni->ni_list_next == NULL)
+		/* fix list tail */
+		nt->nt_node_last = ni->ni_list_prev;
+	else
+		ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
+
+	if (ni->ni_hash_prev == NULL) {
+		/* first in list so fix the list head */
+		hash = ATH6KL_NODE_HASH(ni->ni_macaddr);
+		nt->nt_hash[hash] = ni->ni_hash_next;
+	} else {
+		ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
+	}
+
+	if (ni->ni_hash_next != NULL)
+		ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
+
+	wlan_node_free(ni);
+
+	spin_unlock_bh(&nt->nt_nodelock);
+}
+
+static void wlan_node_dec_free(struct bss *ni)
+{
+	if ((ni->ni_refcnt--) == 1)
+		wlan_node_free(ni);
+}
+
+void wlan_free_allnodes(struct ath6kl_node_table *nt)
+{
+	struct bss *ni;
+
+	while ((ni = nt->nt_node_first) != NULL)
+		wlan_node_reclaim(nt, ni);
+}
+
+void wlan_iterate_nodes(struct ath6kl_node_table *nt, void *arg)
+{
+	struct bss *ni;
+
+	spin_lock_bh(&nt->nt_nodelock);
+	for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
+			ni->ni_refcnt++;
+			ath6kl_cfg80211_scan_node(arg, ni);
+			wlan_node_dec_free(ni);
+	}
+	spin_unlock_bh(&nt->nt_nodelock);
+}
+
+void wlan_node_table_init(struct ath6kl_node_table *nt)
+{
+	ath6kl_dbg(ATH6KL_DBG_WLAN_NODE, "node table = 0x%lx\n",
+		   (unsigned long)nt);
+
+	memset(nt, 0, sizeof(struct ath6kl_node_table));
+
+	spin_lock_init(&nt->nt_nodelock);
+
+	nt->nt_node_age = WLAN_NODE_INACT_TIMEOUT_MSEC;
+}
+
+void wlan_refresh_inactive_nodes(struct ath6kl *ar)
+{
+	struct ath6kl_node_table *nt = &ar->scan_table;
+	struct bss *bss;
+	u32 now;
+
+	now = jiffies_to_msecs(jiffies);
+	bss = nt->nt_node_first;
+	while (bss != NULL) {
+		/* refresh all nodes except the current bss */
+		if (memcmp(ar->bssid, bss->ni_macaddr, ETH_ALEN) != 0) {
+			if (((now - bss->ni_tstamp) > nt->nt_node_age)
+			    || --bss->ni_actcnt == 0) {
+				wlan_node_reclaim(nt, bss);
+			}
+		}
+		bss = bss->ni_list_next;
+	}
+}
+
+void wlan_node_table_cleanup(struct ath6kl_node_table *nt)
+{
+	wlan_free_allnodes(nt);
+}
+
+struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 * ssid,
+				u32 ssid_len, bool is_wpa2, bool match_ssid)
+{
+	struct bss *ni, *found_ni = NULL;
+	u8 *ie_ssid;
+
+	spin_lock_bh(&nt->nt_nodelock);
+
+	for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
+
+		ie_ssid = ni->ni_cie.ie_ssid;
+
+		if ((ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) &&
+		    (memcmp(ssid, &ie_ssid[2], ssid_len) == 0)) {
+
+				if (match_ssid ||
+				    (is_wpa2 && ni->ni_cie.ie_rsn != NULL) ||
+				    (!is_wpa2 && ni->ni_cie.ie_wpa != NULL)) {
+					ni->ni_refcnt++;
+					found_ni = ni;
+					break;
+				}
+		}
+	}
+
+	spin_unlock_bh(&nt->nt_nodelock);
+
+	return found_ni;
+}
+
+void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni)
+{
+	spin_lock_bh(&nt->nt_nodelock);
+	wlan_node_dec_free(ni);
+	spin_unlock_bh(&nt->nt_nodelock);
+}

+ 912 - 0
drivers/net/wireless/ath/ath6kl/sdio.c

@@ -0,0 +1,912 @@
+/*
+ * Copyright (c) 2004-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/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include "htc_hif.h"
+#include "hif-ops.h"
+#include "target.h"
+#include "debug.h"
+
+struct ath6kl_sdio {
+	struct sdio_func *func;
+
+	spinlock_t lock;
+
+	/* free list */
+	struct list_head bus_req_freeq;
+
+	/* available bus requests */
+	struct bus_request bus_req[BUS_REQUEST_MAX_NUM];
+
+	struct ath6kl *ar;
+	u8 *dma_buffer;
+
+	/* scatter request list head */
+	struct list_head scat_req;
+
+	spinlock_t scat_lock;
+	bool is_disabled;
+	atomic_t irq_handling;
+	const struct sdio_device_id *id;
+	struct work_struct wr_async_work;
+	struct list_head wr_asyncq;
+	spinlock_t wr_async_lock;
+};
+
+#define CMD53_ARG_READ          0
+#define CMD53_ARG_WRITE         1
+#define CMD53_ARG_BLOCK_BASIS   1
+#define CMD53_ARG_FIXED_ADDRESS 0
+#define CMD53_ARG_INCR_ADDRESS  1
+
+static inline struct ath6kl_sdio *ath6kl_sdio_priv(struct ath6kl *ar)
+{
+	return ar->hif_priv;
+}
+
+/*
+ * Macro to check if DMA buffer is WORD-aligned and DMA-able.
+ * Most host controllers assume the buffer is DMA'able and will
+ * bug-check otherwise (i.e. buffers on the stack). virt_addr_valid
+ * check fails on stack memory.
+ */
+static inline bool buf_needs_bounce(u8 *buf)
+{
+	return ((unsigned long) buf & 0x3) || !virt_addr_valid(buf);
+}
+
+static void ath6kl_sdio_set_mbox_info(struct ath6kl *ar)
+{
+	struct ath6kl_mbox_info *mbox_info = &ar->mbox_info;
+
+	/* EP1 has an extended range */
+	mbox_info->htc_addr = HIF_MBOX_BASE_ADDR;
+	mbox_info->htc_ext_addr = HIF_MBOX0_EXT_BASE_ADDR;
+	mbox_info->htc_ext_sz = HIF_MBOX0_EXT_WIDTH;
+	mbox_info->block_size = HIF_MBOX_BLOCK_SIZE;
+	mbox_info->gmbox_addr = HIF_GMBOX_BASE_ADDR;
+	mbox_info->gmbox_sz = HIF_GMBOX_WIDTH;
+}
+
+static inline void ath6kl_sdio_set_cmd53_arg(u32 *arg, u8 rw, u8 func,
+					     u8 mode, u8 opcode, u32 addr,
+					     u16 blksz)
+{
+	*arg = (((rw & 1) << 31) |
+		((func & 0x7) << 28) |
+		((mode & 1) << 27) |
+		((opcode & 1) << 26) |
+		((addr & 0x1FFFF) << 9) |
+		(blksz & 0x1FF));
+}
+
+static inline void ath6kl_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw,
+					     unsigned int address,
+					     unsigned char val)
+{
+	const u8 func = 0;
+
+	*arg = ((write & 1) << 31) |
+	       ((func & 0x7) << 28) |
+	       ((raw & 1) << 27) |
+	       (1 << 26) |
+	       ((address & 0x1FFFF) << 9) |
+	       (1 << 8) |
+	       (val & 0xFF);
+}
+
+static int ath6kl_sdio_func0_cmd52_wr_byte(struct mmc_card *card,
+					   unsigned int address,
+					   unsigned char byte)
+{
+	struct mmc_command io_cmd;
+
+	memset(&io_cmd, 0, sizeof(io_cmd));
+	ath6kl_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte);
+	io_cmd.opcode = SD_IO_RW_DIRECT;
+	io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+	return mmc_wait_for_cmd(card->host, &io_cmd, 0);
+}
+
+static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
+			  u8 *buf, u32 len)
+{
+	int ret = 0;
+
+	if (request & HIF_WRITE) {
+		if (addr >= HIF_MBOX_BASE_ADDR &&
+		    addr <= HIF_MBOX_END_ADDR)
+			addr += (HIF_MBOX_WIDTH - len);
+
+		if (addr == HIF_MBOX0_EXT_BASE_ADDR)
+			addr += HIF_MBOX0_EXT_WIDTH - len;
+
+		if (request & HIF_FIXED_ADDRESS)
+			ret = sdio_writesb(func, addr, buf, len);
+		else
+			ret = sdio_memcpy_toio(func, addr, buf, len);
+	} else {
+		if (request & HIF_FIXED_ADDRESS)
+			ret = sdio_readsb(func, buf, addr, len);
+		else
+			ret = sdio_memcpy_fromio(func, buf, addr, len);
+	}
+
+	return ret;
+}
+
+static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio)
+{
+	struct bus_request *bus_req;
+	unsigned long flag;
+
+	spin_lock_irqsave(&ar_sdio->lock, flag);
+
+	if (list_empty(&ar_sdio->bus_req_freeq)) {
+		spin_unlock_irqrestore(&ar_sdio->lock, flag);
+		return NULL;
+	}
+
+	bus_req = list_first_entry(&ar_sdio->bus_req_freeq,
+				   struct bus_request, list);
+	list_del(&bus_req->list);
+
+	spin_unlock_irqrestore(&ar_sdio->lock, flag);
+	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req);
+
+	return bus_req;
+}
+
+static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio,
+				     struct bus_request *bus_req)
+{
+	unsigned long flag;
+
+	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req);
+
+	spin_lock_irqsave(&ar_sdio->lock, flag);
+	list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
+	spin_unlock_irqrestore(&ar_sdio->lock, flag);
+}
+
+static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req,
+					struct mmc_data *data)
+{
+	struct scatterlist *sg;
+	int i;
+
+	data->blksz = HIF_MBOX_BLOCK_SIZE;
+	data->blocks = scat_req->len / HIF_MBOX_BLOCK_SIZE;
+
+	ath6kl_dbg(ATH6KL_DBG_SCATTER,
+		   "hif-scatter: (%s) addr: 0x%X, (block len: %d, block count: %d) , (tot:%d,sg:%d)\n",
+		   (scat_req->req & HIF_WRITE) ? "WR" : "RD", scat_req->addr,
+		   data->blksz, data->blocks, scat_req->len,
+		   scat_req->scat_entries);
+
+	data->flags = (scat_req->req & HIF_WRITE) ? MMC_DATA_WRITE :
+						    MMC_DATA_READ;
+
+	/* fill SG entries */
+	sg = scat_req->sgentries;
+	sg_init_table(sg, scat_req->scat_entries);
+
+	/* assemble SG list */
+	for (i = 0; i < scat_req->scat_entries; i++, sg++) {
+		if ((unsigned long)scat_req->scat_list[i].buf & 0x3)
+			/*
+			 * Some scatter engines can handle unaligned
+			 * buffers, print this as informational only.
+			 */
+			ath6kl_dbg(ATH6KL_DBG_SCATTER,
+				   "(%s) scatter buffer is unaligned 0x%p\n",
+				   scat_req->req & HIF_WRITE ? "WR" : "RD",
+				   scat_req->scat_list[i].buf);
+
+		ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n",
+			   i, scat_req->scat_list[i].buf,
+			   scat_req->scat_list[i].len);
+
+		sg_set_buf(sg, scat_req->scat_list[i].buf,
+			   scat_req->scat_list[i].len);
+	}
+
+	/* set scatter-gather table for request */
+	data->sg = scat_req->sgentries;
+	data->sg_len = scat_req->scat_entries;
+}
+
+static int ath6kl_sdio_scat_rw(struct ath6kl_sdio *ar_sdio,
+			       struct bus_request *req)
+{
+	struct mmc_request mmc_req;
+	struct mmc_command cmd;
+	struct mmc_data data;
+	struct hif_scatter_req *scat_req;
+	u8 opcode, rw;
+	int status, len;
+
+	scat_req = req->scat_req;
+
+	if (scat_req->virt_scat) {
+		len = scat_req->len;
+		if (scat_req->req & HIF_BLOCK_BASIS)
+			len = round_down(len, HIF_MBOX_BLOCK_SIZE);
+
+		status = ath6kl_sdio_io(ar_sdio->func, scat_req->req,
+					scat_req->addr, scat_req->virt_dma_buf,
+					len);
+		goto scat_complete;
+	}
+
+	memset(&mmc_req, 0, sizeof(struct mmc_request));
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	memset(&data, 0, sizeof(struct mmc_data));
+
+	ath6kl_sdio_setup_scat_data(scat_req, &data);
+
+	opcode = (scat_req->req & HIF_FIXED_ADDRESS) ?
+		  CMD53_ARG_FIXED_ADDRESS : CMD53_ARG_INCR_ADDRESS;
+
+	rw = (scat_req->req & HIF_WRITE) ? CMD53_ARG_WRITE : CMD53_ARG_READ;
+
+	/* Fixup the address so that the last byte will fall on MBOX EOM */
+	if (scat_req->req & HIF_WRITE) {
+		if (scat_req->addr == HIF_MBOX_BASE_ADDR)
+			scat_req->addr += HIF_MBOX_WIDTH - scat_req->len;
+		else
+			/* Uses extended address range */
+			scat_req->addr += HIF_MBOX0_EXT_WIDTH - scat_req->len;
+	}
+
+	/* set command argument */
+	ath6kl_sdio_set_cmd53_arg(&cmd.arg, rw, ar_sdio->func->num,
+				  CMD53_ARG_BLOCK_BASIS, opcode, scat_req->addr,
+				  data.blocks);
+
+	cmd.opcode = SD_IO_RW_EXTENDED;
+	cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+
+	mmc_req.cmd = &cmd;
+	mmc_req.data = &data;
+
+	mmc_set_data_timeout(&data, ar_sdio->func->card);
+	/* synchronous call to process request */
+	mmc_wait_for_req(ar_sdio->func->card->host, &mmc_req);
+
+	status = cmd.error ? cmd.error : data.error;
+
+scat_complete:
+	scat_req->status = status;
+
+	if (scat_req->status)
+		ath6kl_err("Scatter write request failed:%d\n",
+			   scat_req->status);
+
+	if (scat_req->req & HIF_ASYNCHRONOUS)
+		scat_req->complete(ar_sdio->ar->htc_target, scat_req);
+
+	return status;
+}
+
+static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
+					   int n_scat_entry, int n_scat_req,
+					   bool virt_scat)
+{
+	struct hif_scatter_req *s_req;
+	struct bus_request *bus_req;
+	int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz;
+	u8 *virt_buf;
+
+	scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item);
+	scat_req_sz = sizeof(*s_req) + scat_list_sz;
+
+	if (!virt_scat)
+		sg_sz = sizeof(struct scatterlist) * n_scat_entry;
+	else
+		buf_sz =  2 * L1_CACHE_BYTES +
+			  ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
+
+	for (i = 0; i < n_scat_req; i++) {
+		/* allocate the scatter request */
+		s_req = kzalloc(scat_req_sz, GFP_KERNEL);
+		if (!s_req)
+			return -ENOMEM;
+
+		if (virt_scat) {
+			virt_buf = kzalloc(buf_sz, GFP_KERNEL);
+			if (!virt_buf) {
+				kfree(s_req);
+				return -ENOMEM;
+			}
+
+			s_req->virt_dma_buf =
+				(u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf);
+		} else {
+			/* allocate sglist */
+			s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL);
+
+			if (!s_req->sgentries) {
+				kfree(s_req);
+				return -ENOMEM;
+			}
+		}
+
+		/* allocate a bus request for this scatter request */
+		bus_req = ath6kl_sdio_alloc_busreq(ar_sdio);
+		if (!bus_req) {
+			kfree(s_req->sgentries);
+			kfree(s_req->virt_dma_buf);
+			kfree(s_req);
+			return -ENOMEM;
+		}
+
+		/* assign the scatter request to this bus request */
+		bus_req->scat_req = s_req;
+		s_req->busrequest = bus_req;
+
+		s_req->virt_scat = virt_scat;
+
+		/* add it to the scatter pool */
+		hif_scatter_req_add(ar_sdio->ar, s_req);
+	}
+
+	return 0;
+}
+
+static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
+				       u32 len, u32 request)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	u8  *tbuf = NULL;
+	int ret;
+	bool bounced = false;
+
+	if (request & HIF_BLOCK_BASIS)
+		len = round_down(len, HIF_MBOX_BLOCK_SIZE);
+
+	if (buf_needs_bounce(buf)) {
+		if (!ar_sdio->dma_buffer)
+			return -ENOMEM;
+		tbuf = ar_sdio->dma_buffer;
+		memcpy(tbuf, buf, len);
+		bounced = true;
+	} else
+		tbuf = buf;
+
+	sdio_claim_host(ar_sdio->func);
+	ret = ath6kl_sdio_io(ar_sdio->func, request, addr, tbuf, len);
+	if ((request & HIF_READ) && bounced)
+		memcpy(buf, tbuf, len);
+	sdio_release_host(ar_sdio->func);
+
+	return ret;
+}
+
+static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio,
+				      struct bus_request *req)
+{
+	if (req->scat_req)
+		ath6kl_sdio_scat_rw(ar_sdio, req);
+	else {
+		void *context;
+		int status;
+
+		status = ath6kl_sdio_read_write_sync(ar_sdio->ar, req->address,
+						     req->buffer, req->length,
+						     req->request);
+		context = req->packet;
+		ath6kl_sdio_free_bus_req(ar_sdio, req);
+		ath6kldev_rw_comp_handler(context, status);
+	}
+}
+
+static void ath6kl_sdio_write_async_work(struct work_struct *work)
+{
+	struct ath6kl_sdio *ar_sdio;
+	unsigned long flags;
+	struct bus_request *req, *tmp_req;
+
+	ar_sdio = container_of(work, struct ath6kl_sdio, wr_async_work);
+	sdio_claim_host(ar_sdio->func);
+
+	spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+	list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
+		list_del(&req->list);
+		spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+		__ath6kl_sdio_write_async(ar_sdio, req);
+		spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+	}
+	spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+
+	sdio_release_host(ar_sdio->func);
+}
+
+static void ath6kl_sdio_irq_handler(struct sdio_func *func)
+{
+	int status;
+	struct ath6kl_sdio *ar_sdio;
+
+	ar_sdio = sdio_get_drvdata(func);
+	atomic_set(&ar_sdio->irq_handling, 1);
+
+	/*
+	 * Release the host during interrups so we can pick it back up when
+	 * we process commands.
+	 */
+	sdio_release_host(ar_sdio->func);
+
+	status = ath6kldev_intr_bh_handler(ar_sdio->ar);
+	sdio_claim_host(ar_sdio->func);
+	atomic_set(&ar_sdio->irq_handling, 0);
+	WARN_ON(status && status != -ECANCELED);
+}
+
+static int ath6kl_sdio_power_on(struct ath6kl_sdio *ar_sdio)
+{
+	struct sdio_func *func = ar_sdio->func;
+	int ret = 0;
+
+	if (!ar_sdio->is_disabled)
+		return 0;
+
+	sdio_claim_host(func);
+
+	ret = sdio_enable_func(func);
+	if (ret) {
+		ath6kl_err("Unable to enable sdio func: %d)\n", ret);
+		sdio_release_host(func);
+		return ret;
+	}
+
+	sdio_release_host(func);
+
+	/*
+	 * Wait for hardware to initialise. It should take a lot less than
+	 * 10 ms but let's be conservative here.
+	 */
+	msleep(10);
+
+	ar_sdio->is_disabled = false;
+
+	return ret;
+}
+
+static int ath6kl_sdio_power_off(struct ath6kl_sdio *ar_sdio)
+{
+	int ret;
+
+	if (ar_sdio->is_disabled)
+		return 0;
+
+	/* Disable the card */
+	sdio_claim_host(ar_sdio->func);
+	ret = sdio_disable_func(ar_sdio->func);
+	sdio_release_host(ar_sdio->func);
+
+	if (ret)
+		return ret;
+
+	ar_sdio->is_disabled = true;
+
+	return ret;
+}
+
+static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
+				   u32 length, u32 request,
+				   struct htc_packet *packet)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	struct bus_request *bus_req;
+	unsigned long flags;
+
+	bus_req = ath6kl_sdio_alloc_busreq(ar_sdio);
+
+	if (!bus_req)
+		return -ENOMEM;
+
+	bus_req->address = address;
+	bus_req->buffer = buffer;
+	bus_req->length = length;
+	bus_req->request = request;
+	bus_req->packet = packet;
+
+	spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+	list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq);
+	spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+	queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work);
+
+	return 0;
+}
+
+static void ath6kl_sdio_irq_enable(struct ath6kl *ar)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	int ret;
+
+	sdio_claim_host(ar_sdio->func);
+
+	/* Register the isr */
+	ret =  sdio_claim_irq(ar_sdio->func, ath6kl_sdio_irq_handler);
+	if (ret)
+		ath6kl_err("Failed to claim sdio irq: %d\n", ret);
+
+	sdio_release_host(ar_sdio->func);
+}
+
+static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	int ret;
+
+	sdio_claim_host(ar_sdio->func);
+
+	/* Mask our function IRQ */
+	while (atomic_read(&ar_sdio->irq_handling)) {
+		sdio_release_host(ar_sdio->func);
+		schedule_timeout(HZ / 10);
+		sdio_claim_host(ar_sdio->func);
+	}
+
+	ret = sdio_release_irq(ar_sdio->func);
+	if (ret)
+		ath6kl_err("Failed to release sdio irq: %d\n", ret);
+
+	sdio_release_host(ar_sdio->func);
+}
+
+static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	struct hif_scatter_req *node = NULL;
+	unsigned long flag;
+
+	spin_lock_irqsave(&ar_sdio->scat_lock, flag);
+
+	if (!list_empty(&ar_sdio->scat_req)) {
+		node = list_first_entry(&ar_sdio->scat_req,
+					struct hif_scatter_req, list);
+		list_del(&node->list);
+	}
+
+	spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+
+	return node;
+}
+
+static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar,
+					struct hif_scatter_req *s_req)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	unsigned long flag;
+
+	spin_lock_irqsave(&ar_sdio->scat_lock, flag);
+
+	list_add_tail(&s_req->list, &ar_sdio->scat_req);
+
+	spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+
+}
+
+/* scatter gather read write request */
+static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar,
+					struct hif_scatter_req *scat_req)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	u32 request = scat_req->req;
+	int status = 0;
+	unsigned long flags;
+
+	if (!scat_req->len)
+		return -EINVAL;
+
+	ath6kl_dbg(ATH6KL_DBG_SCATTER,
+		"hif-scatter: total len: %d scatter entries: %d\n",
+		scat_req->len, scat_req->scat_entries);
+
+	if (request & HIF_SYNCHRONOUS) {
+		sdio_claim_host(ar_sdio->func);
+		status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest);
+		sdio_release_host(ar_sdio->func);
+	} else {
+		spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+		list_add_tail(&scat_req->busrequest->list, &ar_sdio->wr_asyncq);
+		spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+		queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work);
+	}
+
+	return status;
+}
+
+/* clean up scatter support */
+static void ath6kl_sdio_cleanup_scatter(struct ath6kl *ar)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	struct hif_scatter_req *s_req, *tmp_req;
+	unsigned long flag;
+
+	/* empty the free list */
+	spin_lock_irqsave(&ar_sdio->scat_lock, flag);
+	list_for_each_entry_safe(s_req, tmp_req, &ar_sdio->scat_req, list) {
+		list_del(&s_req->list);
+		spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+
+		if (s_req->busrequest)
+			ath6kl_sdio_free_bus_req(ar_sdio, s_req->busrequest);
+		kfree(s_req->virt_dma_buf);
+		kfree(s_req->sgentries);
+		kfree(s_req);
+
+		spin_lock_irqsave(&ar_sdio->scat_lock, flag);
+	}
+	spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+}
+
+/* setup of HIF scatter resources */
+static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
+{
+	struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+	struct htc_target *target = ar->htc_target;
+	int ret;
+	bool virt_scat = false;
+
+	/* check if host supports scatter and it meets our requirements */
+	if (ar_sdio->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) {
+		ath6kl_err("host only supports scatter of :%d entries, need: %d\n",
+			   ar_sdio->func->card->host->max_segs,
+			   MAX_SCATTER_ENTRIES_PER_REQ);
+		virt_scat = true;
+	}
+
+	if (!virt_scat) {
+		ret = ath6kl_sdio_alloc_prep_scat_req(ar_sdio,
+				MAX_SCATTER_ENTRIES_PER_REQ,
+				MAX_SCATTER_REQUESTS, virt_scat);
+
+		if (!ret) {
+			ath6kl_dbg(ATH6KL_DBG_ANY,
+				   "hif-scatter enabled: max scatter req : %d entries: %d\n",
+				   MAX_SCATTER_REQUESTS,
+				   MAX_SCATTER_ENTRIES_PER_REQ);
+
+			target->max_scat_entries = MAX_SCATTER_ENTRIES_PER_REQ;
+			target->max_xfer_szper_scatreq =
+						MAX_SCATTER_REQ_TRANSFER_SIZE;
+		} else {
+			ath6kl_sdio_cleanup_scatter(ar);
+			ath6kl_warn("hif scatter resource setup failed, trying virtual scatter method\n");
+		}
+	}
+
+	if (virt_scat || ret) {
+		ret = ath6kl_sdio_alloc_prep_scat_req(ar_sdio,
+				ATH6KL_SCATTER_ENTRIES_PER_REQ,
+				ATH6KL_SCATTER_REQS, virt_scat);
+
+		if (ret) {
+			ath6kl_err("failed to alloc virtual scatter resources !\n");
+			ath6kl_sdio_cleanup_scatter(ar);
+			return ret;
+		}
+
+		ath6kl_dbg(ATH6KL_DBG_ANY,
+			   "Vitual scatter enabled, max_scat_req:%d, entries:%d\n",
+			   ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ);
+
+		target->max_scat_entries = ATH6KL_SCATTER_ENTRIES_PER_REQ;
+		target->max_xfer_szper_scatreq =
+					ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
+	}
+
+	return 0;
+}
+
+static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
+	.read_write_sync = ath6kl_sdio_read_write_sync,
+	.write_async = ath6kl_sdio_write_async,
+	.irq_enable = ath6kl_sdio_irq_enable,
+	.irq_disable = ath6kl_sdio_irq_disable,
+	.scatter_req_get = ath6kl_sdio_scatter_req_get,
+	.scatter_req_add = ath6kl_sdio_scatter_req_add,
+	.enable_scatter = ath6kl_sdio_enable_scatter,
+	.scat_req_rw = ath6kl_sdio_async_rw_scatter,
+	.cleanup_scatter = ath6kl_sdio_cleanup_scatter,
+};
+
+static int ath6kl_sdio_probe(struct sdio_func *func,
+			     const struct sdio_device_id *id)
+{
+	int ret;
+	struct ath6kl_sdio *ar_sdio;
+	struct ath6kl *ar;
+	int count;
+
+	ath6kl_dbg(ATH6KL_DBG_TRC,
+		   "%s: func: 0x%X, vendor id: 0x%X, dev id: 0x%X, block size: 0x%X/0x%X\n",
+		   __func__, func->num, func->vendor,
+		   func->device, func->max_blksize, func->cur_blksize);
+
+	ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL);
+	if (!ar_sdio)
+		return -ENOMEM;
+
+	ar_sdio->dma_buffer = kzalloc(HIF_DMA_BUFFER_SIZE, GFP_KERNEL);
+	if (!ar_sdio->dma_buffer) {
+		ret = -ENOMEM;
+		goto err_hif;
+	}
+
+	ar_sdio->func = func;
+	sdio_set_drvdata(func, ar_sdio);
+
+	ar_sdio->id = id;
+	ar_sdio->is_disabled = true;
+
+	spin_lock_init(&ar_sdio->lock);
+	spin_lock_init(&ar_sdio->scat_lock);
+	spin_lock_init(&ar_sdio->wr_async_lock);
+
+	INIT_LIST_HEAD(&ar_sdio->scat_req);
+	INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
+	INIT_LIST_HEAD(&ar_sdio->wr_asyncq);
+
+	INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work);
+
+	for (count = 0; count < BUS_REQUEST_MAX_NUM; count++)
+		ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]);
+
+	ar = ath6kl_core_alloc(&ar_sdio->func->dev);
+	if (!ar) {
+		ath6kl_err("Failed to alloc ath6kl core\n");
+		ret = -ENOMEM;
+		goto err_dma;
+	}
+
+	ar_sdio->ar = ar;
+	ar->hif_priv = ar_sdio;
+	ar->hif_ops = &ath6kl_sdio_ops;
+
+	ath6kl_sdio_set_mbox_info(ar);
+
+	sdio_claim_host(func);
+
+	if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >=
+	    MANUFACTURER_ID_AR6003_BASE) {
+		/* enable 4-bit ASYNC interrupt on AR6003 or later */
+		ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card,
+						CCCR_SDIO_IRQ_MODE_REG,
+						SDIO_IRQ_MODE_ASYNC_4BIT_IRQ);
+		if (ret) {
+			ath6kl_err("Failed to enable 4-bit async irq mode %d\n",
+				   ret);
+			sdio_release_host(func);
+			goto err_dma;
+		}
+
+		ath6kl_dbg(ATH6KL_DBG_TRC, "4-bit async irq mode enabled\n");
+	}
+
+	/* give us some time to enable, in ms */
+	func->enable_timeout = 100;
+
+	sdio_release_host(func);
+
+	ret = ath6kl_sdio_power_on(ar_sdio);
+	if (ret)
+		goto err_dma;
+
+	sdio_claim_host(func);
+
+	ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE);
+	if (ret) {
+		ath6kl_err("Set sdio block size %d failed: %d)\n",
+			   HIF_MBOX_BLOCK_SIZE, ret);
+		sdio_release_host(func);
+		goto err_off;
+	}
+
+	sdio_release_host(func);
+
+	ret = ath6kl_core_init(ar);
+	if (ret) {
+		ath6kl_err("Failed to init ath6kl core\n");
+		goto err_off;
+	}
+
+	return ret;
+
+err_off:
+	ath6kl_sdio_power_off(ar_sdio);
+err_dma:
+	kfree(ar_sdio->dma_buffer);
+err_hif:
+	kfree(ar_sdio);
+
+	return ret;
+}
+
+static void ath6kl_sdio_remove(struct sdio_func *func)
+{
+	struct ath6kl_sdio *ar_sdio;
+
+	ar_sdio = sdio_get_drvdata(func);
+
+	ath6kl_stop_txrx(ar_sdio->ar);
+	cancel_work_sync(&ar_sdio->wr_async_work);
+
+	ath6kl_unavail_ev(ar_sdio->ar);
+
+	ath6kl_sdio_power_off(ar_sdio);
+
+	kfree(ar_sdio->dma_buffer);
+	kfree(ar_sdio);
+}
+
+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))},
+	{},
+};
+
+MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices);
+
+static struct sdio_driver ath6kl_sdio_driver = {
+	.name = "ath6kl_sdio",
+	.id_table = ath6kl_sdio_devices,
+	.probe = ath6kl_sdio_probe,
+	.remove = ath6kl_sdio_remove,
+};
+
+static int __init ath6kl_sdio_init(void)
+{
+	int ret;
+
+	ret = sdio_register_driver(&ath6kl_sdio_driver);
+	if (ret)
+		ath6kl_err("sdio driver registration failed: %d\n", ret);
+
+	return ret;
+}
+
+static void __exit ath6kl_sdio_exit(void)
+{
+	sdio_unregister_driver(&ath6kl_sdio_driver);
+}
+
+module_init(ath6kl_sdio_init);
+module_exit(ath6kl_sdio_exit);
+
+MODULE_AUTHOR("Atheros Communications, Inc.");
+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);

+ 331 - 0
drivers/net/wireless/ath/ath6kl/target.h

@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2004-2010 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.
+ */
+
+#ifndef TARGET_H
+#define TARGET_H
+
+#define AR6003_BOARD_DATA_SZ		1024
+#define AR6003_BOARD_EXT_DATA_SZ	768
+
+#define RESET_CONTROL_ADDRESS		0x00000000
+#define RESET_CONTROL_COLD_RST		0x00000100
+#define RESET_CONTROL_MBOX_RST		0x00000004
+
+#define CPU_CLOCK_STANDARD_S		0
+#define CPU_CLOCK_STANDARD		0x00000003
+#define CPU_CLOCK_ADDRESS		0x00000020
+
+#define CLOCK_CONTROL_ADDRESS		0x00000028
+#define CLOCK_CONTROL_LF_CLK32_S	2
+#define CLOCK_CONTROL_LF_CLK32		0x00000004
+
+#define SYSTEM_SLEEP_ADDRESS		0x000000c4
+#define SYSTEM_SLEEP_DISABLE_S		0
+#define SYSTEM_SLEEP_DISABLE		0x00000001
+
+#define LPO_CAL_ADDRESS			0x000000e0
+#define LPO_CAL_ENABLE_S		20
+#define LPO_CAL_ENABLE			0x00100000
+
+#define GPIO_PIN10_ADDRESS		0x00000050
+#define GPIO_PIN11_ADDRESS		0x00000054
+#define GPIO_PIN12_ADDRESS		0x00000058
+#define GPIO_PIN13_ADDRESS		0x0000005c
+
+#define HOST_INT_STATUS_ADDRESS		0x00000400
+#define HOST_INT_STATUS_ERROR_S		7
+#define HOST_INT_STATUS_ERROR		0x00000080
+
+#define HOST_INT_STATUS_CPU_S		6
+#define HOST_INT_STATUS_CPU		0x00000040
+
+#define HOST_INT_STATUS_COUNTER_S	4
+#define HOST_INT_STATUS_COUNTER		0x00000010
+
+#define CPU_INT_STATUS_ADDRESS		0x00000401
+
+#define ERROR_INT_STATUS_ADDRESS	0x00000402
+#define ERROR_INT_STATUS_WAKEUP_S	2
+#define ERROR_INT_STATUS_WAKEUP		0x00000004
+
+#define ERROR_INT_STATUS_RX_UNDERFLOW_S	1
+#define ERROR_INT_STATUS_RX_UNDERFLOW	0x00000002
+
+#define ERROR_INT_STATUS_TX_OVERFLOW_S	0
+#define ERROR_INT_STATUS_TX_OVERFLOW	0x00000001
+
+#define COUNTER_INT_STATUS_ADDRESS	0x00000403
+#define COUNTER_INT_STATUS_COUNTER_S	0
+#define COUNTER_INT_STATUS_COUNTER	0x000000ff
+
+#define RX_LOOKAHEAD_VALID_ADDRESS	0x00000405
+
+#define INT_STATUS_ENABLE_ADDRESS	0x00000418
+#define INT_STATUS_ENABLE_ERROR_S	7
+#define INT_STATUS_ENABLE_ERROR		0x00000080
+
+#define INT_STATUS_ENABLE_CPU_S		6
+#define INT_STATUS_ENABLE_CPU		0x00000040
+
+#define INT_STATUS_ENABLE_INT_S		5
+#define INT_STATUS_ENABLE_INT		0x00000020
+#define INT_STATUS_ENABLE_COUNTER_S	4
+#define INT_STATUS_ENABLE_COUNTER	0x00000010
+
+#define INT_STATUS_ENABLE_MBOX_DATA_S	0
+#define INT_STATUS_ENABLE_MBOX_DATA	0x0000000f
+
+#define CPU_INT_STATUS_ENABLE_ADDRESS	0x00000419
+#define CPU_INT_STATUS_ENABLE_BIT_S	0
+#define CPU_INT_STATUS_ENABLE_BIT	0x000000ff
+
+#define ERROR_STATUS_ENABLE_ADDRESS		0x0000041a
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_S	1
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW	0x00000002
+
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_S	0
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW		0x00000001
+
+#define COUNTER_INT_STATUS_ENABLE_ADDRESS	0x0000041b
+#define COUNTER_INT_STATUS_ENABLE_BIT_S		0
+#define COUNTER_INT_STATUS_ENABLE_BIT		0x000000ff
+
+#define COUNT_ADDRESS			0x00000420
+
+#define COUNT_DEC_ADDRESS		0x00000440
+
+#define WINDOW_DATA_ADDRESS		0x00000474
+#define WINDOW_WRITE_ADDR_ADDRESS	0x00000478
+#define WINDOW_READ_ADDR_ADDRESS	0x0000047c
+#define CPU_DBG_SEL_ADDRESS		0x00000483
+#define CPU_DBG_ADDRESS			0x00000484
+
+#define LOCAL_SCRATCH_ADDRESS		0x000000c0
+#define ATH6KL_OPTION_SLEEP_DISABLE	0x08
+
+#define RTC_BASE_ADDRESS		0x00004000
+#define GPIO_BASE_ADDRESS		0x00014000
+#define MBOX_BASE_ADDRESS		0x00018000
+#define ANALOG_INTF_BASE_ADDRESS	0x0001c000
+
+/* real name of the register is unknown */
+#define ATH6KL_ANALOG_PLL_REGISTER	(ANALOG_INTF_BASE_ADDRESS + 0x284)
+
+#define SM(f, v)	(((v) << f##_S) & f)
+#define MS(f, v)	(((v) & f) >> f##_S)
+
+/*
+ * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
+ * host_interest structure.
+ *
+ * Host Interest is shared between Host and Target in order to coordinate
+ * between the two, and is intended to remain constant (with additions only
+ * at the end).
+ */
+#define ATH6KL_HI_START_ADDR           0x00540600
+
+/*
+ * These are items that the Host may need to access
+ * via BMI or via the Diagnostic Window. The position
+ * of items in this structure must remain constant.
+ * across firmware revisions!
+ *
+ * Types for each item must be fixed size across target and host platforms.
+ * The structure is used only to calculate offset for each register with
+ * HI_ITEM() macro, no values are stored to it.
+ *
+ * More items may be added at the end.
+ */
+struct host_interest {
+	/*
+	 * Pointer to application-defined area, if any.
+	 * Set by Target application during startup.
+	 */
+	u32 hi_app_host_interest;                      /* 0x00 */
+
+	/* Pointer to register dump area, valid after Target crash. */
+	u32 hi_failure_state;                          /* 0x04 */
+
+	/* Pointer to debug logging header */
+	u32 hi_dbglog_hdr;                             /* 0x08 */
+
+	u32 hi_unused1;                       /* 0x0c */
+
+	/*
+	 * General-purpose flag bits, similar to ATH6KL_OPTION_* flags.
+	 * Can be used by application rather than by OS.
+	 */
+	u32 hi_option_flag;                            /* 0x10 */
+
+	/*
+	 * Boolean that determines whether or not to
+	 * display messages on the serial port.
+	 */
+	u32 hi_serial_enable;                          /* 0x14 */
+
+	/* Start address of DataSet index, if any */
+	u32 hi_dset_list_head;                         /* 0x18 */
+
+	/* Override Target application start address */
+	u32 hi_app_start;                              /* 0x1c */
+
+	/* Clock and voltage tuning */
+	u32 hi_skip_clock_init;                        /* 0x20 */
+	u32 hi_core_clock_setting;                     /* 0x24 */
+	u32 hi_cpu_clock_setting;                      /* 0x28 */
+	u32 hi_system_sleep_setting;                   /* 0x2c */
+	u32 hi_xtal_control_setting;                   /* 0x30 */
+	u32 hi_pll_ctrl_setting_24ghz;                 /* 0x34 */
+	u32 hi_pll_ctrl_setting_5ghz;                  /* 0x38 */
+	u32 hi_ref_voltage_trim_setting;               /* 0x3c */
+	u32 hi_clock_info;                             /* 0x40 */
+
+	/*
+	 * Flash configuration overrides, used only
+	 * when firmware is not executing from flash.
+	 * (When using flash, modify the global variables
+	 * with equivalent names.)
+	 */
+	u32 hi_bank0_addr_value;                       /* 0x44 */
+	u32 hi_bank0_read_value;                       /* 0x48 */
+	u32 hi_bank0_write_value;                      /* 0x4c */
+	u32 hi_bank0_config_value;                     /* 0x50 */
+
+	/* Pointer to Board Data  */
+	u32 hi_board_data;                             /* 0x54 */
+	u32 hi_board_data_initialized;                 /* 0x58 */
+
+	u32 hi_dset_ram_index_tbl;                     /* 0x5c */
+
+	u32 hi_desired_baud_rate;                      /* 0x60 */
+	u32 hi_dbglog_config;                          /* 0x64 */
+	u32 hi_end_ram_reserve_sz;                     /* 0x68 */
+	u32 hi_mbox_io_block_sz;                       /* 0x6c */
+
+	u32 hi_num_bpatch_streams;                     /* 0x70 -- unused */
+	u32 hi_mbox_isr_yield_limit;                   /* 0x74 */
+
+	u32 hi_refclk_hz;                              /* 0x78 */
+	u32 hi_ext_clk_detected;                       /* 0x7c */
+	u32 hi_dbg_uart_txpin;                         /* 0x80 */
+	u32 hi_dbg_uart_rxpin;                         /* 0x84 */
+	u32 hi_hci_uart_baud;                          /* 0x88 */
+	u32 hi_hci_uart_pin_assignments;               /* 0x8C */
+	/*
+	 * NOTE: byte [0] = tx pin, [1] = rx pin, [2] = rts pin, [3] = cts
+	 * pin
+	 */
+	u32 hi_hci_uart_baud_scale_val;                /* 0x90 */
+	u32 hi_hci_uart_baud_step_val;                 /* 0x94 */
+
+	u32 hi_allocram_start;                         /* 0x98 */
+	u32 hi_allocram_sz;                            /* 0x9c */
+	u32 hi_hci_bridge_flags;                       /* 0xa0 */
+	u32 hi_hci_uart_support_pins;                  /* 0xa4 */
+	/*
+	 * NOTE: byte [0] = RESET pin (bit 7 is polarity),
+	 * bytes[1]..bytes[3] are for future use
+	 */
+	u32 hi_hci_uart_pwr_mgmt_params;               /* 0xa8 */
+	/*
+	 * 0xa8   - [1]: 0 = UART FC active low, 1 = UART FC active high
+	 *      [31:16]: wakeup timeout in ms
+	 */
+
+	/* Pointer to extended board data */
+	u32 hi_board_ext_data;                /* 0xac */
+	u32 hi_board_ext_data_config;         /* 0xb0 */
+
+	/*
+	 * Bit [0]  :   valid
+	 * Bit[31:16:   size
+	 */
+	/*
+	 * hi_reset_flag is used to do some stuff when target reset.
+	 * such as restore app_start after warm reset or
+	 * preserve host Interest area, or preserve ROM data, literals etc.
+	 */
+	u32 hi_reset_flag;                            /* 0xb4 */
+	/* indicate hi_reset_flag is valid */
+	u32 hi_reset_flag_valid;                      /* 0xb8 */
+	u32 hi_hci_uart_pwr_mgmt_params_ext;           /* 0xbc */
+	/*
+	 * 0xbc - [31:0]: idle timeout in ms
+	 */
+	/* ACS flags */
+	u32 hi_acs_flags;                              /* 0xc0 */
+	u32 hi_console_flags;                          /* 0xc4 */
+	u32 hi_nvram_state;                            /* 0xc8 */
+	u32 hi_option_flag2;                           /* 0xcc */
+
+	/* If non-zero, override values sent to Host in WMI_READY event. */
+	u32 hi_sw_version_override;                    /* 0xd0 */
+	u32 hi_abi_version_override;                   /* 0xd4 */
+
+	/*
+	 * Percentage of high priority RX traffic to total expected RX traffic -
+	 * applicable only to ar6004
+	 */
+	u32 hi_hp_rx_traffic_ratio;                    /* 0xd8 */
+
+	/* test applications flags */
+	u32 hi_test_apps_related    ;                  /* 0xdc */
+	/* location of test script */
+	u32 hi_ota_testscript;                         /* 0xe0 */
+	/* location of CAL data */
+	u32 hi_cal_data;                               /* 0xe4 */
+	/* Number of packet log buffers */
+	u32 hi_pktlog_num_buffers;                     /* 0xe8 */
+
+} __packed;
+
+#define HI_ITEM(item)  offsetof(struct host_interest, item)
+
+#define HI_OPTION_MAC_ADDR_METHOD_SHIFT	3
+
+#define HI_OPTION_FW_MODE_IBSS    0x0
+#define HI_OPTION_FW_MODE_BSS_STA 0x1
+#define HI_OPTION_FW_MODE_AP      0x2
+
+#define HI_OPTION_NUM_DEV_SHIFT   0x9
+
+#define HI_OPTION_FW_BRIDGE_SHIFT 0x04
+
+/* Fw Mode/SubMode Mask
+|------------------------------------------------------------------------------|
+|   SUB   |   SUB   |   SUB   |  SUB    |         |         |         |
+| MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0|
+|   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)
+|------------------------------------------------------------------------------|
+*/
+#define HI_OPTION_FW_MODE_SHIFT        0xC
+
+/* Convert a Target virtual address into a Target physical address */
+#define TARG_VTOP(vaddr)   (vaddr & 0x001fffff)
+
+#define AR6003_REV2_APP_START_OVERRIDE          0x944C00
+#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_START_OVERRIDE          0x945d00
+#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
+
+#endif

+ 1457 - 0
drivers/net/wireless/ath/ath6kl/txrx.c

@@ -0,0 +1,1457 @@
+/*
+ * Copyright (c) 2004-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 "core.h"
+#include "debug.h"
+
+static u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev,
+			       u32 *map_no)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	struct ethhdr *eth_hdr;
+	u32 i, ep_map = -1;
+	u8 *datap;
+
+	*map_no = 0;
+	datap = skb->data;
+	eth_hdr = (struct ethhdr *) (datap + sizeof(struct wmi_data_hdr));
+
+	if (is_multicast_ether_addr(eth_hdr->h_dest))
+		return ENDPOINT_2;
+
+	for (i = 0; i < ar->node_num; i++) {
+		if (memcmp(eth_hdr->h_dest, ar->node_map[i].mac_addr,
+			   ETH_ALEN) == 0) {
+			*map_no = i + 1;
+			ar->node_map[i].tx_pend++;
+			return ar->node_map[i].ep_id;
+		}
+
+		if ((ep_map == -1) && !ar->node_map[i].tx_pend)
+			ep_map = i;
+	}
+
+	if (ep_map == -1) {
+		ep_map = ar->node_num;
+		ar->node_num++;
+		if (ar->node_num > MAX_NODE_NUM)
+			return ENDPOINT_UNUSED;
+	}
+
+	memcpy(ar->node_map[ep_map].mac_addr, eth_hdr->h_dest, ETH_ALEN);
+
+	for (i = ENDPOINT_2; i <= ENDPOINT_5; i++) {
+		if (!ar->tx_pending[i]) {
+			ar->node_map[ep_map].ep_id = i;
+			break;
+		}
+
+		/*
+		 * No free endpoint is available, start redistribution on
+		 * the inuse endpoints.
+		 */
+		if (i == ENDPOINT_5) {
+			ar->node_map[ep_map].ep_id = ar->next_ep_id;
+			ar->next_ep_id++;
+			if (ar->next_ep_id > ENDPOINT_5)
+				ar->next_ep_id = ENDPOINT_2;
+		}
+	}
+
+	*map_no = ep_map + 1;
+	ar->node_map[ep_map].tx_pend++;
+
+	return ar->node_map[ep_map].ep_id;
+}
+
+static bool ath6kl_powersave_ap(struct ath6kl *ar, struct sk_buff *skb,
+				bool *more_data)
+{
+	struct ethhdr *datap = (struct ethhdr *) skb->data;
+	struct ath6kl_sta *conn = NULL;
+	bool ps_queued = false, is_psq_empty = false;
+
+	if (is_multicast_ether_addr(datap->h_dest)) {
+		u8 ctr = 0;
+		bool q_mcast = false;
+
+		for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
+			if (ar->sta_list[ctr].sta_flags & STA_PS_SLEEP) {
+				q_mcast = true;
+				break;
+			}
+		}
+
+		if (q_mcast) {
+			/*
+			 * If this transmit is not because of a Dtim Expiry
+			 * q it.
+			 */
+			if (!test_bit(DTIM_EXPIRED, &ar->flag)) {
+				bool is_mcastq_empty = false;
+
+				spin_lock_bh(&ar->mcastpsq_lock);
+				is_mcastq_empty =
+					skb_queue_empty(&ar->mcastpsq);
+				skb_queue_tail(&ar->mcastpsq, skb);
+				spin_unlock_bh(&ar->mcastpsq_lock);
+
+				/*
+				 * If this is the first Mcast pkt getting
+				 * queued indicate to the target to set the
+				 * BitmapControl LSB of the TIM IE.
+				 */
+				if (is_mcastq_empty)
+					ath6kl_wmi_set_pvb_cmd(ar->wmi,
+							       MCAST_AID, 1);
+
+				ps_queued = true;
+			} else {
+				/*
+				 * This transmit is because of Dtim expiry.
+				 * Determine if MoreData bit has to be set.
+				 */
+				spin_lock_bh(&ar->mcastpsq_lock);
+				if (!skb_queue_empty(&ar->mcastpsq))
+					*more_data = true;
+				spin_unlock_bh(&ar->mcastpsq_lock);
+			}
+		}
+	} else {
+		conn = ath6kl_find_sta(ar, datap->h_dest);
+		if (!conn) {
+			dev_kfree_skb(skb);
+
+			/* Inform the caller that the skb is consumed */
+			return true;
+		}
+
+		if (conn->sta_flags & STA_PS_SLEEP) {
+			if (!(conn->sta_flags & STA_PS_POLLED)) {
+				/* Queue the frames if the STA is sleeping */
+				spin_lock_bh(&conn->psq_lock);
+				is_psq_empty = skb_queue_empty(&conn->psq);
+				skb_queue_tail(&conn->psq, skb);
+				spin_unlock_bh(&conn->psq_lock);
+
+				/*
+				 * If this is the first pkt getting queued
+				 * for this STA, update the PVB for this
+				 * STA.
+				 */
+				if (is_psq_empty)
+					ath6kl_wmi_set_pvb_cmd(ar->wmi,
+							       conn->aid, 1);
+
+				ps_queued = true;
+			} else {
+				/*
+				 * This tx is because of a PsPoll.
+				 * Determine if MoreData bit has to be set.
+				 */
+				spin_lock_bh(&conn->psq_lock);
+				if (!skb_queue_empty(&conn->psq))
+					*more_data = true;
+				spin_unlock_bh(&conn->psq_lock);
+			}
+		}
+	}
+
+	return ps_queued;
+}
+
+/* Tx functions */
+
+int ath6kl_control_tx(void *devt, struct sk_buff *skb,
+		      enum htc_endpoint_id eid)
+{
+	struct ath6kl *ar = devt;
+	int status = 0;
+	struct ath6kl_cookie *cookie = NULL;
+
+	spin_lock_bh(&ar->lock);
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
+		   "%s: skb=0x%p, len=0x%x eid =%d\n", __func__,
+		   skb, skb->len, eid);
+
+	if (test_bit(WMI_CTRL_EP_FULL, &ar->flag) && (eid == ar->ctrl_ep)) {
+		/*
+		 * Control endpoint is full, don't allocate resources, we
+		 * are just going to drop this packet.
+		 */
+		cookie = NULL;
+		ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n",
+			   skb, skb->len);
+	} else
+		cookie = ath6kl_alloc_cookie(ar);
+
+	if (cookie == NULL) {
+		spin_unlock_bh(&ar->lock);
+		status = -ENOMEM;
+		goto fail_ctrl_tx;
+	}
+
+	ar->tx_pending[eid]++;
+
+	if (eid != ar->ctrl_ep)
+		ar->total_tx_data_pend++;
+
+	spin_unlock_bh(&ar->lock);
+
+	cookie->skb = skb;
+	cookie->map_no = 0;
+	set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
+			 eid, ATH6KL_CONTROL_PKT_TAG);
+
+	/*
+	 * This interface is asynchronous, if there is an error, cleanup
+	 * will happen in the TX completion callback.
+	 */
+	ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt);
+
+	return 0;
+
+fail_ctrl_tx:
+	dev_kfree_skb(skb);
+	return status;
+}
+
+int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ath6kl *ar = ath6kl_priv(dev);
+	struct ath6kl_cookie *cookie = NULL;
+	enum htc_endpoint_id eid = ENDPOINT_UNUSED;
+	u32 map_no = 0;
+	u16 htc_tag = ATH6KL_DATA_PKT_TAG;
+	u8 ac = 99 ; /* initialize to unmapped ac */
+	bool chk_adhoc_ps_mapping = false, more_data = false;
+	struct wmi_tx_meta_v2 meta_v2;
+	int ret;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
+		   "%s: skb=0x%p, data=0x%p, len=0x%x\n", __func__,
+		   skb, skb->data, skb->len);
+
+	/* If target is not associated */
+	if (!test_bit(CONNECTED, &ar->flag)) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	if (!test_bit(WMI_READY, &ar->flag))
+		goto fail_tx;
+
+	/* AP mode Power saving processing */
+	if (ar->nw_type == AP_NETWORK) {
+		if (ath6kl_powersave_ap(ar, skb, &more_data))
+			return 0;
+	}
+
+	if (test_bit(WMI_ENABLED, &ar->flag)) {
+		memset(&meta_v2, 0, sizeof(meta_v2));
+
+		if (skb_headroom(skb) < dev->needed_headroom) {
+			WARN_ON(1);
+			goto fail_tx;
+		}
+
+		if (ath6kl_wmi_dix_2_dot3(ar->wmi, skb)) {
+			ath6kl_err("ath6kl_wmi_dix_2_dot3 failed\n");
+			goto fail_tx;
+		}
+
+		if (ath6kl_wmi_data_hdr_add(ar->wmi, skb, DATA_MSGTYPE,
+					    more_data, 0, 0, NULL)) {
+			ath6kl_err("wmi_data_hdr_add failed\n");
+			goto fail_tx;
+		}
+
+		if ((ar->nw_type == ADHOC_NETWORK) &&
+		     ar->ibss_ps_enable && test_bit(CONNECTED, &ar->flag))
+			chk_adhoc_ps_mapping = true;
+		else {
+			/* get the stream mapping */
+			ret = ath6kl_wmi_implicit_create_pstream(ar->wmi, skb,
+				    0, test_bit(WMM_ENABLED, &ar->flag), &ac);
+			if (ret)
+				goto fail_tx;
+		}
+	} else
+		goto fail_tx;
+
+	spin_lock_bh(&ar->lock);
+
+	if (chk_adhoc_ps_mapping)
+		eid = ath6kl_ibss_map_epid(skb, dev, &map_no);
+	else
+		eid = ar->ac2ep_map[ac];
+
+	if (eid == 0 || eid == ENDPOINT_UNUSED) {
+		ath6kl_err("eid %d is not mapped!\n", eid);
+		spin_unlock_bh(&ar->lock);
+		goto fail_tx;
+	}
+
+	/* allocate resource for this packet */
+	cookie = ath6kl_alloc_cookie(ar);
+
+	if (!cookie) {
+		spin_unlock_bh(&ar->lock);
+		goto fail_tx;
+	}
+
+	/* update counts while the lock is held */
+	ar->tx_pending[eid]++;
+	ar->total_tx_data_pend++;
+
+	spin_unlock_bh(&ar->lock);
+
+	cookie->skb = skb;
+	cookie->map_no = map_no;
+	set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
+			 eid, htc_tag);
+
+	ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len);
+
+	/*
+	 * HTC interface is asynchronous, if this fails, cleanup will
+	 * happen in the ath6kl_tx_complete callback.
+	 */
+	ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt);
+
+	return 0;
+
+fail_tx:
+	dev_kfree_skb(skb);
+
+	ar->net_stats.tx_dropped++;
+	ar->net_stats.tx_aborted_errors++;
+
+	return 0;
+}
+
+/* indicate tx activity or inactivity on a WMI stream */
+void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active)
+{
+	struct ath6kl *ar = devt;
+	enum htc_endpoint_id eid;
+	int i;
+
+	eid = ar->ac2ep_map[traffic_class];
+
+	if (!test_bit(WMI_ENABLED, &ar->flag))
+		goto notify_htc;
+
+	spin_lock_bh(&ar->lock);
+
+	ar->ac_stream_active[traffic_class] = active;
+
+	if (active) {
+		/*
+		 * Keep track of the active stream with the highest
+		 * priority.
+		 */
+		if (ar->ac_stream_pri_map[traffic_class] >
+		    ar->hiac_stream_active_pri)
+			/* set the new highest active priority */
+			ar->hiac_stream_active_pri =
+					ar->ac_stream_pri_map[traffic_class];
+
+	} else {
+		/*
+		 * We may have to search for the next active stream
+		 * that is the highest priority.
+		 */
+		if (ar->hiac_stream_active_pri ==
+			ar->ac_stream_pri_map[traffic_class]) {
+			/*
+			 * The highest priority stream just went inactive
+			 * reset and search for the "next" highest "active"
+			 * priority stream.
+			 */
+			ar->hiac_stream_active_pri = 0;
+
+			for (i = 0; i < WMM_NUM_AC; i++) {
+				if (ar->ac_stream_active[i] &&
+				    (ar->ac_stream_pri_map[i] >
+				     ar->hiac_stream_active_pri))
+					/*
+					 * Set the new highest active
+					 * priority.
+					 */
+					ar->hiac_stream_active_pri =
+						ar->ac_stream_pri_map[i];
+			}
+		}
+	}
+
+	spin_unlock_bh(&ar->lock);
+
+notify_htc:
+	/* notify HTC, this may cause credit distribution changes */
+	ath6kl_htc_indicate_activity_change(ar->htc_target, eid, active);
+}
+
+enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
+					       struct htc_packet *packet)
+{
+	struct ath6kl *ar = target->dev->ar;
+	enum htc_endpoint_id endpoint = packet->endpoint;
+
+	if (endpoint == ar->ctrl_ep) {
+		/*
+		 * Under normal WMI if this is getting full, then something
+		 * is running rampant the host should not be exhausting the
+		 * WMI queue with too many commands the only exception to
+		 * this is during testing using endpointping.
+		 */
+		spin_lock_bh(&ar->lock);
+		set_bit(WMI_CTRL_EP_FULL, &ar->flag);
+		spin_unlock_bh(&ar->lock);
+		ath6kl_err("wmi ctrl ep is full\n");
+		return HTC_SEND_FULL_KEEP;
+	}
+
+	if (packet->info.tx.tag == ATH6KL_CONTROL_PKT_TAG)
+		return HTC_SEND_FULL_KEEP;
+
+	if (ar->nw_type == ADHOC_NETWORK)
+		/*
+		 * In adhoc mode, we cannot differentiate traffic
+		 * priorities so there is no need to continue, however we
+		 * should stop the network.
+		 */
+		goto stop_net_queues;
+
+	/*
+	 * The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for
+	 * the highest active stream.
+	 */
+	if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] <
+	    ar->hiac_stream_active_pri &&
+	    ar->cookie_count <= MAX_HI_COOKIE_NUM)
+		/*
+		 * Give preference to the highest priority stream by
+		 * dropping the packets which overflowed.
+		 */
+		return HTC_SEND_FULL_DROP;
+
+stop_net_queues:
+	spin_lock_bh(&ar->lock);
+	set_bit(NETQ_STOPPED, &ar->flag);
+	spin_unlock_bh(&ar->lock);
+	netif_stop_queue(ar->net_dev);
+
+	return HTC_SEND_FULL_KEEP;
+}
+
+/* TODO this needs to be looked at */
+static void ath6kl_tx_clear_node_map(struct ath6kl *ar,
+				     enum htc_endpoint_id eid, u32 map_no)
+{
+	u32 i;
+
+	if (ar->nw_type != ADHOC_NETWORK)
+		return;
+
+	if (!ar->ibss_ps_enable)
+		return;
+
+	if (eid == ar->ctrl_ep)
+		return;
+
+	if (map_no == 0)
+		return;
+
+	map_no--;
+	ar->node_map[map_no].tx_pend--;
+
+	if (ar->node_map[map_no].tx_pend)
+		return;
+
+	if (map_no != (ar->node_num - 1))
+		return;
+
+	for (i = ar->node_num; i > 0; i--) {
+		if (ar->node_map[i - 1].tx_pend)
+			break;
+
+		memset(&ar->node_map[i - 1], 0,
+		       sizeof(struct ath6kl_node_mapping));
+		ar->node_num--;
+	}
+}
+
+void ath6kl_tx_complete(void *context, struct list_head *packet_queue)
+{
+	struct ath6kl *ar = context;
+	struct sk_buff_head skb_queue;
+	struct htc_packet *packet;
+	struct sk_buff *skb;
+	struct ath6kl_cookie *ath6kl_cookie;
+	u32 map_no = 0;
+	int status;
+	enum htc_endpoint_id eid;
+	bool wake_event = false;
+	bool flushing = false;
+
+	skb_queue_head_init(&skb_queue);
+
+	/* lock the driver as we update internal state */
+	spin_lock_bh(&ar->lock);
+
+	/* reap completed packets */
+	while (!list_empty(packet_queue)) {
+
+		packet = list_first_entry(packet_queue, struct htc_packet,
+					  list);
+		list_del(&packet->list);
+
+		ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt;
+		if (!ath6kl_cookie)
+			goto fatal;
+
+		status = packet->status;
+		skb = ath6kl_cookie->skb;
+		eid = packet->endpoint;
+		map_no = ath6kl_cookie->map_no;
+
+		if (!skb || !skb->data)
+			goto fatal;
+
+		packet->buf = skb->data;
+
+		__skb_queue_tail(&skb_queue, skb);
+
+		if (!status && (packet->act_len != skb->len))
+			goto fatal;
+
+		ar->tx_pending[eid]--;
+
+		if (eid != ar->ctrl_ep)
+			ar->total_tx_data_pend--;
+
+		if (eid == ar->ctrl_ep) {
+			if (test_bit(WMI_CTRL_EP_FULL, &ar->flag))
+				clear_bit(WMI_CTRL_EP_FULL, &ar->flag);
+
+			if (ar->tx_pending[eid] == 0)
+				wake_event = true;
+		}
+
+		if (status) {
+			if (status == -ECANCELED)
+				/* a packet was flushed  */
+				flushing = true;
+
+			ar->net_stats.tx_errors++;
+
+			if (status != -ENOSPC)
+				ath6kl_err("tx error, status: 0x%x\n", status);
+			ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
+				   "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n",
+				   __func__, skb, packet->buf, packet->act_len,
+				   eid, "error!");
+		} else {
+			ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
+				   "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n",
+				   __func__, skb, packet->buf, packet->act_len,
+				   eid, "OK");
+
+			flushing = false;
+			ar->net_stats.tx_packets++;
+			ar->net_stats.tx_bytes += skb->len;
+		}
+
+		ath6kl_tx_clear_node_map(ar, eid, map_no);
+
+		ath6kl_free_cookie(ar, ath6kl_cookie);
+
+		if (test_bit(NETQ_STOPPED, &ar->flag))
+			clear_bit(NETQ_STOPPED, &ar->flag);
+	}
+
+	spin_unlock_bh(&ar->lock);
+
+	__skb_queue_purge(&skb_queue);
+
+	if (test_bit(CONNECTED, &ar->flag)) {
+		if (!flushing)
+			netif_wake_queue(ar->net_dev);
+	}
+
+	if (wake_event)
+		wake_up(&ar->event_wq);
+
+	return;
+
+fatal:
+	WARN_ON(1);
+	spin_unlock_bh(&ar->lock);
+	return;
+}
+
+void ath6kl_tx_data_cleanup(struct ath6kl *ar)
+{
+	int i;
+
+	/* flush all the data (non-control) streams */
+	for (i = 0; i < WMM_NUM_AC; i++)
+		ath6kl_htc_flush_txep(ar->htc_target, ar->ac2ep_map[i],
+				      ATH6KL_DATA_PKT_TAG);
+}
+
+/* Rx functions */
+
+static void ath6kl_deliver_frames_to_nw_stack(struct net_device *dev,
+					      struct sk_buff *skb)
+{
+	if (!skb)
+		return;
+
+	skb->dev = dev;
+
+	if (!(skb->dev->flags & IFF_UP)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	skb->protocol = eth_type_trans(skb, skb->dev);
+
+	netif_rx_ni(skb);
+}
+
+static void ath6kl_alloc_netbufs(struct sk_buff_head *q, u16 num)
+{
+	struct sk_buff *skb;
+
+	while (num) {
+		skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE);
+		if (!skb) {
+			ath6kl_err("netbuf allocation failed\n");
+			return;
+		}
+		skb_queue_tail(q, skb);
+		num--;
+	}
+}
+
+static struct sk_buff *aggr_get_free_skb(struct aggr_info *p_aggr)
+{
+	struct sk_buff *skb = NULL;
+
+	if (skb_queue_len(&p_aggr->free_q) < (AGGR_NUM_OF_FREE_NETBUFS >> 2))
+		ath6kl_alloc_netbufs(&p_aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS);
+
+	skb = skb_dequeue(&p_aggr->free_q);
+
+	return skb;
+}
+
+void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint)
+{
+	struct ath6kl *ar = target->dev->ar;
+	struct sk_buff *skb;
+	int rx_buf;
+	int n_buf_refill;
+	struct htc_packet *packet;
+	struct list_head queue;
+
+	n_buf_refill = ATH6KL_MAX_RX_BUFFERS -
+			  ath6kl_htc_get_rxbuf_num(ar->htc_target, endpoint);
+
+	if (n_buf_refill <= 0)
+		return;
+
+	INIT_LIST_HEAD(&queue);
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_RX,
+		   "%s: providing htc with %d buffers at eid=%d\n",
+		   __func__, n_buf_refill, endpoint);
+
+	for (rx_buf = 0; rx_buf < n_buf_refill; rx_buf++) {
+		skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE);
+		if (!skb)
+			break;
+
+		packet = (struct htc_packet *) skb->head;
+		set_htc_rxpkt_info(packet, skb, skb->data,
+				ATH6KL_BUFFER_SIZE, endpoint);
+		list_add_tail(&packet->list, &queue);
+	}
+
+	if (!list_empty(&queue))
+		ath6kl_htc_add_rxbuf_multiple(ar->htc_target, &queue);
+}
+
+void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count)
+{
+	struct htc_packet *packet;
+	struct sk_buff *skb;
+
+	while (count) {
+		skb = ath6kl_buf_alloc(ATH6KL_AMSDU_BUFFER_SIZE);
+		if (!skb)
+			return;
+
+		packet = (struct htc_packet *) skb->head;
+		set_htc_rxpkt_info(packet, skb, skb->data,
+				   ATH6KL_AMSDU_BUFFER_SIZE, 0);
+		spin_lock_bh(&ar->lock);
+		list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue);
+		spin_unlock_bh(&ar->lock);
+		count--;
+	}
+}
+
+/*
+ * Callback to allocate a receive buffer for a pending packet. We use a
+ * pre-allocated list of buffers of maximum AMSDU size (4K).
+ */
+struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target,
+					    enum htc_endpoint_id endpoint,
+					    int len)
+{
+	struct ath6kl *ar = target->dev->ar;
+	struct htc_packet *packet = NULL;
+	struct list_head *pkt_pos;
+	int refill_cnt = 0, depth = 0;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: eid=%d, len:%d\n",
+		   __func__, endpoint, len);
+
+	if ((len <= ATH6KL_BUFFER_SIZE) ||
+	    (len > ATH6KL_AMSDU_BUFFER_SIZE))
+		return NULL;
+
+	spin_lock_bh(&ar->lock);
+
+	if (list_empty(&ar->amsdu_rx_buffer_queue)) {
+		spin_unlock_bh(&ar->lock);
+		refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS;
+		goto refill_buf;
+	}
+
+	packet = list_first_entry(&ar->amsdu_rx_buffer_queue,
+				  struct htc_packet, list);
+	list_del(&packet->list);
+	list_for_each(pkt_pos, &ar->amsdu_rx_buffer_queue)
+		depth++;
+
+	refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS - depth;
+	spin_unlock_bh(&ar->lock);
+
+	/* set actual endpoint ID */
+	packet->endpoint = endpoint;
+
+refill_buf:
+	if (refill_cnt >= ATH6KL_AMSDU_REFILL_THRESHOLD)
+		ath6kl_refill_amsdu_rxbufs(ar, refill_cnt);
+
+	return packet;
+}
+
+static void aggr_slice_amsdu(struct aggr_info *p_aggr,
+			     struct rxtid *rxtid, struct sk_buff *skb)
+{
+	struct sk_buff *new_skb;
+	struct ethhdr *hdr;
+	u16 frame_8023_len, payload_8023_len, mac_hdr_len, amsdu_len;
+	u8 *framep;
+
+	mac_hdr_len = sizeof(struct ethhdr);
+	framep = skb->data + mac_hdr_len;
+	amsdu_len = skb->len - mac_hdr_len;
+
+	while (amsdu_len > mac_hdr_len) {
+		hdr = (struct ethhdr *) framep;
+		payload_8023_len = ntohs(hdr->h_proto);
+
+		if (payload_8023_len < MIN_MSDU_SUBFRAME_PAYLOAD_LEN ||
+		    payload_8023_len > MAX_MSDU_SUBFRAME_PAYLOAD_LEN) {
+			ath6kl_err("802.3 AMSDU frame bound check failed. len %d\n",
+				   payload_8023_len);
+			break;
+		}
+
+		frame_8023_len = payload_8023_len + mac_hdr_len;
+		new_skb = aggr_get_free_skb(p_aggr);
+		if (!new_skb) {
+			ath6kl_err("no buffer available\n");
+			break;
+		}
+
+		memcpy(new_skb->data, framep, frame_8023_len);
+		skb_put(new_skb, frame_8023_len);
+		if (ath6kl_wmi_dot3_2_dix(new_skb)) {
+			ath6kl_err("dot3_2_dix error\n");
+			dev_kfree_skb(new_skb);
+			break;
+		}
+
+		skb_queue_tail(&rxtid->q, new_skb);
+
+		/* Is this the last subframe within this aggregate ? */
+		if ((amsdu_len - frame_8023_len) == 0)
+			break;
+
+		/* Add the length of A-MSDU subframe padding bytes -
+		 * Round to nearest word.
+		 */
+		frame_8023_len = ALIGN(frame_8023_len + 3, 3);
+
+		framep += frame_8023_len;
+		amsdu_len -= frame_8023_len;
+	}
+
+	dev_kfree_skb(skb);
+}
+
+static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid,
+			    u16 seq_no, u8 order)
+{
+	struct sk_buff *skb;
+	struct rxtid *rxtid;
+	struct skb_hold_q *node;
+	u16 idx, idx_end, seq_end;
+	struct rxtid_stats *stats;
+
+	if (!p_aggr)
+		return;
+
+	rxtid = &p_aggr->rx_tid[tid];
+	stats = &p_aggr->stat[tid];
+
+	idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
+
+	/*
+	 * idx_end is typically the last possible frame in the window,
+	 * but changes to 'the' seq_no, when BAR comes. If seq_no
+	 * is non-zero, we will go up to that and stop.
+	 * Note: last seq no in current window will occupy the same
+	 * index position as index that is just previous to start.
+	 * An imp point : if win_sz is 7, for seq_no space of 4095,
+	 * then, there would be holes when sequence wrap around occurs.
+	 * Target should judiciously choose the win_sz, based on
+	 * this condition. For 4095, (TID_WINDOW_SZ = 2 x win_sz
+	 * 2, 4, 8, 16 win_sz works fine).
+	 * We must deque from "idx" to "idx_end", including both.
+	 */
+	seq_end = seq_no ? seq_no : rxtid->seq_next;
+	idx_end = AGGR_WIN_IDX(seq_end, rxtid->hold_q_sz);
+
+	spin_lock_bh(&rxtid->lock);
+
+	do {
+		node = &rxtid->hold_q[idx];
+		if ((order == 1) && (!node->skb))
+			break;
+
+		if (node->skb) {
+			if (node->is_amsdu)
+				aggr_slice_amsdu(p_aggr, rxtid, node->skb);
+			else
+				skb_queue_tail(&rxtid->q, node->skb);
+			node->skb = NULL;
+		} else
+			stats->num_hole++;
+
+		rxtid->seq_next = ATH6KL_NEXT_SEQ_NO(rxtid->seq_next);
+		idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
+	} while (idx != idx_end);
+
+	spin_unlock_bh(&rxtid->lock);
+
+	stats->num_delivered += skb_queue_len(&rxtid->q);
+
+	while ((skb = skb_dequeue(&rxtid->q)))
+		ath6kl_deliver_frames_to_nw_stack(p_aggr->dev, skb);
+}
+
+static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid,
+				  u16 seq_no,
+				  bool is_amsdu, struct sk_buff *frame)
+{
+	struct rxtid *rxtid;
+	struct rxtid_stats *stats;
+	struct sk_buff *skb;
+	struct skb_hold_q *node;
+	u16 idx, st, cur, end;
+	bool is_queued = false;
+	u16 extended_end;
+
+	rxtid = &agg_info->rx_tid[tid];
+	stats = &agg_info->stat[tid];
+
+	stats->num_into_aggr++;
+
+	if (!rxtid->aggr) {
+		if (is_amsdu) {
+			aggr_slice_amsdu(agg_info, rxtid, frame);
+			is_queued = true;
+			stats->num_amsdu++;
+			while ((skb = skb_dequeue(&rxtid->q)))
+				ath6kl_deliver_frames_to_nw_stack(agg_info->dev,
+								  skb);
+		}
+		return is_queued;
+	}
+
+	/* Check the incoming sequence no, if it's in the window */
+	st = rxtid->seq_next;
+	cur = seq_no;
+	end = (st + rxtid->hold_q_sz-1) & ATH6KL_MAX_SEQ_NO;
+
+	if (((st < end) && (cur < st || cur > end)) ||
+	    ((st > end) && (cur > end) && (cur < st))) {
+		extended_end = (end + rxtid->hold_q_sz - 1) &
+			ATH6KL_MAX_SEQ_NO;
+
+		if (((end < extended_end) &&
+		     (cur < end || cur > extended_end)) ||
+		    ((end > extended_end) && (cur > extended_end) &&
+		     (cur < end))) {
+			aggr_deque_frms(agg_info, tid, 0, 0);
+			if (cur >= rxtid->hold_q_sz - 1)
+				rxtid->seq_next = cur - (rxtid->hold_q_sz - 1);
+			else
+				rxtid->seq_next = ATH6KL_MAX_SEQ_NO -
+						  (rxtid->hold_q_sz - 2 - cur);
+		} else {
+			/*
+			 * Dequeue only those frames that are outside the
+			 * new shifted window.
+			 */
+			if (cur >= rxtid->hold_q_sz - 1)
+				st = cur - (rxtid->hold_q_sz - 1);
+			else
+				st = ATH6KL_MAX_SEQ_NO -
+					(rxtid->hold_q_sz - 2 - cur);
+
+			aggr_deque_frms(agg_info, tid, st, 0);
+		}
+
+		stats->num_oow++;
+	}
+
+	idx = AGGR_WIN_IDX(seq_no, rxtid->hold_q_sz);
+
+	node = &rxtid->hold_q[idx];
+
+	spin_lock_bh(&rxtid->lock);
+
+	/*
+	 * Is the cur frame duplicate or something beyond our window(hold_q
+	 * -> which is 2x, already)?
+	 *
+	 * 1. Duplicate is easy - drop incoming frame.
+	 * 2. Not falling in current sliding window.
+	 *  2a. is the frame_seq_no preceding current tid_seq_no?
+	 *      -> drop the frame. perhaps sender did not get our ACK.
+	 *         this is taken care of above.
+	 *  2b. is the frame_seq_no beyond window(st, TID_WINDOW_SZ);
+	 *      -> Taken care of it above, by moving window forward.
+	 */
+	dev_kfree_skb(node->skb);
+	stats->num_dups++;
+
+	node->skb = frame;
+	is_queued = true;
+	node->is_amsdu = is_amsdu;
+	node->seq_no = seq_no;
+
+	if (node->is_amsdu)
+		stats->num_amsdu++;
+	else
+		stats->num_mpdu++;
+
+	spin_unlock_bh(&rxtid->lock);
+
+	aggr_deque_frms(agg_info, tid, 0, 1);
+
+	if (agg_info->timer_scheduled)
+		rxtid->progress = true;
+	else
+		for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) {
+			if (rxtid->hold_q[idx].skb) {
+				/*
+				 * There is a frame in the queue and no
+				 * timer so start a timer to ensure that
+				 * the frame doesn't remain stuck
+				 * forever.
+				 */
+				agg_info->timer_scheduled = true;
+				mod_timer(&agg_info->timer,
+					  (jiffies +
+					   HZ * (AGGR_RX_TIMEOUT) / 1000));
+				rxtid->progress = false;
+				rxtid->timer_mon = true;
+				break;
+			}
+		}
+
+	return is_queued;
+}
+
+void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
+{
+	struct ath6kl *ar = target->dev->ar;
+	struct sk_buff *skb = packet->pkt_cntxt;
+	struct wmi_rx_meta_v2 *meta;
+	struct wmi_data_hdr *dhdr;
+	int min_hdr_len;
+	u8 meta_type, dot11_hdr = 0;
+	int status = packet->status;
+	enum htc_endpoint_id ept = packet->endpoint;
+	bool is_amsdu, prev_ps, ps_state = false;
+	struct ath6kl_sta *conn = NULL;
+	struct sk_buff *skb1 = NULL;
+	struct ethhdr *datap = NULL;
+	u16 seq_no, offset;
+	u8 tid;
+
+	ath6kl_dbg(ATH6KL_DBG_WLAN_RX,
+		   "%s: ar=0x%p eid=%d, skb=0x%p, data=0x%p, len=0x%x status:%d",
+		   __func__, ar, ept, skb, packet->buf,
+		   packet->act_len, status);
+
+	if (status || !(skb->data + HTC_HDR_LENGTH)) {
+		ar->net_stats.rx_errors++;
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	/*
+	 * Take lock to protect buffer counts and adaptive power throughput
+	 * state.
+	 */
+	spin_lock_bh(&ar->lock);
+
+	ar->net_stats.rx_packets++;
+	ar->net_stats.rx_bytes += packet->act_len;
+
+	skb_put(skb, packet->act_len + HTC_HDR_LENGTH);
+	skb_pull(skb, HTC_HDR_LENGTH);
+
+	ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len);
+
+	spin_unlock_bh(&ar->lock);
+
+	skb->dev = ar->net_dev;
+
+	if (!test_bit(WMI_ENABLED, &ar->flag)) {
+		if (EPPING_ALIGNMENT_PAD > 0)
+			skb_pull(skb, EPPING_ALIGNMENT_PAD);
+		ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb);
+		return;
+	}
+
+	if (ept == ar->ctrl_ep) {
+		ath6kl_wmi_control_rx(ar->wmi, skb);
+		return;
+	}
+
+	min_hdr_len = sizeof(struct ethhdr);
+	min_hdr_len += sizeof(struct wmi_data_hdr) +
+		       sizeof(struct ath6kl_llc_snap_hdr);
+
+	dhdr = (struct wmi_data_hdr *) skb->data;
+
+	/*
+	 * In the case of AP mode we may receive NULL data frames
+	 * that do not have LLC hdr. They are 16 bytes in size.
+	 * Allow these frames in the AP mode.
+	 */
+	if (ar->nw_type != AP_NETWORK &&
+	    ((packet->act_len < min_hdr_len) ||
+	     (packet->act_len > WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH))) {
+		ath6kl_info("frame len is too short or too long\n");
+		ar->net_stats.rx_errors++;
+		ar->net_stats.rx_length_errors++;
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	/* Get the Power save state of the STA */
+	if (ar->nw_type == AP_NETWORK) {
+		meta_type = wmi_data_hdr_get_meta(dhdr);
+
+		ps_state = !!((dhdr->info >> WMI_DATA_HDR_PS_SHIFT) &
+			      WMI_DATA_HDR_PS_MASK);
+
+		offset = sizeof(struct wmi_data_hdr);
+
+		switch (meta_type) {
+		case 0:
+			break;
+		case WMI_META_VERSION_1:
+			offset += sizeof(struct wmi_rx_meta_v1);
+			break;
+		case WMI_META_VERSION_2:
+			offset += sizeof(struct wmi_rx_meta_v2);
+			break;
+		default:
+			break;
+		}
+
+		datap = (struct ethhdr *) (skb->data + offset);
+		conn = ath6kl_find_sta(ar, datap->h_source);
+
+		if (!conn) {
+			dev_kfree_skb(skb);
+			return;
+		}
+
+		/*
+		 * If there is a change in PS state of the STA,
+		 * take appropriate steps:
+		 *
+		 * 1. If Sleep-->Awake, flush the psq for the STA
+		 *    Clear the PVB for the STA.
+		 * 2. If Awake-->Sleep, Starting queueing frames
+		 *    the STA.
+		 */
+		prev_ps = !!(conn->sta_flags & STA_PS_SLEEP);
+
+		if (ps_state)
+			conn->sta_flags |= STA_PS_SLEEP;
+		else
+			conn->sta_flags &= ~STA_PS_SLEEP;
+
+		if (prev_ps ^ !!(conn->sta_flags & STA_PS_SLEEP)) {
+			if (!(conn->sta_flags & STA_PS_SLEEP)) {
+				struct sk_buff *skbuff = NULL;
+
+				spin_lock_bh(&conn->psq_lock);
+				while ((skbuff = skb_dequeue(&conn->psq))
+				       != NULL) {
+					spin_unlock_bh(&conn->psq_lock);
+					ath6kl_data_tx(skbuff, ar->net_dev);
+					spin_lock_bh(&conn->psq_lock);
+				}
+				spin_unlock_bh(&conn->psq_lock);
+				/* Clear the PVB for this STA */
+				ath6kl_wmi_set_pvb_cmd(ar->wmi, conn->aid, 0);
+			}
+		}
+
+		/* drop NULL data frames here */
+		if ((packet->act_len < min_hdr_len) ||
+		    (packet->act_len >
+		     WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH)) {
+			dev_kfree_skb(skb);
+			return;
+		}
+	}
+
+	is_amsdu = wmi_data_hdr_is_amsdu(dhdr) ? true : false;
+	tid = wmi_data_hdr_get_up(dhdr);
+	seq_no = wmi_data_hdr_get_seqno(dhdr);
+	meta_type = wmi_data_hdr_get_meta(dhdr);
+	dot11_hdr = wmi_data_hdr_get_dot11(dhdr);
+
+	ath6kl_wmi_data_hdr_remove(ar->wmi, skb);
+
+	switch (meta_type) {
+	case WMI_META_VERSION_1:
+		skb_pull(skb, sizeof(struct wmi_rx_meta_v1));
+		break;
+	case WMI_META_VERSION_2:
+		meta = (struct wmi_rx_meta_v2 *) skb->data;
+		if (meta->csum_flags & 0x1) {
+			skb->ip_summed = CHECKSUM_COMPLETE;
+			skb->csum = (__force __wsum) meta->csum;
+		}
+		skb_pull(skb, sizeof(struct wmi_rx_meta_v2));
+		break;
+	default:
+		break;
+	}
+
+	if (dot11_hdr)
+		status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb);
+	else if (!is_amsdu)
+		status = ath6kl_wmi_dot3_2_dix(skb);
+
+	if (status) {
+		/*
+		 * Drop frames that could not be processed (lack of
+		 * memory, etc.)
+		 */
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (!(ar->net_dev->flags & IFF_UP)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (ar->nw_type == AP_NETWORK) {
+		datap = (struct ethhdr *) skb->data;
+		if (is_multicast_ether_addr(datap->h_dest))
+			/*
+			 * Bcast/Mcast frames should be sent to the
+			 * OS stack as well as on the air.
+			 */
+			skb1 = skb_copy(skb, GFP_ATOMIC);
+		else {
+			/*
+			 * Search for a connected STA with dstMac
+			 * as the Mac address. If found send the
+			 * frame to it on the air else send the
+			 * frame up the stack.
+			 */
+			struct ath6kl_sta *conn = NULL;
+			conn = ath6kl_find_sta(ar, datap->h_dest);
+
+			if (conn && ar->intra_bss) {
+				skb1 = skb;
+				skb = NULL;
+			} else if (conn && !ar->intra_bss) {
+				dev_kfree_skb(skb);
+				skb = NULL;
+			}
+		}
+		if (skb1)
+			ath6kl_data_tx(skb1, ar->net_dev);
+	}
+
+	if (!aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no,
+				   is_amsdu, skb))
+		ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb);
+}
+
+static void aggr_timeout(unsigned long arg)
+{
+	u8 i, j;
+	struct aggr_info *p_aggr = (struct aggr_info *) arg;
+	struct rxtid *rxtid;
+	struct rxtid_stats *stats;
+
+	for (i = 0; i < NUM_OF_TIDS; i++) {
+		rxtid = &p_aggr->rx_tid[i];
+		stats = &p_aggr->stat[i];
+
+		if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress)
+			continue;
+
+		/*
+		 * FIXME: these timeouts happen quite fruently, something
+		 * line once within 60 seconds. Investigate why.
+		 */
+		stats->num_timeouts++;
+		ath6kl_dbg(ATH6KL_DBG_AGGR,
+			   "aggr timeout (st %d end %d)\n",
+			   rxtid->seq_next,
+			   ((rxtid->seq_next + rxtid->hold_q_sz-1) &
+			    ATH6KL_MAX_SEQ_NO));
+		aggr_deque_frms(p_aggr, i, 0, 0);
+	}
+
+	p_aggr->timer_scheduled = false;
+
+	for (i = 0; i < NUM_OF_TIDS; i++) {
+		rxtid = &p_aggr->rx_tid[i];
+
+		if (rxtid->aggr && rxtid->hold_q) {
+			for (j = 0; j < rxtid->hold_q_sz; j++) {
+				if (rxtid->hold_q[j].skb) {
+					p_aggr->timer_scheduled = true;
+					rxtid->timer_mon = true;
+					rxtid->progress = false;
+					break;
+				}
+			}
+
+			if (j >= rxtid->hold_q_sz)
+				rxtid->timer_mon = false;
+		}
+	}
+
+	if (p_aggr->timer_scheduled)
+		mod_timer(&p_aggr->timer,
+			  jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT));
+}
+
+static void aggr_delete_tid_state(struct aggr_info *p_aggr, u8 tid)
+{
+	struct rxtid *rxtid;
+	struct rxtid_stats *stats;
+
+	if (!p_aggr || tid >= NUM_OF_TIDS)
+		return;
+
+	rxtid = &p_aggr->rx_tid[tid];
+	stats = &p_aggr->stat[tid];
+
+	if (rxtid->aggr)
+		aggr_deque_frms(p_aggr, tid, 0, 0);
+
+	rxtid->aggr = false;
+	rxtid->progress = false;
+	rxtid->timer_mon = false;
+	rxtid->win_sz = 0;
+	rxtid->seq_next = 0;
+	rxtid->hold_q_sz = 0;
+
+	kfree(rxtid->hold_q);
+	rxtid->hold_q = NULL;
+
+	memset(stats, 0, sizeof(struct rxtid_stats));
+}
+
+void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no, u8 win_sz)
+{
+	struct aggr_info *p_aggr = ar->aggr_cntxt;
+	struct rxtid *rxtid;
+	struct rxtid_stats *stats;
+	u16 hold_q_size;
+
+	if (!p_aggr)
+		return;
+
+	rxtid = &p_aggr->rx_tid[tid];
+	stats = &p_aggr->stat[tid];
+
+	if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX)
+		ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
+			   __func__, win_sz, tid);
+
+	if (rxtid->aggr)
+		aggr_delete_tid_state(p_aggr, tid);
+
+	rxtid->seq_next = seq_no;
+	hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q);
+	rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL);
+	if (!rxtid->hold_q)
+		return;
+
+	rxtid->win_sz = win_sz;
+	rxtid->hold_q_sz = TID_WINDOW_SZ(win_sz);
+	if (!skb_queue_empty(&rxtid->q))
+		return;
+
+	rxtid->aggr = true;
+}
+
+struct aggr_info *aggr_init(struct net_device *dev)
+{
+	struct aggr_info *p_aggr = NULL;
+	struct rxtid *rxtid;
+	u8 i;
+
+	p_aggr = kzalloc(sizeof(struct aggr_info), GFP_KERNEL);
+	if (!p_aggr) {
+		ath6kl_err("failed to alloc memory for aggr_node\n");
+		return NULL;
+	}
+
+	p_aggr->aggr_sz = AGGR_SZ_DEFAULT;
+	p_aggr->dev = dev;
+	init_timer(&p_aggr->timer);
+	p_aggr->timer.function = aggr_timeout;
+	p_aggr->timer.data = (unsigned long) p_aggr;
+
+	p_aggr->timer_scheduled = false;
+	skb_queue_head_init(&p_aggr->free_q);
+
+	ath6kl_alloc_netbufs(&p_aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS);
+
+	for (i = 0; i < NUM_OF_TIDS; i++) {
+		rxtid = &p_aggr->rx_tid[i];
+		rxtid->aggr = false;
+		rxtid->progress = false;
+		rxtid->timer_mon = false;
+		skb_queue_head_init(&rxtid->q);
+		spin_lock_init(&rxtid->lock);
+	}
+
+	return p_aggr;
+}
+
+void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid)
+{
+	struct aggr_info *p_aggr = ar->aggr_cntxt;
+	struct rxtid *rxtid;
+
+	if (!p_aggr)
+		return;
+
+	rxtid = &p_aggr->rx_tid[tid];
+
+	if (rxtid->aggr)
+		aggr_delete_tid_state(p_aggr, tid);
+}
+
+void aggr_reset_state(struct aggr_info *aggr_info)
+{
+	u8 tid;
+
+	for (tid = 0; tid < NUM_OF_TIDS; tid++)
+		aggr_delete_tid_state(aggr_info, tid);
+}
+
+/* clean up our amsdu buffer list */
+void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar)
+{
+	struct htc_packet *packet, *tmp_pkt;
+
+	spin_lock_bh(&ar->lock);
+	if (list_empty(&ar->amsdu_rx_buffer_queue)) {
+		spin_unlock_bh(&ar->lock);
+		return;
+	}
+
+	list_for_each_entry_safe(packet, tmp_pkt, &ar->amsdu_rx_buffer_queue,
+				 list) {
+		list_del(&packet->list);
+		spin_unlock_bh(&ar->lock);
+		dev_kfree_skb(packet->pkt_cntxt);
+		spin_lock_bh(&ar->lock);
+	}
+
+	spin_unlock_bh(&ar->lock);
+}
+
+void aggr_module_destroy(struct aggr_info *aggr_info)
+{
+	struct rxtid *rxtid;
+	u8 i, k;
+
+	if (!aggr_info)
+		return;
+
+	if (aggr_info->timer_scheduled) {
+		del_timer(&aggr_info->timer);
+		aggr_info->timer_scheduled = false;
+	}
+
+	for (i = 0; i < NUM_OF_TIDS; i++) {
+		rxtid = &aggr_info->rx_tid[i];
+		if (rxtid->hold_q) {
+			for (k = 0; k < rxtid->hold_q_sz; k++)
+				dev_kfree_skb(rxtid->hold_q[k].skb);
+			kfree(rxtid->hold_q);
+		}
+
+		skb_queue_purge(&rxtid->q);
+	}
+
+	skb_queue_purge(&aggr_info->free_q);
+	kfree(aggr_info);
+}

+ 2743 - 0
drivers/net/wireless/ath/ath6kl/wmi.c

@@ -0,0 +1,2743 @@
+/*
+ * Copyright (c) 2004-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/ip.h>
+#include "core.h"
+#include "debug.h"
+
+static int ath6kl_wmi_sync_point(struct wmi *wmi);
+
+static const s32 wmi_rate_tbl[][2] = {
+	/* {W/O SGI, with SGI} */
+	{1000, 1000},
+	{2000, 2000},
+	{5500, 5500},
+	{11000, 11000},
+	{6000, 6000},
+	{9000, 9000},
+	{12000, 12000},
+	{18000, 18000},
+	{24000, 24000},
+	{36000, 36000},
+	{48000, 48000},
+	{54000, 54000},
+	{6500, 7200},
+	{13000, 14400},
+	{19500, 21700},
+	{26000, 28900},
+	{39000, 43300},
+	{52000, 57800},
+	{58500, 65000},
+	{65000, 72200},
+	{13500, 15000},
+	{27000, 30000},
+	{40500, 45000},
+	{54000, 60000},
+	{81000, 90000},
+	{108000, 120000},
+	{121500, 135000},
+	{135000, 150000},
+	{0, 0}
+};
+
+/* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */
+static const u8 up_to_ac[] = {
+	WMM_AC_BE,
+	WMM_AC_BK,
+	WMM_AC_BK,
+	WMM_AC_BE,
+	WMM_AC_VI,
+	WMM_AC_VI,
+	WMM_AC_VO,
+	WMM_AC_VO,
+};
+
+void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id)
+{
+	if (WARN_ON(ep_id == ENDPOINT_UNUSED || ep_id >= ENDPOINT_MAX))
+		return;
+
+	wmi->ep_id = ep_id;
+}
+
+enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi)
+{
+	return wmi->ep_id;
+}
+
+/*  Performs DIX to 802.3 encapsulation for transmit packets.
+ *  Assumes the entire DIX header is contigous and that there is
+ *  enough room in the buffer for a 802.3 mac header and LLC+SNAP headers.
+ */
+int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb)
+{
+	struct ath6kl_llc_snap_hdr *llc_hdr;
+	struct ethhdr *eth_hdr;
+	size_t new_len;
+	__be16 type;
+	u8 *datap;
+	u16 size;
+
+	if (WARN_ON(skb == NULL))
+		return -EINVAL;
+
+	size = sizeof(struct ath6kl_llc_snap_hdr) + sizeof(struct wmi_data_hdr);
+	if (skb_headroom(skb) < size)
+		return -ENOMEM;
+
+	eth_hdr = (struct ethhdr *) skb->data;
+	type = eth_hdr->h_proto;
+
+	if (!is_ethertype(be16_to_cpu(type))) {
+		ath6kl_dbg(ATH6KL_DBG_WMI,
+			"%s: pkt is already in 802.3 format\n", __func__);
+		return 0;
+	}
+
+	new_len = skb->len - sizeof(*eth_hdr) + sizeof(*llc_hdr);
+
+	skb_push(skb, sizeof(struct ath6kl_llc_snap_hdr));
+	datap = skb->data;
+
+	eth_hdr->h_proto = cpu_to_be16(new_len);
+
+	memcpy(datap, eth_hdr, sizeof(*eth_hdr));
+
+	llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap + sizeof(*eth_hdr));
+	llc_hdr->dsap = 0xAA;
+	llc_hdr->ssap = 0xAA;
+	llc_hdr->cntl = 0x03;
+	llc_hdr->org_code[0] = 0x0;
+	llc_hdr->org_code[1] = 0x0;
+	llc_hdr->org_code[2] = 0x0;
+	llc_hdr->eth_type = type;
+
+	return 0;
+}
+
+static int ath6kl_wmi_meta_add(struct wmi *wmi, struct sk_buff *skb,
+			       u8 *version, void *tx_meta_info)
+{
+	struct wmi_tx_meta_v1 *v1;
+	struct wmi_tx_meta_v2 *v2;
+
+	if (WARN_ON(skb == NULL || version == NULL))
+		return -EINVAL;
+
+	switch (*version) {
+	case WMI_META_VERSION_1:
+		skb_push(skb, WMI_MAX_TX_META_SZ);
+		v1 = (struct wmi_tx_meta_v1 *) skb->data;
+		v1->pkt_id = 0;
+		v1->rate_plcy_id = 0;
+		*version = WMI_META_VERSION_1;
+		break;
+	case WMI_META_VERSION_2:
+		skb_push(skb, WMI_MAX_TX_META_SZ);
+		v2 = (struct wmi_tx_meta_v2 *) skb->data;
+		memcpy(v2, (struct wmi_tx_meta_v2 *) tx_meta_info,
+		       sizeof(struct wmi_tx_meta_v2));
+		break;
+	}
+
+	return 0;
+}
+
+int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
+			    u8 msg_type, bool more_data,
+			    enum wmi_data_hdr_data_type data_type,
+			    u8 meta_ver, void *tx_meta_info)
+{
+	struct wmi_data_hdr *data_hdr;
+	int ret;
+
+	if (WARN_ON(skb == NULL))
+		return -EINVAL;
+
+	ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info);
+	if (ret)
+		return ret;
+
+	skb_push(skb, sizeof(struct wmi_data_hdr));
+
+	data_hdr = (struct wmi_data_hdr *)skb->data;
+	memset(data_hdr, 0, sizeof(struct wmi_data_hdr));
+
+	data_hdr->info = msg_type << WMI_DATA_HDR_MSG_TYPE_SHIFT;
+	data_hdr->info |= data_type << WMI_DATA_HDR_DATA_TYPE_SHIFT;
+
+	if (more_data)
+		data_hdr->info |=
+		    WMI_DATA_HDR_MORE_MASK << WMI_DATA_HDR_MORE_SHIFT;
+
+	data_hdr->info2 = cpu_to_le16(meta_ver << WMI_DATA_HDR_META_SHIFT);
+	data_hdr->info3 = 0;
+
+	return 0;
+}
+
+static u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri)
+{
+	struct iphdr *ip_hdr = (struct iphdr *) pkt;
+	u8 ip_pri;
+
+	/*
+	 * Determine IPTOS priority
+	 *
+	 * IP-TOS - 8bits
+	 *          : DSCP(6-bits) ECN(2-bits)
+	 *          : DSCP - P2 P1 P0 X X X
+	 * where (P2 P1 P0) form 802.1D
+	 */
+	ip_pri = ip_hdr->tos >> 5;
+	ip_pri &= 0x7;
+
+	if ((layer2_pri & 0x7) > ip_pri)
+		return (u8) layer2_pri & 0x7;
+	else
+		return ip_pri;
+}
+
+int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
+				       u32 layer2_priority, bool wmm_enabled,
+				       u8 *ac)
+{
+	struct wmi_data_hdr *data_hdr;
+	struct ath6kl_llc_snap_hdr *llc_hdr;
+	struct wmi_create_pstream_cmd cmd;
+	u32 meta_size, hdr_size;
+	u16 ip_type = IP_ETHERTYPE;
+	u8 stream_exist, usr_pri;
+	u8 traffic_class = WMM_AC_BE;
+	u8 *datap;
+
+	if (WARN_ON(skb == NULL))
+		return -EINVAL;
+
+	datap = skb->data;
+	data_hdr = (struct wmi_data_hdr *) datap;
+
+	meta_size = ((le16_to_cpu(data_hdr->info2) >> WMI_DATA_HDR_META_SHIFT) &
+		     WMI_DATA_HDR_META_MASK) ? WMI_MAX_TX_META_SZ : 0;
+
+	if (!wmm_enabled) {
+		/* If WMM is disabled all traffic goes as BE traffic */
+		usr_pri = 0;
+	} else {
+		hdr_size = sizeof(struct ethhdr);
+
+		llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap +
+							 sizeof(struct
+								wmi_data_hdr) +
+							 meta_size + hdr_size);
+
+		if (llc_hdr->eth_type == htons(ip_type)) {
+			/*
+			 * Extract the endpoint info from the TOS field
+			 * in the IP header.
+			 */
+			usr_pri =
+			   ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) +
+					sizeof(struct ath6kl_llc_snap_hdr),
+					layer2_priority);
+		} else
+			usr_pri = layer2_priority & 0x7;
+	}
+
+	/* workaround for WMM S5 */
+	if ((wmi->traffic_class == WMM_AC_VI) &&
+	    ((usr_pri == 5) || (usr_pri == 4)))
+		usr_pri = 1;
+
+	/* Convert user priority to traffic class */
+	traffic_class = up_to_ac[usr_pri & 0x7];
+
+	wmi_data_hdr_set_up(data_hdr, usr_pri);
+
+	spin_lock_bh(&wmi->lock);
+	stream_exist = wmi->fat_pipe_exist;
+	spin_unlock_bh(&wmi->lock);
+
+	if (!(stream_exist & (1 << traffic_class))) {
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.traffic_class = traffic_class;
+		cmd.user_pri = usr_pri;
+		cmd.inactivity_int =
+			cpu_to_le32(WMI_IMPLICIT_PSTREAM_INACTIVITY_INT);
+		/* Implicit streams are created with TSID 0xFF */
+		cmd.tsid = WMI_IMPLICIT_PSTREAM;
+		ath6kl_wmi_create_pstream_cmd(wmi, &cmd);
+	}
+
+	*ac = traffic_class;
+
+	return 0;
+}
+
+int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
+{
+	struct ieee80211_hdr_3addr *pwh, wh;
+	struct ath6kl_llc_snap_hdr *llc_hdr;
+	struct ethhdr eth_hdr;
+	u32 hdr_size;
+	u8 *datap;
+	__le16 sub_type;
+
+	if (WARN_ON(skb == NULL))
+		return -EINVAL;
+
+	datap = skb->data;
+	pwh = (struct ieee80211_hdr_3addr *) datap;
+
+	sub_type = pwh->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE);
+
+	memcpy((u8 *) &wh, datap, sizeof(struct ieee80211_hdr_3addr));
+
+	/* Strip off the 802.11 header */
+	if (sub_type == cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+		hdr_size = roundup(sizeof(struct ieee80211_qos_hdr),
+				   sizeof(u32));
+		skb_pull(skb, hdr_size);
+	} else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA))
+		skb_pull(skb, sizeof(struct ieee80211_hdr_3addr));
+
+	datap = skb->data;
+	llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap);
+
+	memset(&eth_hdr, 0, sizeof(eth_hdr));
+	eth_hdr.h_proto = llc_hdr->eth_type;
+
+	switch ((le16_to_cpu(wh.frame_control)) &
+		(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+	case 0:
+		memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN);
+		memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN);
+		break;
+	case IEEE80211_FCTL_TODS:
+		memcpy(eth_hdr.h_dest, wh.addr3, ETH_ALEN);
+		memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN);
+		break;
+	case IEEE80211_FCTL_FROMDS:
+		memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN);
+		memcpy(eth_hdr.h_source, wh.addr3, ETH_ALEN);
+		break;
+	case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+		break;
+	}
+
+	skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr));
+	skb_push(skb, sizeof(eth_hdr));
+
+	datap = skb->data;
+
+	memcpy(datap, &eth_hdr, sizeof(eth_hdr));
+
+	return 0;
+}
+
+/*
+ * Performs 802.3 to DIX encapsulation for received packets.
+ * Assumes the entire 802.3 header is contigous.
+ */
+int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb)
+{
+	struct ath6kl_llc_snap_hdr *llc_hdr;
+	struct ethhdr eth_hdr;
+	u8 *datap;
+
+	if (WARN_ON(skb == NULL))
+		return -EINVAL;
+
+	datap = skb->data;
+
+	memcpy(&eth_hdr, datap, sizeof(eth_hdr));
+
+	llc_hdr = (struct ath6kl_llc_snap_hdr *) (datap + sizeof(eth_hdr));
+	eth_hdr.h_proto = llc_hdr->eth_type;
+
+	skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr));
+	datap = skb->data;
+
+	memcpy(datap, &eth_hdr, sizeof(eth_hdr));
+
+	return 0;
+}
+
+int ath6kl_wmi_data_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
+{
+	if (WARN_ON(skb == NULL))
+		return -EINVAL;
+
+	skb_pull(skb, sizeof(struct wmi_data_hdr));
+
+	return 0;
+}
+
+static void ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(struct sk_buff *skb,
+						   u8 *datap)
+{
+	struct wmi_bss_info_hdr2 bih2;
+	struct wmi_bss_info_hdr *bih;
+
+	memcpy(&bih2, datap, sizeof(struct wmi_bss_info_hdr2));
+
+	skb_push(skb, 4);
+	bih = (struct wmi_bss_info_hdr *) skb->data;
+
+	bih->ch = bih2.ch;
+	bih->frame_type = bih2.frame_type;
+	bih->snr = bih2.snr;
+	bih->rssi = a_cpu_to_sle16(bih2.snr - 95);
+	bih->ie_mask = cpu_to_le32(le16_to_cpu(bih2.ie_mask));
+	memcpy(bih->bssid, bih2.bssid, ETH_ALEN);
+}
+
+static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
+{
+	struct tx_complete_msg_v1 *msg_v1;
+	struct wmi_tx_complete_event *evt;
+	int index;
+	u16 size;
+
+	evt = (struct wmi_tx_complete_event *) datap;
+
+	ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n",
+		   evt->num_msg, evt->msg_len, evt->msg_type);
+
+	if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_WMI))
+		return 0;
+
+	for (index = 0; index < evt->num_msg; index++) {
+		size = sizeof(struct wmi_tx_complete_event) +
+		    (index * sizeof(struct tx_complete_msg_v1));
+		msg_v1 = (struct tx_complete_msg_v1 *)(datap + size);
+
+		ath6kl_dbg(ATH6KL_DBG_WMI, "msg: %d %d %d %d\n",
+			   msg_v1->status, msg_v1->pkt_id,
+			   msg_v1->rate_idx, msg_v1->ack_failures);
+	}
+
+	return 0;
+}
+
+static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size)
+{
+	struct sk_buff *skb;
+
+	skb = ath6kl_buf_alloc(size);
+	if (!skb)
+		return NULL;
+
+	skb_put(skb, size);
+	if (size)
+		memset(skb->data, 0, size);
+
+	return skb;
+}
+
+/* Send a "simple" wmi command -- one with no arguments */
+static int ath6kl_wmi_simple_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id)
+{
+	struct sk_buff *skb;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(0);
+	if (!skb)
+		return -ENOMEM;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, cmd_id, NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_ready_event_2 *ev = (struct wmi_ready_event_2 *) datap;
+
+	if (len < sizeof(struct wmi_ready_event_2))
+		return -EINVAL;
+
+	wmi->ready = true;
+	ath6kl_ready_event(wmi->parent_dev, ev->mac_addr,
+			   le32_to_cpu(ev->sw_version),
+			   le32_to_cpu(ev->abi_version));
+
+	return 0;
+}
+
+static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_connect_event *ev;
+	u8 *pie, *peie;
+
+	if (len < sizeof(struct wmi_connect_event))
+		return -EINVAL;
+
+	ev = (struct wmi_connect_event *) datap;
+
+	ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM\n",
+		   __func__, ev->ch, ev->bssid);
+
+	/* Start of assoc rsp IEs */
+	pie = ev->assoc_info + ev->beacon_ie_len +
+	      ev->assoc_req_len + (sizeof(u16) * 3); /* capinfo, status, aid */
+
+	/* End of assoc rsp IEs */
+	peie = ev->assoc_info + ev->beacon_ie_len + ev->assoc_req_len +
+	    ev->assoc_resp_len;
+
+	while (pie < peie) {
+		switch (*pie) {
+		case WLAN_EID_VENDOR_SPECIFIC:
+			if (pie[1] > 3 && pie[2] == 0x00 && pie[3] == 0x50 &&
+			    pie[4] == 0xf2 && pie[5] == WMM_OUI_TYPE) {
+				/* WMM OUT (00:50:F2) */
+				if (pie[1] > 5
+				    && pie[6] == WMM_PARAM_OUI_SUBTYPE)
+					wmi->is_wmm_enabled = true;
+			}
+			break;
+		}
+
+		if (wmi->is_wmm_enabled)
+			break;
+
+		pie += pie[1] + 2;
+	}
+
+	ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->ch), ev->bssid,
+			     le16_to_cpu(ev->listen_intvl),
+			     le16_to_cpu(ev->beacon_intvl),
+			     le32_to_cpu(ev->nw_type),
+			     ev->beacon_ie_len, ev->assoc_req_len,
+			     ev->assoc_resp_len, ev->assoc_info);
+
+	return 0;
+}
+
+static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_disconnect_event *ev;
+	wmi->traffic_class = 100;
+
+	if (len < sizeof(struct wmi_disconnect_event))
+		return -EINVAL;
+
+	ev = (struct wmi_disconnect_event *) datap;
+
+	wmi->is_wmm_enabled = false;
+	wmi->pair_crypto_type = NONE_CRYPT;
+	wmi->grp_crypto_type = NONE_CRYPT;
+
+	ath6kl_disconnect_event(wmi->parent_dev, ev->disconn_reason,
+				ev->bssid, ev->assoc_resp_len, ev->assoc_info,
+				le16_to_cpu(ev->proto_reason_status));
+
+	return 0;
+}
+
+static int ath6kl_wmi_peer_node_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_peer_node_event *ev;
+
+	if (len < sizeof(struct wmi_peer_node_event))
+		return -EINVAL;
+
+	ev = (struct wmi_peer_node_event *) datap;
+
+	if (ev->event_code == PEER_NODE_JOIN_EVENT)
+		ath6kl_dbg(ATH6KL_DBG_WMI, "joined node with mac addr: %pM\n",
+			   ev->peer_mac_addr);
+	else if (ev->event_code == PEER_NODE_LEAVE_EVENT)
+		ath6kl_dbg(ATH6KL_DBG_WMI, "left node with mac addr: %pM\n",
+			   ev->peer_mac_addr);
+
+	return 0;
+}
+
+static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_tkip_micerr_event *ev;
+
+	if (len < sizeof(struct wmi_tkip_micerr_event))
+		return -EINVAL;
+
+	ev = (struct wmi_tkip_micerr_event *) datap;
+
+	ath6kl_tkip_micerr_event(wmi->parent_dev, ev->key_id, ev->is_mcast);
+
+	return 0;
+}
+
+static int ath6kl_wlan_parse_beacon(u8 *buf, int frame_len,
+				    struct ath6kl_common_ie *cie)
+{
+	u8 *frm, *efrm;
+	u8 elemid_ssid = false;
+
+	frm = buf;
+	efrm = (u8 *) (frm + frame_len);
+
+	/*
+	 * beacon/probe response frame format
+	 *  [8] time stamp
+	 *  [2] beacon interval
+	 *  [2] capability information
+	 *  [tlv] ssid
+	 *  [tlv] supported rates
+	 *  [tlv] country information
+	 *  [tlv] parameter set (FH/DS)
+	 *  [tlv] erp information
+	 *  [tlv] extended supported rates
+	 *  [tlv] WMM
+	 *  [tlv] WPA or RSN
+	 *  [tlv] Atheros Advanced Capabilities
+	 */
+	if ((efrm - frm) < 12)
+		return -EINVAL;
+
+	memset(cie, 0, sizeof(*cie));
+
+	cie->ie_tstamp = frm;
+	frm += 8;
+	cie->ie_beaconInt = *(u16 *) frm;
+	frm += 2;
+	cie->ie_capInfo = *(u16 *) frm;
+	frm += 2;
+	cie->ie_chan = 0;
+
+	while (frm < efrm) {
+		switch (*frm) {
+		case WLAN_EID_SSID:
+			if (!elemid_ssid) {
+				cie->ie_ssid = frm;
+				elemid_ssid = true;
+			}
+			break;
+		case WLAN_EID_SUPP_RATES:
+			cie->ie_rates = frm;
+			break;
+		case WLAN_EID_COUNTRY:
+			cie->ie_country = frm;
+			break;
+		case WLAN_EID_FH_PARAMS:
+			break;
+		case WLAN_EID_DS_PARAMS:
+			cie->ie_chan = frm[2];
+			break;
+		case WLAN_EID_TIM:
+			cie->ie_tim = frm;
+			break;
+		case WLAN_EID_IBSS_PARAMS:
+			break;
+		case WLAN_EID_EXT_SUPP_RATES:
+			cie->ie_xrates = frm;
+			break;
+		case WLAN_EID_ERP_INFO:
+			if (frm[1] != 1)
+				return -EINVAL;
+
+			cie->ie_erp = frm[2];
+			break;
+		case WLAN_EID_RSN:
+			cie->ie_rsn = frm;
+			break;
+		case WLAN_EID_HT_CAPABILITY:
+			cie->ie_htcap = frm;
+			break;
+		case WLAN_EID_HT_INFORMATION:
+			cie->ie_htop = frm;
+			break;
+		case WLAN_EID_VENDOR_SPECIFIC:
+			if (frm[1] > 3 && frm[2] == 0x00 && frm[3] == 0x50 &&
+			    frm[4] == 0xf2) {
+				/* OUT Type (00:50:F2) */
+
+				if (frm[5] == WPA_OUI_TYPE) {
+					/* WPA OUT */
+					cie->ie_wpa = frm;
+				} else if (frm[5] == WMM_OUI_TYPE) {
+					/* WMM OUT */
+					cie->ie_wmm = frm;
+				} else if (frm[5] == WSC_OUT_TYPE) {
+					/* WSC OUT */
+					cie->ie_wsc = frm;
+				}
+
+			} else if (frm[1] > 3 && frm[2] == 0x00
+				   && frm[3] == 0x03 && frm[4] == 0x7f
+				   && frm[5] == ATH_OUI_TYPE) {
+				/* Atheros OUI (00:03:7f) */
+				cie->ie_ath = frm;
+			}
+			break;
+		default:
+			break;
+		}
+		frm += frm[1] + 2;
+	}
+
+	if ((cie->ie_rates == NULL)
+	    || (cie->ie_rates[1] > ATH6KL_RATE_MAXSIZE))
+		return -EINVAL;
+
+	if ((cie->ie_ssid == NULL)
+	    || (cie->ie_ssid[1] > IEEE80211_MAX_SSID_LEN))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct bss *bss = NULL;
+	struct wmi_bss_info_hdr *bih;
+	u8 cached_ssid_len = 0;
+	u8 cached_ssid[IEEE80211_MAX_SSID_LEN] = { 0 };
+	u8 beacon_ssid_len = 0;
+	u8 *buf, *ie_ssid;
+	u8 *ni_buf;
+	int buf_len;
+
+	int ret;
+
+	if (len <= sizeof(struct wmi_bss_info_hdr))
+		return -EINVAL;
+
+	bih = (struct wmi_bss_info_hdr *) datap;
+	bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid);
+
+	if (a_sle16_to_cpu(bih->rssi) > 0) {
+		if (bss == NULL)
+			return 0;
+		else
+			bih->rssi = a_cpu_to_sle16(bss->ni_rssi);
+	}
+
+	buf = datap + sizeof(struct wmi_bss_info_hdr);
+	len -= sizeof(struct wmi_bss_info_hdr);
+
+	ath6kl_dbg(ATH6KL_DBG_WMI,
+		   "bss info evt - ch %u, rssi %02x, bssid \"%pM\"\n",
+		   bih->ch, a_sle16_to_cpu(bih->rssi), bih->bssid);
+
+	if (bss != NULL) {
+		/*
+		 * Free up the node. We are about to allocate a new node.
+		 * In case of hidden AP, beacon will not have ssid,
+		 * but a directed probe response will have it,
+		 * so cache the probe-resp-ssid if already present.
+		 */
+		if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE)) {
+			ie_ssid = bss->ni_cie.ie_ssid;
+			if (ie_ssid && (ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) &&
+			    (ie_ssid[2] != 0)) {
+				cached_ssid_len = ie_ssid[1];
+				memcpy(cached_ssid, ie_ssid + 2,
+				       cached_ssid_len);
+			}
+		}
+
+		/*
+		 * Use the current average rssi of associated AP base on
+		 * assumption
+		 *   1. Most os with GUI will update RSSI by
+		 *      ath6kl_wmi_get_stats_cmd() periodically.
+		 *   2. ath6kl_wmi_get_stats_cmd(..) will be called when calling
+		 *      ath6kl_wmi_startscan_cmd(...)
+		 * The average value of RSSI give end-user better feeling for
+		 * instance value of scan result. It also sync up RSSI info
+		 * in GUI between scan result and RSSI signal icon.
+		 */
+		if (memcmp(wmi->parent_dev->bssid, bih->bssid, ETH_ALEN) == 0) {
+			bih->rssi = a_cpu_to_sle16(bss->ni_rssi);
+			bih->snr = bss->ni_snr;
+		}
+
+		wlan_node_reclaim(&wmi->parent_dev->scan_table, bss);
+	}
+
+	/*
+	 * beacon/probe response frame format
+	 *  [8] time stamp
+	 *  [2] beacon interval
+	 *  [2] capability information
+	 *  [tlv] ssid
+	 */
+	beacon_ssid_len = buf[SSID_IE_LEN_INDEX];
+
+	/*
+	 * If ssid is cached for this hidden AP, then change
+	 * buffer len accordingly.
+	 */
+	if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) &&
+	    (cached_ssid_len != 0) &&
+	    (beacon_ssid_len == 0 || (cached_ssid_len > beacon_ssid_len &&
+				      buf[SSID_IE_LEN_INDEX + 1] == 0))) {
+
+		len += (cached_ssid_len - beacon_ssid_len);
+	}
+
+	bss = wlan_node_alloc(len);
+	if (!bss)
+		return -ENOMEM;
+
+	bss->ni_snr = bih->snr;
+	bss->ni_rssi = a_sle16_to_cpu(bih->rssi);
+
+	if (WARN_ON(!bss->ni_buf))
+		return -EINVAL;
+
+	/*
+	 * In case of hidden AP, beacon will not have ssid,
+	 * but a directed probe response will have it,
+	 * so place the cached-ssid(probe-resp) in the bss info.
+	 */
+	if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) &&
+	    (cached_ssid_len != 0) &&
+	    (beacon_ssid_len == 0 || (beacon_ssid_len &&
+				      buf[SSID_IE_LEN_INDEX + 1] == 0))) {
+		ni_buf = bss->ni_buf;
+		buf_len = len;
+
+		/*
+		 * Copy the first 14 bytes:
+		 * time-stamp(8), beacon-interval(2),
+		 * cap-info(2), ssid-id(1), ssid-len(1).
+		 */
+		memcpy(ni_buf, buf, SSID_IE_LEN_INDEX + 1);
+
+		ni_buf[SSID_IE_LEN_INDEX] = cached_ssid_len;
+		ni_buf += (SSID_IE_LEN_INDEX + 1);
+
+		buf += (SSID_IE_LEN_INDEX + 1);
+		buf_len -= (SSID_IE_LEN_INDEX + 1);
+
+		memcpy(ni_buf, cached_ssid, cached_ssid_len);
+		ni_buf += cached_ssid_len;
+
+		buf += beacon_ssid_len;
+		buf_len -= beacon_ssid_len;
+
+		if (cached_ssid_len > beacon_ssid_len)
+			buf_len -= (cached_ssid_len - beacon_ssid_len);
+
+		memcpy(ni_buf, buf, buf_len);
+	} else
+		memcpy(bss->ni_buf, buf, len);
+
+	bss->ni_framelen = len;
+
+	ret = ath6kl_wlan_parse_beacon(bss->ni_buf, len, &bss->ni_cie);
+	if (ret) {
+		wlan_node_free(bss);
+		return -EINVAL;
+	}
+
+	/*
+	 * Update the frequency in ie_chan, overwriting of channel number
+	 * which is done in ath6kl_wlan_parse_beacon
+	 */
+	bss->ni_cie.ie_chan = le16_to_cpu(bih->ch);
+	wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid);
+
+	return 0;
+}
+
+static int ath6kl_wmi_opt_frame_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct bss *bss;
+	struct wmi_opt_rx_info_hdr *bih;
+	u8 *buf;
+
+	if (len <= sizeof(struct wmi_opt_rx_info_hdr))
+		return -EINVAL;
+
+	bih = (struct wmi_opt_rx_info_hdr *) datap;
+	buf = datap + sizeof(struct wmi_opt_rx_info_hdr);
+	len -= sizeof(struct wmi_opt_rx_info_hdr);
+
+	ath6kl_dbg(ATH6KL_DBG_WMI, "opt frame event %2.2x:%2.2x\n",
+		   bih->bssid[4], bih->bssid[5]);
+
+	bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid);
+	if (bss != NULL) {
+		/* Free up the node. We are about to allocate a new node. */
+		wlan_node_reclaim(&wmi->parent_dev->scan_table, bss);
+	}
+
+	bss = wlan_node_alloc(len);
+	if (!bss)
+		return -ENOMEM;
+
+	bss->ni_snr = bih->snr;
+	bss->ni_cie.ie_chan = le16_to_cpu(bih->ch);
+
+	if (WARN_ON(!bss->ni_buf))
+		return -EINVAL;
+
+	memcpy(bss->ni_buf, buf, len);
+	wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid);
+
+	return 0;
+}
+
+/* Inactivity timeout of a fatpipe(pstream) at the target */
+static int ath6kl_wmi_pstream_timeout_event_rx(struct wmi *wmi, u8 *datap,
+					       int len)
+{
+	struct wmi_pstream_timeout_event *ev;
+
+	if (len < sizeof(struct wmi_pstream_timeout_event))
+		return -EINVAL;
+
+	ev = (struct wmi_pstream_timeout_event *) datap;
+
+	/*
+	 * When the pstream (fat pipe == AC) timesout, it means there were
+	 * no thinStreams within this pstream & it got implicitly created
+	 * due to data flow on this AC. We start the inactivity timer only
+	 * for implicitly created pstream. Just reset the host state.
+	 */
+	spin_lock_bh(&wmi->lock);
+	wmi->stream_exist_for_ac[ev->traffic_class] = 0;
+	wmi->fat_pipe_exist &= ~(1 << ev->traffic_class);
+	spin_unlock_bh(&wmi->lock);
+
+	/* Indicate inactivity to driver layer for this fatpipe (pstream) */
+	ath6kl_indicate_tx_activity(wmi->parent_dev, ev->traffic_class, false);
+
+	return 0;
+}
+
+static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_bit_rate_reply *reply;
+	s32 rate;
+	u32 sgi, index;
+
+	if (len < sizeof(struct wmi_bit_rate_reply))
+		return -EINVAL;
+
+	reply = (struct wmi_bit_rate_reply *) datap;
+
+	ath6kl_dbg(ATH6KL_DBG_WMI, "rateindex %d\n", reply->rate_index);
+
+	if (reply->rate_index == (s8) RATE_AUTO) {
+		rate = RATE_AUTO;
+	} else {
+		index = reply->rate_index & 0x7f;
+		sgi = (reply->rate_index & 0x80) ? 1 : 0;
+		rate = wmi_rate_tbl[index][sgi];
+	}
+
+	ath6kl_wakeup_event(wmi->parent_dev);
+
+	return 0;
+}
+
+static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	if (len < sizeof(struct wmi_fix_rates_reply))
+		return -EINVAL;
+
+	ath6kl_wakeup_event(wmi->parent_dev);
+
+	return 0;
+}
+
+static int ath6kl_wmi_ch_list_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	if (len < sizeof(struct wmi_channel_list_reply))
+		return -EINVAL;
+
+	ath6kl_wakeup_event(wmi->parent_dev);
+
+	return 0;
+}
+
+static int ath6kl_wmi_tx_pwr_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_tx_pwr_reply *reply;
+
+	if (len < sizeof(struct wmi_tx_pwr_reply))
+		return -EINVAL;
+
+	reply = (struct wmi_tx_pwr_reply *) datap;
+	ath6kl_txpwr_rx_evt(wmi->parent_dev, reply->dbM);
+
+	return 0;
+}
+
+static int ath6kl_wmi_keepalive_reply_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	if (len < sizeof(struct wmi_get_keepalive_cmd))
+		return -EINVAL;
+
+	ath6kl_wakeup_event(wmi->parent_dev);
+
+	return 0;
+}
+
+static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_scan_complete_event *ev;
+
+	ev = (struct wmi_scan_complete_event *) datap;
+
+	if (a_sle32_to_cpu(ev->status) == 0)
+		wlan_refresh_inactive_nodes(wmi->parent_dev);
+
+	ath6kl_scan_complete_evt(wmi->parent_dev, a_sle32_to_cpu(ev->status));
+	wmi->is_probe_ssid = false;
+
+	return 0;
+}
+
+/*
+ * Target is reporting a programming error.  This is for
+ * developer aid only.  Target only checks a few common violations
+ * and it is responsibility of host to do all error checking.
+ * Behavior of target after wmi error event is undefined.
+ * A reset is recommended.
+ */
+static int ath6kl_wmi_error_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	const char *type = "unknown error";
+	struct wmi_cmd_error_event *ev;
+	ev = (struct wmi_cmd_error_event *) datap;
+
+	switch (ev->err_code) {
+	case INVALID_PARAM:
+		type = "invalid parameter";
+		break;
+	case ILLEGAL_STATE:
+		type = "invalid state";
+		break;
+	case INTERNAL_ERROR:
+		type = "internal error";
+		break;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_WMI, "programming error, cmd=%d %s\n",
+		   ev->cmd_id, type);
+
+	return 0;
+}
+
+static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	ath6kl_tgt_stats_event(wmi->parent_dev, datap, len);
+
+	return 0;
+}
+
+static u8 ath6kl_wmi_get_upper_threshold(s16 rssi,
+					 struct sq_threshold_params *sq_thresh,
+					 u32 size)
+{
+	u32 index;
+	u8 threshold = (u8) sq_thresh->upper_threshold[size - 1];
+
+	/* The list is already in sorted order. Get the next lower value */
+	for (index = 0; index < size; index++) {
+		if (rssi < sq_thresh->upper_threshold[index]) {
+			threshold = (u8) sq_thresh->upper_threshold[index];
+			break;
+		}
+	}
+
+	return threshold;
+}
+
+static u8 ath6kl_wmi_get_lower_threshold(s16 rssi,
+					 struct sq_threshold_params *sq_thresh,
+					 u32 size)
+{
+	u32 index;
+	u8 threshold = (u8) sq_thresh->lower_threshold[size - 1];
+
+	/* The list is already in sorted order. Get the next lower value */
+	for (index = 0; index < size; index++) {
+		if (rssi > sq_thresh->lower_threshold[index]) {
+			threshold = (u8) sq_thresh->lower_threshold[index];
+			break;
+		}
+	}
+
+	return threshold;
+}
+
+static int ath6kl_wmi_send_rssi_threshold_params(struct wmi *wmi,
+			struct wmi_rssi_threshold_params_cmd *rssi_cmd)
+{
+	struct sk_buff *skb;
+	struct wmi_rssi_threshold_params_cmd *cmd;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_rssi_threshold_params_cmd *) skb->data;
+	memcpy(cmd, rssi_cmd, sizeof(struct wmi_rssi_threshold_params_cmd));
+
+	return ath6kl_wmi_cmd_send(wmi, skb, WMI_RSSI_THRESHOLD_PARAMS_CMDID,
+				   NO_SYNC_WMIFLAG);
+}
+
+static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap,
+					      int len)
+{
+	struct wmi_rssi_threshold_event *reply;
+	struct wmi_rssi_threshold_params_cmd cmd;
+	struct sq_threshold_params *sq_thresh;
+	enum wmi_rssi_threshold_val new_threshold;
+	u8 upper_rssi_threshold, lower_rssi_threshold;
+	s16 rssi;
+	int ret;
+
+	if (len < sizeof(struct wmi_rssi_threshold_event))
+		return -EINVAL;
+
+	reply = (struct wmi_rssi_threshold_event *) datap;
+	new_threshold = (enum wmi_rssi_threshold_val) reply->range;
+	rssi = a_sle16_to_cpu(reply->rssi);
+
+	sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_RSSI];
+
+	/*
+	 * Identify the threshold breached and communicate that to the app.
+	 * After that install a new set of thresholds based on the signal
+	 * quality reported by the target
+	 */
+	if (new_threshold) {
+		/* Upper threshold breached */
+		if (rssi < sq_thresh->upper_threshold[0]) {
+			ath6kl_dbg(ATH6KL_DBG_WMI,
+				"spurious upper rssi threshold event: %d\n",
+				rssi);
+		} else if ((rssi < sq_thresh->upper_threshold[1]) &&
+			   (rssi >= sq_thresh->upper_threshold[0])) {
+			new_threshold = WMI_RSSI_THRESHOLD1_ABOVE;
+		} else if ((rssi < sq_thresh->upper_threshold[2]) &&
+			   (rssi >= sq_thresh->upper_threshold[1])) {
+			new_threshold = WMI_RSSI_THRESHOLD2_ABOVE;
+		} else if ((rssi < sq_thresh->upper_threshold[3]) &&
+			   (rssi >= sq_thresh->upper_threshold[2])) {
+			new_threshold = WMI_RSSI_THRESHOLD3_ABOVE;
+		} else if ((rssi < sq_thresh->upper_threshold[4]) &&
+			   (rssi >= sq_thresh->upper_threshold[3])) {
+			new_threshold = WMI_RSSI_THRESHOLD4_ABOVE;
+		} else if ((rssi < sq_thresh->upper_threshold[5]) &&
+			   (rssi >= sq_thresh->upper_threshold[4])) {
+			new_threshold = WMI_RSSI_THRESHOLD5_ABOVE;
+		} else if (rssi >= sq_thresh->upper_threshold[5]) {
+			new_threshold = WMI_RSSI_THRESHOLD6_ABOVE;
+		}
+	} else {
+		/* Lower threshold breached */
+		if (rssi > sq_thresh->lower_threshold[0]) {
+			ath6kl_dbg(ATH6KL_DBG_WMI,
+				"spurious lower rssi threshold event: %d %d\n",
+				rssi, sq_thresh->lower_threshold[0]);
+		} else if ((rssi > sq_thresh->lower_threshold[1]) &&
+			   (rssi <= sq_thresh->lower_threshold[0])) {
+			new_threshold = WMI_RSSI_THRESHOLD6_BELOW;
+		} else if ((rssi > sq_thresh->lower_threshold[2]) &&
+			   (rssi <= sq_thresh->lower_threshold[1])) {
+			new_threshold = WMI_RSSI_THRESHOLD5_BELOW;
+		} else if ((rssi > sq_thresh->lower_threshold[3]) &&
+			   (rssi <= sq_thresh->lower_threshold[2])) {
+			new_threshold = WMI_RSSI_THRESHOLD4_BELOW;
+		} else if ((rssi > sq_thresh->lower_threshold[4]) &&
+			   (rssi <= sq_thresh->lower_threshold[3])) {
+			new_threshold = WMI_RSSI_THRESHOLD3_BELOW;
+		} else if ((rssi > sq_thresh->lower_threshold[5]) &&
+			   (rssi <= sq_thresh->lower_threshold[4])) {
+			new_threshold = WMI_RSSI_THRESHOLD2_BELOW;
+		} else if (rssi <= sq_thresh->lower_threshold[5]) {
+			new_threshold = WMI_RSSI_THRESHOLD1_BELOW;
+		}
+	}
+
+	/* Calculate and install the next set of thresholds */
+	lower_rssi_threshold = ath6kl_wmi_get_lower_threshold(rssi, sq_thresh,
+				       sq_thresh->lower_threshold_valid_count);
+	upper_rssi_threshold = ath6kl_wmi_get_upper_threshold(rssi, sq_thresh,
+				       sq_thresh->upper_threshold_valid_count);
+
+	/* Issue a wmi command to install the thresholds */
+	cmd.thresh_above1_val = a_cpu_to_sle16(upper_rssi_threshold);
+	cmd.thresh_below1_val = a_cpu_to_sle16(lower_rssi_threshold);
+	cmd.weight = sq_thresh->weight;
+	cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval);
+
+	ret = ath6kl_wmi_send_rssi_threshold_params(wmi, &cmd);
+	if (ret) {
+		ath6kl_err("unable to configure rssi thresholds\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_cac_event *reply;
+	struct ieee80211_tspec_ie *ts;
+	u16 active_tsids, tsinfo;
+	u8 tsid, index;
+	u8 ts_id;
+
+	if (len < sizeof(struct wmi_cac_event))
+		return -EINVAL;
+
+	reply = (struct wmi_cac_event *) datap;
+
+	if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) &&
+	    (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) {
+
+		ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
+		tsinfo = le16_to_cpu(ts->tsinfo);
+		tsid = (tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
+			IEEE80211_WMM_IE_TSPEC_TID_MASK;
+
+		ath6kl_wmi_delete_pstream_cmd(wmi, reply->ac, tsid);
+	} else if (reply->cac_indication == CAC_INDICATION_NO_RESP) {
+		/*
+		 * Following assumes that there is only one outstanding
+		 * ADDTS request when this event is received
+		 */
+		spin_lock_bh(&wmi->lock);
+		active_tsids = wmi->stream_exist_for_ac[reply->ac];
+		spin_unlock_bh(&wmi->lock);
+
+		for (index = 0; index < sizeof(active_tsids) * 8; index++) {
+			if ((active_tsids >> index) & 1)
+				break;
+		}
+		if (index < (sizeof(active_tsids) * 8))
+			ath6kl_wmi_delete_pstream_cmd(wmi, reply->ac, index);
+	}
+
+	/*
+	 * Clear active tsids and Add missing handling
+	 * for delete qos stream from AP
+	 */
+	else if (reply->cac_indication == CAC_INDICATION_DELETE) {
+
+		ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
+		tsinfo = le16_to_cpu(ts->tsinfo);
+		ts_id = ((tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
+			 IEEE80211_WMM_IE_TSPEC_TID_MASK);
+
+		spin_lock_bh(&wmi->lock);
+		wmi->stream_exist_for_ac[reply->ac] &= ~(1 << ts_id);
+		active_tsids = wmi->stream_exist_for_ac[reply->ac];
+		spin_unlock_bh(&wmi->lock);
+
+		/* Indicate stream inactivity to driver layer only if all tsids
+		 * within this AC are deleted.
+		 */
+		if (!active_tsids) {
+			ath6kl_indicate_tx_activity(wmi->parent_dev, reply->ac,
+						    false);
+			wmi->fat_pipe_exist &= ~(1 << reply->ac);
+		}
+	}
+
+	return 0;
+}
+
+static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi,
+			struct wmi_snr_threshold_params_cmd *snr_cmd)
+{
+	struct sk_buff *skb;
+	struct wmi_snr_threshold_params_cmd *cmd;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_snr_threshold_params_cmd *) skb->data;
+	memcpy(cmd, snr_cmd, sizeof(struct wmi_snr_threshold_params_cmd));
+
+	return ath6kl_wmi_cmd_send(wmi, skb, WMI_SNR_THRESHOLD_PARAMS_CMDID,
+				   NO_SYNC_WMIFLAG);
+}
+
+static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap,
+					     int len)
+{
+	struct wmi_snr_threshold_event *reply;
+	struct sq_threshold_params *sq_thresh;
+	struct wmi_snr_threshold_params_cmd cmd;
+	enum wmi_snr_threshold_val new_threshold;
+	u8 upper_snr_threshold, lower_snr_threshold;
+	s16 snr;
+	int ret;
+
+	if (len < sizeof(struct wmi_snr_threshold_event))
+		return -EINVAL;
+
+	reply = (struct wmi_snr_threshold_event *) datap;
+
+	new_threshold = (enum wmi_snr_threshold_val) reply->range;
+	snr = reply->snr;
+
+	sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_SNR];
+
+	/*
+	 * Identify the threshold breached and communicate that to the app.
+	 * After that install a new set of thresholds based on the signal
+	 * quality reported by the target.
+	 */
+	if (new_threshold) {
+		/* Upper threshold breached */
+		if (snr < sq_thresh->upper_threshold[0]) {
+			ath6kl_dbg(ATH6KL_DBG_WMI,
+				"spurious upper snr threshold event: %d\n",
+				snr);
+		} else if ((snr < sq_thresh->upper_threshold[1]) &&
+			   (snr >= sq_thresh->upper_threshold[0])) {
+			new_threshold = WMI_SNR_THRESHOLD1_ABOVE;
+		} else if ((snr < sq_thresh->upper_threshold[2]) &&
+			   (snr >= sq_thresh->upper_threshold[1])) {
+			new_threshold = WMI_SNR_THRESHOLD2_ABOVE;
+		} else if ((snr < sq_thresh->upper_threshold[3]) &&
+			   (snr >= sq_thresh->upper_threshold[2])) {
+			new_threshold = WMI_SNR_THRESHOLD3_ABOVE;
+		} else if (snr >= sq_thresh->upper_threshold[3]) {
+			new_threshold = WMI_SNR_THRESHOLD4_ABOVE;
+		}
+	} else {
+		/* Lower threshold breached */
+		if (snr > sq_thresh->lower_threshold[0]) {
+			ath6kl_dbg(ATH6KL_DBG_WMI,
+				"spurious lower snr threshold event: %d\n",
+				sq_thresh->lower_threshold[0]);
+		} else if ((snr > sq_thresh->lower_threshold[1]) &&
+			   (snr <= sq_thresh->lower_threshold[0])) {
+			new_threshold = WMI_SNR_THRESHOLD4_BELOW;
+		} else if ((snr > sq_thresh->lower_threshold[2]) &&
+			   (snr <= sq_thresh->lower_threshold[1])) {
+			new_threshold = WMI_SNR_THRESHOLD3_BELOW;
+		} else if ((snr > sq_thresh->lower_threshold[3]) &&
+			   (snr <= sq_thresh->lower_threshold[2])) {
+			new_threshold = WMI_SNR_THRESHOLD2_BELOW;
+		} else if (snr <= sq_thresh->lower_threshold[3]) {
+			new_threshold = WMI_SNR_THRESHOLD1_BELOW;
+		}
+	}
+
+	/* Calculate and install the next set of thresholds */
+	lower_snr_threshold = ath6kl_wmi_get_lower_threshold(snr, sq_thresh,
+				       sq_thresh->lower_threshold_valid_count);
+	upper_snr_threshold = ath6kl_wmi_get_upper_threshold(snr, sq_thresh,
+				       sq_thresh->upper_threshold_valid_count);
+
+	/* Issue a wmi command to install the thresholds */
+	cmd.thresh_above1_val = upper_snr_threshold;
+	cmd.thresh_below1_val = lower_snr_threshold;
+	cmd.weight = sq_thresh->weight;
+	cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval);
+
+	ath6kl_dbg(ATH6KL_DBG_WMI,
+		   "snr: %d, threshold: %d, lower: %d, upper: %d\n",
+		   snr, new_threshold,
+		   lower_snr_threshold, upper_snr_threshold);
+
+	ret = ath6kl_wmi_send_snr_threshold_params(wmi, &cmd);
+	if (ret) {
+		ath6kl_err("unable to configure snr threshold\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int ath6kl_wmi_aplist_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	u16 ap_info_entry_size;
+	struct wmi_aplist_event *ev = (struct wmi_aplist_event *) datap;
+	struct wmi_ap_info_v1 *ap_info_v1;
+	u8 index;
+
+	if (len < sizeof(struct wmi_aplist_event) ||
+	    ev->ap_list_ver != APLIST_VER1)
+		return -EINVAL;
+
+	ap_info_entry_size = sizeof(struct wmi_ap_info_v1);
+	ap_info_v1 = (struct wmi_ap_info_v1 *) ev->ap_list;
+
+	ath6kl_dbg(ATH6KL_DBG_WMI,
+		   "number of APs in aplist event: %d\n", ev->num_ap);
+
+	if (len < (int) (sizeof(struct wmi_aplist_event) +
+			 (ev->num_ap - 1) * ap_info_entry_size))
+		return -EINVAL;
+
+	/* AP list version 1 contents */
+	for (index = 0; index < ev->num_ap; index++) {
+		ath6kl_dbg(ATH6KL_DBG_WMI, "AP#%d BSSID %pM Channel %d\n",
+			   index, ap_info_v1->bssid, ap_info_v1->channel);
+		ap_info_v1++;
+	}
+
+	return 0;
+}
+
+int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
+			enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag)
+{
+	struct wmi_cmd_hdr *cmd_hdr;
+	enum htc_endpoint_id ep_id = wmi->ep_id;
+	int ret;
+
+	if (WARN_ON(skb == NULL))
+		return -EINVAL;
+
+	if (sync_flag >= END_WMIFLAG) {
+		dev_kfree_skb(skb);
+		return -EINVAL;
+	}
+
+	if ((sync_flag == SYNC_BEFORE_WMIFLAG) ||
+	    (sync_flag == SYNC_BOTH_WMIFLAG)) {
+		/*
+		 * Make sure all data currently queued is transmitted before
+		 * the cmd execution.  Establish a new sync point.
+		 */
+		ath6kl_wmi_sync_point(wmi);
+	}
+
+	skb_push(skb, sizeof(struct wmi_cmd_hdr));
+
+	cmd_hdr = (struct wmi_cmd_hdr *) skb->data;
+	cmd_hdr->cmd_id = cpu_to_le16(cmd_id);
+	cmd_hdr->info1 = 0;	/* added for virtual interface */
+
+	/* Only for OPT_TX_CMD, use BE endpoint. */
+	if (cmd_id == WMI_OPT_TX_FRAME_CMDID) {
+		ret = ath6kl_wmi_data_hdr_add(wmi, skb, OPT_MSGTYPE,
+					      false, false, 0, NULL);
+		if (ret) {
+			dev_kfree_skb(skb);
+			return ret;
+		}
+		ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, WMM_AC_BE);
+	}
+
+	ath6kl_control_tx(wmi->parent_dev, skb, ep_id);
+
+	if ((sync_flag == SYNC_AFTER_WMIFLAG) ||
+	    (sync_flag == SYNC_BOTH_WMIFLAG)) {
+		/*
+		 * Make sure all new data queued waits for the command to
+		 * execute. Establish a new sync point.
+		 */
+		ath6kl_wmi_sync_point(wmi);
+	}
+
+	return 0;
+}
+
+int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type,
+			   enum dot11_auth_mode dot11_auth_mode,
+			   enum auth_mode auth_mode,
+			   enum crypto_type pairwise_crypto,
+			   u8 pairwise_crypto_len,
+			   enum crypto_type group_crypto,
+			   u8 group_crypto_len, int ssid_len, u8 *ssid,
+			   u8 *bssid, u16 channel, u32 ctrl_flags)
+{
+	struct sk_buff *skb;
+	struct wmi_connect_cmd *cc;
+	int ret;
+
+	wmi->traffic_class = 100;
+
+	if ((pairwise_crypto == NONE_CRYPT) && (group_crypto != NONE_CRYPT))
+		return -EINVAL;
+
+	if ((pairwise_crypto != NONE_CRYPT) && (group_crypto == NONE_CRYPT))
+		return -EINVAL;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_connect_cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cc = (struct wmi_connect_cmd *) skb->data;
+
+	if (ssid_len)
+		memcpy(cc->ssid, ssid, ssid_len);
+
+	cc->ssid_len = ssid_len;
+	cc->nw_type = nw_type;
+	cc->dot11_auth_mode = dot11_auth_mode;
+	cc->auth_mode = auth_mode;
+	cc->prwise_crypto_type = pairwise_crypto;
+	cc->prwise_crypto_len = pairwise_crypto_len;
+	cc->grp_crypto_type = group_crypto;
+	cc->grp_crypto_len = group_crypto_len;
+	cc->ch = cpu_to_le16(channel);
+	cc->ctrl_flags = cpu_to_le32(ctrl_flags);
+
+	if (bssid != NULL)
+		memcpy(cc->bssid, bssid, ETH_ALEN);
+
+	wmi->pair_crypto_type = pairwise_crypto;
+	wmi->grp_crypto_type = group_crypto;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_CONNECT_CMDID, NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel)
+{
+	struct sk_buff *skb;
+	struct wmi_reconnect_cmd *cc;
+	int ret;
+
+	wmi->traffic_class = 100;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_reconnect_cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cc = (struct wmi_reconnect_cmd *) skb->data;
+	cc->channel = cpu_to_le16(channel);
+
+	if (bssid != NULL)
+		memcpy(cc->bssid, bssid, ETH_ALEN);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_RECONNECT_CMDID,
+				  NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+int ath6kl_wmi_disconnect_cmd(struct wmi *wmi)
+{
+	int ret;
+
+	wmi->traffic_class = 100;
+
+	/* Disconnect command does not need to do a SYNC before. */
+	ret = ath6kl_wmi_simple_cmd(wmi, WMI_DISCONNECT_CMDID);
+
+	return ret;
+}
+
+int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
+			     u32 force_fgscan, u32 is_legacy,
+			     u32 home_dwell_time, u32 force_scan_interval,
+			     s8 num_chan, u16 *ch_list)
+{
+	struct sk_buff *skb;
+	struct wmi_start_scan_cmd *sc;
+	s8 size;
+	int ret;
+
+	size = sizeof(struct wmi_start_scan_cmd);
+
+	if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN))
+		return -EINVAL;
+
+	if (num_chan > WMI_MAX_CHANNELS)
+		return -EINVAL;
+
+	if (num_chan)
+		size += sizeof(u16) * (num_chan - 1);
+
+	skb = ath6kl_wmi_get_new_buf(size);
+	if (!skb)
+		return -ENOMEM;
+
+	sc = (struct wmi_start_scan_cmd *) skb->data;
+	sc->scan_type = scan_type;
+	sc->force_fg_scan = cpu_to_le32(force_fgscan);
+	sc->is_legacy = cpu_to_le32(is_legacy);
+	sc->home_dwell_time = cpu_to_le32(home_dwell_time);
+	sc->force_scan_intvl = cpu_to_le32(force_scan_interval);
+	sc->num_ch = num_chan;
+
+	if (num_chan)
+		memcpy(sc->ch_list, ch_list, num_chan * sizeof(u16));
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_START_SCAN_CMDID,
+				  NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u16 fg_start_sec,
+			      u16 fg_end_sec, u16 bg_sec,
+			      u16 minact_chdw_msec, u16 maxact_chdw_msec,
+			      u16 pas_chdw_msec, u8 short_scan_ratio,
+			      u8 scan_ctrl_flag, u32 max_dfsch_act_time,
+			      u16 maxact_scan_per_ssid)
+{
+	struct sk_buff *skb;
+	struct wmi_scan_params_cmd *sc;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*sc));
+	if (!skb)
+		return -ENOMEM;
+
+	sc = (struct wmi_scan_params_cmd *) skb->data;
+	sc->fg_start_period = cpu_to_le16(fg_start_sec);
+	sc->fg_end_period = cpu_to_le16(fg_end_sec);
+	sc->bg_period = cpu_to_le16(bg_sec);
+	sc->minact_chdwell_time = cpu_to_le16(minact_chdw_msec);
+	sc->maxact_chdwell_time = cpu_to_le16(maxact_chdw_msec);
+	sc->pas_chdwell_time = cpu_to_le16(pas_chdw_msec);
+	sc->short_scan_ratio = short_scan_ratio;
+	sc->scan_ctrl_flags = scan_ctrl_flag;
+	sc->max_dfsch_act_time = cpu_to_le32(max_dfsch_act_time);
+	sc->maxact_scan_per_ssid = cpu_to_le16(maxact_scan_per_ssid);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_SCAN_PARAMS_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 filter, u32 ie_mask)
+{
+	struct sk_buff *skb;
+	struct wmi_bss_filter_cmd *cmd;
+	int ret;
+
+	if (filter >= LAST_BSS_FILTER)
+		return -EINVAL;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_bss_filter_cmd *) skb->data;
+	cmd->bss_filter = filter;
+	cmd->ie_mask = cpu_to_le32(ie_mask);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_BSS_FILTER_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 index, u8 flag,
+			      u8 ssid_len, u8 *ssid)
+{
+	struct sk_buff *skb;
+	struct wmi_probed_ssid_cmd *cmd;
+	int ret;
+
+	if (index > MAX_PROBED_SSID_INDEX)
+		return -EINVAL;
+
+	if (ssid_len > sizeof(cmd->ssid))
+		return -EINVAL;
+
+	if ((flag & (DISABLE_SSID_FLAG | ANY_SSID_FLAG)) && (ssid_len > 0))
+		return -EINVAL;
+
+	if ((flag & SPECIFIC_SSID_FLAG) && !ssid_len)
+		return -EINVAL;
+
+	if (flag & SPECIFIC_SSID_FLAG)
+		wmi->is_probe_ssid = true;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_probed_ssid_cmd *) skb->data;
+	cmd->entry_index = index;
+	cmd->flag = flag;
+	cmd->ssid_len = ssid_len;
+	memcpy(cmd->ssid, ssid, ssid_len);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_PROBED_SSID_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u16 listen_interval,
+				  u16 listen_beacons)
+{
+	struct sk_buff *skb;
+	struct wmi_listen_int_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_listen_int_cmd *) skb->data;
+	cmd->listen_intvl = cpu_to_le16(listen_interval);
+	cmd->num_beacons = cpu_to_le16(listen_beacons);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_LISTEN_INT_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 pwr_mode)
+{
+	struct sk_buff *skb;
+	struct wmi_power_mode_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_power_mode_cmd *) skb->data;
+	cmd->pwr_mode = pwr_mode;
+	wmi->pwr_mode = pwr_mode;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_POWER_MODE_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u16 idle_period,
+			    u16 ps_poll_num, u16 dtim_policy,
+			    u16 tx_wakeup_policy, u16 num_tx_to_wakeup,
+			    u16 ps_fail_event_policy)
+{
+	struct sk_buff *skb;
+	struct wmi_power_params_cmd *pm;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*pm));
+	if (!skb)
+		return -ENOMEM;
+
+	pm = (struct wmi_power_params_cmd *)skb->data;
+	pm->idle_period = cpu_to_le16(idle_period);
+	pm->pspoll_number = cpu_to_le16(ps_poll_num);
+	pm->dtim_policy = cpu_to_le16(dtim_policy);
+	pm->tx_wakeup_policy = cpu_to_le16(tx_wakeup_policy);
+	pm->num_tx_to_wakeup = cpu_to_le16(num_tx_to_wakeup);
+	pm->ps_fail_event_policy = cpu_to_le16(ps_fail_event_policy);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_POWER_PARAMS_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 timeout)
+{
+	struct sk_buff *skb;
+	struct wmi_disc_timeout_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_disc_timeout_cmd *) skb->data;
+	cmd->discon_timeout = timeout;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_DISC_TIMEOUT_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
+			  enum crypto_type key_type,
+			  u8 key_usage, u8 key_len,
+			  u8 *key_rsc, u8 *key_material,
+			  u8 key_op_ctrl, u8 *mac_addr,
+			  enum wmi_sync_flag sync_flag)
+{
+	struct sk_buff *skb;
+	struct wmi_add_cipher_key_cmd *cmd;
+	int ret;
+
+	if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) ||
+	    (key_material == NULL))
+		return -EINVAL;
+
+	if ((WEP_CRYPT != key_type) && (NULL == key_rsc))
+		return -EINVAL;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_add_cipher_key_cmd *) skb->data;
+	cmd->key_index = key_index;
+	cmd->key_type = key_type;
+	cmd->key_usage = key_usage;
+	cmd->key_len = key_len;
+	memcpy(cmd->key, key_material, key_len);
+
+	if (key_rsc != NULL)
+		memcpy(cmd->key_rsc, key_rsc, sizeof(cmd->key_rsc));
+
+	cmd->key_op_ctrl = key_op_ctrl;
+
+	if (mac_addr)
+		memcpy(cmd->key_mac_addr, mac_addr, ETH_ALEN);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_ADD_CIPHER_KEY_CMDID,
+				  sync_flag);
+
+	return ret;
+}
+
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 *krk)
+{
+	struct sk_buff *skb;
+	struct wmi_add_krk_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_add_krk_cmd *) skb->data;
+	memcpy(cmd->krk, krk, WMI_KRK_LEN);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_ADD_KRK_CMDID, NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 key_index)
+{
+	struct sk_buff *skb;
+	struct wmi_delete_cipher_key_cmd *cmd;
+	int ret;
+
+	if (key_index > WMI_MAX_KEY_INDEX)
+		return -EINVAL;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_delete_cipher_key_cmd *) skb->data;
+	cmd->key_index = key_index;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_DELETE_CIPHER_KEY_CMDID,
+				  NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, const u8 *bssid,
+			    const u8 *pmkid, bool set)
+{
+	struct sk_buff *skb;
+	struct wmi_setpmkid_cmd *cmd;
+	int ret;
+
+	if (bssid == NULL)
+		return -EINVAL;
+
+	if (set && pmkid == NULL)
+		return -EINVAL;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_setpmkid_cmd *) skb->data;
+	memcpy(cmd->bssid, bssid, ETH_ALEN);
+	if (set) {
+		memcpy(cmd->pmkid, pmkid, sizeof(cmd->pmkid));
+		cmd->enable = PMKID_ENABLE;
+	} else {
+		memset(cmd->pmkid, 0, sizeof(cmd->pmkid));
+		cmd->enable = PMKID_DISABLE;
+	}
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_PMKID_CMDID,
+				  NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+static int ath6kl_wmi_data_sync_send(struct wmi *wmi, struct sk_buff *skb,
+			      enum htc_endpoint_id ep_id)
+{
+	struct wmi_data_hdr *data_hdr;
+	int ret;
+
+	if (WARN_ON(skb == NULL || ep_id == wmi->ep_id))
+		return -EINVAL;
+
+	skb_push(skb, sizeof(struct wmi_data_hdr));
+
+	data_hdr = (struct wmi_data_hdr *) skb->data;
+	data_hdr->info = SYNC_MSGTYPE << WMI_DATA_HDR_MSG_TYPE_SHIFT;
+	data_hdr->info3 = 0;
+
+	ret = ath6kl_control_tx(wmi->parent_dev, skb, ep_id);
+
+	return ret;
+}
+
+static int ath6kl_wmi_sync_point(struct wmi *wmi)
+{
+	struct sk_buff *skb;
+	struct wmi_sync_cmd *cmd;
+	struct wmi_data_sync_bufs data_sync_bufs[WMM_NUM_AC];
+	enum htc_endpoint_id ep_id;
+	u8 index, num_pri_streams = 0;
+	int ret = 0;
+
+	memset(data_sync_bufs, 0, sizeof(data_sync_bufs));
+
+	spin_lock_bh(&wmi->lock);
+
+	for (index = 0; index < WMM_NUM_AC; index++) {
+		if (wmi->fat_pipe_exist & (1 << index)) {
+			num_pri_streams++;
+			data_sync_bufs[num_pri_streams - 1].traffic_class =
+			    index;
+		}
+	}
+
+	spin_unlock_bh(&wmi->lock);
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb) {
+		ret = -ENOMEM;
+		goto free_skb;
+	}
+
+	cmd = (struct wmi_sync_cmd *) skb->data;
+
+	/*
+	 * In the SYNC cmd sent on the control Ep, send a bitmap
+	 * of the data eps on which the Data Sync will be sent
+	 */
+	cmd->data_sync_map = wmi->fat_pipe_exist;
+
+	for (index = 0; index < num_pri_streams; index++) {
+		data_sync_bufs[index].skb = ath6kl_buf_alloc(0);
+		if (data_sync_bufs[index].skb == NULL) {
+			ret = -ENOMEM;
+			break;
+		}
+	}
+
+	/*
+	 * If buffer allocation for any of the dataSync fails,
+	 * then do not send the Synchronize cmd on the control ep
+	 */
+	if (ret)
+		goto free_skb;
+
+	/*
+	 * Send sync cmd followed by sync data messages on all
+	 * endpoints being used
+	 */
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SYNCHRONIZE_CMDID,
+				  NO_SYNC_WMIFLAG);
+
+	if (ret)
+		goto free_skb;
+
+	/* cmd buffer sent, we no longer own it */
+	skb = NULL;
+
+	for (index = 0; index < num_pri_streams; index++) {
+
+		if (WARN_ON(!data_sync_bufs[index].skb))
+			break;
+
+		ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev,
+					       data_sync_bufs[index].
+					       traffic_class);
+		ret =
+		    ath6kl_wmi_data_sync_send(wmi, data_sync_bufs[index].skb,
+					      ep_id);
+
+		if (ret)
+			break;
+
+		data_sync_bufs[index].skb = NULL;
+	}
+
+free_skb:
+	/* free up any resources left over (possibly due to an error) */
+	if (skb)
+		dev_kfree_skb(skb);
+
+	for (index = 0; index < num_pri_streams; index++) {
+		if (data_sync_bufs[index].skb != NULL) {
+			dev_kfree_skb((struct sk_buff *)data_sync_bufs[index].
+				      skb);
+		}
+	}
+
+	return ret;
+}
+
+int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi,
+				  struct wmi_create_pstream_cmd *params)
+{
+	struct sk_buff *skb;
+	struct wmi_create_pstream_cmd *cmd;
+	u8 fatpipe_exist_for_ac = 0;
+	s32 min_phy = 0;
+	s32 nominal_phy = 0;
+	int ret;
+
+	if (!((params->user_pri < 8) &&
+	      (params->user_pri <= 0x7) &&
+	      (up_to_ac[params->user_pri & 0x7] == params->traffic_class) &&
+	      (params->traffic_direc == UPLINK_TRAFFIC ||
+	       params->traffic_direc == DNLINK_TRAFFIC ||
+	       params->traffic_direc == BIDIR_TRAFFIC) &&
+	      (params->traffic_type == TRAFFIC_TYPE_APERIODIC ||
+	       params->traffic_type == TRAFFIC_TYPE_PERIODIC) &&
+	      (params->voice_psc_cap == DISABLE_FOR_THIS_AC ||
+	       params->voice_psc_cap == ENABLE_FOR_THIS_AC ||
+	       params->voice_psc_cap == ENABLE_FOR_ALL_AC) &&
+	      (params->tsid == WMI_IMPLICIT_PSTREAM ||
+	       params->tsid <= WMI_MAX_THINSTREAM))) {
+		return -EINVAL;
+	}
+
+	/*
+	 * Check nominal PHY rate is >= minimalPHY,
+	 * so that DUT can allow TSRS IE
+	 */
+
+	/* Get the physical rate (units of bps) */
+	min_phy = ((le32_to_cpu(params->min_phy_rate) / 1000) / 1000);
+
+	/* Check minimal phy < nominal phy rate */
+	if (params->nominal_phy >= min_phy) {
+		/* unit of 500 kbps */
+		nominal_phy = (params->nominal_phy * 1000) / 500;
+		ath6kl_dbg(ATH6KL_DBG_WMI,
+			   "TSRS IE enabled::MinPhy %x->NominalPhy ===> %x\n",
+			   min_phy, nominal_phy);
+
+		params->nominal_phy = nominal_phy;
+	} else {
+		params->nominal_phy = 0;
+	}
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	ath6kl_dbg(ATH6KL_DBG_WMI,
+		   "sending create_pstream_cmd: ac=%d  tsid:%d\n",
+		   params->traffic_class, params->tsid);
+
+	cmd = (struct wmi_create_pstream_cmd *) skb->data;
+	memcpy(cmd, params, sizeof(*cmd));
+
+	/* This is an implicitly created Fat pipe */
+	if ((u32) params->tsid == (u32) WMI_IMPLICIT_PSTREAM) {
+		spin_lock_bh(&wmi->lock);
+		fatpipe_exist_for_ac = (wmi->fat_pipe_exist &
+					(1 << params->traffic_class));
+		wmi->fat_pipe_exist |= (1 << params->traffic_class);
+		spin_unlock_bh(&wmi->lock);
+	} else {
+		/* explicitly created thin stream within a fat pipe */
+		spin_lock_bh(&wmi->lock);
+		fatpipe_exist_for_ac = (wmi->fat_pipe_exist &
+					(1 << params->traffic_class));
+		wmi->stream_exist_for_ac[params->traffic_class] |=
+		    (1 << params->tsid);
+		/*
+		 * If a thinstream becomes active, the fat pipe automatically
+		 * becomes active
+		 */
+		wmi->fat_pipe_exist |= (1 << params->traffic_class);
+		spin_unlock_bh(&wmi->lock);
+	}
+
+	/*
+	 * Indicate activty change to driver layer only if this is the
+	 * first TSID to get created in this AC explicitly or an implicit
+	 * fat pipe is getting created.
+	 */
+	if (!fatpipe_exist_for_ac)
+		ath6kl_indicate_tx_activity(wmi->parent_dev,
+					    params->traffic_class, true);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_CREATE_PSTREAM_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 traffic_class, u8 tsid)
+{
+	struct sk_buff *skb;
+	struct wmi_delete_pstream_cmd *cmd;
+	u16 active_tsids = 0;
+	int ret;
+
+	if (traffic_class > 3) {
+		ath6kl_err("invalid traffic class: %d\n", traffic_class);
+		return -EINVAL;
+	}
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_delete_pstream_cmd *) skb->data;
+	cmd->traffic_class = traffic_class;
+	cmd->tsid = tsid;
+
+	spin_lock_bh(&wmi->lock);
+	active_tsids = wmi->stream_exist_for_ac[traffic_class];
+	spin_unlock_bh(&wmi->lock);
+
+	if (!(active_tsids & (1 << tsid))) {
+		dev_kfree_skb(skb);
+		ath6kl_dbg(ATH6KL_DBG_WMI,
+			   "TSID %d doesn't exist for traffic class: %d\n",
+			   tsid, traffic_class);
+		return -ENODATA;
+	}
+
+	ath6kl_dbg(ATH6KL_DBG_WMI,
+		   "sending delete_pstream_cmd: traffic class: %d tsid=%d\n",
+		   traffic_class, tsid);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_DELETE_PSTREAM_CMDID,
+				  SYNC_BEFORE_WMIFLAG);
+
+	spin_lock_bh(&wmi->lock);
+	wmi->stream_exist_for_ac[traffic_class] &= ~(1 << tsid);
+	active_tsids = wmi->stream_exist_for_ac[traffic_class];
+	spin_unlock_bh(&wmi->lock);
+
+	/*
+	 * Indicate stream inactivity to driver layer only if all tsids
+	 * within this AC are deleted.
+	 */
+	if (!active_tsids) {
+		ath6kl_indicate_tx_activity(wmi->parent_dev,
+					    traffic_class, false);
+		wmi->fat_pipe_exist &= ~(1 << traffic_class);
+	}
+
+	return ret;
+}
+
+int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd)
+{
+	struct sk_buff *skb;
+	struct wmi_set_ip_cmd *cmd;
+	int ret;
+
+	/* Multicast address are not valid */
+	if ((*((u8 *) &ip_cmd->ips[0]) >= 0xE0) ||
+	    (*((u8 *) &ip_cmd->ips[1]) >= 0xE0))
+		return -EINVAL;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_ip_cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_set_ip_cmd *) skb->data;
+	memcpy(cmd, ip_cmd, sizeof(struct wmi_set_ip_cmd));
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_IP_CMDID, NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+static int ath6kl_wmi_get_wow_list_event_rx(struct wmi *wmi, u8 * datap,
+					    int len)
+{
+	if (len < sizeof(struct wmi_get_wow_list_reply))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ath6kl_wmi_cmd_send_xtnd(struct wmi *wmi, struct sk_buff *skb,
+				    enum wmix_command_id cmd_id,
+				    enum wmi_sync_flag sync_flag)
+{
+	struct wmix_cmd_hdr *cmd_hdr;
+	int ret;
+
+	skb_push(skb, sizeof(struct wmix_cmd_hdr));
+
+	cmd_hdr = (struct wmix_cmd_hdr *) skb->data;
+	cmd_hdr->cmd_id = cpu_to_le32(cmd_id);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_EXTENSION_CMDID, sync_flag);
+
+	return ret;
+}
+
+int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source)
+{
+	struct sk_buff *skb;
+	struct wmix_hb_challenge_resp_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmix_hb_challenge_resp_cmd *) skb->data;
+	cmd->cookie = cpu_to_le32(cookie);
+	cmd->source = cpu_to_le32(source);
+
+	ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_HB_CHALLENGE_RESP_CMDID,
+				       NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_get_stats_cmd(struct wmi *wmi)
+{
+	return ath6kl_wmi_simple_cmd(wmi, WMI_GET_STATISTICS_CMDID);
+}
+
+int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 dbM)
+{
+	struct sk_buff *skb;
+	struct wmi_set_tx_pwr_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_tx_pwr_cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_set_tx_pwr_cmd *) skb->data;
+	cmd->dbM = dbM;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_TX_PWR_CMDID,
+				  NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi)
+{
+	return ath6kl_wmi_simple_cmd(wmi, WMI_GET_TX_PWR_CMDID);
+}
+
+int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status, u8 preamble_policy)
+{
+	struct sk_buff *skb;
+	struct wmi_set_lpreamble_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_lpreamble_cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_set_lpreamble_cmd *) skb->data;
+	cmd->status = status;
+	cmd->preamble_policy = preamble_policy;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_LPREAMBLE_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u16 threshold)
+{
+	struct sk_buff *skb;
+	struct wmi_set_rts_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_rts_cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_set_rts_cmd *) skb->data;
+	cmd->threshold = cpu_to_le16(threshold);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_RTS_CMDID, NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg)
+{
+	struct sk_buff *skb;
+	struct wmi_set_wmm_txop_cmd *cmd;
+	int ret;
+
+	if (!((cfg == WMI_TXOP_DISABLED) || (cfg == WMI_TXOP_ENABLED)))
+		return -EINVAL;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_wmm_txop_cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_set_wmm_txop_cmd *) skb->data;
+	cmd->txop_enable = cfg;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_WMM_TXOP_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl)
+{
+	struct sk_buff *skb;
+	struct wmi_set_keepalive_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_set_keepalive_cmd *) skb->data;
+	cmd->keep_alive_intvl = keep_alive_intvl;
+	wmi->keep_alive_intvl = keep_alive_intvl;
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_KEEPALIVE_CMDID,
+				  NO_SYNC_WMIFLAG);
+	return ret;
+}
+
+s32 ath6kl_wmi_get_rate(s8 rate_index)
+{
+	if (rate_index == RATE_AUTO)
+		return 0;
+
+	return wmi_rate_tbl[(u32) rate_index][0];
+}
+
+void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss)
+{
+	if (bss)
+		wlan_node_return(&wmi->parent_dev->scan_table, bss);
+}
+
+struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 * ssid,
+				      u32 ssid_len, bool is_wpa2,
+				      bool match_ssid)
+{
+	struct bss *node = NULL;
+
+	node = wlan_find_ssid_node(&wmi->parent_dev->scan_table, ssid,
+				  ssid_len, is_wpa2, match_ssid);
+	return node;
+}
+
+struct bss *ath6kl_wmi_find_node(struct wmi *wmi, const u8 * mac_addr)
+{
+	struct bss *ni = NULL;
+
+	ni = wlan_find_node(&wmi->parent_dev->scan_table, mac_addr);
+
+	return ni;
+}
+
+void ath6kl_wmi_node_free(struct wmi *wmi, const u8 * mac_addr)
+{
+	struct bss *ni = NULL;
+
+	ni = wlan_find_node(&wmi->parent_dev->scan_table, mac_addr);
+	if (ni != NULL)
+		wlan_node_reclaim(&wmi->parent_dev->scan_table, ni);
+
+	return;
+}
+
+static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
+					      u32 len)
+{
+	struct wmi_pmkid_list_reply *reply;
+	u32 expected_len;
+
+	if (len < sizeof(struct wmi_pmkid_list_reply))
+		return -EINVAL;
+
+	reply = (struct wmi_pmkid_list_reply *)datap;
+	expected_len = sizeof(reply->num_pmkid) +
+		le32_to_cpu(reply->num_pmkid) * WMI_PMKID_LEN;
+
+	if (len < expected_len)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ath6kl_wmi_addba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_addba_req_event *cmd = (struct wmi_addba_req_event *) datap;
+
+	aggr_recv_addba_req_evt(wmi->parent_dev, cmd->tid,
+				le16_to_cpu(cmd->st_seq_no), cmd->win_sz);
+
+	return 0;
+}
+
+static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_delba_event *cmd = (struct wmi_delba_event *) datap;
+
+	aggr_recv_delba_req_evt(wmi->parent_dev, cmd->tid);
+
+	return 0;
+}
+
+/*  AP mode functions */
+static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	struct wmi_pspoll_event *ev;
+
+	if (len < sizeof(struct wmi_pspoll_event))
+		return -EINVAL;
+
+	ev = (struct wmi_pspoll_event *) datap;
+
+	ath6kl_pspoll_event(wmi->parent_dev, le16_to_cpu(ev->aid));
+
+	return 0;
+}
+
+static int ath6kl_wmi_dtimexpiry_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+	ath6kl_dtimexpiry_event(wmi->parent_dev);
+
+	return 0;
+}
+
+int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag)
+{
+	struct sk_buff *skb;
+	struct wmi_ap_set_pvb_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_ap_set_pvb_cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_ap_set_pvb_cmd *) skb->data;
+	cmd->aid = cpu_to_le16(aid);
+	cmd->flag = cpu_to_le32(flag);
+
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_AP_SET_PVB_CMDID,
+				  NO_SYNC_WMIFLAG);
+
+	return 0;
+}
+
+int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver,
+				       bool rx_dot11_hdr, bool defrag_on_host)
+{
+	struct sk_buff *skb;
+	struct wmi_rx_frame_format_cmd *cmd;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_rx_frame_format_cmd *) skb->data;
+	cmd->dot11_hdr = rx_dot11_hdr ? 1 : 0;
+	cmd->defrag_on_host = defrag_on_host ? 1 : 0;
+	cmd->meta_ver = rx_meta_ver;
+
+	/* Delete the local aggr state, on host */
+	ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_RX_FRAME_FORMAT_CMDID,
+				  NO_SYNC_WMIFLAG);
+
+	return ret;
+}
+
+static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
+{
+	struct wmix_cmd_hdr *cmd;
+	u32 len;
+	u16 id;
+	u8 *datap;
+	int ret = 0;
+
+	if (skb->len < sizeof(struct wmix_cmd_hdr)) {
+		ath6kl_err("bad packet 1\n");
+		wmi->stat.cmd_len_err++;
+		return -EINVAL;
+	}
+
+	cmd = (struct wmix_cmd_hdr *) skb->data;
+	id = le32_to_cpu(cmd->cmd_id);
+
+	skb_pull(skb, sizeof(struct wmix_cmd_hdr));
+
+	datap = skb->data;
+	len = skb->len;
+
+	switch (id) {
+	case WMIX_HB_CHALLENGE_RESP_EVENTID:
+		break;
+	case WMIX_DBGLOG_EVENTID:
+		break;
+	default:
+		ath6kl_err("unknown cmd id 0x%x\n", id);
+		wmi->stat.cmd_id_err++;
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+/* Control Path */
+int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
+{
+	struct wmi_cmd_hdr *cmd;
+	u32 len;
+	u16 id;
+	u8 *datap;
+	int ret = 0;
+
+	if (WARN_ON(skb == NULL))
+		return -EINVAL;
+
+	if (skb->len < sizeof(struct wmi_cmd_hdr)) {
+		ath6kl_err("bad packet 1\n");
+		dev_kfree_skb(skb);
+		wmi->stat.cmd_len_err++;
+		return -EINVAL;
+	}
+
+	cmd = (struct wmi_cmd_hdr *) skb->data;
+	id = le16_to_cpu(cmd->cmd_id);
+
+	skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+
+	datap = skb->data;
+	len = skb->len;
+
+	ath6kl_dbg(ATH6KL_DBG_WMI, "%s: wmi id: %d\n", __func__, id);
+	ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "msg payload ", datap, len);
+
+	switch (id) {
+	case WMI_GET_BITRATE_CMDID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_BITRATE_CMDID\n");
+		ret = ath6kl_wmi_bitrate_reply_rx(wmi, datap, len);
+		break;
+	case WMI_GET_CHANNEL_LIST_CMDID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_CHANNEL_LIST_CMDID\n");
+		ret = ath6kl_wmi_ch_list_reply_rx(wmi, datap, len);
+		break;
+	case WMI_GET_TX_PWR_CMDID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_TX_PWR_CMDID\n");
+		ret = ath6kl_wmi_tx_pwr_reply_rx(wmi, datap, len);
+		break;
+	case WMI_READY_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_READY_EVENTID\n");
+		ret = ath6kl_wmi_ready_event_rx(wmi, datap, len);
+		break;
+	case WMI_CONNECT_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n");
+		ret = ath6kl_wmi_connect_event_rx(wmi, datap, len);
+		break;
+	case WMI_DISCONNECT_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n");
+		ret = ath6kl_wmi_disconnect_event_rx(wmi, datap, len);
+		break;
+	case WMI_PEER_NODE_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PEER_NODE_EVENTID\n");
+		ret = ath6kl_wmi_peer_node_event_rx(wmi, datap, len);
+		break;
+	case WMI_TKIP_MICERR_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n");
+		ret = ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len);
+		break;
+	case WMI_BSSINFO_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n");
+		ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(skb, datap);
+		ret = ath6kl_wmi_bssinfo_event_rx(wmi, skb->data, skb->len);
+		break;
+	case WMI_REGDOMAIN_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n");
+		break;
+	case WMI_PSTREAM_TIMEOUT_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n");
+		ret = ath6kl_wmi_pstream_timeout_event_rx(wmi, datap, len);
+		break;
+	case WMI_NEIGHBOR_REPORT_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n");
+		break;
+	case WMI_SCAN_COMPLETE_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
+		ret = ath6kl_wmi_scan_complete_rx(wmi, datap, len);
+		break;
+	case WMI_CMDERROR_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CMDERROR_EVENTID\n");
+		ret = ath6kl_wmi_error_event_rx(wmi, datap, len);
+		break;
+	case WMI_REPORT_STATISTICS_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n");
+		ret = ath6kl_wmi_stats_event_rx(wmi, datap, len);
+		break;
+	case WMI_RSSI_THRESHOLD_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RSSI_THRESHOLD_EVENTID\n");
+		ret = ath6kl_wmi_rssi_threshold_event_rx(wmi, datap, len);
+		break;
+	case WMI_ERROR_REPORT_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ERROR_REPORT_EVENTID\n");
+		break;
+	case WMI_OPT_RX_FRAME_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n");
+		ret = ath6kl_wmi_opt_frame_event_rx(wmi, datap, len);
+		break;
+	case WMI_REPORT_ROAM_TBL_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n");
+		break;
+	case WMI_EXTENSION_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_EXTENSION_EVENTID\n");
+		ret = ath6kl_wmi_control_rx_xtnd(wmi, skb);
+		break;
+	case WMI_CAC_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n");
+		ret = ath6kl_wmi_cac_event_rx(wmi, datap, len);
+		break;
+	case WMI_CHANNEL_CHANGE_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CHANNEL_CHANGE_EVENTID\n");
+		break;
+	case WMI_REPORT_ROAM_DATA_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_DATA_EVENTID\n");
+		break;
+	case WMI_GET_FIXRATES_CMDID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n");
+		ret = ath6kl_wmi_ratemask_reply_rx(wmi, datap, len);
+		break;
+	case WMI_TX_RETRY_ERR_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_RETRY_ERR_EVENTID\n");
+		break;
+	case WMI_SNR_THRESHOLD_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SNR_THRESHOLD_EVENTID\n");
+		ret = ath6kl_wmi_snr_threshold_event_rx(wmi, datap, len);
+		break;
+	case WMI_LQ_THRESHOLD_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_LQ_THRESHOLD_EVENTID\n");
+		break;
+	case WMI_APLIST_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_APLIST_EVENTID\n");
+		ret = ath6kl_wmi_aplist_event_rx(wmi, datap, len);
+		break;
+	case WMI_GET_KEEPALIVE_CMDID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_KEEPALIVE_CMDID\n");
+		ret = ath6kl_wmi_keepalive_reply_rx(wmi, datap, len);
+		break;
+	case WMI_GET_WOW_LIST_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_WOW_LIST_EVENTID\n");
+		ret = ath6kl_wmi_get_wow_list_event_rx(wmi, datap, len);
+		break;
+	case WMI_GET_PMKID_LIST_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_PMKID_LIST_EVENTID\n");
+		ret = ath6kl_wmi_get_pmkid_list_event_rx(wmi, datap, len);
+		break;
+	case WMI_PSPOLL_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n");
+		ret = ath6kl_wmi_pspoll_event_rx(wmi, datap, len);
+		break;
+	case WMI_DTIMEXPIRY_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n");
+		ret = ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len);
+		break;
+	case WMI_SET_PARAMS_REPLY_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SET_PARAMS_REPLY_EVENTID\n");
+		break;
+	case WMI_ADDBA_REQ_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n");
+		ret = ath6kl_wmi_addba_req_event_rx(wmi, datap, len);
+		break;
+	case WMI_ADDBA_RESP_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_RESP_EVENTID\n");
+		break;
+	case WMI_DELBA_REQ_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n");
+		ret = ath6kl_wmi_delba_req_event_rx(wmi, datap, len);
+		break;
+	case WMI_REPORT_BTCOEX_CONFIG_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI,
+			   "WMI_REPORT_BTCOEX_CONFIG_EVENTID\n");
+		break;
+	case WMI_REPORT_BTCOEX_STATS_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI,
+			   "WMI_REPORT_BTCOEX_STATS_EVENTID\n");
+		break;
+	case WMI_TX_COMPLETE_EVENTID:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n");
+		ret = ath6kl_wmi_tx_complete_event_rx(datap, len);
+		break;
+	default:
+		ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id);
+		wmi->stat.cmd_id_err++;
+		ret = -EINVAL;
+		break;
+	}
+
+	dev_kfree_skb(skb);
+
+	return ret;
+}
+
+static void ath6kl_wmi_qos_state_init(struct wmi *wmi)
+{
+	if (!wmi)
+		return;
+
+	spin_lock_bh(&wmi->lock);
+
+	wmi->fat_pipe_exist = 0;
+	memset(wmi->stream_exist_for_ac, 0, sizeof(wmi->stream_exist_for_ac));
+
+	spin_unlock_bh(&wmi->lock);
+}
+
+void *ath6kl_wmi_init(struct ath6kl *dev)
+{
+	struct wmi *wmi;
+
+	wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL);
+	if (!wmi)
+		return NULL;
+
+	spin_lock_init(&wmi->lock);
+
+	wmi->parent_dev = dev;
+
+	ath6kl_wmi_qos_state_init(wmi);
+
+	wmi->pwr_mode = REC_POWER;
+	wmi->phy_mode = WMI_11G_MODE;
+
+	wmi->pair_crypto_type = NONE_CRYPT;
+	wmi->grp_crypto_type = NONE_CRYPT;
+
+	wmi->ht_allowed[A_BAND_24GHZ] = 1;
+	wmi->ht_allowed[A_BAND_5GHZ] = 1;
+
+	return wmi;
+}
+
+void ath6kl_wmi_shutdown(struct wmi *wmi)
+{
+	if (!wmi)
+		return;
+
+	kfree(wmi);
+}

+ 2018 - 0
drivers/net/wireless/ath/ath6kl/wmi.h

@@ -0,0 +1,2018 @@
+/*
+ * Copyright (c) 2010-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.
+ */
+
+/*
+ * This file contains the definitions of the WMI protocol specified in the
+ * Wireless Module Interface (WMI).  It includes definitions of all the
+ * commands and events. Commands are messages from the host to the WM.
+ * Events and Replies are messages from the WM to the host.
+ */
+
+#ifndef WMI_H
+#define WMI_H
+
+#include <linux/ieee80211.h>
+
+#include "htc.h"
+
+#define HTC_PROTOCOL_VERSION		0x0002
+#define WMI_PROTOCOL_VERSION		0x0002
+#define WMI_CONTROL_MSG_MAX_LEN		256
+#define is_ethertype(type_or_len)	((type_or_len) >= 0x0600)
+
+#define IP_ETHERTYPE		0x0800
+
+#define WMI_IMPLICIT_PSTREAM	0xFF
+#define WMI_MAX_THINSTREAM	15
+
+#define SSID_IE_LEN_INDEX	13
+
+/* Host side link management data structures */
+#define SIG_QUALITY_THRESH_LVLS		6
+#define SIG_QUALITY_UPPER_THRESH_LVLS	SIG_QUALITY_THRESH_LVLS
+#define SIG_QUALITY_LOWER_THRESH_LVLS	SIG_QUALITY_THRESH_LVLS
+
+#define A_BAND_24GHZ           0
+#define A_BAND_5GHZ            1
+#define A_NUM_BANDS            2
+
+/* in ms */
+#define WMI_IMPLICIT_PSTREAM_INACTIVITY_INT 5000
+
+/*
+ * There are no signed versions of __le16 and __le32, so for a temporary
+ * solution come up with our own version. The idea is from fs/ntfs/types.h.
+ *
+ * Use a_ prefix so that it doesn't conflict if we get proper support to
+ * linux/types.h.
+ */
+typedef __s16 __bitwise a_sle16;
+typedef __s32 __bitwise a_sle32;
+
+static inline a_sle32 a_cpu_to_sle32(s32 val)
+{
+	return (__force a_sle32) cpu_to_le32(val);
+}
+
+static inline s32 a_sle32_to_cpu(a_sle32 val)
+{
+	return le32_to_cpu((__force __le32) val);
+}
+
+static inline a_sle16 a_cpu_to_sle16(s16 val)
+{
+	return (__force a_sle16) cpu_to_le16(val);
+}
+
+static inline s16 a_sle16_to_cpu(a_sle16 val)
+{
+	return le16_to_cpu((__force __le16) val);
+}
+
+struct sq_threshold_params {
+	s16 upper_threshold[SIG_QUALITY_UPPER_THRESH_LVLS];
+	s16 lower_threshold[SIG_QUALITY_LOWER_THRESH_LVLS];
+	u32 upper_threshold_valid_count;
+	u32 lower_threshold_valid_count;
+	u32 polling_interval;
+	u8 weight;
+	u8 last_rssi;
+	u8 last_rssi_poll_event;
+};
+
+struct wmi_stats {
+	u32 cmd_len_err;
+	u32 cmd_id_err;
+};
+
+struct wmi_data_sync_bufs {
+	u8 traffic_class;
+	struct sk_buff *skb;
+};
+
+/* WMM stream classes */
+#define WMM_NUM_AC  4
+#define WMM_AC_BE   0		/* best effort */
+#define WMM_AC_BK   1		/* background */
+#define WMM_AC_VI   2		/* video */
+#define WMM_AC_VO   3		/* voice */
+
+struct wmi {
+	bool ready;
+	u16 stream_exist_for_ac[WMM_NUM_AC];
+	u8 fat_pipe_exist;
+	struct ath6kl *parent_dev;
+	struct wmi_stats stat;
+	u8 pwr_mode;
+	u8 phy_mode;
+	u8 keep_alive_intvl;
+	spinlock_t lock;
+	enum htc_endpoint_id ep_id;
+	struct sq_threshold_params
+	    sq_threshld[SIGNAL_QUALITY_METRICS_NUM_MAX];
+	enum crypto_type pair_crypto_type;
+	enum crypto_type grp_crypto_type;
+	bool is_wmm_enabled;
+	u8 ht_allowed[A_NUM_BANDS];
+	u8 traffic_class;
+	bool is_probe_ssid;
+};
+
+struct host_app_area {
+	u32 wmi_protocol_ver;
+};
+
+enum wmi_msg_type {
+	DATA_MSGTYPE = 0x0,
+	CNTL_MSGTYPE,
+	SYNC_MSGTYPE,
+	OPT_MSGTYPE,
+};
+
+/*
+ * Macros for operating on WMI_DATA_HDR (info) field
+ */
+
+#define WMI_DATA_HDR_MSG_TYPE_MASK  0x03
+#define WMI_DATA_HDR_MSG_TYPE_SHIFT 0
+#define WMI_DATA_HDR_UP_MASK        0x07
+#define WMI_DATA_HDR_UP_SHIFT       2
+
+/* In AP mode, the same bit (b5) is used to indicate Power save state in
+ * the Rx dir and More data bit state in the tx direction.
+ */
+#define WMI_DATA_HDR_PS_MASK        0x1
+#define WMI_DATA_HDR_PS_SHIFT       5
+
+#define WMI_DATA_HDR_MORE_MASK      0x1
+#define WMI_DATA_HDR_MORE_SHIFT     5
+
+enum wmi_data_hdr_data_type {
+	WMI_DATA_HDR_DATA_TYPE_802_3 = 0,
+	WMI_DATA_HDR_DATA_TYPE_802_11,
+
+	/* used to be used for the PAL */
+	WMI_DATA_HDR_DATA_TYPE_ACL,
+};
+
+#define WMI_DATA_HDR_DATA_TYPE_MASK     0x3
+#define WMI_DATA_HDR_DATA_TYPE_SHIFT    6
+
+/* Macros for operating on WMI_DATA_HDR (info2) field */
+#define WMI_DATA_HDR_SEQNO_MASK     0xFFF
+#define WMI_DATA_HDR_SEQNO_SHIFT    0
+
+#define WMI_DATA_HDR_AMSDU_MASK     0x1
+#define WMI_DATA_HDR_AMSDU_SHIFT    12
+
+#define WMI_DATA_HDR_META_MASK      0x7
+#define WMI_DATA_HDR_META_SHIFT     13
+
+struct wmi_data_hdr {
+	s8 rssi;
+
+	/*
+	 * usage of 'info' field(8-bit):
+	 *
+	 *  b1:b0       - WMI_MSG_TYPE
+	 *  b4:b3:b2    - UP(tid)
+	 *  b5          - Used in AP mode.
+	 *  More-data in tx dir, PS in rx.
+	 *  b7:b6       - Dot3 header(0),
+	 *                Dot11 Header(1),
+	 *                ACL data(2)
+	 */
+	u8 info;
+
+	/*
+	 * usage of 'info2' field(16-bit):
+	 *
+	 * b11:b0       - seq_no
+	 * b12          - A-MSDU?
+	 * b15:b13      - META_DATA_VERSION 0 - 7
+	 */
+	__le16 info2;
+	__le16 info3;
+} __packed;
+
+static inline u8 wmi_data_hdr_get_up(struct wmi_data_hdr *dhdr)
+{
+	return (dhdr->info >> WMI_DATA_HDR_UP_SHIFT) & WMI_DATA_HDR_UP_MASK;
+}
+
+static inline void wmi_data_hdr_set_up(struct wmi_data_hdr *dhdr,
+				       u8 usr_pri)
+{
+	dhdr->info &= ~(WMI_DATA_HDR_UP_MASK << WMI_DATA_HDR_UP_SHIFT);
+	dhdr->info |= usr_pri << WMI_DATA_HDR_UP_SHIFT;
+}
+
+static inline u8 wmi_data_hdr_get_dot11(struct wmi_data_hdr *dhdr)
+{
+	u8 data_type;
+
+	data_type = (dhdr->info >> WMI_DATA_HDR_DATA_TYPE_SHIFT) &
+				   WMI_DATA_HDR_DATA_TYPE_MASK;
+	return (data_type == WMI_DATA_HDR_DATA_TYPE_802_11);
+}
+
+static inline u16 wmi_data_hdr_get_seqno(struct wmi_data_hdr *dhdr)
+{
+	return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_SEQNO_SHIFT) &
+				WMI_DATA_HDR_SEQNO_MASK;
+}
+
+static inline u8 wmi_data_hdr_is_amsdu(struct wmi_data_hdr *dhdr)
+{
+	return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_AMSDU_SHIFT) &
+			       WMI_DATA_HDR_AMSDU_MASK;
+}
+
+static inline u8 wmi_data_hdr_get_meta(struct wmi_data_hdr *dhdr)
+{
+	return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_META_SHIFT) &
+			       WMI_DATA_HDR_META_MASK;
+}
+
+/* Tx meta version definitions */
+#define WMI_MAX_TX_META_SZ	12
+#define WMI_META_VERSION_1	0x01
+#define WMI_META_VERSION_2	0x02
+
+struct wmi_tx_meta_v1 {
+	/* packet ID to identify the tx request */
+	u8 pkt_id;
+
+	/* rate policy to be used for the tx of this frame */
+	u8 rate_plcy_id;
+} __packed;
+
+struct wmi_tx_meta_v2 {
+	/*
+	 * Offset from start of the WMI header for csum calculation to
+	 * begin.
+	 */
+	u8 csum_start;
+
+	/* offset from start of WMI header where final csum goes */
+	u8 csum_dest;
+
+	/* no of bytes over which csum is calculated */
+	u8 csum_flags;
+} __packed;
+
+struct wmi_rx_meta_v1 {
+	u8 status;
+
+	/* rate index mapped to rate at which this packet was received. */
+	u8 rix;
+
+	/* rssi of packet */
+	u8 rssi;
+
+	/* rf channel during packet reception */
+	u8 channel;
+
+	__le16 flags;
+} __packed;
+
+struct wmi_rx_meta_v2 {
+	__le16 csum;
+
+	/* bit 0 set -partial csum valid bit 1 set -test mode */
+	u8 csum_flags;
+} __packed;
+
+/* Control Path */
+struct wmi_cmd_hdr {
+	__le16 cmd_id;
+
+	/* info1 - 16 bits
+	 * b03:b00 - id
+	 * b15:b04 - unused */
+	__le16 info1;
+
+	/* for alignment */
+	__le16 reserved;
+} __packed;
+
+/* List of WMI commands */
+enum wmi_cmd_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,
+
+	/* 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,
+	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,
+	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,
+	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,
+	WMI_SET_PARAMS_CMDID,
+	WMI_SET_MCAST_FILTER_CMDID,
+	WMI_DEL_MCAST_FILTER_CMDID,
+
+	WMI_ALLOW_AGGR_CMDID,
+	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,
+	WMI_RX_FRAME_FORMAT_CMDID,
+	WMI_SET_THIN_MODE_CMDID,
+	WMI_SET_BT_WLAN_CONN_PRECEDENCE_CMDID,
+
+	WMI_AP_SET_11BG_RATESET_CMDID,
+	WMI_SET_PMK_CMDID,
+	WMI_MCAST_FILTER_CMDID,
+
+	/* COEX CMDID AR6003 */
+	WMI_SET_BTCOEX_FE_ANT_CMDID,
+	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 commands */
+	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,
+	WMI_GET_RFKILL_MODE_CMDID,
+
+	/* ACS command, consists of sub-commands */
+	WMI_ACS_CTRL_CMDID,
+
+	/* Ultra low power store / recall commands */
+	WMI_STORERECALL_CONFIGURE_CMDID,
+	WMI_STORERECALL_RECALL_CMDID,
+	WMI_STORERECALL_HOST_READY_CMDID,
+	WMI_FORCE_TARGET_ASSERT_CMDID,
+	WMI_SET_EXCESS_TX_RETRY_THRES_CMDID,
+};
+
+/* WMI_CONNECT_CMDID  */
+enum network_type {
+	INFRA_NETWORK = 0x01,
+	ADHOC_NETWORK = 0x02,
+	ADHOC_CREATOR = 0x04,
+	AP_NETWORK = 0x10,
+};
+
+enum dot11_auth_mode {
+	OPEN_AUTH = 0x01,
+	SHARED_AUTH = 0x02,
+
+	/* different from IEEE_AUTH_MODE definitions */
+	LEAP_AUTH = 0x04,
+};
+
+enum {
+	AUTH_IDLE,
+	AUTH_OPEN_IN_PROGRESS,
+};
+
+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,
+};
+
+#define WMI_MIN_CRYPTO_TYPE NONE_CRYPT
+#define WMI_MAX_CRYPTO_TYPE (AES_CRYPT + 1)
+
+#define WMI_MIN_KEY_INDEX   0
+#define WMI_MAX_KEY_INDEX   3
+
+#define WMI_MAX_KEY_LEN     32
+
+/*
+ * NB: these values are ordered carefully; there are lots of
+ * of implications in any reordering.  In particular beware
+ * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ */
+#define ATH6KL_CIPHER_WEP            0
+#define ATH6KL_CIPHER_TKIP           1
+#define ATH6KL_CIPHER_AES_OCB        2
+#define ATH6KL_CIPHER_AES_CCM        3
+#define ATH6KL_CIPHER_CKIP           5
+#define ATH6KL_CIPHER_CCKM_KRK       6
+#define ATH6KL_CIPHER_NONE           7 /* pseudo value */
+
+/*
+ * 802.11 rate set.
+ */
+#define ATH6KL_RATE_MAXSIZE  15	/* max rates we'll handle */
+
+#define ATH_OUI_TYPE            0x01
+#define WPA_OUI_TYPE            0x01
+#define WMM_PARAM_OUI_SUBTYPE   0x01
+#define WMM_OUI_TYPE            0x02
+#define WSC_OUT_TYPE            0x04
+
+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,
+};
+
+struct wmi_connect_cmd {
+	u8 nw_type;
+	u8 dot11_auth_mode;
+	u8 auth_mode;
+	u8 prwise_crypto_type;
+	u8 prwise_crypto_len;
+	u8 grp_crypto_type;
+	u8 grp_crypto_len;
+	u8 ssid_len;
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	__le16 ch;
+	u8 bssid[ETH_ALEN];
+	__le32 ctrl_flags;
+} __packed;
+
+/* WMI_RECONNECT_CMDID */
+struct wmi_reconnect_cmd {
+	/* channel hint */
+	__le16 channel;
+
+	/* mandatory if set */
+	u8 bssid[ETH_ALEN];
+} __packed;
+
+/* WMI_ADD_CIPHER_KEY_CMDID */
+enum key_usage {
+	PAIRWISE_USAGE = 0x00,
+	GROUP_USAGE = 0x01,
+
+	/* default Tx Key - static WEP only */
+	TX_USAGE = 0x02,
+};
+
+/*
+ * Bit Flag
+ * Bit 0 - Initialise TSC - default is Initialize
+ */
+#define KEY_OP_INIT_TSC     0x01
+#define KEY_OP_INIT_RSC     0x02
+
+/* default initialise the TSC & RSC */
+#define KEY_OP_INIT_VAL     0x03
+#define KEY_OP_VALID_MASK   0x03
+
+struct wmi_add_cipher_key_cmd {
+	u8 key_index;
+	u8 key_type;
+
+	/* enum key_usage */
+	u8 key_usage;
+
+	u8 key_len;
+
+	/* key replay sequence counter */
+	u8 key_rsc[8];
+
+	u8 key[WLAN_MAX_KEY_LEN];
+
+	/* additional key control info */
+	u8 key_op_ctrl;
+
+	u8 key_mac_addr[ETH_ALEN];
+} __packed;
+
+/* WMI_DELETE_CIPHER_KEY_CMDID */
+struct wmi_delete_cipher_key_cmd {
+	u8 key_index;
+} __packed;
+
+#define WMI_KRK_LEN     16
+
+/* WMI_ADD_KRK_CMDID */
+struct wmi_add_krk_cmd {
+	u8 krk[WMI_KRK_LEN];
+} __packed;
+
+/* WMI_SETPMKID_CMDID */
+
+#define WMI_PMKID_LEN 16
+
+enum pmkid_enable_flg {
+	PMKID_DISABLE = 0,
+	PMKID_ENABLE = 1,
+};
+
+struct wmi_setpmkid_cmd {
+	u8 bssid[ETH_ALEN];
+
+	/* enum pmkid_enable_flg */
+	u8 enable;
+
+	u8 pmkid[WMI_PMKID_LEN];
+} __packed;
+
+/* WMI_START_SCAN_CMD */
+enum wmi_scan_type {
+	WMI_LONG_SCAN = 0,
+	WMI_SHORT_SCAN = 1,
+};
+
+struct wmi_start_scan_cmd {
+	__le32 force_fg_scan;
+
+	/* for legacy cisco AP compatibility */
+	__le32 is_legacy;
+
+	/* max duration in the home channel(msec) */
+	__le32 home_dwell_time;
+
+	/* time interval between scans (msec) */
+	__le32 force_scan_intvl;
+
+	/* enum wmi_scan_type */
+	u8 scan_type;
+
+	/* how many channels follow */
+	u8 num_ch;
+
+	/* channels in Mhz */
+	__le16 ch_list[1];
+} __packed;
+
+/* WMI_SET_SCAN_PARAMS_CMDID */
+#define WMI_SHORTSCANRATIO_DEFAULT      3
+
+/*
+ *  Warning: scan control flag value of 0xFF is used to disable
+ *  all flags in WMI_SCAN_PARAMS_CMD. Do not add any more
+ *  flags here
+ */
+enum wmi_scan_ctrl_flags_bits {
+
+	/* set if can scan in the connect cmd */
+	CONNECT_SCAN_CTRL_FLAGS = 0x01,
+
+	/* set if scan for the SSID it is already connected to */
+	SCAN_CONNECTED_CTRL_FLAGS = 0x02,
+
+	/* set if enable active scan */
+	ACTIVE_SCAN_CTRL_FLAGS = 0x04,
+
+	/* set if enable roam scan when bmiss and lowrssi */
+	ROAM_SCAN_CTRL_FLAGS = 0x08,
+
+	/* set if follows customer BSSINFO reporting rule */
+	REPORT_BSSINFO_CTRL_FLAGS = 0x10,
+
+	/* if disabled, target doesn't scan after a disconnect event  */
+	ENABLE_AUTO_CTRL_FLAGS = 0x20,
+
+	/*
+	 * Scan complete event with canceled status will be generated when
+	 * a scan is prempted before it gets completed.
+	 */
+	ENABLE_SCAN_ABORT_EVENT = 0x40
+};
+
+#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_scan_params_cmd {
+	  /* sec */
+	__le16 fg_start_period;
+
+	/* sec */
+	__le16 fg_end_period;
+
+	/* sec */
+	__le16 bg_period;
+
+	/* msec */
+	__le16 maxact_chdwell_time;
+
+	/* msec */
+	__le16 pas_chdwell_time;
+
+	  /* how many shorts scan for one long */
+	u8 short_scan_ratio;
+
+	u8 scan_ctrl_flags;
+
+	/* msec */
+	__le16 minact_chdwell_time;
+
+	/* max active scans per ssid */
+	__le16 maxact_scan_per_ssid;
+
+	/* msecs */
+	__le32 max_dfsch_act_time;
+} __packed;
+
+/* WMI_SET_BSS_FILTER_CMDID */
+enum wmi_bss_filter {
+	/* no beacons forwarded */
+	NONE_BSS_FILTER = 0x0,
+
+	/* all beacons forwarded */
+	ALL_BSS_FILTER,
+
+	/* only beacons matching profile */
+	PROFILE_FILTER,
+
+	/* all but beacons matching profile */
+	ALL_BUT_PROFILE_FILTER,
+
+	/* only beacons matching current BSS */
+	CURRENT_BSS_FILTER,
+
+	/* all but beacons matching BSS */
+	ALL_BUT_BSS_FILTER,
+
+	/* beacons matching probed ssid */
+	PROBED_SSID_FILTER,
+
+	/* marker only */
+	LAST_BSS_FILTER,
+};
+
+struct wmi_bss_filter_cmd {
+	/* see, enum wmi_bss_filter */
+	u8 bss_filter;
+
+	/* for alignment */
+	u8 reserved1;
+
+	/* for alignment */
+	__le16 reserved2;
+
+	__le32 ie_mask;
+} __packed;
+
+/* WMI_SET_PROBED_SSID_CMDID */
+#define MAX_PROBED_SSID_INDEX   9
+
+enum wmi_ssid_flag {
+	/* disables entry */
+	DISABLE_SSID_FLAG = 0,
+
+	/* probes specified ssid */
+	SPECIFIC_SSID_FLAG = 0x01,
+
+	/* probes for any ssid */
+	ANY_SSID_FLAG = 0x02,
+};
+
+struct wmi_probed_ssid_cmd {
+	/* 0 to MAX_PROBED_SSID_INDEX */
+	u8 entry_index;
+
+	/* see, enum wmi_ssid_flg */
+	u8 flag;
+
+	u8 ssid_len;
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+} __packed;
+
+/*
+ * WMI_SET_LISTEN_INT_CMDID
+ * The Listen interval is between 15 and 3000 TUs
+ */
+struct wmi_listen_int_cmd {
+	__le16 listen_intvl;
+	__le16 num_beacons;
+} __packed;
+
+/* WMI_SET_POWER_MODE_CMDID */
+enum wmi_power_mode {
+	REC_POWER = 0x01,
+	MAX_PERF_POWER,
+};
+
+struct wmi_power_mode_cmd {
+	/* see, enum wmi_power_mode */
+	u8 pwr_mode;
+} __packed;
+
+/*
+ * Policy to determnine whether power save failure event should be sent to
+ * host during scanning
+ */
+enum power_save_fail_event_policy {
+	SEND_POWER_SAVE_FAIL_EVENT_ALWAYS = 1,
+	IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN = 2,
+};
+
+struct wmi_power_params_cmd {
+	/* msec */
+	__le16 idle_period;
+
+	__le16 pspoll_number;
+	__le16 dtim_policy;
+	__le16 tx_wakeup_policy;
+	__le16 num_tx_to_wakeup;
+	__le16 ps_fail_event_policy;
+} __packed;
+
+/* WMI_SET_DISC_TIMEOUT_CMDID */
+struct wmi_disc_timeout_cmd {
+	/* seconds */
+	u8 discon_timeout;
+} __packed;
+
+enum dir_type {
+	UPLINK_TRAFFIC = 0,
+	DNLINK_TRAFFIC = 1,
+	BIDIR_TRAFFIC = 2,
+};
+
+enum voiceps_cap_type {
+	DISABLE_FOR_THIS_AC = 0,
+	ENABLE_FOR_THIS_AC = 1,
+	ENABLE_FOR_ALL_AC = 2,
+};
+
+enum traffic_type {
+	TRAFFIC_TYPE_APERIODIC = 0,
+	TRAFFIC_TYPE_PERIODIC = 1,
+};
+
+/* WMI_SYNCHRONIZE_CMDID */
+struct wmi_sync_cmd {
+	u8 data_sync_map;
+} __packed;
+
+/* WMI_CREATE_PSTREAM_CMDID */
+struct wmi_create_pstream_cmd {
+	/* msec */
+	__le32 min_service_int;
+
+	/* msec */
+	__le32 max_service_int;
+
+	/* msec */
+	__le32 inactivity_int;
+
+	/* msec */
+	__le32 suspension_int;
+
+	__le32 service_start_time;
+
+	/* in bps */
+	__le32 min_data_rate;
+
+	/* in bps */
+	__le32 mean_data_rate;
+
+	/* in bps */
+	__le32 peak_data_rate;
+
+	__le32 max_burst_size;
+	__le32 delay_bound;
+
+	/* in bps */
+	__le32 min_phy_rate;
+
+	__le32 sba;
+	__le32 medium_time;
+
+	/* in octects */
+	__le16 nominal_msdu;
+
+	/* in octects */
+	__le16 max_msdu;
+
+	u8 traffic_class;
+
+	/* see, enum dir_type */
+	u8 traffic_direc;
+
+	u8 rx_queue_num;
+
+	/* see, enum traffic_type */
+	u8 traffic_type;
+
+	/* see, enum voiceps_cap_type */
+	u8 voice_psc_cap;
+	u8 tsid;
+
+	/* 802.1D user priority */
+	u8 user_pri;
+
+	/* nominal phy rate */
+	u8 nominal_phy;
+} __packed;
+
+/* WMI_DELETE_PSTREAM_CMDID */
+struct wmi_delete_pstream_cmd {
+	u8 tx_queue_num;
+	u8 rx_queue_num;
+	u8 traffic_direc;
+	u8 traffic_class;
+	u8 tsid;
+} __packed;
+
+/* WMI_SET_CHANNEL_PARAMS_CMDID */
+enum wmi_phy_mode {
+	WMI_11A_MODE = 0x1,
+	WMI_11G_MODE = 0x2,
+	WMI_11AG_MODE = 0x3,
+	WMI_11B_MODE = 0x4,
+	WMI_11GONLY_MODE = 0x5,
+};
+
+#define WMI_MAX_CHANNELS        32
+
+/*
+ *  WMI_RSSI_THRESHOLD_PARAMS_CMDID
+ *  Setting the polltime to 0 would disable polling. Threshold values are
+ *  in the ascending order, and should agree to:
+ *  (lowThreshold_lowerVal < lowThreshold_upperVal < highThreshold_lowerVal
+ *   < highThreshold_upperVal)
+ */
+
+struct wmi_rssi_threshold_params_cmd {
+	/* polling time as a factor of LI */
+	__le32 poll_time;
+
+	/* lowest of upper */
+	a_sle16 thresh_above1_val;
+
+	a_sle16 thresh_above2_val;
+	a_sle16 thresh_above3_val;
+	a_sle16 thresh_above4_val;
+	a_sle16 thresh_above5_val;
+
+	/* highest of upper */
+	a_sle16 thresh_above6_val;
+
+	/* lowest of bellow */
+	a_sle16 thresh_below1_val;
+
+	a_sle16 thresh_below2_val;
+	a_sle16 thresh_below3_val;
+	a_sle16 thresh_below4_val;
+	a_sle16 thresh_below5_val;
+
+	/* highest of bellow */
+	a_sle16 thresh_below6_val;
+
+	/* "alpha" */
+	u8 weight;
+
+	u8 reserved[3];
+} __packed;
+
+/*
+ *  WMI_SNR_THRESHOLD_PARAMS_CMDID
+ *  Setting the polltime to 0 would disable polling.
+ */
+
+struct wmi_snr_threshold_params_cmd {
+	/* polling time as a factor of LI */
+	__le32 poll_time;
+
+	/* "alpha" */
+	u8 weight;
+
+	/* lowest of uppper */
+	u8 thresh_above1_val;
+
+	u8 thresh_above2_val;
+	u8 thresh_above3_val;
+
+	/* highest of upper */
+	u8 thresh_above4_val;
+
+	/* lowest of bellow */
+	u8 thresh_below1_val;
+
+	u8 thresh_below2_val;
+	u8 thresh_below3_val;
+
+	/* highest of bellow */
+	u8 thresh_below4_val;
+
+	u8 reserved[3];
+} __packed;
+
+enum wmi_preamble_policy {
+	WMI_IGNORE_BARKER_IN_ERP = 0,
+	WMI_DONOT_IGNORE_BARKER_IN_ERP
+};
+
+struct wmi_set_lpreamble_cmd {
+	u8 status;
+	u8 preamble_policy;
+} __packed;
+
+struct wmi_set_rts_cmd {
+	__le16 threshold;
+} __packed;
+
+/* WMI_SET_TX_PWR_CMDID */
+struct wmi_set_tx_pwr_cmd {
+	/* in dbM units */
+	u8 dbM;
+} __packed;
+
+struct wmi_tx_pwr_reply {
+	/* in dbM units */
+	u8 dbM;
+} __packed;
+
+struct wmi_report_sleep_state_event {
+	__le32 sleep_state;
+};
+
+enum wmi_report_sleep_status {
+	WMI_REPORT_SLEEP_STATUS_IS_DEEP_SLEEP = 0,
+	WMI_REPORT_SLEEP_STATUS_IS_AWAKE
+};
+enum target_event_report_config {
+	/* default */
+	DISCONN_EVT_IN_RECONN = 0,
+
+	NO_DISCONN_EVT_IN_RECONN
+};
+
+/* Command Replies */
+
+/* WMI_GET_CHANNEL_LIST_CMDID reply */
+struct wmi_channel_list_reply {
+	u8 reserved;
+
+	/* number of channels in reply */
+	u8 num_ch;
+
+	/* channel in Mhz */
+	__le16 ch_list[1];
+} __packed;
+
+/* 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,
+
+	/* RFKILL Events */
+	WMI_RFKILL_STATE_CHANGE_EVENTID,
+	WMI_RFKILL_GET_MODE_CMD_EVENTID,
+	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,
+
+	/* Generic ACS event */
+	WMI_ACS_EVENTID,
+	WMI_REPORT_WMM_PARAMS_EVENTID
+};
+
+struct wmi_ready_event_2 {
+	__le32 sw_version;
+	__le32 abi_version;
+	u8 mac_addr[ETH_ALEN];
+	u8 phy_cap;
+} __packed;
+
+/* Connect Event */
+struct wmi_connect_event {
+	__le16 ch;
+	u8 bssid[ETH_ALEN];
+	__le16 listen_intvl;
+	__le16 beacon_intvl;
+	__le32 nw_type;
+	u8 beacon_ie_len;
+	u8 assoc_req_len;
+	u8 assoc_resp_len;
+	u8 assoc_info[1];
+} __packed;
+
+/* Disconnect Event */
+enum wmi_disconnect_reason {
+	NO_NETWORK_AVAIL = 0x01,
+
+	/* bmiss */
+	LOST_LINK = 0x02,
+
+	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 {
+	/* reason code, see 802.11 spec. */
+	__le16 proto_reason_status;
+
+	/* set if known */
+	u8 bssid[ETH_ALEN];
+
+	/* see WMI_DISCONNECT_REASON */
+	u8 disconn_reason;
+
+	u8 assoc_resp_len;
+	u8 assoc_info[1];
+} __packed;
+
+/*
+ * BSS Info Event.
+ * Mechanism used to inform host of the presence and characteristic of
+ * wireless networks present.  Consists of bss info header followed by
+ * the beacon or probe-response frame body.  The 802.11 header is no included.
+ */
+enum wmi_bi_ftype {
+	BEACON_FTYPE = 0x1,
+	PROBERESP_FTYPE,
+	ACTION_MGMT_FTYPE,
+	PROBEREQ_FTYPE,
+};
+
+struct wmi_bss_info_hdr {
+	__le16 ch;
+
+	/* see, enum wmi_bi_ftype */
+	u8 frame_type;
+
+	u8 snr;
+	a_sle16 rssi;
+	u8 bssid[ETH_ALEN];
+	__le32 ie_mask;
+} __packed;
+
+/*
+ * BSS INFO HDR version 2.0
+ * With 6 bytes HTC header and 6 bytes of WMI header
+ * WMI_BSS_INFO_HDR cannot be accommodated in the removed 802.11 management
+ * header space.
+ * - Reduce the ie_mask to 2 bytes as only two bit flags are used
+ * - Remove rssi and compute it on the host. rssi = snr - 95
+ */
+struct wmi_bss_info_hdr2 {
+	__le16 ch;
+
+	/* see, enum wmi_bi_ftype */
+	u8 frame_type;
+
+	u8 snr;
+	u8 bssid[ETH_ALEN];
+	__le16 ie_mask;
+} __packed;
+
+/* Command Error Event */
+enum wmi_error_code {
+	INVALID_PARAM = 0x01,
+	ILLEGAL_STATE = 0x02,
+	INTERNAL_ERROR = 0x03,
+};
+
+struct wmi_cmd_error_event {
+	__le16 cmd_id;
+	u8 err_code;
+} __packed;
+
+struct wmi_pstream_timeout_event {
+	u8 tx_queue_num;
+	u8 rx_queue_num;
+	u8 traffic_direc;
+	u8 traffic_class;
+} __packed;
+
+/*
+ * The WMI_NEIGHBOR_REPORT Event is generated by the target to inform
+ * the host of BSS's it has found that matches the current profile.
+ * It can be used by the host to cache PMKs and/to initiate pre-authentication
+ * if the BSS supports it.  The first bssid is always the current associated
+ * BSS.
+ * The bssid and bssFlags information repeats according to the number
+ * or APs reported.
+ */
+enum wmi_bss_flags {
+	WMI_DEFAULT_BSS_FLAGS = 0x00,
+	WMI_PREAUTH_CAPABLE_BSS = 0x01,
+	WMI_PMKID_VALID_BSS = 0x02,
+};
+
+/* TKIP MIC Error Event */
+struct wmi_tkip_micerr_event {
+	u8 key_id;
+	u8 is_mcast;
+} __packed;
+
+/* WMI_SCAN_COMPLETE_EVENTID */
+struct wmi_scan_complete_event {
+	a_sle32 status;
+} __packed;
+
+#define MAX_OPT_DATA_LEN 1400
+
+/*
+ * Special frame receive Event.
+ * Mechanism used to inform host of the receiption of the special frames.
+ * Consists of special frame info header followed by special frame body.
+ * The 802.11 header is not included.
+ */
+struct wmi_opt_rx_info_hdr {
+	__le16 ch;
+	u8 frame_type;
+	s8 snr;
+	u8 src_addr[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+} __packed;
+
+/* Reporting statistic */
+struct tx_stats {
+	__le32 pkt;
+	__le32 byte;
+	__le32 ucast_pkt;
+	__le32 ucast_byte;
+	__le32 mcast_pkt;
+	__le32 mcast_byte;
+	__le32 bcast_pkt;
+	__le32 bcast_byte;
+	__le32 rts_success_cnt;
+	__le32 pkt_per_ac[4];
+	__le32 err_per_ac[4];
+
+	__le32 err;
+	__le32 fail_cnt;
+	__le32 retry_cnt;
+	__le32 mult_retry_cnt;
+	__le32 rts_fail_cnt;
+	a_sle32 ucast_rate;
+} __packed;
+
+struct rx_stats {
+	__le32 pkt;
+	__le32 byte;
+	__le32 ucast_pkt;
+	__le32 ucast_byte;
+	__le32 mcast_pkt;
+	__le32 mcast_byte;
+	__le32 bcast_pkt;
+	__le32 bcast_byte;
+	__le32 frgment_pkt;
+
+	__le32 err;
+	__le32 crc_err;
+	__le32 key_cache_miss;
+	__le32 decrypt_err;
+	__le32 dupl_frame;
+	a_sle32 ucast_rate;
+} __packed;
+
+struct tkip_ccmp_stats {
+	__le32 tkip_local_mic_fail;
+	__le32 tkip_cnter_measures_invoked;
+	__le32 tkip_replays;
+	__le32 tkip_fmt_err;
+	__le32 ccmp_fmt_err;
+	__le32 ccmp_replays;
+} __packed;
+
+struct pm_stats {
+	__le32 pwr_save_failure_cnt;
+	__le16 stop_tx_failure_cnt;
+	__le16 atim_tx_failure_cnt;
+	__le16 atim_rx_failure_cnt;
+	__le16 bcn_rx_failure_cnt;
+} __packed;
+
+struct cserv_stats {
+	__le32 cs_bmiss_cnt;
+	__le32 cs_low_rssi_cnt;
+	__le16 cs_connect_cnt;
+	__le16 cs_discon_cnt;
+	a_sle16 cs_ave_beacon_rssi;
+	__le16 cs_roam_count;
+	a_sle16 cs_rssi;
+	u8 cs_snr;
+	u8 cs_ave_beacon_snr;
+	u8 cs_last_roam_msec;
+} __packed;
+
+struct wlan_net_stats {
+	struct tx_stats tx;
+	struct rx_stats rx;
+	struct tkip_ccmp_stats tkip_ccmp_stats;
+} __packed;
+
+struct arp_stats {
+	__le32 arp_received;
+	__le32 arp_matched;
+	__le32 arp_replied;
+} __packed;
+
+struct wlan_wow_stats {
+	__le32 wow_pkt_dropped;
+	__le16 wow_evt_discarded;
+	u8 wow_host_pkt_wakeups;
+	u8 wow_host_evt_wakeups;
+} __packed;
+
+struct wmi_target_stats {
+	__le32 lq_val;
+	a_sle32 noise_floor_calib;
+	struct pm_stats pm_stats;
+	struct wlan_net_stats stats;
+	struct wlan_wow_stats wow_stats;
+	struct arp_stats arp_stats;
+	struct cserv_stats cserv_stats;
+} __packed;
+
+/*
+ * WMI_RSSI_THRESHOLD_EVENTID.
+ * Indicate the RSSI events to host. Events are indicated when we breach a
+ * thresold value.
+ */
+enum wmi_rssi_threshold_val {
+	WMI_RSSI_THRESHOLD1_ABOVE = 0,
+	WMI_RSSI_THRESHOLD2_ABOVE,
+	WMI_RSSI_THRESHOLD3_ABOVE,
+	WMI_RSSI_THRESHOLD4_ABOVE,
+	WMI_RSSI_THRESHOLD5_ABOVE,
+	WMI_RSSI_THRESHOLD6_ABOVE,
+	WMI_RSSI_THRESHOLD1_BELOW,
+	WMI_RSSI_THRESHOLD2_BELOW,
+	WMI_RSSI_THRESHOLD3_BELOW,
+	WMI_RSSI_THRESHOLD4_BELOW,
+	WMI_RSSI_THRESHOLD5_BELOW,
+	WMI_RSSI_THRESHOLD6_BELOW
+};
+
+struct wmi_rssi_threshold_event {
+	a_sle16 rssi;
+	u8 range;
+} __packed;
+
+enum wmi_snr_threshold_val {
+	WMI_SNR_THRESHOLD1_ABOVE = 1,
+	WMI_SNR_THRESHOLD1_BELOW,
+	WMI_SNR_THRESHOLD2_ABOVE,
+	WMI_SNR_THRESHOLD2_BELOW,
+	WMI_SNR_THRESHOLD3_ABOVE,
+	WMI_SNR_THRESHOLD3_BELOW,
+	WMI_SNR_THRESHOLD4_ABOVE,
+	WMI_SNR_THRESHOLD4_BELOW
+};
+
+struct wmi_snr_threshold_event {
+	/* see, enum wmi_snr_threshold_val */
+	u8 range;
+
+	u8 snr;
+} __packed;
+
+/* WMI_REPORT_ROAM_TBL_EVENTID */
+#define MAX_ROAM_TBL_CAND   5
+
+struct wmi_bss_roam_info {
+	a_sle32 roam_util;
+	u8 bssid[ETH_ALEN];
+	s8 rssi;
+	s8 rssidt;
+	s8 last_rssi;
+	s8 util;
+	s8 bias;
+
+	/* for alignment */
+	u8 reserved;
+} __packed;
+
+/* WMI_CAC_EVENTID */
+enum cac_indication {
+	CAC_INDICATION_ADMISSION = 0x00,
+	CAC_INDICATION_ADMISSION_RESP = 0x01,
+	CAC_INDICATION_DELETE = 0x02,
+	CAC_INDICATION_NO_RESP = 0x03,
+};
+
+#define WMM_TSPEC_IE_LEN   63
+
+struct wmi_cac_event {
+	u8 ac;
+	u8 cac_indication;
+	u8 status_code;
+	u8 tspec_suggestion[WMM_TSPEC_IE_LEN];
+} __packed;
+
+/* WMI_APLIST_EVENTID */
+
+enum aplist_ver {
+	APLIST_VER1 = 1,
+};
+
+struct wmi_ap_info_v1 {
+	u8 bssid[ETH_ALEN];
+	__le16 channel;
+} __packed;
+
+union wmi_ap_info {
+	struct wmi_ap_info_v1 ap_info_v1;
+} __packed;
+
+struct wmi_aplist_event {
+	u8 ap_list_ver;
+	u8 num_ap;
+	union wmi_ap_info ap_list[1];
+} __packed;
+
+/* Developer Commands */
+
+/*
+ * WMI_SET_BITRATE_CMDID
+ *
+ * Get bit rate cmd uses same definition as set bit rate cmd
+ */
+enum wmi_bit_rate {
+	RATE_AUTO = -1,
+	RATE_1Mb = 0,
+	RATE_2Mb = 1,
+	RATE_5_5Mb = 2,
+	RATE_11Mb = 3,
+	RATE_6Mb = 4,
+	RATE_9Mb = 5,
+	RATE_12Mb = 6,
+	RATE_18Mb = 7,
+	RATE_24Mb = 8,
+	RATE_36Mb = 9,
+	RATE_48Mb = 10,
+	RATE_54Mb = 11,
+	RATE_MCS_0_20 = 12,
+	RATE_MCS_1_20 = 13,
+	RATE_MCS_2_20 = 14,
+	RATE_MCS_3_20 = 15,
+	RATE_MCS_4_20 = 16,
+	RATE_MCS_5_20 = 17,
+	RATE_MCS_6_20 = 18,
+	RATE_MCS_7_20 = 19,
+	RATE_MCS_0_40 = 20,
+	RATE_MCS_1_40 = 21,
+	RATE_MCS_2_40 = 22,
+	RATE_MCS_3_40 = 23,
+	RATE_MCS_4_40 = 24,
+	RATE_MCS_5_40 = 25,
+	RATE_MCS_6_40 = 26,
+	RATE_MCS_7_40 = 27,
+};
+
+struct wmi_bit_rate_reply {
+	/* see, enum wmi_bit_rate */
+	s8 rate_index;
+} __packed;
+
+/*
+ * WMI_SET_FIXRATES_CMDID
+ *
+ * Get fix rates cmd uses same definition as set fix rates cmd
+ */
+struct wmi_fix_rates_reply {
+	/* see wmi_bit_rate */
+	__le32 fix_rate_mask;
+} __packed;
+
+enum roam_data_type {
+	/* get the roam time data */
+	ROAM_DATA_TIME = 1,
+};
+
+struct wmi_target_roam_time {
+	__le32 disassoc_time;
+	__le32 no_txrx_time;
+	__le32 assoc_time;
+	__le32 allow_txrx_time;
+	u8 disassoc_bssid[ETH_ALEN];
+	s8 disassoc_bss_rssi;
+	u8 assoc_bssid[ETH_ALEN];
+	s8 assoc_bss_rssi;
+} __packed;
+
+enum wmi_txop_cfg {
+	WMI_TXOP_DISABLED = 0,
+	WMI_TXOP_ENABLED
+};
+
+struct wmi_set_wmm_txop_cmd {
+	u8 txop_enable;
+} __packed;
+
+struct wmi_set_keepalive_cmd {
+	u8 keep_alive_intvl;
+} __packed;
+
+struct wmi_get_keepalive_cmd {
+	__le32 configured;
+	u8 keep_alive_intvl;
+} __packed;
+
+/* Notify the WSC registration status to the target */
+#define WSC_REG_ACTIVE     1
+#define WSC_REG_INACTIVE   0
+
+#define WOW_MAX_FILTER_LISTS	 1
+#define WOW_MAX_FILTERS_PER_LIST 4
+#define WOW_PATTERN_SIZE	 64
+#define WOW_MASK_SIZE		 64
+
+#define MAC_MAX_FILTERS_PER_LIST 4
+
+struct wow_filter {
+	u8 wow_valid_filter;
+	u8 wow_filter_id;
+	u8 wow_filter_size;
+	u8 wow_filter_offset;
+	u8 wow_filter_mask[WOW_MASK_SIZE];
+	u8 wow_filter_pattern[WOW_PATTERN_SIZE];
+} __packed;
+
+#define MAX_IP_ADDRS  2
+
+struct wmi_set_ip_cmd {
+	/* IP in network byte order */
+	__le32 ips[MAX_IP_ADDRS];
+} __packed;
+
+/* WMI_GET_WOW_LIST_CMD reply  */
+struct wmi_get_wow_list_reply {
+	/* number of patterns in reply */
+	u8 num_filters;
+
+	/* this is filter # x of total num_filters */
+	u8 this_filter_num;
+
+	u8 wow_mode;
+	u8 host_mode;
+	struct wow_filter wow_filters[1];
+} __packed;
+
+/* WMI_SET_AKMP_PARAMS_CMD */
+
+struct wmi_pmkid {
+	u8 pmkid[WMI_PMKID_LEN];
+} __packed;
+
+/* WMI_GET_PMKID_LIST_CMD  Reply */
+struct wmi_pmkid_list_reply {
+	__le32 num_pmkid;
+	u8 bssid_list[ETH_ALEN][1];
+	struct wmi_pmkid pmkid_list[1];
+} __packed;
+
+/* WMI_ADDBA_REQ_EVENTID */
+struct wmi_addba_req_event {
+	u8 tid;
+	u8 win_sz;
+	__le16 st_seq_no;
+
+	/* f/w response for ADDBA Req; OK (0) or failure (!=0) */
+	u8 status;
+} __packed;
+
+/* WMI_ADDBA_RESP_EVENTID */
+struct wmi_addba_resp_event {
+	u8 tid;
+
+	/* OK (0), failure (!=0) */
+	u8 status;
+
+	/* three values: not supported(0), 3839, 8k */
+	__le16 amsdu_sz;
+} __packed;
+
+/* WMI_DELBA_EVENTID
+ * f/w received a DELBA for peer and processed it.
+ * Host is notified of this
+ */
+struct wmi_delba_event {
+	u8 tid;
+	u8 is_peer_initiator;
+	__le16 reason_code;
+} __packed;
+
+#define PEER_NODE_JOIN_EVENT		0x00
+#define PEER_NODE_LEAVE_EVENT		0x01
+#define PEER_FIRST_NODE_JOIN_EVENT	0x10
+#define PEER_LAST_NODE_LEAVE_EVENT	0x11
+
+struct wmi_peer_node_event {
+	u8 event_code;
+	u8 peer_mac_addr[ETH_ALEN];
+} __packed;
+
+/* Transmit complete event data structure(s) */
+
+/* version 1 of tx complete msg */
+struct tx_complete_msg_v1 {
+#define TX_COMPLETE_STATUS_SUCCESS 0
+#define TX_COMPLETE_STATUS_RETRIES 1
+#define TX_COMPLETE_STATUS_NOLINK  2
+#define TX_COMPLETE_STATUS_TIMEOUT 3
+#define TX_COMPLETE_STATUS_OTHER   4
+
+	u8 status;
+
+	/* packet ID to identify parent packet */
+	u8 pkt_id;
+
+	/* rate index on successful transmission */
+	u8 rate_idx;
+
+	/* number of ACK failures in tx attempt */
+	u8 ack_failures;
+} __packed;
+
+struct wmi_tx_complete_event {
+	/* no of tx comp msgs following this struct */
+	u8 num_msg;
+
+	/* length in bytes for each individual msg following this struct */
+	u8 msg_len;
+
+	/* version of tx complete msg data following this struct */
+	u8 msg_type;
+
+	/* individual messages follow this header */
+	u8 reserved;
+} __packed;
+
+/*
+ * ------- AP Mode definitions --------------
+ */
+
+/*
+ * !!! Warning !!!
+ * -Changing the following values needs compilation of both driver and firmware
+ */
+#define AP_MAX_NUM_STA          8
+
+/* Spl. AID used to set DTIM flag in the beacons */
+#define MCAST_AID               0xFF
+
+#define DEF_AP_COUNTRY_CODE     "US "
+
+/* Used with WMI_AP_SET_NUM_STA_CMDID */
+
+struct wmi_ap_set_pvb_cmd {
+	__le32 flag;
+	__le16 aid;
+} __packed;
+
+struct wmi_rx_frame_format_cmd {
+	/* version of meta data for rx packets <0 = default> (0-7 = valid) */
+	u8 meta_ver;
+
+	/*
+	 * 1 == leave .11 header intact,
+	 * 0 == replace .11 header with .3 <default>
+	 */
+	u8 dot11_hdr;
+
+	/*
+	 * 1 == defragmentation is performed by host,
+	 * 0 == performed by target <default>
+	 */
+	u8 defrag_on_host;
+
+	/* for alignment */
+	u8 reserved[1];
+} __packed;
+
+/* AP mode events */
+
+/* WMI_PS_POLL_EVENT */
+struct wmi_pspoll_event {
+	__le16 aid;
+} __packed;
+
+struct wmi_per_sta_stat {
+	__le32 tx_bytes;
+	__le32 tx_pkts;
+	__le32 tx_error;
+	__le32 tx_discard;
+	__le32 rx_bytes;
+	__le32 rx_pkts;
+	__le32 rx_error;
+	__le32 rx_discard;
+	__le32 aid;
+} __packed;
+
+struct wmi_ap_mode_stat {
+	__le32 action;
+	struct wmi_per_sta_stat sta[AP_MAX_NUM_STA + 1];
+} __packed;
+
+/* End of AP mode definitions */
+
+/* Extended WMI (WMIX)
+ *
+ * Extended WMIX commands are encapsulated in a WMI message with
+ * cmd=WMI_EXTENSION_CMD.
+ *
+ * Extended WMI commands are those that are needed during wireless
+ * operation, but which are not really wireless commands.  This allows,
+ * for instance, platform-specific commands.  Extended WMI commands are
+ * embedded in a WMI command message with WMI_COMMAND_ID=WMI_EXTENSION_CMDID.
+ * Extended WMI events are similarly embedded in a WMI event message with
+ * WMI_EVENT_ID=WMI_EXTENSION_EVENTID.
+ */
+struct wmix_cmd_hdr {
+	__le32 cmd_id;
+} __packed;
+
+enum wmix_command_id {
+	WMIX_DSETOPEN_REPLY_CMDID = 0x2001,
+	WMIX_DSETDATA_REPLY_CMDID,
+	WMIX_GPIO_OUTPUT_SET_CMDID,
+	WMIX_GPIO_INPUT_GET_CMDID,
+	WMIX_GPIO_REGISTER_SET_CMDID,
+	WMIX_GPIO_REGISTER_GET_CMDID,
+	WMIX_GPIO_INTR_ACK_CMDID,
+	WMIX_HB_CHALLENGE_RESP_CMDID,
+	WMIX_DBGLOG_CFG_MODULE_CMDID,
+	WMIX_PROF_CFG_CMDID,	/* 0x200a */
+	WMIX_PROF_ADDR_SET_CMDID,
+	WMIX_PROF_START_CMDID,
+	WMIX_PROF_STOP_CMDID,
+	WMIX_PROF_COUNT_GET_CMDID,
+};
+
+enum wmix_event_id {
+	WMIX_DSETOPENREQ_EVENTID = 0x3001,
+	WMIX_DSETCLOSE_EVENTID,
+	WMIX_DSETDATAREQ_EVENTID,
+	WMIX_GPIO_INTR_EVENTID,
+	WMIX_GPIO_DATA_EVENTID,
+	WMIX_GPIO_ACK_EVENTID,
+	WMIX_HB_CHALLENGE_RESP_EVENTID,
+	WMIX_DBGLOG_EVENTID,
+	WMIX_PROF_COUNT_EVENTID,
+};
+
+/*
+ * ------Error Detection support-------
+ */
+
+/*
+ * WMIX_HB_CHALLENGE_RESP_CMDID
+ * Heartbeat Challenge Response command
+ */
+struct wmix_hb_challenge_resp_cmd {
+	__le32 cookie;
+	__le32 source;
+} __packed;
+
+/* End of Extended WMI (WMIX) */
+
+enum wmi_sync_flag {
+	NO_SYNC_WMIFLAG = 0,
+
+	/* transmit all queued data before cmd */
+	SYNC_BEFORE_WMIFLAG,
+
+	/* any new data waits until cmd execs */
+	SYNC_AFTER_WMIFLAG,
+
+	SYNC_BOTH_WMIFLAG,
+
+	/* end marker */
+	END_WMIFLAG
+};
+
+enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi);
+void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id);
+int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb);
+int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
+			    u8 msg_type, bool more_data,
+			    enum wmi_data_hdr_data_type data_type,
+			    u8 meta_ver, void *tx_meta_info);
+
+int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb);
+int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb);
+int ath6kl_wmi_data_hdr_remove(struct wmi *wmi, struct sk_buff *skb);
+int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
+				       u32 layer2_priority, bool wmm_enabled,
+				       u8 *ac);
+
+int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb);
+struct bss *ath6kl_wmi_find_node(struct wmi *wmi, const u8 *mac_addr);
+void ath6kl_wmi_node_free(struct wmi *wmi, const u8 *mac_addr);
+
+int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
+			enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag);
+
+int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type,
+			   enum dot11_auth_mode dot11_auth_mode,
+			   enum auth_mode auth_mode,
+			   enum crypto_type pairwise_crypto,
+			   u8 pairwise_crypto_len,
+			   enum crypto_type group_crypto,
+			   u8 group_crypto_len, int ssid_len, u8 *ssid,
+			   u8 *bssid, u16 channel, u32 ctrl_flags);
+
+int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel);
+int ath6kl_wmi_disconnect_cmd(struct wmi *wmi);
+int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
+			     u32 force_fgscan, u32 is_legacy,
+			     u32 home_dwell_time, u32 force_scan_interval,
+			     s8 num_chan, u16 *ch_list);
+int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u16 fg_start_sec,
+			      u16 fg_end_sec, u16 bg_sec,
+			      u16 minact_chdw_msec, u16 maxact_chdw_msec,
+			      u16 pas_chdw_msec, u8 short_scan_ratio,
+			      u8 scan_ctrl_flag, u32 max_dfsch_act_time,
+			      u16 maxact_scan_per_ssid);
+int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 filter, u32 ie_mask);
+int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 index, u8 flag,
+			      u8 ssid_len, u8 *ssid);
+int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u16 listen_interval,
+				  u16 listen_beacons);
+int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 pwr_mode);
+int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u16 idle_period,
+			    u16 ps_poll_num, u16 dtim_policy,
+			    u16 tx_wakup_policy, u16 num_tx_to_wakeup,
+			    u16 ps_fail_event_policy);
+int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 timeout);
+int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi,
+				  struct wmi_create_pstream_cmd *pstream);
+int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 traffic_class, u8 tsid);
+
+int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u16 threshold);
+int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status,
+				 u8 preamble_policy);
+
+int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source);
+
+int ath6kl_wmi_get_stats_cmd(struct wmi *wmi);
+int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
+			  enum crypto_type key_type,
+			  u8 key_usage, u8 key_len,
+			  u8 *key_rsc, u8 *key_material,
+			  u8 key_op_ctrl, u8 *mac_addr,
+			  enum wmi_sync_flag sync_flag);
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 *krk);
+int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 key_index);
+int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, const u8 *bssid,
+			    const u8 *pmkid, bool set);
+int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 dbM);
+int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi);
+
+int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg);
+int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl);
+
+s32 ath6kl_wmi_get_rate(s8 rate_index);
+
+int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd);
+
+struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 *ssid,
+				      u32 ssid_len, bool is_wpa2,
+				      bool match_ssid);
+
+void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss);
+
+/* AP mode */
+int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag);
+
+int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version,
+				       bool rx_dot11_hdr, bool defrag_on_host);
+
+void *ath6kl_wmi_init(struct ath6kl *devt);
+void ath6kl_wmi_shutdown(struct wmi *wmi);
+
+#endif /* WMI_H */

+ 1 - 1
drivers/net/wireless/ath/ath9k/ahb.c

@@ -133,7 +133,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
 		goto err_free_hw;
 	}
 
-	ret = ath9k_init_device(id->driver_data, sc, 0x0, &ath_ahb_bus_ops);
+	ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to initialize device\n");
 		goto err_irq;

+ 95 - 95
drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h

@@ -835,108 +835,108 @@ static const u32 ar9300_2p2_baseband_core[][2] = {
 
 static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
-	{0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
-	{0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+	{0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352},
+	{0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584},
+	{0x0000a2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800},
 	{0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
-	{0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9},
-	{0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
-	{0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002},
-	{0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004},
-	{0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200},
-	{0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202},
-	{0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400},
-	{0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402},
-	{0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404},
-	{0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603},
-	{0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02},
-	{0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04},
-	{0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20},
-	{0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20},
-	{0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22},
-	{0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24},
-	{0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640},
-	{0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660},
-	{0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861},
-	{0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81},
-	{0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83},
-	{0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84},
-	{0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3},
-	{0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5},
-	{0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9},
-	{0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb},
-	{0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-	{0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-	{0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-	{0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-	{0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-	{0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-	{0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-	{0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000},
-	{0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002},
-	{0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004},
-	{0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200},
-	{0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202},
-	{0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400},
-	{0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402},
-	{0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404},
-	{0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603},
-	{0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02},
-	{0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04},
-	{0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20},
-	{0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20},
-	{0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22},
-	{0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24},
-	{0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640},
-	{0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660},
-	{0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861},
-	{0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81},
-	{0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83},
-	{0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84},
-	{0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3},
-	{0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5},
-	{0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9},
-	{0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb},
-	{0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-	{0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-	{0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-	{0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-	{0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-	{0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-	{0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+	{0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+	{0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+	{0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+	{0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+	{0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+	{0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+	{0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400},
+	{0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402},
+	{0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404},
+	{0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603},
+	{0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02},
+	{0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04},
+	{0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20},
+	{0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20},
+	{0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22},
+	{0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24},
+	{0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640},
+	{0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660},
+	{0x0000a544, 0x52022470, 0x52022470, 0x3f001861, 0x3f001861},
+	{0x0000a548, 0x55022490, 0x55022490, 0x43001a81, 0x43001a81},
+	{0x0000a54c, 0x59022492, 0x59022492, 0x47001a83, 0x47001a83},
+	{0x0000a550, 0x5d022692, 0x5d022692, 0x4a001c84, 0x4a001c84},
+	{0x0000a554, 0x61022892, 0x61022892, 0x4e001ce3, 0x4e001ce3},
+	{0x0000a558, 0x65024890, 0x65024890, 0x52001ce5, 0x52001ce5},
+	{0x0000a55c, 0x69024892, 0x69024892, 0x56001ce9, 0x56001ce9},
+	{0x0000a560, 0x6e024c92, 0x6e024c92, 0x5a001ceb, 0x5a001ceb},
+	{0x0000a564, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec},
+	{0x0000a568, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec},
+	{0x0000a56c, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec},
+	{0x0000a570, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec},
+	{0x0000a574, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec},
+	{0x0000a578, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec},
+	{0x0000a57c, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec},
+	{0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+	{0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
+	{0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
+	{0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
+	{0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202},
+	{0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400},
+	{0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402},
+	{0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404},
+	{0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603},
+	{0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02},
+	{0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04},
+	{0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20},
+	{0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20},
+	{0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22},
+	{0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24},
+	{0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640},
+	{0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660},
+	{0x0000a5c4, 0x52822470, 0x52822470, 0x3f801861, 0x3f801861},
+	{0x0000a5c8, 0x55822490, 0x55822490, 0x43801a81, 0x43801a81},
+	{0x0000a5cc, 0x59822492, 0x59822492, 0x47801a83, 0x47801a83},
+	{0x0000a5d0, 0x5d822692, 0x5d822692, 0x4a801c84, 0x4a801c84},
+	{0x0000a5d4, 0x61822892, 0x61822892, 0x4e801ce3, 0x4e801ce3},
+	{0x0000a5d8, 0x65824890, 0x65824890, 0x52801ce5, 0x52801ce5},
+	{0x0000a5dc, 0x69824892, 0x69824892, 0x56801ce9, 0x56801ce9},
+	{0x0000a5e0, 0x6e824c92, 0x6e824c92, 0x5a801ceb, 0x5a801ceb},
+	{0x0000a5e4, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec},
+	{0x0000a5e8, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec},
+	{0x0000a5ec, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec},
+	{0x0000a5f0, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec},
+	{0x0000a5f4, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec},
+	{0x0000a5f8, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec},
+	{0x0000a5fc, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec},
 	{0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 	{0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 	{0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 	{0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
-	{0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
-	{0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
-	{0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
-	{0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
-	{0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
-	{0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
-	{0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
-	{0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-	{0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-	{0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-	{0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-	{0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-	{0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
-	{0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
-	{0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+	{0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+	{0x0000a614, 0x02004000, 0x02004000, 0x01404000, 0x01404000},
+	{0x0000a618, 0x02004801, 0x02004801, 0x01404501, 0x01404501},
+	{0x0000a61c, 0x02808a02, 0x02808a02, 0x02008501, 0x02008501},
+	{0x0000a620, 0x0380ce03, 0x0380ce03, 0x0280ca03, 0x0280ca03},
+	{0x0000a624, 0x04411104, 0x04411104, 0x03010c04, 0x03010c04},
+	{0x0000a628, 0x04411104, 0x04411104, 0x04014c04, 0x04014c04},
+	{0x0000a62c, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+	{0x0000a630, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+	{0x0000a634, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+	{0x0000a638, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+	{0x0000a63c, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+	{0x0000b2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352},
+	{0x0000b2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584},
+	{0x0000b2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800},
 	{0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
-	{0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
-	{0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
-	{0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+	{0x0000c2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352},
+	{0x0000c2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584},
+	{0x0000c2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800},
 	{0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
-	{0x00016044, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-	{0x00016048, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
-	{0x00016068, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
-	{0x00016444, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-	{0x00016448, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
-	{0x00016468, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
-	{0x00016844, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-	{0x00016848, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
-	{0x00016868, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
+	{0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+	{0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+	{0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+	{0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+	{0x00016448, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+	{0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+	{0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+	{0x00016848, 0x66480001, 0x66480001, 0x66480001, 0x66480001},
+	{0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
 };
 
 static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = {

+ 130 - 20
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c

@@ -3418,6 +3418,133 @@ static bool ath9k_hw_ar9300_fill_eeprom(struct ath_hw *ah)
 	return true;
 }
 
+#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
+static u32 ar9003_dump_modal_eeprom(char *buf, u32 len, u32 size,
+				    struct ar9300_modal_eep_header *modal_hdr)
+{
+	PR_EEP("Chain0 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[0]));
+	PR_EEP("Chain1 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[1]));
+	PR_EEP("Chain2 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[2]));
+	PR_EEP("Ant. Common Control", le32_to_cpu(modal_hdr->antCtrlCommon));
+	PR_EEP("Ant. Common Control2", le32_to_cpu(modal_hdr->antCtrlCommon2));
+	PR_EEP("Ant. Gain", modal_hdr->antennaGain);
+	PR_EEP("Switch Settle", modal_hdr->switchSettling);
+	PR_EEP("Chain0 xatten1DB", modal_hdr->xatten1DB[0]);
+	PR_EEP("Chain1 xatten1DB", modal_hdr->xatten1DB[1]);
+	PR_EEP("Chain2 xatten1DB", modal_hdr->xatten1DB[2]);
+	PR_EEP("Chain0 xatten1Margin", modal_hdr->xatten1Margin[0]);
+	PR_EEP("Chain1 xatten1Margin", modal_hdr->xatten1Margin[1]);
+	PR_EEP("Chain2 xatten1Margin", modal_hdr->xatten1Margin[2]);
+	PR_EEP("Temp Slope", modal_hdr->tempSlope);
+	PR_EEP("Volt Slope", modal_hdr->voltSlope);
+	PR_EEP("spur Channels0", modal_hdr->spurChans[0]);
+	PR_EEP("spur Channels1", modal_hdr->spurChans[1]);
+	PR_EEP("spur Channels2", modal_hdr->spurChans[2]);
+	PR_EEP("spur Channels3", modal_hdr->spurChans[3]);
+	PR_EEP("spur Channels4", modal_hdr->spurChans[4]);
+	PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]);
+	PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]);
+	PR_EEP("Chain2 NF Threshold", modal_hdr->noiseFloorThreshCh[2]);
+	PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl);
+	PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart);
+	PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn);
+	PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn);
+	PR_EEP("txClip", modal_hdr->txClip);
+	PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize);
+	PR_EEP("Chain0 ob", modal_hdr->ob[0]);
+	PR_EEP("Chain1 ob", modal_hdr->ob[1]);
+	PR_EEP("Chain2 ob", modal_hdr->ob[2]);
+
+	PR_EEP("Chain0 db_stage2", modal_hdr->db_stage2[0]);
+	PR_EEP("Chain1 db_stage2", modal_hdr->db_stage2[1]);
+	PR_EEP("Chain2 db_stage2", modal_hdr->db_stage2[2]);
+	PR_EEP("Chain0 db_stage3", modal_hdr->db_stage3[0]);
+	PR_EEP("Chain1 db_stage3", modal_hdr->db_stage3[1]);
+	PR_EEP("Chain2 db_stage3", modal_hdr->db_stage3[2]);
+	PR_EEP("Chain0 db_stage4", modal_hdr->db_stage4[0]);
+	PR_EEP("Chain1 db_stage4", modal_hdr->db_stage4[1]);
+	PR_EEP("Chain2 db_stage4", modal_hdr->db_stage4[2]);
+
+	return len;
+}
+
+static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+				       u8 *buf, u32 len, u32 size)
+{
+	struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+	struct ar9300_base_eep_hdr *pBase;
+
+	if (!dump_base_hdr) {
+		len += snprintf(buf + len, size - len,
+				"%20s :\n", "2GHz modal Header");
+		len += ar9003_dump_modal_eeprom(buf, len, size,
+						&eep->modalHeader2G);
+		len += snprintf(buf + len, size - len,
+				"%20s :\n", "5GHz modal Header");
+		len += ar9003_dump_modal_eeprom(buf, len, size,
+						&eep->modalHeader5G);
+		goto out;
+	}
+
+	pBase = &eep->baseEepHeader;
+
+	PR_EEP("EEPROM Version", ah->eeprom.ar9300_eep.eepromVersion);
+	PR_EEP("RegDomain1", le16_to_cpu(pBase->regDmn[0]));
+	PR_EEP("RegDomain2", le16_to_cpu(pBase->regDmn[1]));
+	PR_EEP("TX Mask", (pBase->txrxMask >> 4));
+	PR_EEP("RX Mask", (pBase->txrxMask & 0x0f));
+	PR_EEP("Allow 5GHz", !!(pBase->opCapFlags.opFlags &
+				AR5416_OPFLAGS_11A));
+	PR_EEP("Allow 2GHz", !!(pBase->opCapFlags.opFlags &
+				AR5416_OPFLAGS_11G));
+	PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags.opFlags &
+					AR5416_OPFLAGS_N_2G_HT20));
+	PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags.opFlags &
+					AR5416_OPFLAGS_N_2G_HT40));
+	PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags.opFlags &
+					AR5416_OPFLAGS_N_5G_HT20));
+	PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags.opFlags &
+					AR5416_OPFLAGS_N_5G_HT40));
+	PR_EEP("Big Endian", !!(pBase->opCapFlags.eepMisc & 0x01));
+	PR_EEP("RF Silent", pBase->rfSilent);
+	PR_EEP("BT option", pBase->blueToothOptions);
+	PR_EEP("Device Cap", pBase->deviceCap);
+	PR_EEP("Device Type", pBase->deviceType);
+	PR_EEP("Power Table Offset", pBase->pwrTableOffset);
+	PR_EEP("Tuning Caps1", pBase->params_for_tuning_caps[0]);
+	PR_EEP("Tuning Caps2", pBase->params_for_tuning_caps[1]);
+	PR_EEP("Enable Tx Temp Comp", !!(pBase->featureEnable & BIT(0)));
+	PR_EEP("Enable Tx Volt Comp", !!(pBase->featureEnable & BIT(1)));
+	PR_EEP("Enable fast clock", !!(pBase->featureEnable & BIT(2)));
+	PR_EEP("Enable doubling", !!(pBase->featureEnable & BIT(3)));
+	PR_EEP("Internal regulator", !!(pBase->featureEnable & BIT(4)));
+	PR_EEP("Enable Paprd", !!(pBase->featureEnable & BIT(5)));
+	PR_EEP("Driver Strength", !!(pBase->miscConfiguration & BIT(0)));
+	PR_EEP("Chain mask Reduce", (pBase->miscConfiguration >> 0x3) & 0x1);
+	PR_EEP("Write enable Gpio", pBase->eepromWriteEnableGpio);
+	PR_EEP("WLAN Disable Gpio", pBase->wlanDisableGpio);
+	PR_EEP("WLAN LED Gpio", pBase->wlanLedGpio);
+	PR_EEP("Rx Band Select Gpio", pBase->rxBandSelectGpio);
+	PR_EEP("Tx Gain", pBase->txrxgain >> 4);
+	PR_EEP("Rx Gain", pBase->txrxgain & 0xf);
+	PR_EEP("SW Reg", le32_to_cpu(pBase->swreg));
+
+	len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+			ah->eeprom.ar9300_eep.macAddr);
+out:
+	if (len > size)
+		len = size;
+
+	return len;
+}
+#else
+static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+				       u8 *buf, u32 len, u32 size)
+{
+	return 0;
+}
+#endif
+
 /* XXX: review hardware docs */
 static int ath9k_hw_ar9300_get_eeprom_ver(struct ath_hw *ah)
 {
@@ -4061,7 +4188,7 @@ static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray)
         /* Write the power for duplicated frames - HT40 */
 
         /* dup40_cck (LSB), dup40_ofdm, ext20_cck, ext20_ofdm (MSB) */
-	REG_WRITE(ah, 0xa3e0,
+	REG_WRITE(ah, AR_PHY_POWER_TX_RATE(8),
 		  POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 24) |
 		  POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 16) |
 		  POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24],  8) |
@@ -4922,25 +5049,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
 			"TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]);
 	}
 
-	/*
-	 * This is the TX power we send back to driver core,
-	 * and it can use to pass to userspace to display our
-	 * currently configured TX power setting.
-	 *
-	 * Since power is rate dependent, use one of the indices
-	 * from the AR9300_Rates enum to select an entry from
-	 * targetPowerValT2[] to report. Currently returns the
-	 * power for HT40 MCS 0, HT20 MCS 0, or OFDM 6 Mbps
-	 * as CCK power is less interesting (?).
-	 */
-	i = ALL_TARGET_LEGACY_6_24; /* legacy */
-	if (IS_CHAN_HT40(chan))
-		i = ALL_TARGET_HT40_0_8_16; /* ht40 */
-	else if (IS_CHAN_HT20(chan))
-		i = ALL_TARGET_HT20_0_8_16; /* ht20 */
-
-	ah->txpower_limit = targetPowerValT2[i];
-	regulatory->max_power_level = targetPowerValT2[i];
+	ah->txpower_limit = regulatory->max_power_level;
 
 	/* Write target power array to registers */
 	ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
@@ -5015,6 +5124,7 @@ const struct eeprom_ops eep_ar9300_ops = {
 	.check_eeprom = ath9k_hw_ar9300_check_eeprom,
 	.get_eeprom = ath9k_hw_ar9300_get_eeprom,
 	.fill_eeprom = ath9k_hw_ar9300_fill_eeprom,
+	.dump_eeprom = ath9k_hw_ar9003_dump_eeprom,
 	.get_eeprom_ver = ath9k_hw_ar9300_get_eeprom_ver,
 	.get_eeprom_rev = ath9k_hw_ar9300_get_eeprom_rev,
 	.set_board_values = ath9k_hw_ar9300_set_board_values,

+ 8 - 7
drivers/net/wireless/ath/ath9k/ar9003_mac.c

@@ -531,17 +531,18 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
 
 	/* TODO: byte swap on big endian for ar9300_10 */
 
-	if ((rxsp->status11 & AR_RxDone) == 0)
-		return -EINPROGRESS;
+	if (!rxs) {
+		if ((rxsp->status11 & AR_RxDone) == 0)
+			return -EINPROGRESS;
 
-	if (MS(rxsp->ds_info, AR_DescId) != 0x168c)
-		return -EINVAL;
+		if (MS(rxsp->ds_info, AR_DescId) != 0x168c)
+			return -EINVAL;
 
-	if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0)
-		return -EINPROGRESS;
+		if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0)
+			return -EINPROGRESS;
 
-	if (!rxs)
 		return 0;
+	}
 
 	rxs->rs_status = 0;
 	rxs->rs_flags =  0;

+ 2 - 2
drivers/net/wireless/ath/ath9k/ar9003_phy.c

@@ -370,7 +370,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah,
 			else
 				spur_subchannel_sd = 0;
 
-			spur_freq_sd = ((freq_offset + 10) << 9) / 11;
+			spur_freq_sd = (freq_offset << 9) / 11;
 
 		} else {
 			if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL,
@@ -379,7 +379,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah,
 			else
 				spur_subchannel_sd = 1;
 
-			spur_freq_sd = ((freq_offset - 10) << 9) / 11;
+			spur_freq_sd = (freq_offset << 9) / 11;
 
 		}
 

+ 1 - 5
drivers/net/wireless/ath/ath9k/ath9k.h

@@ -217,7 +217,6 @@ struct ath_buf_state {
 	u8 bf_type;
 	u8 bfs_paprd;
 	unsigned long bfs_paprd_timestamp;
-	enum ath9k_internal_frame_type bfs_ftype;
 };
 
 struct ath_buf {
@@ -273,8 +272,6 @@ struct ath_node {
 struct ath_tx_control {
 	struct ath_txq *txq;
 	struct ath_node *an;
-	int if_id;
-	enum ath9k_internal_frame_type frame_type;
 	u8 paprd;
 };
 
@@ -570,7 +567,6 @@ struct ath_ant_comb {
 #define PS_WAIT_FOR_PSPOLL_DATA   BIT(2)
 #define PS_WAIT_FOR_TX_ACK        BIT(3)
 #define PS_BEACON_SYNC            BIT(4)
-#define PS_TSFOOR_SYNC            BIT(5)
 
 struct ath_rate_table;
 
@@ -669,7 +665,7 @@ extern bool is_ath9k_unloaded;
 
 irqreturn_t ath_isr(int irq, void *dev);
 void ath9k_init_crypto(struct ath_softc *sc);
-int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
+int ath9k_init_device(u16 devid, struct ath_softc *sc,
 		    const struct ath_bus_ops *bus_ops);
 void ath9k_deinit_device(struct ath_softc *sc);
 void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);

+ 6 - 12
drivers/net/wireless/ath/ath9k/beacon.c

@@ -522,6 +522,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
 	ath9k_beacon_init(sc, nexttbtt, intval);
 	sc->beacon.bmisscnt = 0;
 	ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_hw_enable_interrupts(ah);
 }
 
 /*
@@ -648,12 +649,8 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
 	ath9k_hw_set_sta_beacon_timers(ah, &bs);
 	ah->imask |= ATH9K_INT_BMISS;
 
-	/*
-	 * If the beacon config is called beacause of TSFOOR,
-	 * Interrupts will be enabled back at the end of ath9k_tasklet
-	 */
-	if (!(sc->ps_flags & PS_TSFOOR_SYNC))
-		ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_hw_enable_interrupts(ah);
 }
 
 static void ath_beacon_config_adhoc(struct ath_softc *sc,
@@ -687,12 +684,9 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
 	ath9k_hw_disable_interrupts(ah);
 	ath9k_beacon_init(sc, nexttbtt, intval);
 	sc->beacon.bmisscnt = 0;
-	/*
-	 * If the beacon config is called beacause of TSFOOR,
-	 * Interrupts will be enabled back at the end of ath9k_tasklet
-	 */
-	if (!(sc->ps_flags & PS_TSFOOR_SYNC))
-		ath9k_hw_set_interrupts(ah, ah->imask);
+
+	ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_hw_enable_interrupts(ah);
 }
 
 static bool ath9k_allow_beacon_config(struct ath_softc *sc,

+ 15 - 0
drivers/net/wireless/ath/ath9k/calib.c

@@ -63,6 +63,19 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
 	return ath9k_hw_get_nf_limits(ah, chan)->nominal;
 }
 
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+	s8 noise = ATH_DEFAULT_NOISE_FLOOR;
+
+	if (chan && chan->noisefloor) {
+		s8 delta = chan->noisefloor -
+			   ath9k_hw_get_default_nf(ah, chan);
+		if (delta > 0)
+			noise += delta;
+	}
+	return noise;
+}
+EXPORT_SYMBOL(ath9k_hw_getchan_noise);
 
 static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
 					      struct ath9k_hw_cal_data *cal,
@@ -378,6 +391,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
 
 	if (!caldata) {
 		chan->noisefloor = nf;
+		ah->noise = ath9k_hw_getchan_noise(ah, chan);
 		return false;
 	}
 
@@ -385,6 +399,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
 	caldata->nfcal_pending = false;
 	ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray);
 	chan->noisefloor = h[0].privNF;
+	ah->noise = ath9k_hw_getchan_noise(ah, chan);
 	return true;
 }
 

+ 1 - 0
drivers/net/wireless/ath/ath9k/calib.h

@@ -108,6 +108,7 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
 void ath9k_hw_bstuck_nfcal(struct ath_hw *ah);
 void ath9k_hw_reset_calibration(struct ath_hw *ah,
 				struct ath9k_cal_list *currCal);
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
 
 
 #endif /* CALIB_H */

+ 60 - 0
drivers/net/wireless/ath/ath9k/debug.c

@@ -1163,6 +1163,62 @@ static const struct file_operations fops_regdump = {
 	.llseek = default_llseek,/* read accesses f_pos */
 };
 
+static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	struct ath_hw *ah = sc->sc_ah;
+	u32 len = 0, size = 1500;
+	ssize_t retval = 0;
+	char *buf;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size);
+
+	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static const struct file_operations fops_base_eeprom = {
+	.read = read_file_base_eeprom,
+	.open = ath9k_debugfs_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	struct ath_hw *ah = sc->sc_ah;
+	u32 len = 0, size = 6000;
+	char *buf;
+	size_t retval;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size);
+
+	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static const struct file_operations fops_modal_eeprom = {
+	.read = read_file_modal_eeprom,
+	.open = ath9k_debugfs_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath9k_init_debug(struct ath_hw *ah)
 {
 	struct ath_common *common = ath9k_hw_common(ah);
@@ -1206,6 +1262,10 @@ int ath9k_init_debug(struct ath_hw *ah)
 			    &ah->config.cwm_ignore_extcca);
 	debugfs_create_file("regdump", S_IRUSR, sc->debug.debugfs_phy, sc,
 			    &fops_regdump);
+	debugfs_create_file("base_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
+			    &fops_base_eeprom);
+	debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
+			    &fops_modal_eeprom);
 
 	debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
 			   sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);

+ 2 - 0
drivers/net/wireless/ath/ath9k/eeprom.h

@@ -649,6 +649,8 @@ struct eeprom_ops {
 	int (*check_eeprom)(struct ath_hw *hw);
 	u32 (*get_eeprom)(struct ath_hw *hw, enum eeprom_param param);
 	bool (*fill_eeprom)(struct ath_hw *hw);
+	u32 (*dump_eeprom)(struct ath_hw *hw, bool dump_base_hdr, u8 *buf,
+			   u32 len, u32 size);
 	int (*get_eeprom_ver)(struct ath_hw *hw);
 	int (*get_eeprom_rev)(struct ath_hw *hw);
 	void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);

+ 115 - 20
drivers/net/wireless/ath/ath9k/eeprom_4k.c

@@ -72,6 +72,117 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
 		return __ath9k_hw_4k_fill_eeprom(ah);
 }
 
+#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
+static u32 ath9k_dump_4k_modal_eeprom(char *buf, u32 len, u32 size,
+				      struct modal_eep_4k_header *modal_hdr)
+{
+	PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]);
+	PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon);
+	PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]);
+	PR_EEP("Switch Settle", modal_hdr->switchSettling);
+	PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]);
+	PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]);
+	PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize);
+	PR_EEP("PGA Desired size", modal_hdr->pgaDesiredSize);
+	PR_EEP("Chain0 xlna Gain", modal_hdr->xlnaGainCh[0]);
+	PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff);
+	PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn);
+	PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn);
+	PR_EEP("CCA Threshold)", modal_hdr->thresh62);
+	PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]);
+	PR_EEP("xpdGain", modal_hdr->xpdGain);
+	PR_EEP("External PD", modal_hdr->xpd);
+	PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]);
+	PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]);
+	PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap);
+	PR_EEP("O/D Bias Version", modal_hdr->version);
+	PR_EEP("CCK OutputBias", modal_hdr->ob_0);
+	PR_EEP("BPSK OutputBias", modal_hdr->ob_1);
+	PR_EEP("QPSK OutputBias", modal_hdr->ob_2);
+	PR_EEP("16QAM OutputBias", modal_hdr->ob_3);
+	PR_EEP("64QAM OutputBias", modal_hdr->ob_4);
+	PR_EEP("CCK Driver1_Bias", modal_hdr->db1_0);
+	PR_EEP("BPSK Driver1_Bias", modal_hdr->db1_1);
+	PR_EEP("QPSK Driver1_Bias", modal_hdr->db1_2);
+	PR_EEP("16QAM Driver1_Bias", modal_hdr->db1_3);
+	PR_EEP("64QAM Driver1_Bias", modal_hdr->db1_4);
+	PR_EEP("CCK Driver2_Bias", modal_hdr->db2_0);
+	PR_EEP("BPSK Driver2_Bias", modal_hdr->db2_1);
+	PR_EEP("QPSK Driver2_Bias", modal_hdr->db2_2);
+	PR_EEP("16QAM Driver2_Bias", modal_hdr->db2_3);
+	PR_EEP("64QAM Driver2_Bias", modal_hdr->db2_4);
+	PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl);
+	PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart);
+	PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn);
+	PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc);
+	PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]);
+	PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]);
+	PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40);
+	PR_EEP("Chain0 xatten2Db", modal_hdr->xatten2Db[0]);
+	PR_EEP("Chain0 xatten2Margin", modal_hdr->xatten2Margin[0]);
+	PR_EEP("Ant. Diversity ctl1", modal_hdr->antdiv_ctl1);
+	PR_EEP("Ant. Diversity ctl2", modal_hdr->antdiv_ctl2);
+	PR_EEP("TX Diversity", modal_hdr->tx_diversity);
+
+	return len;
+}
+
+static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+				       u8 *buf, u32 len, u32 size)
+{
+	struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
+	struct base_eep_header_4k *pBase = &eep->baseEepHeader;
+
+	if (!dump_base_hdr) {
+		len += snprintf(buf + len, size - len,
+				"%20s :\n", "2GHz modal Header");
+		len += ath9k_dump_4k_modal_eeprom(buf, len, size,
+						  &eep->modalHeader);
+		goto out;
+	}
+
+	PR_EEP("Major Version", pBase->version >> 12);
+	PR_EEP("Minor Version", pBase->version & 0xFFF);
+	PR_EEP("Checksum", pBase->checksum);
+	PR_EEP("Length", pBase->length);
+	PR_EEP("RegDomain1", pBase->regDmn[0]);
+	PR_EEP("RegDomain2", pBase->regDmn[1]);
+	PR_EEP("TX Mask", pBase->txMask);
+	PR_EEP("RX Mask", pBase->rxMask);
+	PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
+	PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
+	PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_2G_HT20));
+	PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_2G_HT40));
+	PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_5G_HT20));
+	PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_5G_HT40));
+	PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01));
+	PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF);
+	PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF);
+	PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF);
+	PR_EEP("TX Gain type", pBase->txGainType);
+
+	len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+			pBase->macAddr);
+
+out:
+	if (len > size)
+		len = size;
+
+	return len;
+}
+#else
+static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+				       u8 *buf, u32 len, u32 size)
+{
+	return 0;
+}
+#endif
+
+
 #undef SIZE_EEPROM_4K
 
 static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
@@ -238,18 +349,14 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah,
 	case EEP_ANT_DIV_CTL1:
 		return pModal->antdiv_ctl1;
 	case EEP_TXGAIN_TYPE:
-		if (ver_minor >= AR5416_EEP_MINOR_VER_19)
-			return pBase->txGainType;
-		else
-			return AR5416_EEP_TXGAIN_ORIGINAL;
+		return pBase->txGainType;
 	default:
 		return 0;
 	}
 }
 
 static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
-				  struct ath9k_channel *chan,
-				  int16_t *pTxPowerIndexOffset)
+				  struct ath9k_channel *chan)
 {
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k;
@@ -356,8 +463,6 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
 			REGWRITE_BUFFER_FLUSH(ah);
 		}
 	}
-
-	*pTxPowerIndexOffset = 0;
 }
 
 static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah,
@@ -580,7 +685,6 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
 	struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k;
 	struct modal_eep_4k_header *pModal = &pEepData->modalHeader;
 	int16_t ratesArray[Ar5416RateSize];
-	int16_t txPowerIndexOffset = 0;
 	u8 ht40PowerIncForPdadc = 2;
 	int i;
 
@@ -597,11 +701,10 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
 					     twiceMaxRegulatoryPower,
 					     powerLimit);
 
-	ath9k_hw_set_4k_power_cal_table(ah, chan, &txPowerIndexOffset);
+	ath9k_hw_set_4k_power_cal_table(ah, chan);
 
 	regulatory->max_power_level = 0;
 	for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
-		ratesArray[i] =	(int16_t)(txPowerIndexOffset + ratesArray[i]);
 		if (ratesArray[i] > MAX_RATE_POWER)
 			ratesArray[i] = MAX_RATE_POWER;
 
@@ -612,15 +715,6 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
 	if (test)
 	    return;
 
-	/* Update regulatory */
-	i = rate6mb;
-	if (IS_CHAN_HT40(chan))
-		i = rateHt40_0;
-	else if (IS_CHAN_HT20(chan))
-		i = rateHt20_0;
-
-	regulatory->max_power_level = ratesArray[i];
-
 	if (AR_SREV_9280_20_OR_LATER(ah)) {
 		for (i = 0; i < Ar5416RateSize; i++)
 			ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
@@ -1063,6 +1157,7 @@ const struct eeprom_ops eep_4k_ops = {
 	.check_eeprom		= ath9k_hw_4k_check_eeprom,
 	.get_eeprom		= ath9k_hw_4k_get_eeprom,
 	.fill_eeprom		= ath9k_hw_4k_fill_eeprom,
+	.dump_eeprom		= ath9k_hw_4k_dump_eeprom,
 	.get_eeprom_ver		= ath9k_hw_4k_get_eeprom_ver,
 	.get_eeprom_rev		= ath9k_hw_4k_get_eeprom_rev,
 	.set_board_values	= ath9k_hw_4k_set_board_values,

+ 108 - 14
drivers/net/wireless/ath/ath9k/eeprom_9287.c

@@ -76,6 +76,111 @@ static bool ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah)
 		return __ath9k_hw_ar9287_fill_eeprom(ah);
 }
 
+#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
+static u32 ar9287_dump_modal_eeprom(char *buf, u32 len, u32 size,
+				    struct modal_eep_ar9287_header *modal_hdr)
+{
+	PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]);
+	PR_EEP("Chain1 Ant. Control", modal_hdr->antCtrlChain[1]);
+	PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon);
+	PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]);
+	PR_EEP("Chain1 Ant. Gain", modal_hdr->antennaGainCh[1]);
+	PR_EEP("Switch Settle", modal_hdr->switchSettling);
+	PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]);
+	PR_EEP("Chain1 TxRxAtten", modal_hdr->txRxAttenCh[1]);
+	PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]);
+	PR_EEP("Chain1 RxTxMargin", modal_hdr->rxTxMarginCh[1]);
+	PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize);
+	PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff);
+	PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn);
+	PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn);
+	PR_EEP("CCA Threshold)", modal_hdr->thresh62);
+	PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]);
+	PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]);
+	PR_EEP("xpdGain", modal_hdr->xpdGain);
+	PR_EEP("External PD", modal_hdr->xpd);
+	PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]);
+	PR_EEP("Chain1 I Coefficient", modal_hdr->iqCalICh[1]);
+	PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]);
+	PR_EEP("Chain1 Q Coefficient", modal_hdr->iqCalQCh[1]);
+	PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap);
+	PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl);
+	PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart);
+	PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn);
+	PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc);
+	PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]);
+	PR_EEP("Chain1 bswAtten", modal_hdr->bswAtten[1]);
+	PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]);
+	PR_EEP("Chain1 bswMargin", modal_hdr->bswMargin[1]);
+	PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40);
+	PR_EEP("AR92x7 Version", modal_hdr->version);
+	PR_EEP("DriverBias1", modal_hdr->db1);
+	PR_EEP("DriverBias2", modal_hdr->db1);
+	PR_EEP("CCK OutputBias", modal_hdr->ob_cck);
+	PR_EEP("PSK OutputBias", modal_hdr->ob_psk);
+	PR_EEP("QAM OutputBias", modal_hdr->ob_qam);
+	PR_EEP("PAL_OFF OutputBias", modal_hdr->ob_pal_off);
+
+	return len;
+}
+
+static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+				       u8 *buf, u32 len, u32 size)
+{
+	struct ar9287_eeprom *eep = &ah->eeprom.map9287;
+	struct base_eep_ar9287_header *pBase = &eep->baseEepHeader;
+
+	if (!dump_base_hdr) {
+		len += snprintf(buf + len, size - len,
+				"%20s :\n", "2GHz modal Header");
+		len += ar9287_dump_modal_eeprom(buf, len, size,
+						&eep->modalHeader);
+		goto out;
+	}
+
+	PR_EEP("Major Version", pBase->version >> 12);
+	PR_EEP("Minor Version", pBase->version & 0xFFF);
+	PR_EEP("Checksum", pBase->checksum);
+	PR_EEP("Length", pBase->length);
+	PR_EEP("RegDomain1", pBase->regDmn[0]);
+	PR_EEP("RegDomain2", pBase->regDmn[1]);
+	PR_EEP("TX Mask", pBase->txMask);
+	PR_EEP("RX Mask", pBase->rxMask);
+	PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
+	PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
+	PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_2G_HT20));
+	PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_2G_HT40));
+	PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_5G_HT20));
+	PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_5G_HT40));
+	PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01));
+	PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF);
+	PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF);
+	PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF);
+	PR_EEP("Power Table Offset", pBase->pwrTableOffset);
+	PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl);
+
+	len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+			pBase->macAddr);
+
+out:
+	if (len > size)
+		len = size;
+
+	return len;
+}
+#else
+static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+				       u8 *buf, u32 len, u32 size)
+{
+	return 0;
+}
+#endif
+
+
 static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah)
 {
 	u32 sum = 0, el, integer;
@@ -307,8 +412,7 @@ static void ar9287_eeprom_olpc_set_pdadcs(struct ath_hw *ah,
 }
 
 static void ath9k_hw_set_ar9287_power_cal_table(struct ath_hw *ah,
-						struct ath9k_channel *chan,
-						int16_t *pTxPowerIndexOffset)
+						struct ath9k_channel *chan)
 {
 	struct cal_data_per_freq_ar9287 *pRawDataset;
 	struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop;
@@ -444,8 +548,6 @@ static void ath9k_hw_set_ar9287_power_cal_table(struct ath_hw *ah,
 			REGWRITE_BUFFER_FLUSH(ah);
 		}
 	}
-
-	*pTxPowerIndexOffset = 0;
 }
 
 static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah,
@@ -720,7 +822,6 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah,
 	struct ar9287_eeprom *pEepData = &ah->eeprom.map9287;
 	struct modal_eep_ar9287_header *pModal = &pEepData->modalHeader;
 	int16_t ratesArray[Ar5416RateSize];
-	int16_t txPowerIndexOffset = 0;
 	u8 ht40PowerIncForPdadc = 2;
 	int i;
 
@@ -736,11 +837,10 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah,
 						 twiceMaxRegulatoryPower,
 						 powerLimit);
 
-	ath9k_hw_set_ar9287_power_cal_table(ah, chan, &txPowerIndexOffset);
+	ath9k_hw_set_ar9287_power_cal_table(ah, chan);
 
 	regulatory->max_power_level = 0;
 	for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
-		ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
 		if (ratesArray[i] > MAX_RATE_POWER)
 			ratesArray[i] = MAX_RATE_POWER;
 
@@ -751,13 +851,6 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah,
 	if (test)
 		return;
 
-	if (IS_CHAN_2GHZ(chan))
-		i = rate1l;
-	else
-		i = rate6mb;
-
-	regulatory->max_power_level = ratesArray[i];
-
 	if (AR_SREV_9280_20_OR_LATER(ah)) {
 		for (i = 0; i < Ar5416RateSize; i++)
 			ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 2;
@@ -1003,6 +1096,7 @@ const struct eeprom_ops eep_ar9287_ops = {
 	.check_eeprom		= ath9k_hw_ar9287_check_eeprom,
 	.get_eeprom		= ath9k_hw_ar9287_get_eeprom,
 	.fill_eeprom		= ath9k_hw_ar9287_fill_eeprom,
+	.dump_eeprom		= ath9k_hw_ar9287_dump_eeprom,
 	.get_eeprom_ver		= ath9k_hw_ar9287_get_eeprom_ver,
 	.get_eeprom_rev		= ath9k_hw_ar9287_get_eeprom_rev,
 	.set_board_values	= ath9k_hw_ar9287_set_board_values,

+ 133 - 17
drivers/net/wireless/ath/ath9k/eeprom_def.c

@@ -133,6 +133,136 @@ static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah)
 
 #undef SIZE_EEPROM_DEF
 
+#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
+static u32 ath9k_def_dump_modal_eeprom(char *buf, u32 len, u32 size,
+				       struct modal_eep_header *modal_hdr)
+{
+	PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]);
+	PR_EEP("Chain1 Ant. Control", modal_hdr->antCtrlChain[1]);
+	PR_EEP("Chain2 Ant. Control", modal_hdr->antCtrlChain[2]);
+	PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon);
+	PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]);
+	PR_EEP("Chain1 Ant. Gain", modal_hdr->antennaGainCh[1]);
+	PR_EEP("Chain2 Ant. Gain", modal_hdr->antennaGainCh[2]);
+	PR_EEP("Switch Settle", modal_hdr->switchSettling);
+	PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]);
+	PR_EEP("Chain1 TxRxAtten", modal_hdr->txRxAttenCh[1]);
+	PR_EEP("Chain2 TxRxAtten", modal_hdr->txRxAttenCh[2]);
+	PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]);
+	PR_EEP("Chain1 RxTxMargin", modal_hdr->rxTxMarginCh[1]);
+	PR_EEP("Chain2 RxTxMargin", modal_hdr->rxTxMarginCh[2]);
+	PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize);
+	PR_EEP("PGA Desired size", modal_hdr->pgaDesiredSize);
+	PR_EEP("Chain0 xlna Gain", modal_hdr->xlnaGainCh[0]);
+	PR_EEP("Chain1 xlna Gain", modal_hdr->xlnaGainCh[1]);
+	PR_EEP("Chain2 xlna Gain", modal_hdr->xlnaGainCh[2]);
+	PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff);
+	PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn);
+	PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn);
+	PR_EEP("CCA Threshold)", modal_hdr->thresh62);
+	PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]);
+	PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]);
+	PR_EEP("Chain2 NF Threshold", modal_hdr->noiseFloorThreshCh[2]);
+	PR_EEP("xpdGain", modal_hdr->xpdGain);
+	PR_EEP("External PD", modal_hdr->xpd);
+	PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]);
+	PR_EEP("Chain1 I Coefficient", modal_hdr->iqCalICh[1]);
+	PR_EEP("Chain2 I Coefficient", modal_hdr->iqCalICh[2]);
+	PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]);
+	PR_EEP("Chain1 Q Coefficient", modal_hdr->iqCalQCh[1]);
+	PR_EEP("Chain2 Q Coefficient", modal_hdr->iqCalQCh[2]);
+	PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap);
+	PR_EEP("Chain0 OutputBias", modal_hdr->ob);
+	PR_EEP("Chain0 DriverBias", modal_hdr->db);
+	PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl);
+	PR_EEP("2chain pwr decrease", modal_hdr->pwrDecreaseFor2Chain);
+	PR_EEP("3chain pwr decrease", modal_hdr->pwrDecreaseFor3Chain);
+	PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart);
+	PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn);
+	PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc);
+	PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]);
+	PR_EEP("Chain1 bswAtten", modal_hdr->bswAtten[1]);
+	PR_EEP("Chain2 bswAtten", modal_hdr->bswAtten[2]);
+	PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]);
+	PR_EEP("Chain1 bswMargin", modal_hdr->bswMargin[1]);
+	PR_EEP("Chain2 bswMargin", modal_hdr->bswMargin[2]);
+	PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40);
+	PR_EEP("Chain0 xatten2Db", modal_hdr->xatten2Db[0]);
+	PR_EEP("Chain1 xatten2Db", modal_hdr->xatten2Db[1]);
+	PR_EEP("Chain2 xatten2Db", modal_hdr->xatten2Db[2]);
+	PR_EEP("Chain0 xatten2Margin", modal_hdr->xatten2Margin[0]);
+	PR_EEP("Chain1 xatten2Margin", modal_hdr->xatten2Margin[1]);
+	PR_EEP("Chain2 xatten2Margin", modal_hdr->xatten2Margin[2]);
+	PR_EEP("Chain1 OutputBias", modal_hdr->ob_ch1);
+	PR_EEP("Chain1 DriverBias", modal_hdr->db_ch1);
+	PR_EEP("LNA Control", modal_hdr->lna_ctl);
+	PR_EEP("XPA Bias Freq0", modal_hdr->xpaBiasLvlFreq[0]);
+	PR_EEP("XPA Bias Freq1", modal_hdr->xpaBiasLvlFreq[1]);
+	PR_EEP("XPA Bias Freq2", modal_hdr->xpaBiasLvlFreq[2]);
+
+	return len;
+}
+
+static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+				    u8 *buf, u32 len, u32 size)
+{
+	struct ar5416_eeprom_def *eep = &ah->eeprom.def;
+	struct base_eep_header *pBase = &eep->baseEepHeader;
+
+	if (!dump_base_hdr) {
+		len += snprintf(buf + len, size - len,
+				"%20s :\n", "2GHz modal Header");
+		len += ath9k_def_dump_modal_eeprom(buf, len, size,
+						   &eep->modalHeader[0]);
+		len += snprintf(buf + len, size - len,
+				"%20s :\n", "5GHz modal Header");
+		len += ath9k_def_dump_modal_eeprom(buf, len, size,
+						   &eep->modalHeader[1]);
+		goto out;
+	}
+
+	PR_EEP("Major Version", pBase->version >> 12);
+	PR_EEP("Minor Version", pBase->version & 0xFFF);
+	PR_EEP("Checksum", pBase->checksum);
+	PR_EEP("Length", pBase->length);
+	PR_EEP("RegDomain1", pBase->regDmn[0]);
+	PR_EEP("RegDomain2", pBase->regDmn[1]);
+	PR_EEP("TX Mask", pBase->txMask);
+	PR_EEP("RX Mask", pBase->rxMask);
+	PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
+	PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
+	PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_2G_HT20));
+	PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_2G_HT40));
+	PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_5G_HT20));
+	PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags &
+					AR5416_OPFLAGS_N_5G_HT40));
+	PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01));
+	PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF);
+	PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF);
+	PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF);
+	PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl);
+
+	len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+			pBase->macAddr);
+
+out:
+	if (len > size)
+		len = size;
+
+	return len;
+}
+#else
+static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
+				    u8 *buf, u32 len, u32 size)
+{
+	return 0;
+}
+#endif
+
+
 static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
 {
 	struct ar5416_eeprom_def *eep =
@@ -693,8 +823,7 @@ static void ath9k_adjust_pdadc_values(struct ath_hw *ah,
 }
 
 static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
-				  struct ath9k_channel *chan,
-				  int16_t *pTxPowerIndexOffset)
+				  struct ath9k_channel *chan)
 {
 #define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x)
 #define SM_PDGAIN_B(x, y) \
@@ -855,7 +984,6 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
 		}
 	}
 
-	*pTxPowerIndexOffset = 0;
 #undef SM_PD_GAIN
 #undef SM_PDGAIN_B
 }
@@ -1143,7 +1271,6 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
 	struct modal_eep_header *pModal =
 		&(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]);
 	int16_t ratesArray[Ar5416RateSize];
-	int16_t txPowerIndexOffset = 0;
 	u8 ht40PowerIncForPdadc = 2;
 	int i, cck_ofdm_delta = 0;
 
@@ -1160,28 +1287,16 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
 					       twiceMaxRegulatoryPower,
 					       powerLimit);
 
-	ath9k_hw_set_def_power_cal_table(ah, chan, &txPowerIndexOffset);
+	ath9k_hw_set_def_power_cal_table(ah, chan);
 
 	regulatory->max_power_level = 0;
 	for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
-		ratesArray[i] =	(int16_t)(txPowerIndexOffset + ratesArray[i]);
 		if (ratesArray[i] > MAX_RATE_POWER)
 			ratesArray[i] = MAX_RATE_POWER;
 		if (ratesArray[i] > regulatory->max_power_level)
 			regulatory->max_power_level = ratesArray[i];
 	}
 
-	if (!test) {
-		i = rate6mb;
-
-		if (IS_CHAN_HT40(chan))
-			i = rateHt40_0;
-		else if (IS_CHAN_HT20(chan))
-			i = rateHt20_0;
-
-		regulatory->max_power_level = ratesArray[i];
-	}
-
 	switch(ar5416_get_ntxchains(ah->txchainmask)) {
 	case 1:
 		break;
@@ -1336,6 +1451,7 @@ const struct eeprom_ops eep_def_ops = {
 	.check_eeprom		= ath9k_hw_def_check_eeprom,
 	.get_eeprom		= ath9k_hw_def_get_eeprom,
 	.fill_eeprom		= ath9k_hw_def_fill_eeprom,
+	.dump_eeprom		= ath9k_hw_def_dump_eeprom,
 	.get_eeprom_ver		= ath9k_hw_def_get_eeprom_ver,
 	.get_eeprom_rev		= ath9k_hw_def_get_eeprom_rev,
 	.set_board_values	= ath9k_hw_def_set_board_values,

+ 2 - 0
drivers/net/wireless/ath/ath9k/gpio.c

@@ -149,6 +149,7 @@ static void ath9k_gen_timer_start(struct ath_hw *ah,
 		ath9k_hw_disable_interrupts(ah);
 		ah->imask |= ATH9K_INT_GENTIMER;
 		ath9k_hw_set_interrupts(ah, ah->imask);
+		ath9k_hw_enable_interrupts(ah);
 	}
 }
 
@@ -163,6 +164,7 @@ static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
 		ath9k_hw_disable_interrupts(ah);
 		ah->imask &= ~ATH9K_INT_GENTIMER;
 		ath9k_hw_set_interrupts(ah, ah->imask);
+		ath9k_hw_enable_interrupts(ah);
 	}
 }
 

+ 0 - 3
drivers/net/wireless/ath/ath9k/htc.h

@@ -521,8 +521,6 @@ void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb,
 
 int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv,
 				u8 enable_coex);
-void ath9k_htc_station_work(struct work_struct *work);
-void ath9k_htc_aggr_work(struct work_struct *work);
 void ath9k_htc_ani_work(struct work_struct *work);
 void ath9k_htc_start_ani(struct ath9k_htc_priv *priv);
 void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv);
@@ -542,7 +540,6 @@ int ath9k_htc_tx_get_slot(struct ath9k_htc_priv *priv);
 void ath9k_htc_tx_clear_slot(struct ath9k_htc_priv *priv, int slot);
 void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv);
 void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event);
-void ath9k_htc_tx_failed(struct ath9k_htc_priv *priv);
 void ath9k_tx_failed_tasklet(unsigned long data);
 void ath9k_htc_tx_cleanup_timer(unsigned long data);
 

+ 0 - 1
drivers/net/wireless/ath/ath9k/htc_drv_init.c

@@ -666,7 +666,6 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
 		return -ENOMEM;
 
 	ah->hw_version.devid = devid;
-	ah->hw_version.subsysid = 0; /* FIXME */
 	ah->hw_version.usbdev = drv_info;
 	ah->ah_flags |= AH_USE_EEPROM;
 	ah->reg_ops.read = ath9k_regread;

+ 7 - 3
drivers/net/wireless/ath/ath9k/hw.c

@@ -1486,6 +1486,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 		memset(caldata, 0, sizeof(*caldata));
 		ath9k_init_nfcal_hist_buffer(ah, chan);
 	}
+	ah->noise = ath9k_hw_getchan_noise(ah, chan);
 
 	if (bChannelChange &&
 	    (ah->chip_fullsleep != true) &&
@@ -2439,15 +2440,18 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test)
 	struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
 	struct ath9k_channel *chan = ah->curchan;
 	struct ieee80211_channel *channel = chan->chan;
+	int reg_pwr = min_t(int, MAX_RATE_POWER, regulatory->power_limit);
+	int chan_pwr = channel->max_power * 2;
+
+	if (test)
+		reg_pwr = chan_pwr = MAX_RATE_POWER;
 
 	regulatory->power_limit = min(limit, (u32) MAX_RATE_POWER);
 
 	ah->eep_ops->set_txpower(ah, chan,
 				 ath9k_regd_get_ctl(regulatory, chan),
 				 channel->max_antenna_gain * 2,
-				 channel->max_power * 2,
-				 min((u32) MAX_RATE_POWER,
-				 (u32) regulatory->power_limit), test);
+				 chan_pwr, reg_pwr, test);
 }
 EXPORT_SYMBOL(ath9k_hw_set_txpowerlimit);
 

+ 8 - 1
drivers/net/wireless/ath/ath9k/hw.h

@@ -93,6 +93,12 @@
 			(_ah)->reg_ops.write_flush((_ah));	\
 	} while (0)
 
+#define PR_EEP(_s, _val)						\
+	do {								\
+		len += snprintf(buf + len, size - len, "%20s : %10d\n",	\
+				_s, (_val));				\
+	} while (0)
+
 #define SM(_v, _f)  (((_v) << _f##_S) & _f)
 #define MS(_v, _f)  (((_v) & _f) >> _f##_S)
 #define REG_RMW_FIELD(_a, _r, _f, _v) \
@@ -438,7 +444,6 @@ struct ath9k_hw_version {
 	u16 phyRev;
 	u16 analog5GhzRev;
 	u16 analog2GhzRev;
-	u16 subsysid;
 	enum ath_usb_dev usbdev;
 };
 
@@ -690,6 +695,7 @@ struct ath_hw {
 	enum nl80211_iftype opmode;
 	enum ath9k_power_mode power_mode;
 
+	s8 noise;
 	struct ath9k_hw_cal_data *caldata;
 	struct ath9k_pacal_info pacal_info;
 	struct ar5416Stats stats;
@@ -703,6 +709,7 @@ struct ath_hw {
 	u32 txdesc_interrupt_mask;
 	u32 txeol_interrupt_mask;
 	u32 txurn_interrupt_mask;
+	atomic_t intr_ref_cnt;
 	bool chip_fullsleep;
 	u32 atim_window;
 

+ 4 - 4
drivers/net/wireless/ath/ath9k/init.c

@@ -548,7 +548,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
 		sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
 }
 
-static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
+static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
 			    const struct ath_bus_ops *bus_ops)
 {
 	struct ath9k_platform_data *pdata = sc->dev->platform_data;
@@ -563,10 +563,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
 
 	ah->hw = sc->hw;
 	ah->hw_version.devid = devid;
-	ah->hw_version.subsysid = subsysid;
 	ah->reg_ops.read = ath9k_ioread32;
 	ah->reg_ops.write = ath9k_iowrite32;
 	ah->reg_ops.rmw = ath9k_reg_rmw;
+	atomic_set(&ah->intr_ref_cnt, -1);
 	sc->sc_ah = ah;
 
 	if (!pdata) {
@@ -743,7 +743,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 	SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
 }
 
-int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
+int ath9k_init_device(u16 devid, struct ath_softc *sc,
 		    const struct ath_bus_ops *bus_ops)
 {
 	struct ieee80211_hw *hw = sc->hw;
@@ -753,7 +753,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
 	struct ath_regulatory *reg;
 
 	/* Bring up device */
-	error = ath9k_init_softc(devid, sc, subsysid, bus_ops);
+	error = ath9k_init_softc(devid, sc, bus_ops);
 	if (error != 0)
 		goto error_init;
 

+ 12 - 4
drivers/net/wireless/ath/ath9k/mac.c

@@ -800,6 +800,11 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah)
 {
 	struct ath_common *common = ath9k_hw_common(ah);
 
+	if (!(ah->imask & ATH9K_INT_GLOBAL))
+		atomic_set(&ah->intr_ref_cnt, -1);
+	else
+		atomic_dec(&ah->intr_ref_cnt);
+
 	ath_dbg(common, ATH_DBG_INTERRUPT, "disable IER\n");
 	REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
 	(void) REG_READ(ah, AR_IER);
@@ -821,6 +826,13 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah)
 	if (!(ah->imask & ATH9K_INT_GLOBAL))
 		return;
 
+	if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
+		ath_dbg(common, ATH_DBG_INTERRUPT,
+			"Do not enable IER ref count %d\n",
+			atomic_read(&ah->intr_ref_cnt));
+		return;
+	}
+
 	if (AR_SREV_9340(ah))
 		sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
 
@@ -852,7 +864,6 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
 
 	ath_dbg(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
 
-	/* TODO: global int Ref count */
 	mask = ints & ATH9K_INT_COMMON;
 	mask2 = 0;
 
@@ -929,9 +940,6 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
 			REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER);
 	}
 
-	if (ints & ATH9K_INT_GLOBAL)
-		ath9k_hw_enable_interrupts(ah);
-
 	return;
 }
 EXPORT_SYMBOL(ath9k_hw_set_interrupts);

+ 7 - 3
drivers/net/wireless/ath/ath9k/main.c

@@ -163,7 +163,7 @@ static void ath_update_survey_nf(struct ath_softc *sc, int channel)
 
 	if (chan->noisefloor) {
 		survey->filled |= SURVEY_INFO_NOISE_DBM;
-		survey->noise = chan->noisefloor;
+		survey->noise = ath9k_hw_getchan_noise(ah, chan);
 	}
 }
 
@@ -294,6 +294,7 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
 	ath9k_cmn_update_txpow(ah, sc->curtxpow,
 			       sc->config.txpowlimit, &sc->curtxpow);
 	ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_hw_enable_interrupts(ah);
 
 	if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
 		if (sc->sc_flags & SC_OP_BEACONS)
@@ -706,8 +707,7 @@ void ath9k_tasklet(unsigned long data)
 		 */
 		ath_dbg(common, ATH_DBG_PS,
 			"TSFOOR - Sync with next Beacon\n");
-		sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC |
-				PS_TSFOOR_SYNC;
+		sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC;
 	}
 
 	if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
@@ -886,6 +886,7 @@ static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
 
 	ath9k_ps_wakeup(sc);
 	spin_lock_bh(&sc->sc_pcu_lock);
+	atomic_set(&ah->intr_ref_cnt, -1);
 
 	ath9k_hw_configpcipowersave(ah, 0, 0);
 
@@ -910,6 +911,7 @@ static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
 
 	/* Re-Enable  interrupts */
 	ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_hw_enable_interrupts(ah);
 
 	/* Enable LED */
 	ath9k_hw_cfg_output(ah, ah->led_pin,
@@ -1016,6 +1018,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
 		ath_set_beacon(sc);	/* restart beacons */
 
 	ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_hw_enable_interrupts(ah);
 
 	if (retry_tx) {
 		int i;
@@ -1130,6 +1133,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
 	/* Disable BMISS interrupt when we're not associated */
 	ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
 	ath9k_hw_set_interrupts(ah, ah->imask);
+	ath9k_hw_enable_interrupts(ah);
 
 	ieee80211_wake_queues(hw);
 

+ 1 - 3
drivers/net/wireless/ath/ath9k/pci.c

@@ -156,7 +156,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	struct ath_softc *sc;
 	struct ieee80211_hw *hw;
 	u8 csz;
-	u16 subsysid;
 	u32 val;
 	int ret = 0;
 	char hw_name[64];
@@ -250,8 +249,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	sc->irq = pdev->irq;
 
-	pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsysid);
-	ret = ath9k_init_device(id->device, sc, subsysid, &ath_pci_bus_ops);
+	ret = ath9k_init_device(id->device, sc, &ath_pci_bus_ops);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to initialize device\n");
 		goto err_init;

+ 2 - 2
drivers/net/wireless/ath/ath9k/rc.c

@@ -1484,7 +1484,7 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
 	if (rc->rate_table == NULL)
 		return 0;
 
-	max = 80 + rc->rate_table->rate_cnt * 1024 + 1;
+	max = 80 + rc->rate_table_size * 1024 + 1;
 	buf = kmalloc(max, GFP_KERNEL);
 	if (buf == NULL)
 		return -ENOMEM;
@@ -1494,7 +1494,7 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
 		       "HT", "MCS", "Rate",
 		       "Success", "Retries", "XRetries", "PER");
 
-	for (i = 0; i < rc->rate_table->rate_cnt; i++) {
+	for (i = 0; i < rc->rate_table_size; i++) {
 		u32 ratekbps = rc->rate_table->info[i].ratekbps;
 		struct ath_rc_stats *stats = &rc->rcstats[i];
 		char mcs[5];

+ 0 - 6
drivers/net/wireless/ath/ath9k/rc.h

@@ -221,12 +221,6 @@ struct ath_rate_priv {
 	struct ath_rc_stats rcstats[RATE_TABLE_SIZE];
 };
 
-enum ath9k_internal_frame_type {
-	ATH9K_IFT_NOT_INTERNAL,
-	ATH9K_IFT_PAUSE,
-	ATH9K_IFT_UNPAUSE
-};
-
 #ifdef CONFIG_ATH9K_RATE_CONTROL
 int ath_rate_control_register(void);
 void ath_rate_control_unregister(void);

+ 3 - 7
drivers/net/wireless/ath/ath9k/recv.c

@@ -601,7 +601,6 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
 		ath_dbg(common, ATH_DBG_PS,
 			"Reconfigure Beacon timers based on timestamp from the AP\n");
 		ath_set_beacon(sc);
-		sc->ps_flags &= ~PS_TSFOOR_SYNC;
 	}
 
 	if (ath_beacon_dtim_pending_cab(skb)) {
@@ -995,6 +994,8 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common,
 				   struct ieee80211_rx_status *rx_status,
 				   bool *decrypt_error)
 {
+	struct ath_hw *ah = common->ah;
+
 	memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
 
 	/*
@@ -1015,7 +1016,7 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common,
 
 	rx_status->band = hw->conf.channel->band;
 	rx_status->freq = hw->conf.channel->center_freq;
-	rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + rx_stats->rs_rssi;
+	rx_status->signal = ah->noise + rx_stats->rs_rssi;
 	rx_status->antenna = rx_stats->rs_antenna;
 	rx_status->flag |= RX_FLAG_MACTIME_MPDU;
 
@@ -1783,11 +1784,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 	struct ieee80211_rx_status *rxs;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_common *common = ath9k_hw_common(ah);
-	/*
-	 * The hw can technically differ from common->hw when using ath9k
-	 * virtual wiphy so to account for that we iterate over the active
-	 * wiphys and find the appropriate wiphy and therefore hw.
-	 */
 	struct ieee80211_hw *hw = sc->hw;
 	struct ieee80211_hdr *hdr;
 	int retval;

部分文件因为文件数量过多而无法显示