aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath6kl
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl')
-rw-r--r--drivers/net/wireless/ath/ath6kl/Kconfig62
-rw-r--r--drivers/net/wireless/ath/ath6kl/Makefile40
-rw-r--r--drivers/net/wireless/ath/ath6kl/bmi.c259
-rw-r--r--drivers/net/wireless/ath/ath6kl/bmi.h32
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c3063
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.h38
-rw-r--r--drivers/net/wireless/ath/ath6kl/common.h22
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.c346
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.h569
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.c1295
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.h90
-rw-r--r--drivers/net/wireless/ath/ath6kl/hif-ops.h114
-rw-r--r--drivers/net/wireless/ath/ath6kl/hif.c (renamed from drivers/net/wireless/ath/ath6kl/htc_hif.c)177
-rw-r--r--drivers/net/wireless/ath/ath6kl/hif.h78
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc-ops.h113
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc.h124
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_hif.h92
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_mbox.c (renamed from drivers/net/wireless/ath/ath6kl/htc.c)1001
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_pipe.c1711
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c1465
-rw-r--r--drivers/net/wireless/ath/ath6kl/main.c1176
-rw-r--r--drivers/net/wireless/ath/ath6kl/recovery.c160
-rw-r--r--drivers/net/wireless/ath/ath6kl/sdio.c783
-rw-r--r--drivers/net/wireless/ath/ath6kl/target.h26
-rw-r--r--drivers/net/wireless/ath/ath6kl/testmode.c107
-rw-r--r--drivers/net/wireless/ath/ath6kl/testmode.h14
-rw-r--r--drivers/net/wireless/ath/ath6kl/trace.c23
-rw-r--r--drivers/net/wireless/ath/ath6kl/trace.h332
-rw-r--r--drivers/net/wireless/ath/ath6kl/txrx.c919
-rw-r--r--drivers/net/wireless/ath/ath6kl/usb.c1264
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.c1753
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.h667
32 files changed, 13998 insertions, 3917 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig
index 3d5f8be20ea..9c125ff083f 100644
--- a/drivers/net/wireless/ath/ath6kl/Kconfig
+++ b/drivers/net/wireless/ath/ath6kl/Kconfig
@@ -1,15 +1,65 @@
config ATH6KL
- tristate "Atheros ath6kl support"
- depends on MMC
+ tristate "Atheros mobile chipsets support"
depends on CFG80211
+ ---help---
+ This module adds core support for wireless adapters based on
+ Atheros AR6003 and AR6004 chipsets. You still need separate
+ bus drivers for USB and SDIO to be able to use real devices.
+
+ If you choose to build it as a module, it will be called
+ ath6kl_core. Please note that AR6002 and AR6001 are not
+ supported by this driver.
+
+config ATH6KL_SDIO
+ tristate "Atheros ath6kl SDIO support"
+ depends on ATH6KL
+ depends on MMC
+ ---help---
+ This module adds support for wireless adapters based on
+ Atheros AR6003 and AR6004 chipsets running over SDIO. If you
+ choose to build it as a module, it will be called ath6kl_sdio.
+ Please note that AR6002 and AR6001 are not supported by this
+ driver.
+
+config ATH6KL_USB
+ tristate "Atheros ath6kl USB support"
+ depends on ATH6KL
+ depends on USB
---help---
This module adds support for wireless adapters based on
- Atheros AR6003 chipset running over SDIO. If you choose to
- build it as a module, it will be called ath6kl. Pls note
- that AR6002 and AR6001 are not supported by this driver.
+ Atheros AR6004 chipset and chipsets based on it running over
+ USB. If you choose to build it as a module, it will be
+ called ath6kl_usb.
config ATH6KL_DEBUG
bool "Atheros ath6kl debugging"
depends on ATH6KL
---help---
- Enables debug support
+ Enables ath6kl debug support, including debug messages
+ enabled with debug_mask module parameter and debugfs
+ interface.
+
+ If unsure, say Y to make it easier to debug problems.
+
+config ATH6KL_TRACING
+ bool "Atheros ath6kl tracing support"
+ depends on ATH6KL
+ depends on EVENT_TRACING
+ ---help---
+ Select this to ath6kl use tracing infrastructure which, for
+ example, can be enabled with help of trace-cmd. All debug
+ messages and commands are delivered to using individually
+ enablable trace points.
+
+ If unsure, say Y to make it easier to debug problems.
+
+config ATH6KL_REGDOMAIN
+ bool "Atheros ath6kl regdomain support"
+ depends on ATH6KL
+ depends on CFG80211_CERTIFICATION_ONUS
+ ---help---
+ Enabling this makes it possible to change the regdomain in
+ the firmware. This can be only enabled if regulatory requirements
+ are taken into account.
+
+ If unsure, say N.
diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile
index 8f7a0d1c290..dc2b3b46781 100644
--- a/drivers/net/wireless/ath/ath6kl/Makefile
+++ b/drivers/net/wireless/ath/ath6kl/Makefile
@@ -1,5 +1,6 @@
#------------------------------------------------------------------------------
-# Copyright (c) 2004-2010 Atheros Communications Inc.
+# Copyright (c) 2004-2011 Atheros Communications Inc.
+# Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
# All rights reserved.
#
#
@@ -21,17 +22,28 @@
# 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 += sdio.o
-ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o
+obj-$(CONFIG_ATH6KL) += ath6kl_core.o
+ath6kl_core-y += debug.o
+ath6kl_core-y += hif.o
+ath6kl_core-y += htc_mbox.o
+ath6kl_core-y += htc_pipe.o
+ath6kl_core-y += bmi.o
+ath6kl_core-y += cfg80211.o
+ath6kl_core-y += init.o
+ath6kl_core-y += main.o
+ath6kl_core-y += txrx.o
+ath6kl_core-y += wmi.o
+ath6kl_core-y += core.o
+ath6kl_core-y += recovery.o
-ccflags-y += -D__CHECK_ENDIAN__
+ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o
+ath6kl_core-$(CONFIG_ATH6KL_TRACING) += trace.o
+
+obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o
+ath6kl_sdio-y += sdio.o
+
+obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o
+ath6kl_usb-y += usb.o
+
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c
index c5d11cc536e..334dbd834b3 100644
--- a/drivers/net/wireless/ath/ath6kl/bmi.c
+++ b/drivers/net/wireless/ath/ath6kl/bmi.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -19,165 +20,6 @@
#include "target.h"
#include "debug.h"
-static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar)
-{
- u32 addr;
- unsigned long timeout;
- int ret;
-
- ar->bmi.cmd_credits = 0;
-
- /* Read the counter register to get the command credits */
- addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4;
-
- timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
- while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
-
- /*
- * Hit the credit counter with a 4-byte access, the first byte
- * read will hit the counter and cause a decrement, while the
- * remaining 3 bytes has no effect. The rationale behind this
- * is to make all HIF accesses 4-byte aligned.
- */
- ret = hif_read_write_sync(ar, addr,
- (u8 *)&ar->bmi.cmd_credits, 4,
- HIF_RD_SYNC_BYTE_INC);
- if (ret) {
- ath6kl_err("Unable to decrement the command credit count register: %d\n",
- ret);
- return ret;
- }
-
- /* The counter is only 8 bits.
- * Ignore anything in the upper 3 bytes
- */
- ar->bmi.cmd_credits &= 0xFF;
- }
-
- if (!ar->bmi.cmd_credits) {
- ath6kl_err("bmi communication timeout\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar)
-{
- unsigned long timeout;
- u32 rx_word = 0;
- int ret = 0;
-
- timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
- while (time_before(jiffies, timeout) && !rx_word) {
- ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS,
- (u8 *)&rx_word, sizeof(rx_word),
- HIF_RD_SYNC_BYTE_INC);
- if (ret) {
- ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n");
- return ret;
- }
-
- /* all we really want is one bit */
- rx_word &= (1 << ENDPOINT1);
- }
-
- if (!rx_word) {
- ath6kl_err("bmi_recv_buf FIFO empty\n");
- return -EINVAL;
- }
-
- return ret;
-}
-
-static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len)
-{
- int ret;
- u32 addr;
-
- ret = ath6kl_get_bmi_cmd_credits(ar);
- if (ret)
- return ret;
-
- addr = ar->mbox_info.htc_addr;
-
- ret = hif_read_write_sync(ar, addr, buf, len,
- HIF_WR_SYNC_BYTE_INC);
- if (ret)
- ath6kl_err("unable to send the bmi data to the device\n");
-
- return ret;
-}
-
-static int ath6kl_bmi_recv_buf(struct ath6kl *ar, u8 *buf, u32 len)
-{
- int ret;
- u32 addr;
-
- /*
- * During normal bootup, small reads may be required.
- * Rather than issue an HIF Read and then wait as the Target
- * adds successive bytes to the FIFO, we wait here until
- * we know that response data is available.
- *
- * This allows us to cleanly timeout on an unexpected
- * Target failure rather than risk problems at the HIF level.
- * In particular, this avoids SDIO timeouts and possibly garbage
- * data on some host controllers. And on an interconnect
- * such as Compact Flash (as well as some SDIO masters) which
- * does not provide any indication on data timeout, it avoids
- * a potential hang or garbage response.
- *
- * Synchronization is more difficult for reads larger than the
- * size of the MBOX FIFO (128B), because the Target is unable
- * to push the 129th byte of data until AFTER the Host posts an
- * HIF Read and removes some FIFO data. So for large reads the
- * Host proceeds to post an HIF Read BEFORE all the data is
- * actually available to read. Fortunately, large BMI reads do
- * not occur in practice -- they're supported for debug/development.
- *
- * So Host/Target BMI synchronization is divided into these cases:
- * CASE 1: length < 4
- * Should not happen
- *
- * CASE 2: 4 <= length <= 128
- * Wait for first 4 bytes to be in FIFO
- * If CONSERVATIVE_BMI_READ is enabled, also wait for
- * a BMI command credit, which indicates that the ENTIRE
- * response is available in the the FIFO
- *
- * CASE 3: length > 128
- * Wait for the first 4 bytes to be in FIFO
- *
- * For most uses, a small timeout should be sufficient and we will
- * usually see a response quickly; but there may be some unusual
- * (debug) cases of BMI_EXECUTE where we want an larger timeout.
- * For now, we use an unbounded busy loop while waiting for
- * BMI_EXECUTE.
- *
- * If BMI_EXECUTE ever needs to support longer-latency execution,
- * especially in production, this code needs to be enhanced to sleep
- * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently
- * a function of Host processor speed.
- */
- if (len >= 4) { /* NB: Currently, always true */
- ret = ath6kl_bmi_get_rx_lkahd(ar);
- if (ret)
- return ret;
- }
-
- addr = ar->mbox_info.htc_addr;
- ret = hif_read_write_sync(ar, addr, buf, len,
- HIF_RD_SYNC_BYTE_INC);
- if (ret) {
- ath6kl_err("Unable to read the bmi data from the device: %d\n",
- ret);
- return ret;
- }
-
- return 0;
-}
-
int ath6kl_bmi_done(struct ath6kl *ar)
{
int ret;
@@ -190,14 +32,12 @@ int ath6kl_bmi_done(struct ath6kl *ar)
ar->bmi.done_sent = true;
- ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
+ ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
if (ret) {
ath6kl_err("Unable to send bmi done: %d\n", ret);
return ret;
}
- ath6kl_bmi_cleanup(ar);
-
return 0;
}
@@ -212,14 +52,20 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
return -EACCES;
}
- ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid));
+ ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
if (ret) {
ath6kl_err("Unable to send get target info: %d\n", ret);
return ret;
}
- ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version,
- sizeof(targ_info->version));
+ if (ar->hif_type == ATH6KL_HIF_TYPE_USB) {
+ ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info,
+ sizeof(*targ_info));
+ } else {
+ ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version,
+ sizeof(targ_info->version));
+ }
+
if (ret) {
ath6kl_err("Unable to recv target info: %d\n", ret);
return ret;
@@ -227,7 +73,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
/* Determine how many bytes are in the Target's targ_info */
- ret = ath6kl_bmi_recv_buf(ar,
+ ret = ath6kl_hif_bmi_read(ar,
(u8 *)&targ_info->byte_count,
sizeof(targ_info->byte_count));
if (ret) {
@@ -246,7 +92,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
}
/* Read the remainder of the targ_info */
- ret = ath6kl_bmi_recv_buf(ar,
+ ret = ath6kl_hif_bmi_read(ar,
((u8 *)targ_info) +
sizeof(targ_info->byte_count),
sizeof(*targ_info) -
@@ -260,7 +106,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
}
ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
- targ_info->version, targ_info->type);
+ targ_info->version, targ_info->type);
return 0;
}
@@ -278,8 +124,8 @@ int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
return -EACCES;
}
- size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len);
- if (size > MAX_BMI_CMDBUF_SZ) {
+ size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len);
+ if (size > ar->bmi.max_cmd_size) {
WARN_ON(1);
return -EINVAL;
}
@@ -292,8 +138,8 @@ int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
len_remain = len;
while (len_remain) {
- rx_len = (len_remain < BMI_DATASZ_MAX) ?
- len_remain : BMI_DATASZ_MAX;
+ rx_len = (len_remain < ar->bmi.max_data_size) ?
+ len_remain : ar->bmi.max_data_size;
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
@@ -302,13 +148,13 @@ int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
offset += sizeof(len);
- ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n",
ret);
return ret;
}
- ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len);
+ ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len);
if (ret) {
ath6kl_err("Unable to read from the device: %d\n",
ret);
@@ -328,7 +174,7 @@ int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
u32 offset;
u32 len_remain, tx_len;
const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
- u8 aligned_buf[BMI_DATASZ_MAX];
+ u8 aligned_buf[400];
u8 *src;
if (ar->bmi.done_sent) {
@@ -336,21 +182,24 @@ int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
return -EACCES;
}
- if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) {
+ if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) {
WARN_ON(1);
return -EINVAL;
}
- memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header);
+ if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf)))
+ return -E2BIG;
+
+ memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header);
ath6kl_dbg(ATH6KL_DBG_BMI,
- "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
+ "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 < (ar->bmi.max_data_size - header)) {
if (len_remain & 3) {
/* align it with 4 bytes */
len_remain = len_remain +
@@ -360,7 +209,7 @@ int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
}
tx_len = len_remain;
} else {
- tx_len = (BMI_DATASZ_MAX - header);
+ tx_len = (ar->bmi.max_data_size - header);
}
offset = 0;
@@ -373,7 +222,7 @@ int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
offset += tx_len;
- ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n",
ret);
@@ -398,7 +247,7 @@ int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
}
size = sizeof(cid) + sizeof(addr) + sizeof(param);
- if (size > MAX_BMI_CMDBUF_SZ) {
+ if (size > ar->bmi.max_cmd_size) {
WARN_ON(1);
return -EINVAL;
}
@@ -415,13 +264,13 @@ int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
offset += sizeof(*param);
- ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n", ret);
return ret;
}
- ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
+ ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
if (ret) {
ath6kl_err("Unable to read from the device: %d\n", ret);
return ret;
@@ -445,7 +294,7 @@ int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
}
size = sizeof(cid) + sizeof(addr);
- if (size > MAX_BMI_CMDBUF_SZ) {
+ if (size > ar->bmi.max_cmd_size) {
WARN_ON(1);
return -EINVAL;
}
@@ -459,7 +308,7 @@ int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
- ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n", ret);
return ret;
@@ -481,7 +330,7 @@ int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
}
size = sizeof(cid) + sizeof(addr);
- if (size > MAX_BMI_CMDBUF_SZ) {
+ if (size > ar->bmi.max_cmd_size) {
WARN_ON(1);
return -EINVAL;
}
@@ -495,13 +344,13 @@ int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
- ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n", ret);
return ret;
}
- ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
+ ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
if (ret) {
ath6kl_err("Unable to read from the device: %d\n", ret);
return ret;
@@ -524,7 +373,7 @@ int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
}
size = sizeof(cid) + sizeof(addr) + sizeof(param);
- if (size > MAX_BMI_CMDBUF_SZ) {
+ if (size > ar->bmi.max_cmd_size) {
WARN_ON(1);
return -EINVAL;
}
@@ -542,7 +391,7 @@ int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
offset += sizeof(param);
- ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n", ret);
return ret;
@@ -565,8 +414,8 @@ int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
return -EACCES;
}
- size = BMI_DATASZ_MAX + header;
- if (size > MAX_BMI_CMDBUF_SZ) {
+ size = ar->bmi.max_data_size + header;
+ if (size > ar->bmi.max_cmd_size) {
WARN_ON(1);
return -EINVAL;
}
@@ -577,8 +426,8 @@ int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
len_remain = len;
while (len_remain) {
- tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ?
- len_remain : (BMI_DATASZ_MAX - header);
+ tx_len = (len_remain < (ar->bmi.max_data_size - header)) ?
+ len_remain : (ar->bmi.max_data_size - header);
offset = 0;
memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
@@ -586,10 +435,10 @@ int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
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);
+ tx_len);
offset += tx_len;
- ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to write to the device: %d\n",
ret);
@@ -615,7 +464,7 @@ int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
}
size = sizeof(cid) + sizeof(addr);
- if (size > MAX_BMI_CMDBUF_SZ) {
+ if (size > ar->bmi.max_cmd_size) {
WARN_ON(1);
return -EINVAL;
}
@@ -631,7 +480,7 @@ int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
offset += sizeof(addr);
- ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset);
+ ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
if (ret) {
ath6kl_err("Unable to start LZ stream to the device: %d\n",
ret);
@@ -672,10 +521,20 @@ int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
return ret;
}
+void ath6kl_bmi_reset(struct ath6kl *ar)
+{
+ ar->bmi.done_sent = false;
+}
+
int ath6kl_bmi_init(struct ath6kl *ar)
{
- ar->bmi.cmd_buf = kzalloc(MAX_BMI_CMDBUF_SZ, GFP_ATOMIC);
+ if (WARN_ON(ar->bmi.max_data_size == 0))
+ return -EINVAL;
+
+ /* cmd + addr + len + data_size */
+ ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3);
+ ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_ATOMIC);
if (!ar->bmi.cmd_buf)
return -ENOMEM;
diff --git a/drivers/net/wireless/ath/ath6kl/bmi.h b/drivers/net/wireless/ath/ath6kl/bmi.h
index 96851d5df24..18fdd69e1f7 100644
--- a/drivers/net/wireless/ath/ath6kl/bmi.h
+++ b/drivers/net/wireless/ath/ath6kl/bmi.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -44,12 +45,6 @@
* BMI handles all required Target-side cache flushing.
*/
-#define MAX_BMI_CMDBUF_SZ (BMI_DATASZ_MAX + \
- (sizeof(u32) * 3 /* cmd + addr + len */))
-
-/* Maximum data size used for BMI transfers */
-#define BMI_DATASZ_MAX 256
-
/* BMI Commands */
#define BMI_NO_COMMAND 0
@@ -228,8 +223,33 @@ struct ath6kl_bmi_target_info {
__le32 type; /* target type */
} __packed;
+#define ath6kl_bmi_write_hi32(ar, item, val) \
+ ({ \
+ u32 addr; \
+ __le32 v; \
+ \
+ addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \
+ v = cpu_to_le32(val); \
+ ath6kl_bmi_write(ar, addr, (u8 *) &v, sizeof(v)); \
+ })
+
+#define ath6kl_bmi_read_hi32(ar, item, val) \
+ ({ \
+ u32 addr, *check_type = val; \
+ __le32 tmp; \
+ int ret; \
+ \
+ (void) (check_type == val); \
+ addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \
+ ret = ath6kl_bmi_read(ar, addr, (u8 *) &tmp, 4); \
+ *val = le32_to_cpu(tmp); \
+ ret; \
+ })
+
int ath6kl_bmi_init(struct ath6kl *ar);
void ath6kl_bmi_cleanup(struct ath6kl *ar);
+void ath6kl_bmi_reset(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);
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 3aff36bad5d..0e26f4a34fd 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,16 +15,18 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/moduleparam.h>
+#include <linux/inetdevice.h>
+#include <linux/export.h>
+
#include "core.h"
#include "cfg80211.h"
#include "debug.h"
#include "hif-ops.h"
#include "testmode.h"
-static unsigned int ath6kl_p2p;
-
-module_param(ath6kl_p2p, uint, 0644);
-
#define RATETAB_ENT(_rate, _rateid, _flags) { \
.bitrate = (_rate), \
.flags = (_flags), \
@@ -48,6 +51,13 @@ module_param(ath6kl_p2p, uint, 0644);
.max_power = 30, \
}
+#define DEFAULT_BG_SCAN_PERIOD 60
+
+struct ath6kl_cfg80211_match_probe_ssid {
+ struct cfg80211_ssid ssid;
+ u8 flag;
+};
+
static struct ieee80211_rate ath6kl_rates[] = {
RATETAB_ENT(10, 0x1, 0),
RATETAB_ENT(20, 0x2, 0),
@@ -68,6 +78,11 @@ static struct ieee80211_rate ath6kl_rates[] = {
#define ath6kl_g_rates (ath6kl_rates + 0)
#define ath6kl_g_rates_size 12
+#define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20
+#define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
+ IEEE80211_HT_CAP_SGI_20 | \
+ IEEE80211_HT_CAP_SGI_40)
+
static struct ieee80211_channel ath6kl_2ghz_channels[] = {
CHAN2G(1, 2412, 0),
CHAN2G(2, 2417, 0),
@@ -112,6 +127,8 @@ static struct ieee80211_supported_band ath6kl_band_2ghz = {
.channels = ath6kl_2ghz_channels,
.n_bitrates = ath6kl_g_rates_size,
.bitrates = ath6kl_g_rates,
+ .ht_cap.cap = ath6kl_g_htcap,
+ .ht_cap.ht_supported = true,
};
static struct ieee80211_supported_band ath6kl_band_5ghz = {
@@ -119,19 +136,54 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = {
.channels = ath6kl_5ghz_a_channels,
.n_bitrates = ath6kl_a_rates_size,
.bitrates = ath6kl_a_rates,
+ .ht_cap.cap = ath6kl_a_htcap,
+ .ht_cap.ht_supported = true,
};
-static int ath6kl_set_wpa_version(struct ath6kl *ar,
+#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
+
+/* returns true if scheduled scan was stopped */
+static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
+{
+ struct ath6kl *ar = vif->ar;
+
+ if (!test_and_clear_bit(SCHED_SCANNING, &vif->flags))
+ return false;
+
+ del_timer_sync(&vif->sched_scan_timer);
+
+ if (ar->state == ATH6KL_STATE_RECOVERY)
+ return true;
+
+ ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, false);
+
+ return true;
+}
+
+static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
+{
+ struct ath6kl *ar = vif->ar;
+ bool stopped;
+
+ stopped = __ath6kl_cfg80211_sscan_stop(vif);
+
+ if (!stopped)
+ return;
+
+ cfg80211_sched_scan_stopped(ar->wiphy);
+}
+
+static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
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;
+ vif->auth_mode = NONE_AUTH;
} else if (wpa_version & NL80211_WPA_VERSION_2) {
- ar->auth_mode = WPA2_AUTH;
+ vif->auth_mode = WPA2_AUTH;
} else if (wpa_version & NL80211_WPA_VERSION_1) {
- ar->auth_mode = WPA_AUTH;
+ vif->auth_mode = WPA_AUTH;
} else {
ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
return -ENOTSUPP;
@@ -140,40 +192,39 @@ static int ath6kl_set_wpa_version(struct ath6kl *ar,
return 0;
}
-static int ath6kl_set_auth_type(struct ath6kl *ar,
+static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
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;
+ vif->dot11_auth_mode = OPEN_AUTH;
break;
case NL80211_AUTHTYPE_SHARED_KEY:
- ar->dot11_auth_mode = SHARED_AUTH;
+ vif->dot11_auth_mode = SHARED_AUTH;
break;
case NL80211_AUTHTYPE_NETWORK_EAP:
- ar->dot11_auth_mode = LEAP_AUTH;
+ vif->dot11_auth_mode = LEAP_AUTH;
break;
case NL80211_AUTHTYPE_AUTOMATIC:
- ar->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
+ vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
break;
default:
- ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
+ ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
return -ENOTSUPP;
}
return 0;
}
-static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast)
+static int ath6kl_set_cipher(struct ath6kl_vif *vif, 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_crypto_len;
+ u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
+ u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
+ &vif->grp_crypto_len;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
__func__, cipher, ucast);
@@ -200,6 +251,10 @@ static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast)
*ar_cipher = AES_CRYPT;
*ar_cipher_len = 0;
break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ *ar_cipher = WAPI_CRYPT;
+ *ar_cipher_len = 0;
+ break;
default:
ath6kl_err("cipher 0x%x not supported\n", cipher);
return -ENOTSUPP;
@@ -208,28 +263,35 @@ static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast)
return 0;
}
-static void ath6kl_set_key_mgmt(struct ath6kl *ar, u32 key_mgmt)
+static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, 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;
+ if (vif->auth_mode == WPA_AUTH)
+ vif->auth_mode = WPA_PSK_AUTH;
+ else if (vif->auth_mode == WPA2_AUTH)
+ vif->auth_mode = WPA2_PSK_AUTH;
+ } else if (key_mgmt == 0x00409600) {
+ if (vif->auth_mode == WPA_AUTH)
+ vif->auth_mode = WPA_AUTH_CCKM;
+ else if (vif->auth_mode == WPA2_AUTH)
+ vif->auth_mode = WPA2_AUTH_CCKM;
} else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
- ar->auth_mode = NONE_AUTH;
+ vif->auth_mode = NONE_AUTH;
}
}
-static bool ath6kl_cfg80211_ready(struct ath6kl *ar)
+static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
{
+ struct ath6kl *ar = vif->ar;
+
if (!test_bit(WMI_READY, &ar->flag)) {
ath6kl_err("wmi is not ready\n");
return false;
}
- if (!test_bit(WLAN_ENABLED, &ar->flag)) {
+ if (!test_bit(WLAN_ENABLED, &vif->flags)) {
ath6kl_err("wlan disabled\n");
return false;
}
@@ -237,15 +299,173 @@ static bool ath6kl_cfg80211_ready(struct ath6kl *ar)
return true;
}
+static bool ath6kl_is_wpa_ie(const u8 *pos)
+{
+ return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ pos[2] == 0x00 && pos[3] == 0x50 &&
+ pos[4] == 0xf2 && pos[5] == 0x01;
+}
+
+static bool ath6kl_is_rsn_ie(const u8 *pos)
+{
+ return pos[0] == WLAN_EID_RSN;
+}
+
+static bool ath6kl_is_wps_ie(const u8 *pos)
+{
+ return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ pos[1] >= 4 &&
+ pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
+ pos[5] == 0x04);
+}
+
+static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
+ size_t ies_len)
+{
+ struct ath6kl *ar = vif->ar;
+ const u8 *pos;
+ u8 *buf = NULL;
+ size_t len = 0;
+ int ret;
+
+ /*
+ * Clear previously set flag
+ */
+
+ ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
+
+ /*
+ * Filter out RSN/WPA IE(s)
+ */
+
+ if (ies && ies_len) {
+ buf = kmalloc(ies_len, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ pos = ies;
+
+ while (pos + 1 < ies + ies_len) {
+ if (pos + 2 + pos[1] > ies + ies_len)
+ break;
+ if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
+ memcpy(buf + len, pos, 2 + pos[1]);
+ len += 2 + pos[1];
+ }
+
+ if (ath6kl_is_wps_ie(pos))
+ ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
+
+ pos += 2 + pos[1];
+ }
+ }
+
+ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
+ WMI_FRAME_ASSOC_REQ, buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
+{
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ *nw_type = INFRA_NETWORK;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ *nw_type = ADHOC_NETWORK;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ *nw_type = AP_NETWORK;
+ break;
+ default:
+ ath6kl_err("invalid interface type %u\n", type);
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
+ u8 *if_idx, u8 *nw_type)
+{
+ int i;
+
+ if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
+ return false;
+
+ if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
+ ar->num_vif))
+ return false;
+
+ if (type == NL80211_IFTYPE_STATION ||
+ type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
+ for (i = 0; i < ar->vif_max; i++) {
+ if ((ar->avail_idx_map) & BIT(i)) {
+ *if_idx = i;
+ return true;
+ }
+ }
+ }
+
+ if (type == NL80211_IFTYPE_P2P_CLIENT ||
+ type == NL80211_IFTYPE_P2P_GO) {
+ for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
+ if ((ar->avail_idx_map) & BIT(i)) {
+ *if_idx = i;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool ath6kl_is_tx_pending(struct ath6kl *ar)
+{
+ return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0;
+}
+
+static void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif,
+ bool enable)
+{
+ int err;
+
+ if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag)))
+ return;
+
+ if (vif->nw_type != INFRA_NETWORK)
+ return;
+
+ if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE,
+ vif->ar->fw_capabilities))
+ return;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n",
+ enable ? "enable" : "disable");
+
+ err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi,
+ vif->fw_vif_idx, enable);
+ if (err)
+ ath6kl_err("failed to %s enhanced bmiss detection: %d\n",
+ enable ? "enable" : "disable", err);
+}
+
static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme)
{
struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
int status;
+ u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
+ u16 interval;
- ar->sme_state = SME_CONNECTING;
+ ath6kl_cfg80211_sscan_disable(vif);
- if (!ath6kl_cfg80211_ready(ar))
+ vif->sme_state = SME_CONNECTING;
+
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
@@ -276,8 +496,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
* 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);
+ ath6kl_is_tx_pending(ar),
+ WMI_TIMEOUT);
if (signal_pending(current)) {
ath6kl_err("cmd queue drain timeout\n");
up(&ar->sem);
@@ -285,12 +505,22 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
}
}
- 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);
+ status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
+ if (status) {
+ up(&ar->sem);
+ return status;
+ }
+
+ if (sme->ie == NULL || sme->ie_len == 0)
+ ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
+
+ if (test_bit(CONNECTED, &vif->flags) &&
+ vif->ssid_len == sme->ssid_len &&
+ !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
+ vif->reconnect_flag = true;
+ status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
+ vif->req_bssid,
+ vif->ch_hint);
up(&ar->sem);
if (status) {
@@ -298,102 +528,134 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
return -EIO;
}
return 0;
- } else if (ar->ssid_len == sme->ssid_len &&
- !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) {
- ath6kl_disconnect(ar);
+ } else if (vif->ssid_len == sme->ssid_len &&
+ !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
+ ath6kl_disconnect(vif);
}
- memset(ar->ssid, 0, sizeof(ar->ssid));
- ar->ssid_len = sme->ssid_len;
- memcpy(ar->ssid, sme->ssid, sme->ssid_len);
+ memset(vif->ssid, 0, sizeof(vif->ssid));
+ vif->ssid_len = sme->ssid_len;
+ memcpy(vif->ssid, sme->ssid, sme->ssid_len);
if (sme->channel)
- ar->ch_hint = sme->channel->center_freq;
+ vif->ch_hint = sme->channel->center_freq;
- memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+ memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
- memcpy(ar->req_bssid, sme->bssid, sizeof(ar->req_bssid));
+ memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
- ath6kl_set_wpa_version(ar, sme->crypto.wpa_versions);
+ ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
- status = ath6kl_set_auth_type(ar, sme->auth_type);
+ status = ath6kl_set_auth_type(vif, 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);
+ ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
else
- ath6kl_set_cipher(ar, 0, true);
+ ath6kl_set_cipher(vif, 0, true);
- ath6kl_set_cipher(ar, sme->crypto.cipher_group, false);
+ ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
if (sme->crypto.n_akm_suites)
- ath6kl_set_key_mgmt(ar, sme->crypto.akm_suites[0]);
+ ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
if ((sme->key_len) &&
- (ar->auth_mode == NONE_AUTH) && (ar->prwise_crypto == WEP_CRYPT)) {
+ (vif->auth_mode == NONE_AUTH) &&
+ (vif->prwise_crypto == WEP_CRYPT)) {
struct ath6kl_key *key = NULL;
- if (sme->key_idx < WMI_MIN_KEY_INDEX ||
- sme->key_idx > WMI_MAX_KEY_INDEX) {
+ if (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 = &vif->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;
+ key->cipher = vif->prwise_crypto;
+ vif->def_txkey_index = sme->key_idx;
- ath6kl_wmi_addkey_cmd(ar->wmi, sme->key_idx,
- ar->prwise_crypto,
+ ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
+ vif->prwise_crypto,
GROUP_USAGE | TX_USAGE,
key->key_len,
- NULL,
+ NULL, 0,
key->key, KEY_OP_INIT_VAL, NULL,
NO_SYNC_WMIFLAG);
}
if (!ar->usr_bss_filter) {
- clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
- if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) {
+ clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
+ if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+ ALL_BSS_FILTER, 0) != 0) {
ath6kl_err("couldn't set bss filtering\n");
up(&ar->sem);
return -EIO;
}
}
- ar->nw_type = ar->next_mode;
+ vif->nw_type = vif->next_mode;
+
+ /* enable enhanced bmiss detection if applicable */
+ ath6kl_cfg80211_sta_bmiss_enhance(vif, true);
+
+ if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+ nw_subtype = SUBTYPE_P2PCLIENT;
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_crypto_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_crypto_len,
- ar->ssid_len, ar->ssid,
- ar->req_bssid, ar->ch_hint,
- ar->connect_ctrl_flags);
+ vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
+ vif->prwise_crypto_len, vif->grp_crypto,
+ vif->grp_crypto_len, vif->ch_hint);
+
+ vif->reconnect_flag = 0;
+
+ if (vif->nw_type == INFRA_NETWORK) {
+ interval = max_t(u16, vif->listen_intvl_t,
+ ATH6KL_MAX_WOW_LISTEN_INTL);
+ status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
+ interval,
+ 0);
+ if (status) {
+ ath6kl_err("couldn't set listen intervel\n");
+ up(&ar->sem);
+ return status;
+ }
+ }
+
+ status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
+ vif->dot11_auth_mode, vif->auth_mode,
+ vif->prwise_crypto,
+ vif->prwise_crypto_len,
+ vif->grp_crypto, vif->grp_crypto_len,
+ vif->ssid_len, vif->ssid,
+ vif->req_bssid, vif->ch_hint,
+ ar->connect_ctrl_flags, nw_subtype);
+
+ if (sme->bg_scan_period == 0) {
+ /* disable background scan if period is 0 */
+ sme->bg_scan_period = 0xffff;
+ } else if (sme->bg_scan_period == -1) {
+ /* configure default value if not specified */
+ sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+ }
+
+ ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0,
+ sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
up(&ar->sem);
if (status == -EINVAL) {
- memset(ar->ssid, 0, sizeof(ar->ssid));
- ar->ssid_len = 0;
+ memset(vif->ssid, 0, sizeof(vif->ssid));
+ vif->ssid_len = 0;
ath6kl_err("invalid request\n");
return -ENOENT;
} else if (status) {
@@ -402,28 +664,42 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
}
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,
+ ((vif->auth_mode == WPA_PSK_AUTH) ||
+ (vif->auth_mode == WPA2_PSK_AUTH))) {
+ mod_timer(&vif->disconnect_timer,
jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
}
ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
- set_bit(CONNECT_PEND, &ar->flag);
+ set_bit(CONNECT_PEND, &vif->flags);
return 0;
}
-static int ath6kl_add_bss_if_needed(struct ath6kl *ar, const u8 *bssid,
- struct ieee80211_channel *chan,
- const u8 *beacon_ie, size_t beacon_ie_len)
+static struct cfg80211_bss *
+ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
+ enum network_type nw_type,
+ const u8 *bssid,
+ struct ieee80211_channel *chan,
+ const u8 *beacon_ie,
+ size_t beacon_ie_len)
{
+ struct ath6kl *ar = vif->ar;
struct cfg80211_bss *bss;
+ u16 cap_mask, cap_val;
u8 *ie;
- bss = cfg80211_get_bss(ar->wdev->wiphy, chan, bssid,
- ar->ssid, ar->ssid_len, WLAN_CAPABILITY_ESS,
- WLAN_CAPABILITY_ESS);
+ if (nw_type & ADHOC_NETWORK) {
+ cap_mask = WLAN_CAPABILITY_IBSS;
+ cap_val = WLAN_CAPABILITY_IBSS;
+ } else {
+ cap_mask = WLAN_CAPABILITY_ESS;
+ cap_val = WLAN_CAPABILITY_ESS;
+ }
+
+ bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
+ vif->ssid, vif->ssid_len,
+ cap_mask, cap_val);
if (bss == NULL) {
/*
* Since cfg80211 may not yet know about the BSS,
@@ -433,35 +709,29 @@ static int ath6kl_add_bss_if_needed(struct ath6kl *ar, const u8 *bssid,
* Prepend SSID element since it is not included in the Beacon
* IEs from the target.
*/
- ie = kmalloc(2 + ar->ssid_len + beacon_ie_len, GFP_KERNEL);
+ ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
if (ie == NULL)
- return -ENOMEM;
+ return NULL;
ie[0] = WLAN_EID_SSID;
- ie[1] = ar->ssid_len;
- memcpy(ie + 2, ar->ssid, ar->ssid_len);
- memcpy(ie + 2 + ar->ssid_len, beacon_ie, beacon_ie_len);
- bss = cfg80211_inform_bss(ar->wdev->wiphy, chan,
- bssid, 0, WLAN_CAPABILITY_ESS, 100,
- ie, 2 + ar->ssid_len + beacon_ie_len,
+ ie[1] = vif->ssid_len;
+ memcpy(ie + 2, vif->ssid, vif->ssid_len);
+ memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
+ bss = cfg80211_inform_bss(ar->wiphy, chan,
+ bssid, 0, cap_val, 100,
+ ie, 2 + vif->ssid_len + beacon_ie_len,
0, GFP_KERNEL);
if (bss)
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for "
- "%pM prior to indicating connect/roamed "
- "event\n", bssid);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "added bss %pM to cfg80211\n", bssid);
kfree(ie);
- } else
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
- "entry\n");
-
- if (bss == NULL)
- return -ENOMEM;
-
- cfg80211_put_bss(bss);
+ } else {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
+ }
- return 0;
+ return bss;
}
-void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
+void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
u8 *bssid, u16 listen_intvl,
u16 beacon_intvl,
enum network_type nw_type,
@@ -469,6 +739,8 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
u8 assoc_resp_len, u8 *assoc_info)
{
struct ieee80211_channel *chan;
+ struct ath6kl *ar = vif->ar;
+ struct cfg80211_bss *bss;
/* capinfo + listen interval */
u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
@@ -487,11 +759,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
* Store Beacon interval here; DTIM period will be available only once
* a Beacon frame from the AP is seen.
*/
- ar->assoc_bss_beacon_int = beacon_intvl;
- clear_bit(DTIM_PERIOD_AVAIL, &ar->flag);
+ vif->assoc_bss_beacon_int = beacon_intvl;
+ clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
if (nw_type & ADHOC_NETWORK) {
- if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
+ if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in ibss mode\n", __func__);
return;
@@ -499,53 +771,58 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
}
if (nw_type & INFRA_NETWORK) {
- if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
- ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
+ if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
+ vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in station mode\n", __func__);
return;
}
}
- chan = ieee80211_get_channel(ar->wdev->wiphy, (int) channel);
+ chan = ieee80211_get_channel(ar->wiphy, (int) channel);
-
- if (nw_type & ADHOC_NETWORK) {
- cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
+ bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
+ assoc_info, beacon_ie_len);
+ if (!bss) {
+ ath6kl_err("could not add cfg80211 bss entry\n");
return;
}
- if (ath6kl_add_bss_if_needed(ar, bssid, chan, assoc_info,
- beacon_ie_len) < 0) {
- ath6kl_err("could not add cfg80211 bss entry for "
- "connect/roamed notification\n");
+ if (nw_type & ADHOC_NETWORK) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
+ nw_type & ADHOC_CREATOR ? "creator" : "joiner");
+ cfg80211_ibss_joined(vif->ndev, bssid, chan, GFP_KERNEL);
+ cfg80211_put_bss(ar->wiphy, bss);
return;
}
- if (ar->sme_state == SME_CONNECTING) {
+ if (vif->sme_state == SME_CONNECTING) {
/* inform connect result to cfg80211 */
- ar->sme_state = SME_CONNECTED;
- cfg80211_connect_result(ar->net_dev, bssid,
+ vif->sme_state = SME_CONNECTED;
+ cfg80211_connect_result(vif->ndev, 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) {
+ cfg80211_put_bss(ar->wiphy, bss);
+ } else if (vif->sme_state == SME_CONNECTED) {
/* inform roam event to cfg80211 */
- cfg80211_roamed(ar->net_dev, chan, bssid,
- assoc_req_ie, assoc_req_len,
- assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
+ cfg80211_roamed_bss(vif->ndev, bss, 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);
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
reason_code);
- if (!ath6kl_cfg80211_ready(ar))
+ ath6kl_cfg80211_sscan_disable(vif);
+
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
@@ -558,124 +835,190 @@ static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
return -ERESTARTSYS;
}
- ar->reconnect_flag = 0;
- ath6kl_disconnect(ar);
- memset(ar->ssid, 0, sizeof(ar->ssid));
- ar->ssid_len = 0;
+ vif->reconnect_flag = 0;
+ ath6kl_disconnect(vif);
+ memset(vif->ssid, 0, sizeof(vif->ssid));
+ vif->ssid_len = 0;
if (!test_bit(SKIP_SCAN, &ar->flag))
- memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+ memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
up(&ar->sem);
- ar->sme_state = SME_DISCONNECTED;
+ vif->sme_state = SME_DISCONNECTED;
return 0;
}
-void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
+void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
u8 *bssid, u8 assoc_resp_len,
u8 *assoc_info, u16 proto_reason)
{
- if (ar->scan_req) {
- cfg80211_scan_done(ar->scan_req, true);
- ar->scan_req = NULL;
+ struct ath6kl *ar = vif->ar;
+
+ if (vif->scan_req) {
+ cfg80211_scan_done(vif->scan_req, true);
+ vif->scan_req = NULL;
}
- if (ar->nw_type & ADHOC_NETWORK) {
- if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
+ if (vif->nw_type & ADHOC_NETWORK) {
+ if (vif->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 &&
- ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
+ if (vif->nw_type & INFRA_NETWORK) {
+ if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
+ vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in station mode\n", __func__);
return;
}
}
+ clear_bit(CONNECT_PEND, &vif->flags);
+
+ if (vif->sme_state == SME_CONNECTING) {
+ cfg80211_connect_result(vif->ndev,
+ bssid, NULL, 0,
+ NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ } else if (vif->sme_state == SME_CONNECTED) {
+ cfg80211_disconnected(vif->ndev, proto_reason,
+ NULL, 0, GFP_KERNEL);
+ }
+
+ vif->sme_state = SME_DISCONNECTED;
+
/*
* Send a disconnect command to target when a disconnect event is
* received with reason code other than 3 (DISCONNECT_CMD - disconnect
* request from host) to make the firmware stop trying to connect even
* after giving disconnect event. There will be one more disconnect
* event for this disconnect command with reason code DISCONNECT_CMD
- * which will be notified to cfg80211.
+ * which won't be notified to cfg80211.
*/
+ if (reason != DISCONNECT_CMD)
+ ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
+}
- if (reason != DISCONNECT_CMD) {
- ath6kl_wmi_disconnect_cmd(ar->wmi);
- return;
+static int ath6kl_set_probed_ssids(struct ath6kl *ar,
+ struct ath6kl_vif *vif,
+ struct cfg80211_ssid *ssids, int n_ssids,
+ struct cfg80211_match_set *match_set,
+ int n_match_ssid)
+{
+ u8 i, j, index_to_add, ssid_found = false;
+ struct ath6kl_cfg80211_match_probe_ssid ssid_list[MAX_PROBED_SSIDS];
+
+ memset(ssid_list, 0, sizeof(ssid_list));
+
+ if (n_ssids > MAX_PROBED_SSIDS ||
+ n_match_ssid > MAX_PROBED_SSIDS)
+ return -EINVAL;
+
+ for (i = 0; i < n_ssids; i++) {
+ memcpy(ssid_list[i].ssid.ssid,
+ ssids[i].ssid,
+ ssids[i].ssid_len);
+ ssid_list[i].ssid.ssid_len = ssids[i].ssid_len;
+
+ if (ssids[i].ssid_len)
+ ssid_list[i].flag = SPECIFIC_SSID_FLAG;
+ else
+ ssid_list[i].flag = ANY_SSID_FLAG;
+
+ if (n_match_ssid == 0)
+ ssid_list[i].flag |= MATCH_SSID_FLAG;
}
- clear_bit(CONNECT_PEND, &ar->flag);
+ index_to_add = i;
+
+ for (i = 0; i < n_match_ssid; i++) {
+ ssid_found = false;
+
+ for (j = 0; j < n_ssids; j++) {
+ if ((match_set[i].ssid.ssid_len ==
+ ssid_list[j].ssid.ssid_len) &&
+ (!memcmp(ssid_list[j].ssid.ssid,
+ match_set[i].ssid.ssid,
+ match_set[i].ssid.ssid_len))) {
+ ssid_list[j].flag |= MATCH_SSID_FLAG;
+ ssid_found = true;
+ break;
+ }
+ }
+
+ if (ssid_found)
+ continue;
+
+ if (index_to_add >= MAX_PROBED_SSIDS)
+ continue;
+
+ ssid_list[index_to_add].ssid.ssid_len =
+ match_set[i].ssid.ssid_len;
+ memcpy(ssid_list[index_to_add].ssid.ssid,
+ match_set[i].ssid.ssid,
+ match_set[i].ssid.ssid_len);
+ ssid_list[index_to_add].flag |= MATCH_SSID_FLAG;
+ index_to_add++;
+ }
+
+ for (i = 0; i < index_to_add; i++) {
+ ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
+ ssid_list[i].flag,
+ ssid_list[i].ssid.ssid_len,
+ ssid_list[i].ssid.ssid);
+ }
- if (ar->sme_state == SME_CONNECTING) {
- cfg80211_connect_result(ar->net_dev,
- bssid, NULL, 0,
- NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
- } else if (ar->sme_state == SME_CONNECTED) {
- cfg80211_disconnected(ar->net_dev, reason,
- NULL, 0, GFP_KERNEL);
+ /* Make sure no old entries are left behind */
+ for (i = index_to_add; i < MAX_PROBED_SSIDS; i++) {
+ ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
+ DISABLE_SSID_FLAG, 0, NULL);
}
- ar->sme_state = SME_DISCONNECTED;
+ return 0;
}
-static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+static int ath6kl_cfg80211_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *request)
{
- struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+ struct ath6kl_vif *vif = ath6kl_vif_from_wdev(request->wdev);
+ struct ath6kl *ar = ath6kl_priv(vif->ndev);
s8 n_channels = 0;
u16 *channels = NULL;
int ret = 0;
+ u32 force_fg_scan = 0;
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
+ ath6kl_cfg80211_sscan_disable(vif);
+
if (!ar->usr_bss_filter) {
- clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
- ret = ath6kl_wmi_bssfilter_cmd(
- ar->wmi,
- (test_bit(CONNECTED, &ar->flag) ?
- ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
+ clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
+ ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+ ALL_BSS_FILTER, 0);
if (ret) {
ath6kl_err("couldn't set bss filtering\n");
return ret;
}
}
- 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;
+ ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
+ request->n_ssids, NULL, 0);
+ if (ret < 0)
+ return ret;
- 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 (request->ie) {
- ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_REQ,
- request->ie, request->ie_len);
- if (ret) {
- ath6kl_err("failed to set Probe Request appie for "
- "scan");
- return ret;
- }
+ /* this also clears IE in fw if it's not set */
+ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
+ WMI_FRAME_PROBE_REQ,
+ request->ie, request->ie_len);
+ if (ret) {
+ ath6kl_err("failed to set Probe Request appie for scan\n");
+ return ret;
}
/*
@@ -691,8 +1034,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
if (channels == NULL) {
- ath6kl_warn("failed to set scan channels, "
- "scan all channels");
+ ath6kl_warn("failed to set scan channels, scan all channels");
n_channels = 0;
}
@@ -700,44 +1042,72 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
channels[i] = request->channels[i]->center_freq;
}
- ret = ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0,
- false, 0, 0, n_channels, channels);
- if (ret)
- ath6kl_err("wmi_startscan_cmd failed\n");
- else
- ar->scan_req = request;
+ if (test_bit(CONNECTED, &vif->flags))
+ force_fg_scan = 1;
+
+ vif->scan_req = request;
+
+ ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
+ WMI_LONG_SCAN, force_fg_scan,
+ false, 0,
+ ATH6KL_FG_SCAN_INTERVAL,
+ n_channels, channels,
+ request->no_cck,
+ request->rates);
+ if (ret) {
+ ath6kl_err("failed to start scan: %d\n", ret);
+ vif->scan_req = NULL;
+ }
kfree(channels);
return ret;
}
-void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status)
+void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
{
+ struct ath6kl *ar = vif->ar;
int i;
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status %d\n", __func__, status);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
+ aborted ? " aborted" : "");
- if (!ar->scan_req)
+ if (!vif->scan_req)
return;
- if ((status == -ECANCELED) || (status == -EBUSY)) {
- cfg80211_scan_done(ar->scan_req, true);
+ if (aborted)
goto out;
- }
-
- 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,
+ if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
+ for (i = 0; i < vif->scan_req->n_ssids; i++) {
+ ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
+ i + 1, DISABLE_SSID_FLAG,
0, NULL);
}
}
out:
- ar->scan_req = NULL;
+ cfg80211_scan_done(vif->scan_req, aborted);
+ vif->scan_req = NULL;
+}
+
+void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
+ enum wmi_phy_mode mode)
+{
+ struct cfg80211_chan_def chandef;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "channel switch notify nw_type %d freq %d mode %d\n",
+ vif->nw_type, freq, mode);
+
+ cfg80211_chandef_create(&chandef,
+ ieee80211_get_channel(vif->ar->wiphy, freq),
+ (mode == WMI_11G_HT20) ?
+ NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT);
+
+ mutex_lock(&vif->wdev.mtx);
+ cfg80211_ch_switch_notify(vif->ndev, &chandef);
+ mutex_unlock(&vif->wdev.mtx);
}
static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
@@ -745,23 +1115,31 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
const u8 *mac_addr,
struct key_params *params)
{
- struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+ struct ath6kl *ar = ath6kl_priv(ndev);
+ struct ath6kl_vif *vif = netdev_priv(ndev);
struct ath6kl_key *key = NULL;
+ int seq_len;
u8 key_usage;
u8 key_type;
- int status = 0;
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
- if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+ if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
+ if (params->key_len != WMI_KRK_LEN)
+ return -EINVAL;
+ return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
+ params->key);
+ }
+
+ if (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];
+ key = &vif->keys[key_index];
memset(key, 0, sizeof(struct ath6kl_key));
if (pairwise)
@@ -769,17 +1147,21 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
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;
+ seq_len = params->seq_len;
+ if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
+ seq_len > ATH6KL_KEY_SEQ_LEN) {
+ /* Only first half of the WPI PN is configured */
+ seq_len = ATH6KL_KEY_SEQ_LEN;
}
+ if (params->key_len > WLAN_MAX_KEY_LEN ||
+ seq_len > sizeof(key->seq))
+ return -EINVAL;
+
+ key->key_len = params->key_len;
+ memcpy(key->key, params->key, key->key_len);
+ key->seq_len = seq_len;
+ memcpy(key->seq, params->seq, key->seq_len);
+ key->cipher = params->cipher;
switch (key->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
@@ -794,34 +1176,35 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
case WLAN_CIPHER_SUITE_CCMP:
key_type = AES_CRYPT;
break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ key_type = WAPI_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);
+ if (((vif->auth_mode == WPA_PSK_AUTH) ||
+ (vif->auth_mode == WPA2_PSK_AUTH)) &&
+ (key_usage & GROUP_USAGE))
+ del_timer(&vif->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;
-
- if (ar->nw_type == AP_NETWORK && !pairwise &&
- (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
+ if (vif->nw_type == AP_NETWORK && !pairwise &&
+ (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
+ key_type == WAPI_CRYPT)) {
ar->ap_mode_bkey.valid = true;
ar->ap_mode_bkey.key_index = key_index;
ar->ap_mode_bkey.key_type = key_type;
ar->ap_mode_bkey.key_len = key->key_len;
memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
- if (!test_bit(CONNECTED, &ar->flag)) {
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
- "key configuration until AP mode has been "
- "started\n");
+ if (!test_bit(CONNECTED, &vif->flags)) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "Delay initial group key configuration until AP mode has been started\n");
/*
* The key will be set in ath6kl_connect_ap_mode() once
* the connected event is received from the target.
@@ -830,58 +1213,56 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
}
}
- if (ar->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
- !test_bit(CONNECTED, &ar->flag)) {
+ if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
+ !test_bit(CONNECTED, &vif->flags)) {
/*
* Store the key locally so that it can be re-configured after
* the AP mode has properly started
* (ath6kl_install_statioc_wep_keys).
*/
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
- "until AP mode has been started\n");
- ar->wep_key_list[key_index].key_len = key->key_len;
- memcpy(ar->wep_key_list[key_index].key, key->key, key->key_len);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "Delay WEP key configuration until AP mode has been started\n");
+ vif->wep_key_list[key_index].key_len = key->key_len;
+ memcpy(vif->wep_key_list[key_index].key, key->key,
+ key->key_len);
return 0;
}
- 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;
+ return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
+ key_type, key_usage, key->key_len,
+ key->seq, key->seq_len, key->key,
+ KEY_OP_INIT_VAL,
+ (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
}
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);
+ struct ath6kl *ar = ath6kl_priv(ndev);
+ struct ath6kl_vif *vif = netdev_priv(ndev);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
- if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+ if (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) {
+ if (!vif->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;
+ vif->keys[key_index].key_len = 0;
- return ath6kl_wmi_deletekey_cmd(ar->wmi, key_index);
+ return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
}
static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
@@ -890,23 +1271,23 @@ static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
void (*callback) (void *cookie,
struct key_params *))
{
- struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+ struct ath6kl_vif *vif = netdev_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))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
- if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+ if (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];
+ key = &vif->keys[key_index];
memset(&params, 0, sizeof(params));
params.cipher = key->cipher;
params.key_len = key->key_len;
@@ -924,61 +1305,59 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
u8 key_index, bool unicast,
bool multicast)
{
- struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+ struct ath6kl *ar = ath6kl_priv(ndev);
+ struct ath6kl_vif *vif = netdev_priv(ndev);
struct ath6kl_key *key = NULL;
- int status = 0;
u8 key_usage;
enum crypto_type key_type = NONE_CRYPT;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
- if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+ if (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) {
+ if (!vif->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];
+ vif->def_txkey_index = key_index;
+ key = &vif->keys[vif->def_txkey_index];
key_usage = GROUP_USAGE;
- if (ar->prwise_crypto == WEP_CRYPT)
+ if (vif->prwise_crypto == WEP_CRYPT)
key_usage |= TX_USAGE;
if (unicast)
- key_type = ar->prwise_crypto;
+ key_type = vif->prwise_crypto;
if (multicast)
- key_type = ar->grp_crypto;
+ key_type = vif->grp_crypto;
- if (ar->next_mode == AP_NETWORK && !test_bit(CONNECTED, &ar->flag))
+ if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
return 0; /* Delay until AP mode has been started */
- 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, NULL,
- SYNC_BOTH_WMIFLAG);
- if (status)
- return -EIO;
-
- return 0;
+ return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
+ vif->def_txkey_index,
+ key_type, key_usage,
+ key->key_len, key->seq, key->seq_len,
+ key->key,
+ KEY_OP_INIT_VAL, NULL,
+ SYNC_BOTH_WMIFLAG);
}
-void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl *ar, u8 keyid,
+void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, 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,
+ cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
(ismcast ? NL80211_KEYTYPE_GROUP :
NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
GFP_KERNEL);
@@ -987,12 +1366,17 @@ void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl *ar, u8 keyid,
static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
+ struct ath6kl_vif *vif;
int ret;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
changed);
- if (!ath6kl_cfg80211_ready(ar))
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
@@ -1006,28 +1390,30 @@ static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
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,
+ struct wireless_dev *wdev,
enum nl80211_tx_power_setting type,
- int dbm)
+ int mbm)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
- u8 ath6kl_dbm;
+ struct ath6kl_vif *vif;
+ int dbm = MBM_TO_DBM(mbm);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
type, dbm);
- if (!ath6kl_cfg80211_ready(ar))
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
return 0;
case NL80211_TX_POWER_LIMITED:
- ar->tx_pwr = ath6kl_dbm = dbm;
+ ar->tx_pwr = dbm;
break;
default:
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
@@ -1035,22 +1421,29 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
return -EOPNOTSUPP;
}
- ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, ath6kl_dbm);
+ ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
return 0;
}
-static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
+static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ int *dbm)
{
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
+ struct ath6kl_vif *vif;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
- if (test_bit(CONNECTED, &ar->flag)) {
+ if (test_bit(CONNECTED, &vif->flags)) {
ar->tx_pwr = 0;
- if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi) != 0) {
+ if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
return -EIO;
}
@@ -1074,22 +1467,24 @@ static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
{
struct ath6kl *ar = ath6kl_priv(dev);
struct wmi_power_mode_cmd mode;
+ struct ath6kl_vif *vif = netdev_priv(dev);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
__func__, pmgmt, timeout);
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
if (pmgmt) {
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
mode.pwr_mode = REC_POWER;
} else {
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
mode.pwr_mode = MAX_PERF_POWER;
}
- if (ath6kl_wmi_powermode_cmd(ar->wmi, mode.pwr_mode) != 0) {
+ if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
+ mode.pwr_mode) != 0) {
ath6kl_err("wmi_powermode_cmd failed\n");
return -EIO;
}
@@ -1097,41 +1492,113 @@ static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
return 0;
}
+static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
+ const char *name,
+ enum nl80211_iftype type,
+ u32 *flags,
+ struct vif_params *params)
+{
+ struct ath6kl *ar = wiphy_priv(wiphy);
+ struct wireless_dev *wdev;
+ u8 if_idx, nw_type;
+
+ if (ar->num_vif == ar->vif_max) {
+ ath6kl_err("Reached maximum number of supported vif\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
+ ath6kl_err("Not a supported interface type\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ wdev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
+ if (!wdev)
+ return ERR_PTR(-ENOMEM);
+
+ ar->num_vif++;
+
+ return wdev;
+}
+
+static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct ath6kl *ar = wiphy_priv(wiphy);
+ struct ath6kl_vif *vif = netdev_priv(wdev->netdev);
+
+ spin_lock_bh(&ar->list_lock);
+ list_del(&vif->list);
+ spin_unlock_bh(&ar->list_lock);
+
+ ath6kl_cfg80211_vif_stop(vif, test_bit(WMI_READY, &ar->flag));
+
+ rtnl_lock();
+ ath6kl_cfg80211_vif_cleanup(vif);
+ rtnl_unlock();
+
+ 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;
+ struct ath6kl_vif *vif = netdev_priv(ndev);
+ int i;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
- if (!ath6kl_cfg80211_ready(ar))
- return -EIO;
+ /*
+ * Don't bring up p2p on an interface which is not initialized
+ * for p2p operation where fw does not have capability to switch
+ * dynamically between non-p2p and p2p type interface.
+ */
+ if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+ vif->ar->fw_capabilities) &&
+ (type == NL80211_IFTYPE_P2P_CLIENT ||
+ type == NL80211_IFTYPE_P2P_GO)) {
+ if (vif->ar->vif_max == 1) {
+ if (vif->fw_vif_idx != 0)
+ return -EINVAL;
+ else
+ goto set_iface_type;
+ }
+
+ for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) {
+ if (i == vif->fw_vif_idx)
+ break;
+ }
+
+ if (i == vif->ar->vif_max) {
+ ath6kl_err("Invalid interface to bring up P2P\n");
+ return -EINVAL;
+ }
+ }
+
+ /* need to clean up enhanced bmiss detection fw state */
+ ath6kl_cfg80211_sta_bmiss_enhance(vif, false);
+set_iface_type:
switch (type) {
case NL80211_IFTYPE_STATION:
- ar->next_mode = INFRA_NETWORK;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ vif->next_mode = INFRA_NETWORK;
break;
case NL80211_IFTYPE_ADHOC:
- ar->next_mode = ADHOC_NETWORK;
+ vif->next_mode = ADHOC_NETWORK;
break;
case NL80211_IFTYPE_AP:
- ar->next_mode = AP_NETWORK;
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- ar->next_mode = INFRA_NETWORK;
- break;
case NL80211_IFTYPE_P2P_GO:
- ar->next_mode = AP_NETWORK;
+ vif->next_mode = AP_NETWORK;
break;
default:
ath6kl_err("invalid interface type %u\n", type);
return -EOPNOTSUPP;
}
- wdev->iftype = type;
+ vif->wdev.iftype = type;
return 0;
}
@@ -1141,16 +1608,17 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
struct cfg80211_ibss_params *ibss_param)
{
struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
int status;
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
- ar->ssid_len = ibss_param->ssid_len;
- memcpy(ar->ssid, ibss_param->ssid, ar->ssid_len);
+ vif->ssid_len = ibss_param->ssid_len;
+ memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
- if (ibss_param->channel)
- ar->ch_hint = ibss_param->channel->center_freq;
+ if (ibss_param->chandef.chan)
+ vif->ch_hint = ibss_param->chandef.chan->center_freq;
if (ibss_param->channel_fixed) {
/*
@@ -1162,44 +1630,45 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
return -EOPNOTSUPP;
}
- memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
+ memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
- memcpy(ar->req_bssid, ibss_param->bssid, sizeof(ar->req_bssid));
+ memcpy(vif->req_bssid, ibss_param->bssid,
+ sizeof(vif->req_bssid));
- ath6kl_set_wpa_version(ar, 0);
+ ath6kl_set_wpa_version(vif, 0);
- status = ath6kl_set_auth_type(ar, NL80211_AUTHTYPE_OPEN_SYSTEM);
+ status = ath6kl_set_auth_type(vif, 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);
+ ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
+ ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
} else {
- ath6kl_set_cipher(ar, 0, true);
- ath6kl_set_cipher(ar, 0, false);
+ ath6kl_set_cipher(vif, 0, true);
+ ath6kl_set_cipher(vif, 0, false);
}
- ar->nw_type = ar->next_mode;
+ vif->nw_type = vif->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_crypto_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_crypto_len,
- ar->ssid_len, ar->ssid,
- ar->req_bssid, ar->ch_hint,
- ar->connect_ctrl_flags);
- set_bit(CONNECT_PEND, &ar->flag);
+ vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
+ vif->prwise_crypto_len, vif->grp_crypto,
+ vif->grp_crypto_len, vif->ch_hint);
+
+ status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
+ vif->dot11_auth_mode, vif->auth_mode,
+ vif->prwise_crypto,
+ vif->prwise_crypto_len,
+ vif->grp_crypto, vif->grp_crypto_len,
+ vif->ssid_len, vif->ssid,
+ vif->req_bssid, vif->ch_hint,
+ ar->connect_ctrl_flags, SUBTYPE_NONE);
+ set_bit(CONNECT_PEND, &vif->flags);
return 0;
}
@@ -1207,14 +1676,14 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
struct net_device *dev)
{
- struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
- ath6kl_disconnect(ar);
- memset(ar->ssid, 0, sizeof(ar->ssid));
- ar->ssid_len = 0;
+ ath6kl_disconnect(vif);
+ memset(vif->ssid, 0, sizeof(vif->ssid));
+ vif->ssid_len = 0;
return 0;
}
@@ -1224,6 +1693,8 @@ static const u32 cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
+ CCKM_KRK_CIPHER_SUITE,
+ WLAN_CIPHER_SUITE_SMS4,
};
static bool is_rate_legacy(s32 rate)
@@ -1288,24 +1759,25 @@ static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
}
static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
- u8 *mac, struct station_info *sinfo)
+ const u8 *mac, struct station_info *sinfo)
{
struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
long left;
bool sgi;
s32 rate;
int ret;
u8 mcs;
- if (memcmp(mac, ar->bssid, ETH_ALEN) != 0)
+ if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
return -ENOENT;
if (down_interruptible(&ar->sem))
return -EBUSY;
- set_bit(STATS_UPDATE_PEND, &ar->flag);
+ set_bit(STATS_UPDATE_PEND, &vif->flags);
- ret = ath6kl_wmi_get_stats_cmd(ar->wmi);
+ ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
if (ret != 0) {
up(&ar->sem);
@@ -1314,7 +1786,7 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
left = wait_event_interruptible_timeout(ar->event_wq,
!test_bit(STATS_UPDATE_PEND,
- &ar->flag),
+ &vif->flags),
WMI_TIMEOUT);
up(&ar->sem);
@@ -1324,24 +1796,24 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
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;
+ if (vif->target_stats.rx_byte) {
+ sinfo->rx_bytes = vif->target_stats.rx_byte;
+ sinfo->filled |= STATION_INFO_RX_BYTES64;
+ sinfo->rx_packets = vif->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;
+ if (vif->target_stats.tx_byte) {
+ sinfo->tx_bytes = vif->target_stats.tx_byte;
+ sinfo->filled |= STATION_INFO_TX_BYTES64;
+ sinfo->tx_packets = vif->target_stats.tx_pkt;
sinfo->filled |= STATION_INFO_TX_PACKETS;
}
- sinfo->signal = ar->target_stats.cs_rssi;
+ sinfo->signal = vif->target_stats.cs_rssi;
sinfo->filled |= STATION_INFO_SIGNAL;
- rate = ar->target_stats.tx_ucast_rate;
+ rate = vif->target_stats.tx_ucast_rate;
if (is_rate_legacy(rate)) {
sinfo->txrate.legacy = rate / 100;
@@ -1373,13 +1845,13 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
sinfo->filled |= STATION_INFO_TX_BITRATE;
- if (test_bit(CONNECTED, &ar->flag) &&
- test_bit(DTIM_PERIOD_AVAIL, &ar->flag) &&
- ar->nw_type == INFRA_NETWORK) {
+ if (test_bit(CONNECTED, &vif->flags) &&
+ test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
+ vif->nw_type == INFRA_NETWORK) {
sinfo->filled |= STATION_INFO_BSS_PARAM;
sinfo->bss_param.flags = 0;
- sinfo->bss_param.dtim_period = ar->assoc_bss_dtim_period;
- sinfo->bss_param.beacon_interval = ar->assoc_bss_beacon_int;
+ sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
+ sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
}
return 0;
@@ -1389,7 +1861,9 @@ 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,
+ struct ath6kl_vif *vif = netdev_priv(netdev);
+
+ return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
pmksa->pmkid, true);
}
@@ -1397,44 +1871,753 @@ 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,
+ struct ath6kl_vif *vif = netdev_priv(netdev);
+
+ return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, 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);
+ struct ath6kl_vif *vif = netdev_priv(netdev);
+
+ if (test_bit(CONNECTED, &vif->flags))
+ return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
+ vif->bssid, NULL, false);
+ return 0;
+}
+
+static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
+ struct cfg80211_wowlan *wow, u32 *filter)
+{
+ int ret, pos;
+ u8 mask[WOW_PATTERN_SIZE];
+ u16 i;
+
+ /* Configure the patterns that we received from the user. */
+ for (i = 0; i < wow->n_patterns; i++) {
+ /*
+ * Convert given nl80211 specific mask value to equivalent
+ * driver specific mask value and send it to the chip along
+ * with patterns. For example, If the mask value defined in
+ * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
+ * then equivalent driver specific mask value is
+ * "0xFF 0x00 0xFF 0x00".
+ */
+ memset(&mask, 0, sizeof(mask));
+ for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
+ if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
+ mask[pos] = 0xFF;
+ }
+ /*
+ * Note: Pattern's offset is not passed as part of wowlan
+ * parameter from CFG layer. So it's always passed as ZERO
+ * to the firmware. It means, given WOW patterns are always
+ * matched from the first byte of received pkt in the firmware.
+ */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ wow->patterns[i].pattern_len,
+ 0 /* pattern offset */,
+ wow->patterns[i].pattern, mask);
+ if (ret)
+ return ret;
+ }
+
+ if (wow->disconnect)
+ *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
+
+ if (wow->magic_pkt)
+ *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
+
+ if (wow->gtk_rekey_failure)
+ *filter |= WOW_FILTER_OPTION_GTK_ERROR;
+
+ if (wow->eap_identity_req)
+ *filter |= WOW_FILTER_OPTION_EAP_REQ;
+
+ if (wow->four_way_handshake)
+ *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
+
+ return 0;
+}
+
+static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+ static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08 };
+ static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7f };
+ u8 unicst_offset = 0;
+ static const u8 arp_pattern[] = { 0x08, 0x06 };
+ static const u8 arp_mask[] = { 0xff, 0xff };
+ u8 arp_offset = 20;
+ static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
+ static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
+ u8 discvr_offset = 38;
+ static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
+ static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
+ u8 dhcp_offset = 0;
+ int ret;
+
+ /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(unicst_pattern), unicst_offset,
+ unicst_pattern, unicst_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW unicast IP pattern\n");
+ return ret;
+ }
+
+ /* Setup all ARP pkt pattern */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(arp_pattern), arp_offset,
+ arp_pattern, arp_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW ARP pattern\n");
+ return ret;
+ }
+
+ /*
+ * Setup multicast pattern for mDNS 224.0.0.251,
+ * SSDP 239.255.255.250 and LLMNR 224.0.0.252
+ */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(discvr_pattern), discvr_offset,
+ discvr_pattern, discvr_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
+ return ret;
+ }
+
+ /* Setup all DHCP broadcast pkt pattern */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(dhcp_pattern), dhcp_offset,
+ dhcp_pattern, dhcp_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+ struct net_device *ndev = vif->ndev;
+ static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
+ static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
+ u8 discvr_offset = 38;
+ u8 mac_mask[ETH_ALEN];
+ int ret;
+
+ /* Setup unicast pkt pattern */
+ memset(mac_mask, 0xff, ETH_ALEN);
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ ETH_ALEN, 0, ndev->dev_addr,
+ mac_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW unicast pattern\n");
+ return ret;
+ }
+
+ /*
+ * Setup multicast pattern for mDNS 224.0.0.251,
+ * SSDP 239.255.255.250 and LLMNR 224.0.0.252
+ */
+ if ((ndev->flags & IFF_ALLMULTI) ||
+ (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(discvr_pattern), discvr_offset,
+ discvr_pattern, discvr_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
+{
+ return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+}
+
+static bool is_ctrl_ep_empty(struct ath6kl *ar)
+{
+ return !ar->tx_pending[ar->ctrl_ep];
+}
+
+static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+ int ret, left;
+
+ clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+
+ ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
+ ATH6KL_HOST_MODE_ASLEEP);
+ if (ret)
+ return ret;
+
+ left = wait_event_interruptible_timeout(ar->event_wq,
+ is_hsleep_mode_procsed(vif),
+ WMI_TIMEOUT);
+ if (left == 0) {
+ ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
+ ret = -ETIMEDOUT;
+ } else if (left < 0) {
+ ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
+ left);
+ ret = left;
+ }
+
+ if (ar->tx_pending[ar->ctrl_ep]) {
+ left = wait_event_interruptible_timeout(ar->event_wq,
+ is_ctrl_ep_empty(ar),
+ WMI_TIMEOUT);
+ if (left == 0) {
+ ath6kl_warn("clear wmi ctrl data timeout\n");
+ ret = -ETIMEDOUT;
+ } else if (left < 0) {
+ ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
+ ret = left;
+ }
+ }
+
+ return ret;
+}
+
+static int ath6kl_wow_suspend_vif(struct ath6kl_vif *vif,
+ struct cfg80211_wowlan *wow, u32 *filter)
+{
+ struct ath6kl *ar = vif->ar;
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+ int ret;
+ u16 i, bmiss_time;
+ __be32 ips[MAX_IP_ADDRS];
+ u8 index = 0;
+
+ if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
+ test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
+ ar->fw_capabilities)) {
+ ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
+ vif->fw_vif_idx, false);
+ if (ret)
+ return ret;
+ }
+
+ /* Clear existing WOW patterns */
+ for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
+ ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
+ WOW_LIST_ID, i);
+
+ /*
+ * Skip the default WOW pattern configuration
+ * if the driver receives any WOW patterns from
+ * the user.
+ */
+ if (wow)
+ ret = ath6kl_wow_usr(ar, vif, wow, filter);
+ else if (vif->nw_type == AP_NETWORK)
+ ret = ath6kl_wow_ap(ar, vif);
+ else
+ ret = ath6kl_wow_sta(ar, vif);
+
+ if (ret)
+ return ret;
+
+ netif_stop_queue(vif->ndev);
+
+ if (vif->nw_type != AP_NETWORK) {
+ ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
+ ATH6KL_MAX_WOW_LISTEN_INTL,
+ 0);
+ if (ret)
+ return ret;
+
+ /* Set listen interval x 15 times as bmiss time */
+ bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
+ if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
+ bmiss_time = ATH6KL_MAX_BMISS_TIME;
+
+ ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
+ bmiss_time, 0);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
+ 0xFFFF, 0, 0xFFFF, 0, 0, 0,
+ 0, 0, 0, 0);
+ if (ret)
+ return ret;
+ }
+
+ /* Setup own IP addr for ARP agent. */
+ in_dev = __in_dev_get_rtnl(vif->ndev);
+ if (!in_dev)
+ return 0;
+
+ ifa = in_dev->ifa_list;
+ memset(&ips, 0, sizeof(ips));
+
+ /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
+ while (index < MAX_IP_ADDRS && ifa) {
+ ips[index] = ifa->ifa_local;
+ ifa = ifa->ifa_next;
+ index++;
+ }
+
+ if (ifa) {
+ ath6kl_err("total IP addr count is exceeding fw limit\n");
+ return -EINVAL;
+ }
+
+ ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
+ if (ret) {
+ ath6kl_err("fail to setup ip for arp agent\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+ struct ath6kl_vif *first_vif, *vif;
+ int ret = 0;
+ u32 filter = 0;
+ bool connected = false;
+
+ /* enter / leave wow suspend on first vif always */
+ first_vif = ath6kl_vif_first(ar);
+ if (WARN_ON(unlikely(!first_vif)) ||
+ !ath6kl_cfg80211_ready(first_vif))
+ return -EIO;
+
+ if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
+ return -EINVAL;
+
+ /* install filters for each connected vif */
+ spin_lock_bh(&ar->list_lock);
+ list_for_each_entry(vif, &ar->vif_list, list) {
+ if (!test_bit(CONNECTED, &vif->flags) ||
+ !ath6kl_cfg80211_ready(vif))
+ continue;
+ connected = true;
+
+ ret = ath6kl_wow_suspend_vif(vif, wow, &filter);
+ if (ret)
+ break;
+ }
+ spin_unlock_bh(&ar->list_lock);
+
+ if (!connected)
+ return -ENOTCONN;
+ else if (ret)
+ return ret;
+
+ ar->state = ATH6KL_STATE_SUSPENDING;
+
+ ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, first_vif->fw_vif_idx,
+ ATH6KL_WOW_MODE_ENABLE,
+ filter,
+ WOW_HOST_REQ_DELAY);
+ if (ret)
+ return ret;
+
+ return ath6kl_cfg80211_host_sleep(ar, first_vif);
+}
+
+static int ath6kl_wow_resume_vif(struct ath6kl_vif *vif)
+{
+ struct ath6kl *ar = vif->ar;
+ int ret;
+
+ if (vif->nw_type != AP_NETWORK) {
+ ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
+ 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
+ vif->listen_intvl_t, 0);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
+ vif->bmiss_time_t, 0);
+ if (ret)
+ return ret;
+ }
+
+ if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
+ test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
+ ar->fw_capabilities)) {
+ ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
+ vif->fw_vif_idx, true);
+ if (ret)
+ return ret;
+ }
+
+ netif_wake_queue(vif->ndev);
+
+ return 0;
+}
+
+static int ath6kl_wow_resume(struct ath6kl *ar)
+{
+ struct ath6kl_vif *vif;
+ int ret;
+
+ vif = ath6kl_vif_first(ar);
+ if (WARN_ON(unlikely(!vif)) ||
+ !ath6kl_cfg80211_ready(vif))
+ return -EIO;
+
+ ar->state = ATH6KL_STATE_RESUMING;
+
+ ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
+ ATH6KL_HOST_MODE_AWAKE);
+ if (ret) {
+ ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
+ ret);
+ goto cleanup;
+ }
+
+ spin_lock_bh(&ar->list_lock);
+ list_for_each_entry(vif, &ar->vif_list, list) {
+ if (!test_bit(CONNECTED, &vif->flags) ||
+ !ath6kl_cfg80211_ready(vif))
+ continue;
+ ret = ath6kl_wow_resume_vif(vif);
+ if (ret)
+ break;
+ }
+ spin_unlock_bh(&ar->list_lock);
+
+ if (ret)
+ goto cleanup;
+
+ ar->state = ATH6KL_STATE_ON;
+ return 0;
+
+cleanup:
+ ar->state = ATH6KL_STATE_WOW;
+ return ret;
+}
+
+static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
+{
+ struct ath6kl_vif *vif;
+ int ret;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ if (!test_bit(WMI_READY, &ar->flag)) {
+ ath6kl_err("deepsleep failed as wmi is not ready\n");
+ return -EIO;
+ }
+
+ ath6kl_cfg80211_stop_all(ar);
+
+ /* Save the current power mode before enabling power save */
+ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
+
+ ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
+ if (ret)
+ return ret;
+
+ /* Disable WOW mode */
+ ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
+ ATH6KL_WOW_MODE_DISABLE,
+ 0, 0);
+ if (ret)
+ return ret;
+
+ /* Flush all non control pkts in TX path */
+ ath6kl_tx_data_cleanup(ar);
+
+ ret = ath6kl_cfg80211_host_sleep(ar, vif);
+ if (ret)
+ return ret;
+
return 0;
}
+static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
+{
+ struct ath6kl_vif *vif;
+ int ret;
+
+ vif = ath6kl_vif_first(ar);
+
+ if (!vif)
+ return -EIO;
+
+ if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
+ ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
+ ar->wmi->saved_pwr_mode);
+ if (ret)
+ return ret;
+ }
+
+ ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
+ ATH6KL_HOST_MODE_AWAKE);
+ if (ret)
+ return ret;
+
+ ar->state = ATH6KL_STATE_ON;
+
+ /* Reset scan parameter to default values */
+ ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
+ 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int ath6kl_cfg80211_suspend(struct ath6kl *ar,
+ enum ath6kl_cfg_suspend_mode mode,
+ struct cfg80211_wowlan *wow)
+{
+ struct ath6kl_vif *vif;
+ enum ath6kl_state prev_state;
+ int ret;
+
+ switch (mode) {
+ case ATH6KL_CFG_SUSPEND_WOW:
+
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
+
+ /* Flush all non control pkts in TX path */
+ ath6kl_tx_data_cleanup(ar);
+
+ prev_state = ar->state;
+
+ ret = ath6kl_wow_suspend(ar, wow);
+ if (ret) {
+ ar->state = prev_state;
+ return ret;
+ }
+
+ ar->state = ATH6KL_STATE_WOW;
+ break;
+
+ case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
+
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
+
+ ret = ath6kl_cfg80211_deepsleep_suspend(ar);
+ if (ret) {
+ ath6kl_err("deepsleep suspend failed: %d\n", ret);
+ return ret;
+ }
+
+ ar->state = ATH6KL_STATE_DEEPSLEEP;
+
+ break;
+
+ case ATH6KL_CFG_SUSPEND_CUTPOWER:
+
+ ath6kl_cfg80211_stop_all(ar);
+
+ if (ar->state == ATH6KL_STATE_OFF) {
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND,
+ "suspend hw off, no action for cutpower\n");
+ break;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
+
+ ret = ath6kl_init_hw_stop(ar);
+ if (ret) {
+ ath6kl_warn("failed to stop hw during suspend: %d\n",
+ ret);
+ }
+
+ ar->state = ATH6KL_STATE_CUTPOWER;
+
+ break;
+
+ default:
+ break;
+ }
+
+ list_for_each_entry(vif, &ar->vif_list, list)
+ ath6kl_cfg80211_scan_complete_event(vif, true);
+
+ return 0;
+}
+EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
+
+int ath6kl_cfg80211_resume(struct ath6kl *ar)
+{
+ int ret;
+
+ switch (ar->state) {
+ case ATH6KL_STATE_WOW:
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
+
+ ret = ath6kl_wow_resume(ar);
+ if (ret) {
+ ath6kl_warn("wow mode resume failed: %d\n", ret);
+ return ret;
+ }
+
+ break;
+
+ case ATH6KL_STATE_DEEPSLEEP:
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
+
+ ret = ath6kl_cfg80211_deepsleep_resume(ar);
+ if (ret) {
+ ath6kl_warn("deep sleep resume failed: %d\n", ret);
+ return ret;
+ }
+ break;
+
+ case ATH6KL_STATE_CUTPOWER:
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
+
+ ret = ath6kl_init_hw_start(ar);
+ if (ret) {
+ ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
+ return ret;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ath6kl_cfg80211_resume);
+
#ifdef CONFIG_PM
-static int ar6k_cfg80211_suspend(struct wiphy *wiphy,
+
+/* hif layer decides what suspend mode to use */
+static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
struct cfg80211_wowlan *wow)
{
struct ath6kl *ar = wiphy_priv(wiphy);
- return ath6kl_hif_suspend(ar);
+ ath6kl_recovery_suspend(ar);
+
+ return ath6kl_hif_suspend(ar, wow);
}
-#endif
-static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
{
- struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl *ar = wiphy_priv(wiphy);
+ int err;
- if (!ath6kl_cfg80211_ready(ar))
- return -EIO;
+ err = ath6kl_hif_resume(ar);
+ if (err)
+ return err;
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
- __func__, chan->center_freq, chan->hw_value);
- ar->next_chan = chan->center_freq;
+ ath6kl_recovery_resume(ar);
return 0;
}
+/*
+ * FIXME: WOW suspend mode is selected if the host sdio controller supports
+ * both sdio irq wake up and keep power. The target pulls sdio data line to
+ * wake up the host when WOW pattern matches. This causes sdio irq handler
+ * is being called in the host side which internally hits ath6kl's RX path.
+ *
+ * Since sdio interrupt is not disabled, RX path executes even before
+ * the host executes the actual resume operation from PM module.
+ *
+ * In the current scenario, WOW resume should happen before start processing
+ * any data from the target. So It's required to perform WOW resume in RX path.
+ * Ideally we should perform WOW resume only in the actual platform
+ * resume path. This area needs bit rework to avoid WOW resume in RX path.
+ *
+ * ath6kl_check_wow_status() is called from ath6kl_rx().
+ */
+void ath6kl_check_wow_status(struct ath6kl *ar)
+{
+ if (ar->state == ATH6KL_STATE_SUSPENDING)
+ return;
+
+ if (ar->state == ATH6KL_STATE_WOW)
+ ath6kl_cfg80211_resume(ar);
+}
+
+#else
+
+void ath6kl_check_wow_status(struct ath6kl *ar)
+{
+}
+#endif
+
+static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
+ bool ht_enable)
+{
+ struct ath6kl_htcap *htcap = &vif->htcap[band];
+
+ if (htcap->ht_enable == ht_enable)
+ return 0;
+
+ if (ht_enable) {
+ /* Set default ht capabilities */
+ htcap->ht_enable = true;
+ htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
+ ath6kl_g_htcap : ath6kl_a_htcap;
+ htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
+ } else /* Disable ht */
+ memset(htcap, 0, sizeof(*htcap));
+
+ return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
+ band, htcap);
+}
+
+static int ath6kl_restore_htcap(struct ath6kl_vif *vif)
+{
+ struct wiphy *wiphy = vif->ar->wiphy;
+ int band, ret = 0;
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ if (!wiphy->bands[band])
+ continue;
+
+ ret = ath6kl_set_htcap(vif, band,
+ wiphy->bands[band]->ht_cap.ht_supported);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
static bool ath6kl_is_p2p_ie(const u8 *pos)
{
return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
@@ -1442,9 +2625,10 @@ static bool ath6kl_is_p2p_ie(const u8 *pos)
pos[4] == 0x9a && pos[5] == 0x09;
}
-static int ath6kl_set_ap_probe_resp_ies(struct ath6kl *ar, const u8 *ies,
- size_t ies_len)
+static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
+ const u8 *ies, size_t ies_len)
{
+ struct ath6kl *ar = vif->ar;
const u8 *pos;
u8 *buf = NULL;
size_t len = 0;
@@ -1471,78 +2655,157 @@ static int ath6kl_set_ap_probe_resp_ies(struct ath6kl *ar, const u8 *ies,
}
}
- ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP,
- buf, len);
+ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
+ WMI_FRAME_PROBE_RESP, buf, len);
kfree(buf);
return ret;
}
-static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
- struct beacon_parameters *info, bool add)
+static int ath6kl_set_ies(struct ath6kl_vif *vif,
+ struct cfg80211_beacon_data *info)
+{
+ struct ath6kl *ar = vif->ar;
+ int res;
+
+ /* this also clears IE in fw if it's not set */
+ res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
+ WMI_FRAME_BEACON,
+ info->beacon_ies,
+ info->beacon_ies_len);
+ if (res)
+ return res;
+
+ /* this also clears IE in fw if it's not set */
+ res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
+ info->proberesp_ies_len);
+ if (res)
+ return res;
+
+ /* this also clears IE in fw if it's not set */
+ res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
+ WMI_FRAME_ASSOC_RESP,
+ info->assocresp_ies,
+ info->assocresp_ies_len);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
+ u8 *rsn_capab)
+{
+ const u8 *rsn_ie;
+ size_t rsn_ie_len;
+ u16 cnt;
+
+ if (!beacon->tail)
+ return -EINVAL;
+
+ rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
+ if (!rsn_ie)
+ return -EINVAL;
+
+ rsn_ie_len = *(rsn_ie + 1);
+ /* skip element id and length */
+ rsn_ie += 2;
+
+ /* skip version */
+ if (rsn_ie_len < 2)
+ return -EINVAL;
+ rsn_ie += 2;
+ rsn_ie_len -= 2;
+
+ /* skip group cipher suite */
+ if (rsn_ie_len < 4)
+ return 0;
+ rsn_ie += 4;
+ rsn_ie_len -= 4;
+
+ /* skip pairwise cipher suite */
+ if (rsn_ie_len < 2)
+ return 0;
+ cnt = get_unaligned_le16(rsn_ie);
+ rsn_ie += (2 + cnt * 4);
+ rsn_ie_len -= (2 + cnt * 4);
+
+ /* skip akm suite */
+ if (rsn_ie_len < 2)
+ return 0;
+ cnt = get_unaligned_le16(rsn_ie);
+ rsn_ie += (2 + cnt * 4);
+ rsn_ie_len -= (2 + cnt * 4);
+
+ if (rsn_ie_len < 2)
+ return 0;
+
+ memcpy(rsn_capab, rsn_ie, 2);
+
+ return 0;
+}
+
+static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ap_settings *info)
{
struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
struct ieee80211_mgmt *mgmt;
+ bool hidden = false;
u8 *ies;
int ies_len;
struct wmi_connect_cmd p;
int res;
- int i;
+ int i, ret;
+ u16 rsn_capab = 0;
+ int inactivity_timeout = 0;
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
- if (!ath6kl_cfg80211_ready(ar))
+ if (!ath6kl_cfg80211_ready(vif))
return -EIO;
- if (ar->next_mode != AP_NETWORK)
+ if (vif->next_mode != AP_NETWORK)
return -EOPNOTSUPP;
- if (info->beacon_ies) {
- res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON,
- info->beacon_ies,
- info->beacon_ies_len);
- if (res)
- return res;
- }
- if (info->proberesp_ies) {
- res = ath6kl_set_ap_probe_resp_ies(ar, info->proberesp_ies,
- info->proberesp_ies_len);
- if (res)
- return res;
- }
- if (info->assocresp_ies) {
- res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP,
- info->assocresp_ies,
- info->assocresp_ies_len);
- if (res)
- return res;
- }
-
- if (!add)
- return 0;
+ res = ath6kl_set_ies(vif, &info->beacon);
ar->ap_mode_bkey.valid = false;
- /* TODO:
- * info->interval
- * info->dtim_period
- */
+ ret = ath6kl_wmi_ap_set_beacon_intvl_cmd(ar->wmi, vif->fw_vif_idx,
+ info->beacon_interval);
- if (info->head == NULL)
+ if (ret)
+ ath6kl_warn("Failed to set beacon interval: %d\n", ret);
+
+ ret = ath6kl_wmi_ap_set_dtim_cmd(ar->wmi, vif->fw_vif_idx,
+ info->dtim_period);
+
+ /* ignore error, just print a warning and continue normally */
+ if (ret)
+ ath6kl_warn("Failed to set dtim_period in beacon: %d\n", ret);
+
+ if (info->beacon.head == NULL)
return -EINVAL;
- mgmt = (struct ieee80211_mgmt *) info->head;
+ mgmt = (struct ieee80211_mgmt *) info->beacon.head;
ies = mgmt->u.beacon.variable;
- if (ies > info->head + info->head_len)
+ if (ies > info->beacon.head + info->beacon.head_len)
return -EINVAL;
- ies_len = info->head + info->head_len - ies;
+ ies_len = info->beacon.head + info->beacon.head_len - ies;
if (info->ssid == NULL)
return -EINVAL;
- memcpy(ar->ssid, info->ssid, info->ssid_len);
- ar->ssid_len = info->ssid_len;
+ memcpy(vif->ssid, info->ssid, info->ssid_len);
+ vif->ssid_len = info->ssid_len;
if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
- return -EOPNOTSUPP; /* TODO */
+ hidden = true;
+
+ res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
+ if (res)
+ return res;
- ar->dot11_auth_mode = OPEN_AUTH;
+ ret = ath6kl_set_auth_type(vif, info->auth_type);
+ if (ret)
+ return ret;
memset(&p, 0, sizeof(p));
@@ -1564,7 +2827,7 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
}
if (p.auth_mode == 0)
p.auth_mode = NONE_AUTH;
- ar->auth_mode = p.auth_mode;
+ vif->auth_mode = p.auth_mode;
for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
switch (info->crypto.ciphers_pairwise[i]) {
@@ -1578,13 +2841,17 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
case WLAN_CIPHER_SUITE_CCMP:
p.prwise_crypto_type |= AES_CRYPT;
break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ p.prwise_crypto_type |= WAPI_CRYPT;
+ break;
}
}
if (p.prwise_crypto_type == 0) {
p.prwise_crypto_type = NONE_CRYPT;
- ath6kl_set_cipher(ar, 0, true);
- } else if (info->crypto.n_ciphers_pairwise == 1)
- ath6kl_set_cipher(ar, info->crypto.ciphers_pairwise[0], true);
+ ath6kl_set_cipher(vif, 0, true);
+ } else if (info->crypto.n_ciphers_pairwise == 1) {
+ ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
+ }
switch (info->crypto.cipher_group) {
case WLAN_CIPHER_SUITE_WEP40:
@@ -1597,105 +2864,191 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
case WLAN_CIPHER_SUITE_CCMP:
p.grp_crypto_type = AES_CRYPT;
break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ p.grp_crypto_type = WAPI_CRYPT;
+ break;
default:
p.grp_crypto_type = NONE_CRYPT;
break;
}
- ath6kl_set_cipher(ar, info->crypto.cipher_group, false);
+ ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
p.nw_type = AP_NETWORK;
- ar->nw_type = ar->next_mode;
+ vif->nw_type = vif->next_mode;
- p.ssid_len = ar->ssid_len;
- memcpy(p.ssid, ar->ssid, ar->ssid_len);
- p.dot11_auth_mode = ar->dot11_auth_mode;
- p.ch = cpu_to_le16(ar->next_chan);
+ p.ssid_len = vif->ssid_len;
+ memcpy(p.ssid, vif->ssid, vif->ssid_len);
+ p.dot11_auth_mode = vif->dot11_auth_mode;
+ p.ch = cpu_to_le16(info->chandef.chan->center_freq);
- res = ath6kl_wmi_ap_profile_commit(ar->wmi, &p);
+ /* Enable uAPSD support by default */
+ res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
+ if (res < 0)
+ return res;
+
+ if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+ p.nw_subtype = SUBTYPE_P2PGO;
+ } else {
+ /*
+ * Due to firmware limitation, it is not possible to
+ * do P2P mgmt operations in AP mode
+ */
+ p.nw_subtype = SUBTYPE_NONE;
+ }
+
+ if (info->inactivity_timeout) {
+ inactivity_timeout = info->inactivity_timeout;
+
+ if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS)
+ inactivity_timeout = DIV_ROUND_UP(inactivity_timeout,
+ 60);
+
+ res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
+ inactivity_timeout);
+ if (res < 0)
+ return res;
+ }
+
+ if (ath6kl_set_htcap(vif, info->chandef.chan->band,
+ cfg80211_get_chandef_type(&info->chandef)
+ != NL80211_CHAN_NO_HT))
+ return -EIO;
+
+ /*
+ * Get the PTKSA replay counter in the RSN IE. Supplicant
+ * will use the RSN IE in M3 message and firmware has to
+ * advertise the same in beacon/probe response. Send
+ * the complete RSN IE capability field to firmware
+ */
+ if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
+ test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+ ar->fw_capabilities)) {
+ res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
+ WLAN_EID_RSN, WMI_RSN_IE_CAPB,
+ (const u8 *) &rsn_capab,
+ sizeof(rsn_capab));
+ vif->rsn_capab = rsn_capab;
+ if (res < 0)
+ return res;
+ }
+
+ memcpy(&vif->profile, &p, sizeof(p));
+ res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
if (res < 0)
return res;
return 0;
}
-static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
- struct beacon_parameters *info)
+static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_beacon_data *beacon)
{
- return ath6kl_ap_beacon(wiphy, dev, info, true);
-}
+ struct ath6kl_vif *vif = netdev_priv(dev);
-static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
- struct beacon_parameters *info)
-{
- return ath6kl_ap_beacon(wiphy, dev, info, false);
+ if (!ath6kl_cfg80211_ready(vif))
+ return -EIO;
+
+ if (vif->next_mode != AP_NETWORK)
+ return -EOPNOTSUPP;
+
+ return ath6kl_set_ies(vif, beacon);
}
-static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
+static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
{
struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
- if (ar->nw_type != AP_NETWORK)
+ if (vif->nw_type != AP_NETWORK)
return -EOPNOTSUPP;
- if (!test_bit(CONNECTED, &ar->flag))
+ if (!test_bit(CONNECTED, &vif->flags))
return -ENOTCONN;
- ath6kl_wmi_disconnect_cmd(ar->wmi);
- clear_bit(CONNECTED, &ar->flag);
+ ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
+ clear_bit(CONNECTED, &vif->flags);
- return 0;
+ /* Restore ht setting in firmware */
+ return ath6kl_restore_htcap(vif);
+}
+
+static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
+ const u8 *addr = mac ? mac : bcast_addr;
+
+ return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
+ addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
}
static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
- u8 *mac, struct station_parameters *params)
+ const u8 *mac,
+ struct station_parameters *params)
{
struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
+ int err;
- if (ar->nw_type != AP_NETWORK)
+ if (vif->nw_type != AP_NETWORK)
return -EOPNOTSUPP;
- /* Use this only for authorizing/unauthorizing a station */
- if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
- return -EOPNOTSUPP;
+ err = cfg80211_check_station_change(wiphy, params,
+ CFG80211_STA_AP_MLME_CLIENT);
+ if (err)
+ return err;
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
- return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_AUTHORIZE,
- mac, 0);
- return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_UNAUTHORIZE, mac,
- 0);
+ return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
+ WMI_AP_MLME_AUTHORIZE, mac, 0);
+ return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
+ WMI_AP_MLME_UNAUTHORIZE, mac, 0);
}
static int ath6kl_remain_on_channel(struct wiphy *wiphy,
- struct net_device *dev,
+ struct wireless_dev *wdev,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie)
{
- struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
+ struct ath6kl *ar = ath6kl_priv(vif->ndev);
+ u32 id;
/* TODO: if already pending or ongoing remain-on-channel,
* return -EBUSY */
- *cookie = 1; /* only a single pending request is supported */
+ id = ++vif->last_roc_id;
+ if (id == 0) {
+ /* Do not use 0 as the cookie value */
+ id = ++vif->last_roc_id;
+ }
+ *cookie = id;
- return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, chan->center_freq,
- duration);
+ return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
+ chan->center_freq, duration);
}
static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
- struct net_device *dev,
+ struct wireless_dev *wdev,
u64 cookie)
{
- struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
+ struct ath6kl *ar = ath6kl_priv(vif->ndev);
- if (cookie != 1)
+ if (cookie != vif->last_roc_id)
return -ENOENT;
+ vif->last_cancel_roc_id = cookie;
- return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi);
+ return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
}
-static int ath6kl_send_go_probe_resp(struct ath6kl *ar, const u8 *buf,
- size_t len, unsigned int freq)
+static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
+ const u8 *buf, size_t len,
+ unsigned int freq)
{
+ struct ath6kl *ar = vif->ar;
const u8 *pos;
u8 *p2p;
int p2p_len;
@@ -1722,54 +3075,162 @@ static int ath6kl_send_go_probe_resp(struct ath6kl *ar, const u8 *buf,
pos += 2 + pos[1];
}
- ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, freq, mgmt->da,
- p2p, p2p_len);
+ ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
+ mgmt->da, p2p, p2p_len);
kfree(p2p);
return ret;
}
-static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
- struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck, u64 *cookie)
+static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
+ u32 id,
+ u32 freq,
+ u32 wait,
+ const u8 *buf,
+ size_t len,
+ bool *more_data,
+ bool no_cck)
+{
+ struct ieee80211_mgmt *mgmt;
+ struct ath6kl_sta *conn;
+ bool is_psq_empty = false;
+ struct ath6kl_mgmt_buff *mgmt_buf;
+ size_t mgmt_buf_size;
+ struct ath6kl *ar = vif->ar;
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+ if (is_multicast_ether_addr(mgmt->da))
+ return false;
+
+ conn = ath6kl_find_sta(vif, mgmt->da);
+ if (!conn)
+ return false;
+
+ if (conn->sta_flags & STA_PS_SLEEP) {
+ if (!(conn->sta_flags & STA_PS_POLLED)) {
+ /* Queue the frames if the STA is sleeping */
+ mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
+ mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
+ if (!mgmt_buf)
+ return false;
+
+ INIT_LIST_HEAD(&mgmt_buf->list);
+ mgmt_buf->id = id;
+ mgmt_buf->freq = freq;
+ mgmt_buf->wait = wait;
+ mgmt_buf->len = len;
+ mgmt_buf->no_cck = no_cck;
+ memcpy(mgmt_buf->buf, buf, len);
+ spin_lock_bh(&conn->psq_lock);
+ is_psq_empty = skb_queue_empty(&conn->psq) &&
+ (conn->mgmt_psq_len == 0);
+ list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
+ conn->mgmt_psq_len++;
+ 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, vif->fw_vif_idx,
+ conn->aid, 1);
+ return true;
+ }
+
+ /*
+ * 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) || (conn->mgmt_psq_len != 0))
+ *more_data = true;
+ spin_unlock_bh(&conn->psq_lock);
+ }
+
+ return false;
+}
+
+/* Check if SSID length is greater than DIRECT- */
+static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
{
- struct ath6kl *ar = ath6kl_priv(dev);
- u32 id;
const struct ieee80211_mgmt *mgmt;
+ mgmt = (const struct ieee80211_mgmt *) buf;
+
+ /* variable[1] contains the SSID tag length */
+ if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
+ (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
+ return true;
+ }
+
+ return false;
+}
+
+static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
+ struct ath6kl *ar = ath6kl_priv(vif->ndev);
+ struct ieee80211_channel *chan = params->chan;
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ unsigned int wait = params->wait;
+ bool no_cck = params->no_cck;
+ u32 id, freq;
+ const struct ieee80211_mgmt *mgmt;
+ bool more_data, queued;
+
+ /* default to the current channel, but use the one specified as argument
+ * if any
+ */
+ freq = vif->ch_hint;
+ if (chan)
+ freq = chan->center_freq;
+
+ /* never send freq zero to the firmware */
+ if (WARN_ON(freq == 0))
+ return -EINVAL;
mgmt = (const struct ieee80211_mgmt *) buf;
- if (buf + len >= mgmt->u.probe_resp.variable &&
- ar->nw_type == AP_NETWORK && test_bit(CONNECTED, &ar->flag) &&
- ieee80211_is_probe_resp(mgmt->frame_control)) {
+ if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
+ ieee80211_is_probe_resp(mgmt->frame_control) &&
+ ath6kl_is_p2p_go_ssid(buf, len)) {
/*
- * Send Probe Response frame in AP mode using a separate WMI
+ * Send Probe Response frame in GO mode using a separate WMI
* command to allow the target to fill in the generic IEs.
*/
*cookie = 0; /* TX status not supported */
- return ath6kl_send_go_probe_resp(ar, buf, len,
- chan->center_freq);
+ return ath6kl_send_go_probe_resp(vif, buf, len, freq);
}
- id = ar->send_action_id++;
+ id = vif->send_action_id++;
if (id == 0) {
/*
* 0 is a reserved value in the WMI command and shall not be
* used for the command.
*/
- id = ar->send_action_id++;
+ id = vif->send_action_id++;
}
*cookie = id;
- return ath6kl_wmi_send_action_cmd(ar->wmi, id, chan->center_freq, wait,
- buf, len);
+
+ /* AP mode Power saving processing */
+ if (vif->nw_type == AP_NETWORK) {
+ queued = ath6kl_mgmt_powersave_ap(vif, id, freq, wait, buf, len,
+ &more_data, no_cck);
+ if (queued)
+ return 0;
+ }
+
+ return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, freq,
+ wait, buf, len, no_cck);
}
static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
- struct net_device *dev,
+ struct wireless_dev *wdev,
u16 frame_type, bool reg)
{
- struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
__func__, frame_type, reg);
@@ -1779,10 +3240,145 @@ static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
* we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
* hardcode target to report Probe Request frames all the time.
*/
- ar->probe_req_report = reg;
+ vif->probe_req_report = reg;
}
}
+static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_sched_scan_request *request)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
+ u16 interval;
+ int ret, rssi_thold;
+ int n_match_sets = request->n_match_sets;
+
+ /*
+ * If there's a matchset w/o an SSID, then assume it's just for
+ * the RSSI (nothing else is currently supported) and ignore it.
+ * The device only supports a global RSSI filter that we set below.
+ */
+ if (n_match_sets == 1 && !request->match_sets[0].ssid.ssid_len)
+ n_match_sets = 0;
+
+ if (ar->state != ATH6KL_STATE_ON)
+ return -EIO;
+
+ if (vif->sme_state != SME_DISCONNECTED)
+ return -EBUSY;
+
+ ath6kl_cfg80211_scan_complete_event(vif, true);
+
+ ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
+ request->n_ssids,
+ request->match_sets,
+ n_match_sets);
+ if (ret < 0)
+ return ret;
+
+ if (!n_match_sets) {
+ ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+ ALL_BSS_FILTER, 0);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+ MATCHED_SSID_FILTER, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
+ ar->fw_capabilities)) {
+ if (request->min_rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
+ rssi_thold = 0;
+ else if (request->min_rssi_thold < -127)
+ rssi_thold = -127;
+ else
+ rssi_thold = request->min_rssi_thold;
+
+ ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,
+ rssi_thold);
+ if (ret) {
+ ath6kl_err("failed to set RSSI threshold for scan\n");
+ return ret;
+ }
+ }
+
+ /* fw uses seconds, also make sure that it's >0 */
+ interval = max_t(u16, 1, request->interval / 1000);
+
+ ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
+ interval, interval,
+ vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
+
+ /* this also clears IE in fw if it's not set */
+ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
+ WMI_FRAME_PROBE_REQ,
+ request->ie, request->ie_len);
+ if (ret) {
+ ath6kl_warn("Failed to set probe request IE for scheduled scan: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, true);
+ if (ret)
+ return ret;
+
+ set_bit(SCHED_SCANNING, &vif->flags);
+
+ return 0;
+}
+
+static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
+ struct net_device *dev)
+{
+ struct ath6kl_vif *vif = netdev_priv(dev);
+ bool stopped;
+
+ stopped = __ath6kl_cfg80211_sscan_stop(vif);
+
+ if (!stopped)
+ return -EIO;
+
+ return 0;
+}
+
+static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
+
+ return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx,
+ mask);
+}
+
+static int ath6kl_cfg80211_set_txe_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ u32 rate, u32 pkts, u32 intvl)
+{
+ struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
+
+ if (vif->nw_type != INFRA_NETWORK ||
+ !test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, ar->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ if (vif->sme_state != SME_CONNECTED)
+ return -ENOTCONN;
+
+ /* save this since the firmware won't report the interval */
+ vif->txe_intvl = intvl;
+
+ return ath6kl_wmi_set_txe_notify(ar->wmi, vif->fw_vif_idx,
+ rate, pkts, intvl);
+}
+
static const struct ieee80211_txrx_stypes
ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
@@ -1791,6 +3387,12 @@ ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
+ [NL80211_IFTYPE_AP] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
@@ -1806,6 +3408,8 @@ ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
};
static struct cfg80211_ops ath6kl_cfg80211_ops = {
+ .add_virtual_intf = ath6kl_cfg80211_add_iface,
+ .del_virtual_intf = ath6kl_cfg80211_del_iface,
.change_virtual_intf = ath6kl_cfg80211_change_iface,
.scan = ath6kl_cfg80211_scan,
.connect = ath6kl_cfg80211_connect,
@@ -1826,89 +3430,446 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
.flush_pmksa = ath6kl_flush_pmksa,
CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
#ifdef CONFIG_PM
- .suspend = ar6k_cfg80211_suspend,
+ .suspend = __ath6kl_cfg80211_suspend,
+ .resume = __ath6kl_cfg80211_resume,
#endif
- .set_channel = ath6kl_set_channel,
- .add_beacon = ath6kl_add_beacon,
- .set_beacon = ath6kl_set_beacon,
- .del_beacon = ath6kl_del_beacon,
+ .start_ap = ath6kl_start_ap,
+ .change_beacon = ath6kl_change_beacon,
+ .stop_ap = ath6kl_stop_ap,
+ .del_station = ath6kl_del_station,
.change_station = ath6kl_change_station,
.remain_on_channel = ath6kl_remain_on_channel,
.cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
.mgmt_tx = ath6kl_mgmt_tx,
.mgmt_frame_register = ath6kl_mgmt_frame_register,
+ .sched_scan_start = ath6kl_cfg80211_sscan_start,
+ .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
+ .set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
+ .set_cqm_txe_config = ath6kl_cfg80211_set_txe_config,
};
-struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
+void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
{
- int ret = 0;
- struct wireless_dev *wdev;
- struct ath6kl *ar;
+ ath6kl_cfg80211_sscan_disable(vif);
- wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
- if (!wdev) {
- ath6kl_err("couldn't allocate wireless device\n");
- return NULL;
+ switch (vif->sme_state) {
+ case SME_DISCONNECTED:
+ break;
+ case SME_CONNECTING:
+ cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
+ NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ break;
+ case SME_CONNECTED:
+ cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
+ break;
}
- /* 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;
+ if (vif->ar->state != ATH6KL_STATE_RECOVERY &&
+ (test_bit(CONNECTED, &vif->flags) ||
+ test_bit(CONNECT_PEND, &vif->flags)))
+ ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
+
+ vif->sme_state = SME_DISCONNECTED;
+ clear_bit(CONNECTED, &vif->flags);
+ clear_bit(CONNECT_PEND, &vif->flags);
+
+ /* Stop netdev queues, needed during recovery */
+ netif_stop_queue(vif->ndev);
+ netif_carrier_off(vif->ndev);
+
+ /* disable scanning */
+ if (vif->ar->state != ATH6KL_STATE_RECOVERY &&
+ ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
+ ath6kl_warn("failed to disable scan during stop\n");
+
+ ath6kl_cfg80211_scan_complete_event(vif, true);
+}
+
+void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
+{
+ struct ath6kl_vif *vif;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif && ar->state != ATH6KL_STATE_RECOVERY) {
+ /* save the current power mode before enabling power save */
+ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
+
+ if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
+ ath6kl_warn("ath6kl_deep_sleep_enable: wmi_powermode_cmd failed\n");
+ return;
+ }
+
+ /*
+ * FIXME: we should take ar->list_lock to protect changes in the
+ * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
+ * sleeps.
+ */
+ list_for_each_entry(vif, &ar->vif_list, list)
+ ath6kl_cfg80211_stop(vif);
+}
+
+static void ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct ath6kl *ar = wiphy_priv(wiphy);
+ u32 rates[IEEE80211_NUM_BANDS];
+ int ret, i;
+
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "cfg reg_notify %c%c%s%s initiator %d hint_type %d\n",
+ request->alpha2[0], request->alpha2[1],
+ request->intersect ? " intersect" : "",
+ request->processed ? " processed" : "",
+ request->initiator, request->user_reg_hint_type);
+
+ if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
+ return;
+
+ ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2);
+ if (ret) {
+ ath6kl_err("failed to set regdomain: %d\n", ret);
+ return;
+ }
+
+ /*
+ * Firmware will apply the regdomain change only after a scan is
+ * issued and it will send a WMI_REGDOMAIN_EVENTID when it has been
+ * changed.
+ */
+
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ if (wiphy->bands[i])
+ rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
+
+
+ ret = ath6kl_wmi_beginscan_cmd(ar->wmi, 0, WMI_LONG_SCAN, false,
+ false, 0, ATH6KL_FG_SCAN_INTERVAL,
+ 0, NULL, false, rates);
+ if (ret) {
+ ath6kl_err("failed to start scan for a regdomain change: %d\n",
+ ret);
+ return;
+ }
+}
+
+static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
+{
+ vif->aggr_cntxt = aggr_init(vif);
+ if (!vif->aggr_cntxt) {
+ ath6kl_err("failed to initialize aggr\n");
+ return -ENOMEM;
}
- ar = wiphy_priv(wdev->wiphy);
- ar->p2p = !!ath6kl_p2p;
+ setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
+ (unsigned long) vif->ndev);
+ setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
+ (unsigned long) vif);
+
+ set_bit(WMM_ENABLED, &vif->flags);
+ spin_lock_init(&vif->if_lock);
+
+ INIT_LIST_HEAD(&vif->mc_filter);
+
+ return 0;
+}
+
+void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready)
+{
+ static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ bool discon_issued;
- wdev->wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
+ netif_stop_queue(vif->ndev);
- wdev->wiphy->max_remain_on_channel_duration = 5000;
+ clear_bit(WLAN_ENABLED, &vif->flags);
+
+ if (wmi_ready) {
+ discon_issued = test_bit(CONNECTED, &vif->flags) ||
+ test_bit(CONNECT_PEND, &vif->flags);
+ ath6kl_disconnect(vif);
+ del_timer(&vif->disconnect_timer);
+
+ if (discon_issued)
+ ath6kl_disconnect_event(vif, DISCONNECT_CMD,
+ (vif->nw_type & AP_NETWORK) ?
+ bcast_mac : vif->bssid,
+ 0, NULL, 0);
+ }
+
+ if (vif->scan_req) {
+ cfg80211_scan_done(vif->scan_req, true);
+ vif->scan_req = NULL;
+ }
+
+ /* need to clean up enhanced bmiss detection fw state */
+ ath6kl_cfg80211_sta_bmiss_enhance(vif, false);
+}
+
+void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
+{
+ struct ath6kl *ar = vif->ar;
+ struct ath6kl_mc_filter *mc_filter, *tmp;
+
+ aggr_module_destroy(vif->aggr_cntxt);
+
+ ar->avail_idx_map |= BIT(vif->fw_vif_idx);
+
+ if (vif->nw_type == ADHOC_NETWORK)
+ ar->ibss_if_active = false;
+
+ list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
+ list_del(&mc_filter->list);
+ kfree(mc_filter);
+ }
+
+ unregister_netdevice(vif->ndev);
+
+ ar->num_vif--;
+}
+
+struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
+ enum nl80211_iftype type,
+ u8 fw_vif_idx, u8 nw_type)
+{
+ struct net_device *ndev;
+ struct ath6kl_vif *vif;
+
+ ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
+ if (!ndev)
+ return NULL;
+
+ vif = netdev_priv(ndev);
+ ndev->ieee80211_ptr = &vif->wdev;
+ vif->wdev.wiphy = ar->wiphy;
+ vif->ar = ar;
+ vif->ndev = ndev;
+ SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
+ vif->wdev.netdev = ndev;
+ vif->wdev.iftype = type;
+ vif->fw_vif_idx = fw_vif_idx;
+ vif->nw_type = nw_type;
+ vif->next_mode = nw_type;
+ vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
+ vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
+ vif->bg_scan_period = 0;
+ vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true;
+ vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true;
+
+ memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
+ if (fw_vif_idx != 0) {
+ ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
+ 0x2;
+ if (test_bit(ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR,
+ ar->fw_capabilities))
+ ndev->dev_addr[4] ^= 0x80;
+ }
+
+ init_netdev(ndev);
+
+ ath6kl_init_control_info(vif);
+
+ if (ath6kl_cfg80211_vif_init(vif))
+ goto err;
+
+ if (register_netdevice(ndev))
+ goto err;
+
+ ar->avail_idx_map &= ~BIT(fw_vif_idx);
+ vif->sme_state = SME_DISCONNECTED;
+ set_bit(WLAN_ENABLED, &vif->flags);
+ ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
+
+ if (type == NL80211_IFTYPE_ADHOC)
+ ar->ibss_if_active = true;
+
+ spin_lock_bh(&ar->list_lock);
+ list_add_tail(&vif->list, &ar->vif_list);
+ spin_unlock_bh(&ar->list_lock);
+
+ return &vif->wdev;
+
+err:
+ aggr_module_destroy(vif->aggr_cntxt);
+ free_netdev(ndev);
+ return NULL;
+}
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support ath6kl_wowlan_support = {
+ .flags = WIPHY_WOWLAN_MAGIC_PKT |
+ WIPHY_WOWLAN_DISCONNECT |
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+ WIPHY_WOWLAN_4WAY_HANDSHAKE,
+ .n_patterns = WOW_MAX_FILTERS_PER_LIST,
+ .pattern_min_len = 1,
+ .pattern_max_len = WOW_PATTERN_SIZE,
+};
+#endif
+
+int ath6kl_cfg80211_init(struct ath6kl *ar)
+{
+ struct wiphy *wiphy = ar->wiphy;
+ bool band_2gig = false, band_5gig = false, ht = false;
+ int ret;
+
+ wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
+
+ wiphy->max_remain_on_channel_duration = 5000;
/* set device pointer for wiphy */
- set_wiphy_dev(wdev->wiphy, dev);
+ set_wiphy_dev(wiphy, ar->dev);
- wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP);
if (ar->p2p) {
- wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_CLIENT);
+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT);
}
+
+ if (config_enabled(CONFIG_ATH6KL_REGDOMAIN) &&
+ test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities)) {
+ wiphy->reg_notifier = ath6kl_cfg80211_reg_notify;
+ ar->wiphy->features |= NL80211_FEATURE_CELL_BASE_REG_HINTS;
+ }
+
/* max num of ssids that can be probed during scanning */
- wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
- wdev->wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
- 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;
+ wiphy->max_scan_ssids = MAX_PROBED_SSIDS;
+
+ /* max num of ssids that can be matched after scan */
+ if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST,
+ ar->fw_capabilities))
+ wiphy->max_match_sets = MAX_PROBED_SSIDS;
+
+ wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
+ switch (ar->hw.cap) {
+ case WMI_11AN_CAP:
+ ht = true;
+ case WMI_11A_CAP:
+ band_5gig = true;
+ break;
+ case WMI_11GN_CAP:
+ ht = true;
+ case WMI_11G_CAP:
+ band_2gig = true;
+ break;
+ case WMI_11AGN_CAP:
+ ht = true;
+ case WMI_11AG_CAP:
+ band_2gig = true;
+ band_5gig = true;
+ break;
+ default:
+ ath6kl_err("invalid phy capability!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Even if the fw has HT support, advertise HT cap only when
+ * the firmware has support to override RSN capability, otherwise
+ * 4-way handshake would fail.
+ */
+ if (!(ht &&
+ test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+ ar->fw_capabilities))) {
+ ath6kl_band_2ghz.ht_cap.cap = 0;
+ ath6kl_band_2ghz.ht_cap.ht_supported = false;
+ ath6kl_band_5ghz.ht_cap.cap = 0;
+ ath6kl_band_5ghz.ht_cap.ht_supported = false;
+ }
+
+ if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) {
+ ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+ ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+ ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
+ ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
+ } else {
+ ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+ ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+ }
+
+ if (band_2gig)
+ wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
+ if (band_5gig)
+ wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
- wdev->wiphy->cipher_suites = cipher_suites;
- wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
- ret = wiphy_register(wdev->wiphy);
+ wiphy->cipher_suites = cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+#ifdef CONFIG_PM
+ wiphy->wowlan = &ath6kl_wowlan_support;
+#endif
+
+ wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
+
+ ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
+ WIPHY_FLAG_HAVE_AP_SME |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+ WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+
+ if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, ar->fw_capabilities))
+ ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+
+ if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
+ ar->fw_capabilities))
+ ar->wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
+
+ ar->wiphy->probe_resp_offload =
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
+
+ ret = wiphy_register(wiphy);
if (ret < 0) {
ath6kl_err("couldn't register wiphy device\n");
- wiphy_free(wdev->wiphy);
- kfree(wdev);
- return NULL;
+ return ret;
}
- return wdev;
+ ar->wiphy_registered = true;
+
+ return 0;
+}
+
+void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
+{
+ wiphy_unregister(ar->wiphy);
+
+ ar->wiphy_registered = false;
}
-void ath6kl_cfg80211_deinit(struct ath6kl *ar)
+struct ath6kl *ath6kl_cfg80211_create(void)
{
- struct wireless_dev *wdev = ar->wdev;
+ struct ath6kl *ar;
+ struct wiphy *wiphy;
+
+ /* create a new wiphy for use with cfg80211 */
+ wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
- if (ar->scan_req) {
- cfg80211_scan_done(ar->scan_req, true);
- ar->scan_req = NULL;
+ if (!wiphy) {
+ ath6kl_err("couldn't allocate wiphy device\n");
+ return NULL;
}
- if (!wdev)
- return;
+ ar = wiphy_priv(wiphy);
+ ar->wiphy = wiphy;
- wiphy_unregister(wdev->wiphy);
- wiphy_free(wdev->wiphy);
- kfree(wdev);
+ return ar;
}
+
+/* Note: ar variable must not be accessed after calling this! */
+void ath6kl_cfg80211_destroy(struct ath6kl *ar)
+{
+ int i;
+
+ for (i = 0; i < AP_MAX_NUM_STA; i++)
+ kfree(ar->sta_list[i].aggr_conn);
+
+ wiphy_free(ar->wiphy);
+}
+
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h
index a84adc249c6..b59becd91ae 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.h
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,23 +18,48 @@
#ifndef ATH6KL_CFG80211_H
#define ATH6KL_CFG80211_H
-struct wireless_dev *ath6kl_cfg80211_init(struct device *dev);
-void ath6kl_cfg80211_deinit(struct ath6kl *ar);
+enum ath6kl_cfg_suspend_mode {
+ ATH6KL_CFG_SUSPEND_DEEPSLEEP,
+ ATH6KL_CFG_SUSPEND_CUTPOWER,
+ ATH6KL_CFG_SUSPEND_WOW,
+};
-void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status);
+struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
+ enum nl80211_iftype type,
+ u8 fw_vif_idx, u8 nw_type);
+void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
+ enum wmi_phy_mode mode);
+void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted);
-void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
+void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, 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,
+void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, 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,
+void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
bool ismcast);
+int ath6kl_cfg80211_suspend(struct ath6kl *ar,
+ enum ath6kl_cfg_suspend_mode mode,
+ struct cfg80211_wowlan *wow);
+
+int ath6kl_cfg80211_resume(struct ath6kl *ar);
+
+void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif);
+
+void ath6kl_cfg80211_stop(struct ath6kl_vif *vif);
+void ath6kl_cfg80211_stop_all(struct ath6kl *ar);
+
+int ath6kl_cfg80211_init(struct ath6kl *ar);
+void ath6kl_cfg80211_cleanup(struct ath6kl *ar);
+
+struct ath6kl *ath6kl_cfg80211_create(void);
+void ath6kl_cfg80211_destroy(struct ath6kl *ar);
+
#endif /* ATH6KL_CFG80211_H */
diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h
index b92f0e5d233..05debf700a8 100644
--- a/drivers/net/wireless/ath/ath6kl/common.h
+++ b/drivers/net/wireless/ath/ath6kl/common.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -21,9 +22,7 @@
#define ATH6KL_MAX_IE 256
-extern int ath6kl_printk(const char *level, const char *fmt, ...);
-
-#define A_CACHE_LINE_PAD 128
+__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...);
/*
* Reflects the version of binary interface exposed by ATH6KL target
@@ -73,25 +72,14 @@ enum crypto_type {
WEP_CRYPT = 0x02,
TKIP_CRYPT = 0x04,
AES_CRYPT = 0x08,
+ WAPI_CRYPT = 0x10,
};
struct htc_endpoint_credit_dist;
struct ath6kl;
+struct ath6kl_htcap;
enum htc_credit_dist_reason;
-struct htc_credit_state_info;
+struct ath6kl_htc_credit_info;
-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 */
diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c
new file mode 100644
index 00000000000..b0b65204276
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/core.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/export.h>
+#include <linux/vmalloc.h>
+
+#include "debug.h"
+#include "hif-ops.h"
+#include "htc-ops.h"
+#include "cfg80211.h"
+
+unsigned int debug_mask;
+static unsigned int suspend_mode;
+static unsigned int wow_mode;
+static unsigned int uart_debug;
+static unsigned int ath6kl_p2p;
+static unsigned int testmode;
+static unsigned int recovery_enable;
+static unsigned int heart_beat_poll;
+
+module_param(debug_mask, uint, 0644);
+module_param(suspend_mode, uint, 0644);
+module_param(wow_mode, uint, 0644);
+module_param(uart_debug, uint, 0644);
+module_param(ath6kl_p2p, uint, 0644);
+module_param(testmode, uint, 0644);
+module_param(recovery_enable, uint, 0644);
+module_param(heart_beat_poll, uint, 0644);
+MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error");
+MODULE_PARM_DESC(heart_beat_poll,
+ "Enable fw error detection periodic polling in msecs - Also set recovery_enable for this to be effective");
+
+
+void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb)
+{
+ ath6kl_htc_tx_complete(ar, skb);
+}
+EXPORT_SYMBOL(ath6kl_core_tx_complete);
+
+void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe)
+{
+ ath6kl_htc_rx_complete(ar, skb, pipe);
+}
+EXPORT_SYMBOL(ath6kl_core_rx_complete);
+
+int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
+{
+ struct ath6kl_bmi_target_info targ_info;
+ struct wireless_dev *wdev;
+ int ret = 0, i;
+
+ switch (htc_type) {
+ case ATH6KL_HTC_TYPE_MBOX:
+ ath6kl_htc_mbox_attach(ar);
+ break;
+ case ATH6KL_HTC_TYPE_PIPE:
+ ath6kl_htc_pipe_attach(ar);
+ break;
+ default:
+ WARN_ON(1);
+ return -ENOMEM;
+ }
+
+ ar->ath6kl_wq = create_singlethread_workqueue("ath6kl");
+ if (!ar->ath6kl_wq)
+ return -ENOMEM;
+
+ ret = ath6kl_bmi_init(ar);
+ if (ret)
+ goto err_wq;
+
+ /*
+ * Turn on power to get hardware (target) version and leave power
+ * on delibrately as we will boot the hardware anyway within few
+ * seconds.
+ */
+ ret = ath6kl_hif_power_on(ar);
+ if (ret)
+ goto err_bmi_cleanup;
+
+ ret = ath6kl_bmi_get_target_info(ar, &targ_info);
+ if (ret)
+ goto err_power_off;
+
+ ar->version.target_ver = le32_to_cpu(targ_info.version);
+ ar->target_type = le32_to_cpu(targ_info.type);
+ ar->wiphy->hw_version = le32_to_cpu(targ_info.version);
+
+ ret = ath6kl_init_hw_params(ar);
+ if (ret)
+ goto err_power_off;
+
+ ar->htc_target = ath6kl_htc_create(ar);
+
+ if (!ar->htc_target) {
+ ret = -ENOMEM;
+ goto err_power_off;
+ }
+
+ ar->testmode = testmode;
+
+ ret = ath6kl_init_fetch_firmwares(ar);
+ if (ret)
+ goto err_htc_cleanup;
+
+ /* FIXME: we should free all firmwares in the error cases below */
+
+ /* 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");
+ ret = -EIO;
+ goto err_htc_cleanup;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi);
+
+ /* 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 */
+
+ /* allocate some buffers that handle larger AMSDU frames */
+ ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS);
+
+ ath6kl_cookie_init(ar);
+
+ ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER |
+ ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST;
+
+ if (suspend_mode &&
+ suspend_mode >= WLAN_POWER_STATE_CUT_PWR &&
+ suspend_mode <= WLAN_POWER_STATE_WOW)
+ ar->suspend_mode = suspend_mode;
+ else
+ ar->suspend_mode = 0;
+
+ if (suspend_mode == WLAN_POWER_STATE_WOW &&
+ (wow_mode == WLAN_POWER_STATE_CUT_PWR ||
+ wow_mode == WLAN_POWER_STATE_DEEP_SLEEP))
+ ar->wow_suspend_mode = wow_mode;
+ else
+ ar->wow_suspend_mode = 0;
+
+ if (uart_debug)
+ ar->conf_flags |= ATH6KL_CONF_UART_DEBUG;
+
+ set_bit(FIRST_BOOT, &ar->flag);
+
+ ath6kl_debug_init(ar);
+
+ ret = ath6kl_init_hw_start(ar);
+ if (ret) {
+ ath6kl_err("Failed to start hardware: %d\n", ret);
+ goto err_rxbuf_cleanup;
+ }
+
+ /* 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]);
+
+ ret = ath6kl_cfg80211_init(ar);
+ if (ret)
+ goto err_rxbuf_cleanup;
+
+ ret = ath6kl_debug_init_fs(ar);
+ if (ret) {
+ wiphy_unregister(ar->wiphy);
+ goto err_rxbuf_cleanup;
+ }
+
+ for (i = 0; i < ar->vif_max; i++)
+ ar->avail_idx_map |= BIT(i);
+
+ rtnl_lock();
+
+ /* Add an initial station interface */
+ wdev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0,
+ INFRA_NETWORK);
+
+ rtnl_unlock();
+
+ if (!wdev) {
+ ath6kl_err("Failed to instantiate a network device\n");
+ ret = -ENOMEM;
+ wiphy_unregister(ar->wiphy);
+ goto err_rxbuf_cleanup;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n",
+ __func__, wdev->netdev->name, wdev->netdev, ar);
+
+ ar->fw_recovery.enable = !!recovery_enable;
+ if (!ar->fw_recovery.enable)
+ return ret;
+
+ if (heart_beat_poll &&
+ test_bit(ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL,
+ ar->fw_capabilities))
+ ar->fw_recovery.hb_poll = heart_beat_poll;
+
+ ath6kl_recovery_init(ar);
+
+ return ret;
+
+err_rxbuf_cleanup:
+ ath6kl_debug_cleanup(ar);
+ ath6kl_htc_flush_rx_buf(ar->htc_target);
+ ath6kl_cleanup_amsdu_rxbufs(ar);
+ ath6kl_wmi_shutdown(ar->wmi);
+ clear_bit(WMI_ENABLED, &ar->flag);
+ ar->wmi = NULL;
+err_htc_cleanup:
+ ath6kl_htc_cleanup(ar->htc_target);
+err_power_off:
+ ath6kl_hif_power_off(ar);
+err_bmi_cleanup:
+ ath6kl_bmi_cleanup(ar);
+err_wq:
+ destroy_workqueue(ar->ath6kl_wq);
+
+ return ret;
+}
+EXPORT_SYMBOL(ath6kl_core_init);
+
+struct ath6kl *ath6kl_core_create(struct device *dev)
+{
+ struct ath6kl *ar;
+ u8 ctr;
+
+ ar = ath6kl_cfg80211_create();
+ if (!ar)
+ return NULL;
+
+ ar->p2p = !!ath6kl_p2p;
+ ar->dev = dev;
+
+ ar->vif_max = 1;
+
+ ar->max_norm_iface = 1;
+
+ spin_lock_init(&ar->lock);
+ spin_lock_init(&ar->mcastpsq_lock);
+ spin_lock_init(&ar->list_lock);
+
+ init_waitqueue_head(&ar->event_wq);
+ sema_init(&ar->sem, 1);
+
+ INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
+ INIT_LIST_HEAD(&ar->vif_list);
+
+ clear_bit(WMI_ENABLED, &ar->flag);
+ clear_bit(SKIP_SCAN, &ar->flag);
+ clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
+
+ ar->tx_pwr = 0;
+ ar->intra_bss = 1;
+ ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
+
+ ar->state = ATH6KL_STATE_OFF;
+
+ memset((u8 *)ar->sta_list, 0,
+ AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
+
+ /* 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->sta_list[ctr].apsdq);
+ ar->sta_list[ctr].mgmt_psq_len = 0;
+ INIT_LIST_HEAD(&ar->sta_list[ctr].mgmt_psq);
+ ar->sta_list[ctr].aggr_conn =
+ kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL);
+ if (!ar->sta_list[ctr].aggr_conn) {
+ ath6kl_err("Failed to allocate memory for sta aggregation information\n");
+ ath6kl_core_destroy(ar);
+ return NULL;
+ }
+ }
+
+ skb_queue_head_init(&ar->mcastpsq);
+
+ memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
+
+ return ar;
+}
+EXPORT_SYMBOL(ath6kl_core_create);
+
+void ath6kl_core_cleanup(struct ath6kl *ar)
+{
+ ath6kl_hif_power_off(ar);
+
+ ath6kl_recovery_cleanup(ar);
+
+ destroy_workqueue(ar->ath6kl_wq);
+
+ if (ar->htc_target)
+ ath6kl_htc_cleanup(ar->htc_target);
+
+ ath6kl_cookie_cleanup(ar);
+
+ ath6kl_cleanup_amsdu_rxbufs(ar);
+
+ ath6kl_bmi_cleanup(ar);
+
+ ath6kl_debug_cleanup(ar);
+
+ kfree(ar->fw_board);
+ kfree(ar->fw_otp);
+ vfree(ar->fw);
+ kfree(ar->fw_patch);
+ kfree(ar->fw_testscript);
+
+ ath6kl_cfg80211_cleanup(ar);
+}
+EXPORT_SYMBOL(ath6kl_core_cleanup);
+
+void ath6kl_core_destroy(struct ath6kl *ar)
+{
+ ath6kl_cfg80211_destroy(ar);
+}
+EXPORT_SYMBOL(ath6kl_core_destroy);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Core module for AR600x SDIO and USB devices.");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index 6d8a4845baa..26b0f92424e 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -44,6 +45,10 @@
#define ATH6KL_MAX_ENDPOINTS 4
#define MAX_NODE_NUM 15
+#define ATH6KL_APSD_ALL_FRAME 0xFFFF
+#define ATH6KL_APSD_NUM_OF_AC 0x4
+#define ATH6KL_APSD_FRAME_MASK 0xF
+
/* Extra bytes for htc header alignment */
#define ATH6KL_HTC_ALIGN_BYTES 3
@@ -55,8 +60,9 @@
#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
+
+/* Channel dwell time in fg scan */
+#define ATH6KL_FG_SCAN_INTERVAL 50 /* in ms */
/* includes also the null byte */
#define ATH6KL_FIRMWARE_MAGIC "QCA-ATH6KL"
@@ -70,10 +76,65 @@ enum ath6kl_fw_ie_type {
ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5,
ATH6KL_FW_IE_CAPABILITIES = 6,
ATH6KL_FW_IE_PATCH_ADDR = 7,
+ ATH6KL_FW_IE_BOARD_ADDR = 8,
+ ATH6KL_FW_IE_VIF_MAX = 9,
};
enum ath6kl_fw_capability {
ATH6KL_FW_CAPABILITY_HOST_P2P = 0,
+ ATH6KL_FW_CAPABILITY_SCHED_SCAN = 1,
+
+ /*
+ * Firmware is capable of supporting P2P mgmt operations on a
+ * station interface. After group formation, the station
+ * interface will become a P2P client/GO interface as the case may be
+ */
+ ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+
+ /*
+ * Firmware has support to cleanup inactive stations
+ * in AP mode.
+ */
+ ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
+
+ /* Firmware has support to override rsn cap of rsn ie */
+ ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+
+ /*
+ * Multicast support in WOW and host awake mode.
+ * Allow all multicast in host awake mode.
+ * Apply multicast filter in WOW mode.
+ */
+ ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
+
+ /* Firmware supports enhanced bmiss detection */
+ ATH6KL_FW_CAPABILITY_BMISS_ENHANCE,
+
+ /*
+ * FW supports matching of ssid in schedule scan
+ */
+ ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST,
+
+ /* Firmware supports filtering BSS results by RSSI */
+ ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
+
+ /* FW sets mac_addr[4] ^= 0x80 for newly created interfaces */
+ ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR,
+
+ /* Firmware supports TX error rate notification */
+ ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY,
+
+ /* supports WMI_SET_REGDOMAIN_CMDID command */
+ ATH6KL_FW_CAPABILITY_REGDOMAIN,
+
+ /* Firmware supports sched scan decoupled from host sleep */
+ ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2,
+
+ /*
+ * Firmware capability for hang detection through heart beat
+ * challenge messages.
+ */
+ ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL,
/* this needs to be last */
ATH6KL_FW_CAPABILITY_MAX,
@@ -87,43 +148,82 @@ struct ath6kl_fw_ie {
u8 data[0];
};
+enum ath6kl_hw_flags {
+ ATH6KL_HW_64BIT_RATES = BIT(0),
+ ATH6KL_HW_AP_INACTIVITY_MINS = BIT(1),
+ ATH6KL_HW_MAP_LP_ENDPOINT = BIT(2),
+ ATH6KL_HW_SDIO_CRC_ERROR_WAR = BIT(3),
+};
+
+#define ATH6KL_FW_API2_FILE "fw-2.bin"
+#define ATH6KL_FW_API3_FILE "fw-3.bin"
+#define ATH6KL_FW_API4_FILE "fw-4.bin"
+
/* AR6003 1.0 definitions */
-#define AR6003_REV1_VERSION 0x300002ba
+#define AR6003_HW_1_0_VERSION 0x300002ba
/* AR6003 2.0 definitions */
-#define AR6003_REV2_VERSION 0x30000384
-#define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS 0x57e910
-#define AR6003_REV2_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77"
-#define AR6003_REV2_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77"
-#define AR6003_REV2_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athtcmd_ram.bin"
-#define AR6003_REV2_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin"
-#define AR6003_REV2_FIRMWARE_2_FILE "ath6k/AR6003/hw2.0/fw-2.bin"
-#define AR6003_REV2_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin"
-#define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin"
+#define AR6003_HW_2_0_VERSION 0x30000384
+#define AR6003_HW_2_0_PATCH_DOWNLOAD_ADDRESS 0x57e910
+#define AR6003_HW_2_0_FW_DIR "ath6k/AR6003/hw2.0"
+#define AR6003_HW_2_0_OTP_FILE "otp.bin.z77"
+#define AR6003_HW_2_0_FIRMWARE_FILE "athwlan.bin.z77"
+#define AR6003_HW_2_0_TCMD_FIRMWARE_FILE "athtcmd_ram.bin"
+#define AR6003_HW_2_0_PATCH_FILE "data.patch.bin"
+#define AR6003_HW_2_0_BOARD_DATA_FILE AR6003_HW_2_0_FW_DIR "/bdata.bin"
+#define AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE \
+ AR6003_HW_2_0_FW_DIR "/bdata.SD31.bin"
/* AR6003 3.0 definitions */
-#define AR6003_REV3_VERSION 0x30000582
-#define AR6003_REV3_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin"
-#define AR6003_REV3_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin"
-#define AR6003_REV3_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin"
-#define AR6003_REV3_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin"
-#define AR6003_REV3_FIRMWARE_2_FILE "ath6k/AR6003/hw2.1.1/fw-2.bin"
-#define AR6003_REV3_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin"
-#define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \
- "ath6k/AR6003/hw2.1.1/bdata.SD31.bin"
+#define AR6003_HW_2_1_1_VERSION 0x30000582
+#define AR6003_HW_2_1_1_FW_DIR "ath6k/AR6003/hw2.1.1"
+#define AR6003_HW_2_1_1_OTP_FILE "otp.bin"
+#define AR6003_HW_2_1_1_FIRMWARE_FILE "athwlan.bin"
+#define AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE "athtcmd_ram.bin"
+#define AR6003_HW_2_1_1_UTF_FIRMWARE_FILE "utf.bin"
+#define AR6003_HW_2_1_1_TESTSCRIPT_FILE "nullTestFlow.bin"
+#define AR6003_HW_2_1_1_PATCH_FILE "data.patch.bin"
+#define AR6003_HW_2_1_1_BOARD_DATA_FILE AR6003_HW_2_1_1_FW_DIR "/bdata.bin"
+#define AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE \
+ AR6003_HW_2_1_1_FW_DIR "/bdata.SD31.bin"
/* AR6004 1.0 definitions */
-#define AR6004_REV1_VERSION 0x30000623
-#define AR6004_REV1_FIRMWARE_FILE "ath6k/AR6004/hw6.1/fw.ram.bin"
-#define AR6004_REV1_FIRMWARE_2_FILE "ath6k/AR6004/hw6.1/fw-2.bin"
-#define AR6004_REV1_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.bin"
-#define AR6004_REV1_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.DB132.bin"
-#define AR6004_REV1_EPPING_FIRMWARE_FILE "ath6k/AR6004/hw6.1/endpointping.bin"
+#define AR6004_HW_1_0_VERSION 0x30000623
+#define AR6004_HW_1_0_FW_DIR "ath6k/AR6004/hw1.0"
+#define AR6004_HW_1_0_FIRMWARE_FILE "fw.ram.bin"
+#define AR6004_HW_1_0_BOARD_DATA_FILE AR6004_HW_1_0_FW_DIR "/bdata.bin"
+#define AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE \
+ AR6004_HW_1_0_FW_DIR "/bdata.DB132.bin"
+
+/* AR6004 1.1 definitions */
+#define AR6004_HW_1_1_VERSION 0x30000001
+#define AR6004_HW_1_1_FW_DIR "ath6k/AR6004/hw1.1"
+#define AR6004_HW_1_1_FIRMWARE_FILE "fw.ram.bin"
+#define AR6004_HW_1_1_BOARD_DATA_FILE AR6004_HW_1_1_FW_DIR "/bdata.bin"
+#define AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE \
+ AR6004_HW_1_1_FW_DIR "/bdata.DB132.bin"
+
+/* AR6004 1.2 definitions */
+#define AR6004_HW_1_2_VERSION 0x300007e8
+#define AR6004_HW_1_2_FW_DIR "ath6k/AR6004/hw1.2"
+#define AR6004_HW_1_2_FIRMWARE_FILE "fw.ram.bin"
+#define AR6004_HW_1_2_BOARD_DATA_FILE AR6004_HW_1_2_FW_DIR "/bdata.bin"
+#define AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE \
+ AR6004_HW_1_2_FW_DIR "/bdata.bin"
+
+/* AR6004 1.3 definitions */
+#define AR6004_HW_1_3_VERSION 0x31c8088a
+#define AR6004_HW_1_3_FW_DIR "ath6k/AR6004/hw1.3"
+#define AR6004_HW_1_3_FIRMWARE_FILE "fw.ram.bin"
+#define AR6004_HW_1_3_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.bin"
+#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.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)
+#define STA_PS_APSD_TRIGGER BIT(3)
+#define STA_PS_APSD_EOSP BIT(4)
/* HTC TX packet tagging definitions */
#define ATH6KL_CONTROL_PKT_TAG HTC_TX_PACKET_TAG_USER_DEFINED
@@ -147,12 +247,17 @@ struct ath6kl_fw_ie {
#define AGGR_NUM_OF_FREE_NETBUFS 16
-#define AGGR_RX_TIMEOUT 400 /* in ms */
+#define AGGR_RX_TIMEOUT 100 /* in ms */
#define WMI_TIMEOUT (2 * HZ)
#define MBOX_YIELD_LIMIT 99
+#define ATH6KL_DEFAULT_LISTEN_INTVAL 100 /* in TUs */
+#define ATH6KL_DEFAULT_BMISS_TIME 1500
+#define ATH6KL_MAX_WOW_LISTEN_INTL 300 /* in TUs */
+#define ATH6KL_MAX_BMISS_TIME 5000
+
/* configuration lags */
/*
* ATH6KL_CONF_IGNORE_ERP_BARKER: Ignore the barker premable in
@@ -166,6 +271,9 @@ struct ath6kl_fw_ie {
#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)
+#define ATH6KL_CONF_UART_DEBUG BIT(4)
+
+#define P2P_WILDCARD_SSID_LEN 7 /* DIRECT- */
enum wlan_low_pwr_state {
WLAN_POWER_STATE_ON,
@@ -188,13 +296,24 @@ struct skb_hold_q {
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;
+
+ /*
+ * lock mainly protects seq_next and hold_q. Movement of seq_next
+ * needs to be protected between aggr_timeout() and
+ * aggr_process_recv_frm(). hold_q will be holding the pending
+ * reorder frames and it's access should also be protected.
+ * Some of the other fields like hold_q_sz, win_sz and aggr are
+ * initialized/reset when receiving addba/delba req, also while
+ * deleting aggr state all the pending buffers are flushed before
+ * resetting these fields, so there should not be any race in accessing
+ * these fields.
+ */
spinlock_t lock;
};
@@ -210,14 +329,19 @@ struct rxtid_stats {
u32 num_bar;
};
-struct aggr_info {
+struct aggr_info_conn {
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 aggr_info *aggr_info;
+};
+
+struct aggr_info {
+ struct aggr_info_conn *aggr_conn;
+ struct sk_buff_head rx_amsdu_freeq;
};
struct ath6kl_wep_key {
@@ -249,6 +373,16 @@ struct ath6kl_cookie {
struct ath6kl_cookie *arc_list_next;
};
+struct ath6kl_mgmt_buff {
+ struct list_head list;
+ u32 freq;
+ u32 wait;
+ u32 id;
+ bool no_cck;
+ size_t len;
+ u8 buf[0];
+};
+
struct ath6kl_sta {
u16 sta_flags;
u8 mac[ETH_ALEN];
@@ -258,7 +392,15 @@ struct ath6kl_sta {
u8 auth;
u8 wpa_ie[ATH6KL_MAX_IE];
struct sk_buff_head psq;
+
+ /* protects psq, mgmt_psq, apsdq, and mgmt_psq_len fields */
spinlock_t psq_lock;
+
+ struct list_head mgmt_psq;
+ size_t mgmt_psq_len;
+ u8 apsd_info;
+ struct sk_buff_head apsdq;
+ struct aggr_info_conn *aggr_conn;
};
struct ath6kl_version {
@@ -271,6 +413,8 @@ struct ath6kl_bmi {
u32 cmd_credits;
bool done_sent;
u8 *cmd_buf;
+ u32 max_data_size;
+ u32 max_cmd_size;
};
struct target_stats {
@@ -380,40 +524,63 @@ struct ath6kl_req_key {
u8 key_len;
};
-/* 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
-#define TESTMODE 13
-#define CLEAR_BSSFILTER_ON_BEACON 14
-#define DTIM_PERIOD_AVAIL 15
+enum ath6kl_hif_type {
+ ATH6KL_HIF_TYPE_SDIO,
+ ATH6KL_HIF_TYPE_USB,
+};
-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;
+enum ath6kl_htc_type {
+ ATH6KL_HTC_TYPE_MBOX,
+ ATH6KL_HTC_TYPE_PIPE,
+};
+
+/* Max number of filters that hw supports */
+#define ATH6K_MAX_MC_FILTERS_PER_LIST 7
+struct ath6kl_mc_filter {
+ struct list_head list;
+ char hw_addr[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE];
+};
+
+struct ath6kl_htcap {
+ bool ht_enable;
+ u8 ampdu_factor;
+ unsigned short cap_info;
+};
+
+/*
+ * Driver's maximum limit, note that some firmwares support only one vif
+ * and the runtime (current) limit must be checked from ar->vif_max.
+ */
+#define ATH6KL_VIF_MAX 3
+
+/* vif flags info */
+enum ath6kl_vif_state {
+ CONNECTED,
+ CONNECT_PEND,
+ WMM_ENABLED,
+ NETQ_STOPPED,
+ DTIM_EXPIRED,
+ CLEAR_BSSFILTER_ON_BEACON,
+ DTIM_PERIOD_AVAIL,
+ WLAN_ENABLED,
+ STATS_UPDATE_PEND,
+ HOST_SLEEP_MODE_CMD_PROCESSED,
+ NETDEV_MCAST_ALL_ON,
+ NETDEV_MCAST_ALL_OFF,
+ SCHED_SCANNING,
+};
+
+struct ath6kl_vif {
+ struct list_head list;
+ struct wireless_dev wdev;
+ struct net_device *ndev;
+ struct ath6kl *ar;
+ /* Lock to protect vif specific net_stats and flags */
+ spinlock_t if_lock;
+ u8 fw_vif_idx;
+ unsigned long flags;
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;
@@ -421,21 +588,122 @@ struct ath6kl {
u8 grp_crypto;
u8 grp_crypto_len;
u8 def_txkey_index;
- struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
+ u8 next_mode;
+ u8 nw_type;
u8 bssid[ETH_ALEN];
u8 req_bssid[ETH_ALEN];
u16 ch_hint;
u16 bss_ch;
- u16 listen_intvl_b;
+ struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
+ struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1];
+ struct aggr_info *aggr_cntxt;
+ struct ath6kl_htcap htcap[IEEE80211_NUM_BANDS];
+
+ struct timer_list disconnect_timer;
+ struct timer_list sched_scan_timer;
+
+ struct cfg80211_scan_request *scan_req;
+ enum sme_state sme_state;
+ int reconnect_flag;
+ u32 last_roc_id;
+ u32 last_cancel_roc_id;
+ u32 send_action_id;
+ bool probe_req_report;
+ u16 assoc_bss_beacon_int;
u16 listen_intvl_t;
+ u16 bmiss_time_t;
+ u32 txe_intvl;
+ u16 bg_scan_period;
+ u8 assoc_bss_dtim_period;
+ struct net_device_stats net_stats;
+ struct target_stats target_stats;
+ struct wmi_connect_cmd profile;
+ u16 rsn_capab;
+
+ struct list_head mc_filter;
+};
+
+static inline struct ath6kl_vif *ath6kl_vif_from_wdev(struct wireless_dev *wdev)
+{
+ return container_of(wdev, struct ath6kl_vif, wdev);
+}
+
+#define WOW_LIST_ID 0
+#define WOW_HOST_REQ_DELAY 500 /* ms */
+
+#define ATH6KL_SCHED_SCAN_RESULT_DELAY 5000 /* ms */
+
+/* Flag info */
+enum ath6kl_dev_state {
+ WMI_ENABLED,
+ WMI_READY,
+ WMI_CTRL_EP_FULL,
+ TESTMODE,
+ DESTROY_IN_PROGRESS,
+ SKIP_SCAN,
+ ROAM_TBL_PEND,
+ FIRST_BOOT,
+ RECOVERY_CLEANUP,
+};
+
+enum ath6kl_state {
+ ATH6KL_STATE_OFF,
+ ATH6KL_STATE_ON,
+ ATH6KL_STATE_SUSPENDING,
+ ATH6KL_STATE_RESUMING,
+ ATH6KL_STATE_DEEPSLEEP,
+ ATH6KL_STATE_CUTPOWER,
+ ATH6KL_STATE_WOW,
+ ATH6KL_STATE_RECOVERY,
+};
+
+/* Fw error recovery */
+#define ATH6KL_HB_RESP_MISS_THRES 5
+
+enum ath6kl_fw_err {
+ ATH6KL_FW_ASSERT,
+ ATH6KL_FW_HB_RESP_FAILURE,
+ ATH6KL_FW_EP_FULL,
+};
+
+struct ath6kl {
+ struct device *dev;
+ struct wiphy *wiphy;
+
+ enum ath6kl_state state;
+ unsigned int testmode;
+
+ struct ath6kl_bmi bmi;
+ const struct ath6kl_hif_ops *hif_ops;
+ const struct ath6kl_htc_ops *htc_ops;
+ struct wmi *wmi;
+ int tx_pending[ENDPOINT_MAX];
+ int total_tx_data_pend;
+ struct htc_target *htc_target;
+ enum ath6kl_hif_type hif_type;
+ void *hif_priv;
+ struct list_head vif_list;
+ /* Lock to avoid race in vif_list entries among add/del/traverse */
+ spinlock_t list_lock;
+ u8 num_vif;
+ unsigned int vif_max;
+ u8 max_norm_iface;
+ u8 avail_idx_map;
+
+ /*
+ * Protects at least amsdu_rx_buffer_queue, ath6kl_alloc_cookie()
+ * calls, tx_pending and total_tx_data_pend.
+ */
+ spinlock_t lock;
+
+ struct semaphore sem;
u8 lrssi_roam_threshold;
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;
+ bool ibss_if_active;
u8 node_num;
u8 next_ep_id;
struct ath6kl_cookie *cookie_list;
@@ -446,7 +714,7 @@ struct ath6kl {
u8 hiac_stream_active_pri;
u8 ep2ac_map[ENDPOINT_MAX];
enum htc_endpoint_id ctrl_ep;
- struct htc_credit_state_info credit_state_info;
+ struct ath6kl_htc_credit_info credit_state_info;
u32 connect_ctrl_flags;
u32 user_key_ctrl;
u8 usr_bss_filter;
@@ -454,40 +722,65 @@ struct ath6kl {
u8 sta_list_index;
struct ath6kl_req_key ap_mode_bkey;
struct sk_buff_head mcastpsq;
+ u32 want_ch_switch;
+ u16 last_ch;
+
+ /*
+ * FIXME: protects access to mcastpsq but is actually useless as
+ * all skbe_queue_*() functions provide serialisation themselves
+ */
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;
+ u8 mac_addr[ETH_ALEN];
#define AR_MCAST_FILTER_MAC_ADDR_SIZE 4
struct {
void *rx_report;
size_t rx_report_len;
} tm;
- struct {
+ struct ath6kl_hw {
+ u32 id;
+ const char *name;
u32 dataset_patch_addr;
u32 app_load_addr;
u32 app_start_override_addr;
u32 board_ext_data_addr;
u32 reserved_ram_size;
+ u32 board_addr;
+ u32 refclk_hz;
+ u32 uarttx_pin;
+ u32 testscript_addr;
+ enum wmi_phy_cap cap;
+
+ u32 flags;
+
+ struct ath6kl_hw_fw {
+ const char *dir;
+ const char *otp;
+ const char *fw;
+ const char *tcmd;
+ const char *patch;
+ const char *utf;
+ const char *testscript;
+ } fw;
+
+ const char *fw_board;
+ const char *fw_default_board;
} hw;
u16 conf_flags;
+ u16 suspend_mode;
+ u16 wow_suspend_mode;
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;
@@ -502,26 +795,39 @@ struct ath6kl {
u8 *fw_patch;
size_t fw_patch_len;
+ u8 *fw_testscript;
+ size_t fw_testscript_len;
+
+ unsigned int fw_api;
unsigned long fw_capabilities[ATH6KL_CAPABILITY_LEN];
struct workqueue_struct *ath6kl_wq;
struct dentry *debugfs_phy;
- u32 send_action_id;
- bool probe_req_report;
- u16 next_chan;
-
bool p2p;
- u16 assoc_bss_beacon_int;
- u8 assoc_bss_dtim_period;
+
+ bool wiphy_registered;
+
+ struct ath6kl_fw_recovery {
+ struct work_struct recovery_work;
+ unsigned long err_reason;
+ unsigned long hb_poll;
+ struct timer_list hb_timer;
+ u32 seq_num;
+ bool hb_pending;
+ u8 hb_misscnt;
+ bool enable;
+ } fw_recovery;
#ifdef CONFIG_ATH6KL_DEBUG
struct {
- struct circ_buf fwlog_buf;
- spinlock_t fwlog_lock;
- void *fwlog_tmp;
+ struct sk_buff_head fwlog_queue;
+ struct completion fwlog_completion;
+ bool fwlog_open;
+
u32 fwlog_mask;
+
unsigned int dbgfs_diag_reg;
u32 diag_reg_addr_wr;
u32 diag_reg_val_wr;
@@ -529,23 +835,19 @@ struct ath6kl {
struct {
unsigned int invalid_rate;
} war_stats;
+
+ u8 *roam_tbl;
+ unsigned int roam_tbl_len;
+
+ u8 keepalive;
+ u8 disc_timeout;
} debug;
#endif /* CONFIG_ATH6KL_DEBUG */
};
-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)
+static inline struct ath6kl *ath6kl_priv(struct net_device *dev)
{
- ep_dist->credits += credits;
- ep_dist->cred_assngd += credits;
- cred_info->cur_free_credits -= credits;
+ return ((struct ath6kl_vif *) netdev_priv(dev))->ar;
}
static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
@@ -561,7 +863,6 @@ static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
return addr;
}
-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);
@@ -569,7 +870,8 @@ 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);
+void ath6kl_tx_complete(struct htc_target *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);
@@ -579,16 +881,16 @@ int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length);
int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value);
int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length);
int ath6kl_read_fwlogs(struct ath6kl *ar);
-void ath6kl_init_profile_info(struct ath6kl *ar);
+void ath6kl_init_profile_info(struct ath6kl_vif *vif);
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);
+struct aggr_info *aggr_init(struct ath6kl_vif *vif);
+void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info,
+ struct aggr_info_conn *aggr_conn);
void ath6kl_rx_refill(struct htc_target *target,
enum htc_endpoint_id endpoint);
void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count);
@@ -596,42 +898,67 @@ 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);
+void aggr_reset_state(struct aggr_info_conn *aggr_conn);
-struct ath6kl_sta *ath6kl_find_sta(struct ath6kl *ar, u8 * node_addr);
+struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, 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);
+void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver,
+ enum wmi_phy_cap cap);
int ath6kl_control_tx(void *devt, struct sk_buff *skb,
enum htc_endpoint_id eid);
-void ath6kl_connect_event(struct ath6kl *ar, u16 channel,
+void ath6kl_connect_event(struct ath6kl_vif *vif, 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_connect_ap_mode_bss(struct ath6kl *ar, u16 channel);
-void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
+void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel);
+void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr,
u8 keymgmt, u8 ucipher, u8 auth,
- u8 assoc_req_len, u8 *assoc_info);
-void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason,
+ u8 assoc_req_len, u8 *assoc_info, u8 apsd_info);
+void ath6kl_disconnect_event(struct ath6kl_vif *vif, 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_tkip_micerr_event(struct ath6kl_vif *vif, 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_scan_complete_evt(struct ath6kl_vif *vif, int status);
+void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, 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_pspoll_event(struct ath6kl_vif *vif, u8 aid);
-void ath6kl_dtimexpiry_event(struct ath6kl *ar);
-void ath6kl_disconnect(struct ath6kl *ar);
-void ath6kl_deep_sleep_enable(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,
+void ath6kl_dtimexpiry_event(struct ath6kl_vif *vif);
+void ath6kl_disconnect(struct ath6kl_vif *vif);
+void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid);
+void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid, u16 seq_no,
u8 win_sz);
void ath6kl_wakeup_event(void *dev);
-void ath6kl_target_failure(struct ath6kl *ar);
+void ath6kl_init_control_info(struct ath6kl_vif *vif);
+struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar);
+void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready);
+int ath6kl_init_hw_start(struct ath6kl *ar);
+int ath6kl_init_hw_stop(struct ath6kl *ar);
+int ath6kl_init_fetch_firmwares(struct ath6kl *ar);
+int ath6kl_init_hw_params(struct ath6kl *ar);
+
+void ath6kl_check_wow_status(struct ath6kl *ar);
+
+void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb);
+void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe);
+
+struct ath6kl *ath6kl_core_create(struct device *dev);
+int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type);
+void ath6kl_core_cleanup(struct ath6kl *ar);
+void ath6kl_core_destroy(struct ath6kl *ar);
+
+/* Fw error recovery */
+void ath6kl_init_hw_restart(struct ath6kl *ar);
+void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason);
+void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie);
+void ath6kl_recovery_init(struct ath6kl *ar);
+void ath6kl_recovery_cleanup(struct ath6kl *ar);
+void ath6kl_recovery_suspend(struct ath6kl *ar);
+void ath6kl_recovery_resume(struct ath6kl *ar);
#endif /* CORE_H */
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index ba3f23d7115..55c4064dd50 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -16,9 +17,10 @@
#include "core.h"
-#include <linux/circ_buf.h>
+#include <linux/skbuff.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include "debug.h"
#include "target.h"
@@ -31,9 +33,8 @@ struct ath6kl_fwlog_slot {
u8 payload[0];
};
-#define ATH6KL_FWLOG_SIZE 32768
-#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
- ATH6KL_FWLOG_PAYLOAD_SIZE)
+#define ATH6KL_FWLOG_MAX_ENTRIES 20
+
#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
int ath6kl_printk(const char *level, const char *fmt, ...)
@@ -53,9 +54,100 @@ int ath6kl_printk(const char *level, const char *fmt, ...)
return rtn;
}
+EXPORT_SYMBOL(ath6kl_printk);
+
+int ath6kl_info(const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = ath6kl_printk(KERN_INFO, "%pV", &vaf);
+ trace_ath6kl_log_info(&vaf);
+ va_end(args);
+
+ return ret;
+}
+EXPORT_SYMBOL(ath6kl_info);
+
+int ath6kl_err(const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = ath6kl_printk(KERN_ERR, "%pV", &vaf);
+ trace_ath6kl_log_err(&vaf);
+ va_end(args);
+
+ return ret;
+}
+EXPORT_SYMBOL(ath6kl_err);
+
+int ath6kl_warn(const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = ath6kl_printk(KERN_WARNING, "%pV", &vaf);
+ trace_ath6kl_log_warn(&vaf);
+ va_end(args);
+
+ return ret;
+}
+EXPORT_SYMBOL(ath6kl_warn);
#ifdef CONFIG_ATH6KL_DEBUG
+void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (debug_mask & mask)
+ ath6kl_printk(KERN_DEBUG, "%pV", &vaf);
+
+ trace_ath6kl_log_dbg(mask, &vaf);
+
+ va_end(args);
+}
+EXPORT_SYMBOL(ath6kl_dbg);
+
+void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len)
+{
+ if (debug_mask & mask) {
+ if (msg)
+ ath6kl_dbg(mask, "%s\n", msg);
+
+ print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+ }
+
+ /* tracing code doesn't like null strings :/ */
+ trace_ath6kl_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
+ buf, len);
+}
+EXPORT_SYMBOL(ath6kl_dbg_dump);
+
#define REG_OUTPUT_LEN_PER_LINE 25
#define REGTYPE_STR_LEN 100
@@ -80,117 +172,105 @@ 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"));
+ ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- 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,
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "Host Int status: 0x%x\n",
+ irq_proc_reg->host_int_status);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
"CPU Int status: 0x%x\n",
- irq_proc_reg->cpu_int_status);
- ath6kl_dbg(ATH6KL_DBG_ANY,
+ irq_proc_reg->cpu_int_status);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
"Error Int status: 0x%x\n",
- irq_proc_reg->error_int_status);
- ath6kl_dbg(ATH6KL_DBG_ANY,
+ irq_proc_reg->error_int_status);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
"Counter Int status: 0x%x\n",
- irq_proc_reg->counter_int_status);
- ath6kl_dbg(ATH6KL_DBG_ANY,
+ irq_proc_reg->counter_int_status);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
"Mbox Frame: 0x%x\n",
- irq_proc_reg->mbox_frame);
- ath6kl_dbg(ATH6KL_DBG_ANY,
+ irq_proc_reg->mbox_frame);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
"Rx Lookahead Valid: 0x%x\n",
- irq_proc_reg->rx_lkahd_valid);
- ath6kl_dbg(ATH6KL_DBG_ANY,
+ irq_proc_reg->rx_lkahd_valid);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
"Rx Lookahead 0: 0x%x\n",
- irq_proc_reg->rx_lkahd[0]);
- ath6kl_dbg(ATH6KL_DBG_ANY,
+ irq_proc_reg->rx_lkahd[0]);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
"Rx Lookahead 1: 0x%x\n",
- irq_proc_reg->rx_lkahd[1]);
+ 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]);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "GMBOX Host Int status 2: 0x%x\n",
+ irq_proc_reg->host_int_status2);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "GMBOX RX Avail: 0x%x\n",
+ irq_proc_reg->gmbox_rx_avail);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "GMBOX lookahead alias 0: 0x%x\n",
+ irq_proc_reg->rx_gmbox_lkahd_alias[0]);
+ ath6kl_dbg(ATH6KL_DBG_IRQ,
+ "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_IRQ,
+ "Int status Enable: 0x%x\n",
+ irq_enable_reg->int_status_en);
+ ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status Enable: 0x%x\n",
+ irq_enable_reg->cntr_int_status_en);
}
- ath6kl_dbg(ATH6KL_DBG_ANY, "<------------------------------->\n");
+ ath6kl_dbg(ATH6KL_DBG_IRQ, "<------------------------------->\n");
}
static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist)
{
- ath6kl_dbg(ATH6KL_DBG_ANY,
+ ath6kl_dbg(ATH6KL_DBG_CREDIT,
"--- endpoint: %d svc_id: 0x%X ---\n",
ep_dist->endpoint, ep_dist->svc_id);
- ath6kl_dbg(ATH6KL_DBG_ANY, " dist_flags : 0x%X\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " dist_flags : 0x%X\n",
ep_dist->dist_flags);
- ath6kl_dbg(ATH6KL_DBG_ANY, " cred_norm : %d\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_norm : %d\n",
ep_dist->cred_norm);
- ath6kl_dbg(ATH6KL_DBG_ANY, " cred_min : %d\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_min : %d\n",
ep_dist->cred_min);
- ath6kl_dbg(ATH6KL_DBG_ANY, " credits : %d\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " credits : %d\n",
ep_dist->credits);
- ath6kl_dbg(ATH6KL_DBG_ANY, " cred_assngd : %d\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_assngd : %d\n",
ep_dist->cred_assngd);
- ath6kl_dbg(ATH6KL_DBG_ANY, " seek_cred : %d\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " seek_cred : %d\n",
ep_dist->seek_cred);
- ath6kl_dbg(ATH6KL_DBG_ANY, " cred_sz : %d\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_sz : %d\n",
ep_dist->cred_sz);
- ath6kl_dbg(ATH6KL_DBG_ANY, " cred_per_msg : %d\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_per_msg : %d\n",
ep_dist->cred_per_msg);
- ath6kl_dbg(ATH6KL_DBG_ANY, " cred_to_dist : %d\n",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " 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,
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, " txq_depth : %d\n",
+ get_queue_depth(&ep_dist->htc_ep->txq));
+ ath6kl_dbg(ATH6KL_DBG_CREDIT,
"----------------------------------\n");
}
+/* FIXME: move to htc.c */
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);
-}
-
-static int ath6kl_debugfs_open(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
+ ath6kl_dbg(ATH6KL_DBG_CREDIT,
+ "credit distribution total %d free %d\n",
+ target->credit_info->total_avail_credits,
+ target->credit_info->cur_free_credits);
}
void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
@@ -233,110 +313,108 @@ static ssize_t read_file_war_stats(struct file *file, char __user *user_buf,
static const struct file_operations fops_war_stats = {
.read = read_file_war_stats,
- .open = ath6kl_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
-static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf,
- size_t buf_len)
+void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
{
- struct circ_buf *fwlog = &ar->debug.fwlog_buf;
- size_t space;
- int i;
+ struct ath6kl_fwlog_slot *slot;
+ struct sk_buff *skb;
+ size_t slot_len;
- /* entries must all be equal size */
- if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE))
+ if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
return;
- space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE);
- if (space < buf_len)
- /* discard oldest slot */
- fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) &
- (ATH6KL_FWLOG_SIZE - 1);
+ slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE;
- for (i = 0; i < buf_len; i += space) {
- space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail,
- ATH6KL_FWLOG_SIZE);
+ skb = alloc_skb(slot_len, GFP_KERNEL);
+ if (!skb)
+ return;
- if ((size_t) space > buf_len - i)
- space = buf_len - i;
+ slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len);
+ slot->timestamp = cpu_to_le32(jiffies);
+ slot->length = cpu_to_le32(len);
+ memcpy(slot->payload, buf, len);
- memcpy(&fwlog->buf[fwlog->head], buf, space);
- fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1);
- }
+ /* Need to pad each record to fixed length ATH6KL_FWLOG_PAYLOAD_SIZE */
+ memset(slot->payload + len, 0, ATH6KL_FWLOG_PAYLOAD_SIZE - len);
-}
+ spin_lock(&ar->debug.fwlog_queue.lock);
-void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
-{
- struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp;
- size_t slot_len;
+ __skb_queue_tail(&ar->debug.fwlog_queue, skb);
+ complete(&ar->debug.fwlog_completion);
- if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
- return;
+ /* drop oldest entries */
+ while (skb_queue_len(&ar->debug.fwlog_queue) >
+ ATH6KL_FWLOG_MAX_ENTRIES) {
+ skb = __skb_dequeue(&ar->debug.fwlog_queue);
+ kfree_skb(skb);
+ }
- spin_lock_bh(&ar->debug.fwlog_lock);
+ spin_unlock(&ar->debug.fwlog_queue.lock);
- slot->timestamp = cpu_to_le32(jiffies);
- slot->length = cpu_to_le32(len);
- memcpy(slot->payload, buf, len);
+ return;
+}
- slot_len = sizeof(*slot) + len;
+static int ath6kl_fwlog_open(struct inode *inode, struct file *file)
+{
+ struct ath6kl *ar = inode->i_private;
- if (slot_len < ATH6KL_FWLOG_SLOT_SIZE)
- memset(slot->payload + len, 0,
- ATH6KL_FWLOG_SLOT_SIZE - slot_len);
+ if (ar->debug.fwlog_open)
+ return -EBUSY;
- ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE);
+ ar->debug.fwlog_open = true;
- spin_unlock_bh(&ar->debug.fwlog_lock);
+ file->private_data = inode->i_private;
+ return 0;
}
-static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar)
+static int ath6kl_fwlog_release(struct inode *inode, struct file *file)
{
- return CIRC_CNT(ar->debug.fwlog_buf.head,
- ar->debug.fwlog_buf.tail,
- ATH6KL_FWLOG_SLOT_SIZE) == 0;
+ struct ath6kl *ar = inode->i_private;
+
+ ar->debug.fwlog_open = false;
+
+ return 0;
}
static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
- struct circ_buf *fwlog = &ar->debug.fwlog_buf;
- size_t len = 0, buf_len = count;
+ struct sk_buff *skb;
ssize_t ret_cnt;
+ size_t len = 0;
char *buf;
- int ccnt;
- buf = vmalloc(buf_len);
+ buf = vmalloc(count);
if (!buf)
return -ENOMEM;
/* read undelivered logs from firmware */
ath6kl_read_fwlogs(ar);
- spin_lock_bh(&ar->debug.fwlog_lock);
+ spin_lock(&ar->debug.fwlog_queue.lock);
- while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
- ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail,
- ATH6KL_FWLOG_SIZE);
+ while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
+ if (skb->len > count - len) {
+ /* not enough space, put skb back and leave */
+ __skb_queue_head(&ar->debug.fwlog_queue, skb);
+ break;
+ }
- if ((size_t) ccnt > buf_len - len)
- ccnt = buf_len - len;
- memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt);
- len += ccnt;
+ memcpy(buf + len, skb->data, skb->len);
+ len += skb->len;
- fwlog->tail = (fwlog->tail + ccnt) &
- (ATH6KL_FWLOG_SIZE - 1);
+ kfree_skb(skb);
}
- spin_unlock_bh(&ar->debug.fwlog_lock);
+ spin_unlock(&ar->debug.fwlog_queue.lock);
- if (WARN_ON(len > buf_len))
- len = buf_len;
+ /* FIXME: what to do if len == 0? */
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
@@ -346,12 +424,89 @@ static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
}
static const struct file_operations fops_fwlog = {
- .open = ath6kl_debugfs_open,
+ .open = ath6kl_fwlog_open,
+ .release = ath6kl_fwlog_release,
.read = ath6kl_fwlog_read,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
+static ssize_t ath6kl_fwlog_block_read(struct file *file,
+ char __user *user_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct sk_buff *skb;
+ ssize_t ret_cnt;
+ size_t len = 0, not_copied;
+ char *buf;
+ int ret;
+
+ buf = vmalloc(count);
+ if (!buf)
+ return -ENOMEM;
+
+ spin_lock(&ar->debug.fwlog_queue.lock);
+
+ if (skb_queue_len(&ar->debug.fwlog_queue) == 0) {
+ /* we must init under queue lock */
+ init_completion(&ar->debug.fwlog_completion);
+
+ spin_unlock(&ar->debug.fwlog_queue.lock);
+
+ ret = wait_for_completion_interruptible(
+ &ar->debug.fwlog_completion);
+ if (ret == -ERESTARTSYS) {
+ vfree(buf);
+ return ret;
+ }
+
+ spin_lock(&ar->debug.fwlog_queue.lock);
+ }
+
+ while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
+ if (skb->len > count - len) {
+ /* not enough space, put skb back and leave */
+ __skb_queue_head(&ar->debug.fwlog_queue, skb);
+ break;
+ }
+
+
+ memcpy(buf + len, skb->data, skb->len);
+ len += skb->len;
+
+ kfree_skb(skb);
+ }
+
+ spin_unlock(&ar->debug.fwlog_queue.lock);
+
+ /* FIXME: what to do if len == 0? */
+
+ not_copied = copy_to_user(user_buf, buf, len);
+ if (not_copied != 0) {
+ ret_cnt = -EFAULT;
+ goto out;
+ }
+
+ *ppos = *ppos + len;
+
+ ret_cnt = len;
+
+out:
+ vfree(buf);
+
+ return ret_cnt;
+}
+
+static const struct file_operations fops_fwlog_block = {
+ .open = ath6kl_fwlog_open,
+ .release = ath6kl_fwlog_release,
+ .read = ath6kl_fwlog_block_read,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -385,7 +540,7 @@ static ssize_t ath6kl_fwlog_mask_write(struct file *file,
}
static const struct file_operations fops_fwlog_mask = {
- .open = ath6kl_debugfs_open,
+ .open = simple_open,
.read = ath6kl_fwlog_mask_read,
.write = ath6kl_fwlog_mask_write,
.owner = THIS_MODULE,
@@ -396,13 +551,20 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
- struct target_stats *tgt_stats = &ar->target_stats;
+ struct ath6kl_vif *vif;
+ struct target_stats *tgt_stats;
char *buf;
unsigned int len = 0, buf_len = 1500;
int i;
long left;
ssize_t ret_cnt;
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ tgt_stats = &vif->target_stats;
+
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -412,9 +574,9 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
return -EBUSY;
}
- set_bit(STATS_UPDATE_PEND, &ar->flag);
+ set_bit(STATS_UPDATE_PEND, &vif->flags);
- if (ath6kl_wmi_get_stats_cmd(ar->wmi)) {
+ if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) {
up(&ar->sem);
kfree(buf);
return -EIO;
@@ -422,7 +584,7 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
left = wait_event_interruptible_timeout(ar->event_wq,
!test_bit(STATS_UPDATE_PEND,
- &ar->flag), WMI_TIMEOUT);
+ &vif->flags), WMI_TIMEOUT);
up(&ar->sem);
@@ -512,6 +674,12 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
"Num disconnects", tgt_stats->cs_discon_cnt);
len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
"Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+ "ARP pkt received", tgt_stats->arp_received);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+ "ARP pkt matched", tgt_stats->arp_matched);
+ len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+ "ARP pkt replied", tgt_stats->arp_replied);
if (len > buf_len)
len = buf_len;
@@ -524,7 +692,7 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
static const struct file_operations fops_tgt_stats = {
.read = read_file_tgt_stats,
- .open = ath6kl_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
@@ -554,10 +722,10 @@ static ssize_t read_file_credit_dist_stats(struct file *file,
len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
"Total Avail Credits: ",
- target->cred_dist_cntxt->total_avail_credits);
+ target->credit_info->total_avail_credits);
len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
"Free credits :",
- target->cred_dist_cntxt->cur_free_credits);
+ target->credit_info->cur_free_credits);
len += scnprintf(buf + len, buf_len - len,
" Epid Flags Cred_norm Cred_min Credits Cred_assngd"
@@ -576,8 +744,7 @@ static ssize_t read_file_credit_dist_stats(struct file *file,
print_credit_info("%9d", cred_per_msg);
print_credit_info("%14d", cred_to_dist);
len += scnprintf(buf + len, buf_len - len, "%12d\n",
- get_queue_depth(&((struct htc_endpoint *)
- ep_list->htc_rsvd)->txq));
+ get_queue_depth(&ep_list->htc_ep->txq));
}
if (len > buf_len)
@@ -590,7 +757,112 @@ static ssize_t read_file_credit_dist_stats(struct file *file,
static const struct file_operations fops_credit_dist_stats = {
.read = read_file_credit_dist_stats,
- .open = ath6kl_debugfs_open,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static unsigned int print_endpoint_stat(struct htc_target *target, char *buf,
+ unsigned int buf_len, unsigned int len,
+ int offset, const char *name)
+{
+ int i;
+ struct htc_endpoint_stats *ep_st;
+ u32 *counter;
+
+ len += scnprintf(buf + len, buf_len - len, "%s:", name);
+ for (i = 0; i < ENDPOINT_MAX; i++) {
+ ep_st = &target->endpoint[i].ep_st;
+ counter = ((u32 *) ep_st) + (offset / 4);
+ len += scnprintf(buf + len, buf_len - len, " %u", *counter);
+ }
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ return len;
+}
+
+static ssize_t ath6kl_endpoint_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct htc_target *target = ar->htc_target;
+ char *buf;
+ unsigned int buf_len, len = 0;
+ ssize_t ret_cnt;
+
+ buf_len = sizeof(struct htc_endpoint_stats) / sizeof(u32) *
+ (25 + ENDPOINT_MAX * 11);
+ buf = kmalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+#define EPSTAT(name) \
+ do { \
+ len = print_endpoint_stat(target, buf, buf_len, len, \
+ offsetof(struct htc_endpoint_stats, \
+ name), \
+ #name); \
+ } while (0)
+
+ EPSTAT(cred_low_indicate);
+ EPSTAT(tx_issued);
+ EPSTAT(tx_pkt_bundled);
+ EPSTAT(tx_bundles);
+ EPSTAT(tx_dropped);
+ EPSTAT(tx_cred_rpt);
+ EPSTAT(cred_rpt_from_rx);
+ EPSTAT(cred_rpt_from_other);
+ EPSTAT(cred_rpt_ep0);
+ EPSTAT(cred_from_rx);
+ EPSTAT(cred_from_other);
+ EPSTAT(cred_from_ep0);
+ EPSTAT(cred_cosumd);
+ EPSTAT(cred_retnd);
+ EPSTAT(rx_pkts);
+ EPSTAT(rx_lkahds);
+ EPSTAT(rx_bundl);
+ EPSTAT(rx_bundle_lkahd);
+ EPSTAT(rx_bundle_from_hdr);
+ EPSTAT(rx_alloc_thresh_hit);
+ EPSTAT(rxalloc_thresh_byte);
+#undef EPSTAT
+
+ if (len > buf_len)
+ len = buf_len;
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret_cnt;
+}
+
+static ssize_t ath6kl_endpoint_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct htc_target *target = ar->htc_target;
+ int ret, i;
+ u32 val;
+ struct htc_endpoint_stats *ep_st;
+
+ ret = kstrtou32_from_user(user_buf, count, 0, &val);
+ if (ret)
+ return ret;
+ if (val == 0) {
+ for (i = 0; i < ENDPOINT_MAX; i++) {
+ ep_st = &target->endpoint[i].ep_st;
+ memset(ep_st, 0, sizeof(*ep_st));
+ }
+ }
+
+ return count;
+}
+
+static const struct file_operations fops_endpoint_stats = {
+ .open = simple_open,
+ .read = ath6kl_endpoint_stats_read,
+ .write = ath6kl_endpoint_stats_write,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
@@ -642,17 +914,9 @@ static ssize_t ath6kl_regread_write(struct file *file,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
- u8 buf[50];
- unsigned int len;
unsigned long reg_addr;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
-
- buf[len] = '\0';
-
- if (strict_strtoul(buf, 0, &reg_addr))
+ if (kstrtoul_from_user(user_buf, count, 0, &reg_addr))
return -EINVAL;
if ((reg_addr % 4) != 0)
@@ -669,7 +933,7 @@ static ssize_t ath6kl_regread_write(struct file *file,
static const struct file_operations fops_diag_reg_read = {
.read = ath6kl_regread_read,
.write = ath6kl_regread_write,
- .open = ath6kl_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
@@ -766,15 +1030,8 @@ static ssize_t ath6kl_lrssi_roam_write(struct file *file,
{
struct ath6kl *ar = file->private_data;
unsigned long lrssi_roam_threshold;
- char buf[32];
- ssize_t len;
-
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
- if (strict_strtoul(buf, 0, &lrssi_roam_threshold))
+ if (kstrtoul_from_user(user_buf, count, 0, &lrssi_roam_threshold))
return -EINVAL;
ar->lrssi_roam_threshold = lrssi_roam_threshold;
@@ -800,7 +1057,7 @@ static ssize_t ath6kl_lrssi_roam_read(struct file *file,
static const struct file_operations fops_lrssi_roam_threshold = {
.read = ath6kl_lrssi_roam_read,
.write = ath6kl_lrssi_roam_write,
- .open = ath6kl_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
@@ -862,48 +1119,701 @@ static ssize_t ath6kl_regwrite_write(struct file *file,
static const struct file_operations fops_diag_reg_write = {
.read = ath6kl_regwrite_read,
.write = ath6kl_regwrite_write,
- .open = ath6kl_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
-int ath6kl_debug_init(struct ath6kl *ar)
+int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf,
+ size_t len)
+{
+ const struct wmi_target_roam_tbl *tbl;
+ u16 num_entries;
+
+ if (len < sizeof(*tbl))
+ return -EINVAL;
+
+ tbl = (const struct wmi_target_roam_tbl *) buf;
+ num_entries = le16_to_cpu(tbl->num_entries);
+ if (sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) >
+ len)
+ return -EINVAL;
+
+ if (ar->debug.roam_tbl == NULL ||
+ ar->debug.roam_tbl_len < (unsigned int) len) {
+ kfree(ar->debug.roam_tbl);
+ ar->debug.roam_tbl = kmalloc(len, GFP_ATOMIC);
+ if (ar->debug.roam_tbl == NULL)
+ return -ENOMEM;
+ }
+
+ memcpy(ar->debug.roam_tbl, buf, len);
+ ar->debug.roam_tbl_len = len;
+
+ if (test_bit(ROAM_TBL_PEND, &ar->flag)) {
+ clear_bit(ROAM_TBL_PEND, &ar->flag);
+ wake_up(&ar->event_wq);
+ }
+
+ return 0;
+}
+
+static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
{
- ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE);
- if (ar->debug.fwlog_buf.buf == NULL)
+ struct ath6kl *ar = file->private_data;
+ int ret;
+ long left;
+ struct wmi_target_roam_tbl *tbl;
+ u16 num_entries, i;
+ char *buf;
+ unsigned int len, buf_len;
+ ssize_t ret_cnt;
+
+ if (down_interruptible(&ar->sem))
+ return -EBUSY;
+
+ set_bit(ROAM_TBL_PEND, &ar->flag);
+
+ ret = ath6kl_wmi_get_roam_tbl_cmd(ar->wmi);
+ if (ret) {
+ up(&ar->sem);
+ return ret;
+ }
+
+ left = wait_event_interruptible_timeout(
+ ar->event_wq, !test_bit(ROAM_TBL_PEND, &ar->flag), WMI_TIMEOUT);
+ up(&ar->sem);
+
+ if (left <= 0)
+ return -ETIMEDOUT;
+
+ if (ar->debug.roam_tbl == NULL)
return -ENOMEM;
- ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL);
- if (ar->debug.fwlog_tmp == NULL) {
- vfree(ar->debug.fwlog_buf.buf);
+ tbl = (struct wmi_target_roam_tbl *) ar->debug.roam_tbl;
+ num_entries = le16_to_cpu(tbl->num_entries);
+
+ buf_len = 100 + num_entries * 100;
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (buf == NULL)
return -ENOMEM;
+ len = 0;
+ len += scnprintf(buf + len, buf_len - len,
+ "roam_mode=%u\n\n"
+ "# roam_util bssid rssi rssidt last_rssi util bias\n",
+ le16_to_cpu(tbl->roam_mode));
+
+ for (i = 0; i < num_entries; i++) {
+ struct wmi_bss_roam_info *info = &tbl->info[i];
+ len += scnprintf(buf + len, buf_len - len,
+ "%d %pM %d %d %d %d %d\n",
+ a_sle32_to_cpu(info->roam_util), info->bssid,
+ info->rssi, info->rssidt, info->last_rssi,
+ info->util, info->bias);
}
- spin_lock_init(&ar->debug.fwlog_lock);
+ if (len > buf_len)
+ len = buf_len;
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+ return ret_cnt;
+}
+
+static const struct file_operations fops_roam_table = {
+ .read = ath6kl_roam_table_read,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_force_roam_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ int ret;
+ char buf[20];
+ size_t len;
+ u8 bssid[ETH_ALEN];
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+ if (!mac_pton(buf, bssid))
+ return -EINVAL;
+
+ ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations fops_force_roam = {
+ .write = ath6kl_force_roam_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_roam_mode_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ int ret;
+ char buf[20];
+ size_t len;
+ enum wmi_roam_mode mode;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ if (strcasecmp(buf, "default") == 0)
+ mode = WMI_DEFAULT_ROAM_MODE;
+ else if (strcasecmp(buf, "bssbias") == 0)
+ mode = WMI_HOST_BIAS_ROAM_MODE;
+ else if (strcasecmp(buf, "lock") == 0)
+ mode = WMI_LOCK_BSS_MODE;
+ else
+ return -EINVAL;
+
+ ret = ath6kl_wmi_set_roam_mode_cmd(ar->wmi, mode);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations fops_roam_mode = {
+ .write = ath6kl_roam_mode_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive)
+{
+ ar->debug.keepalive = keepalive;
+}
+
+static ssize_t ath6kl_keepalive_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ char buf[16];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.keepalive);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_keepalive_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ int ret;
+ u8 val;
+
+ ret = kstrtou8_from_user(user_buf, count, 0, &val);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, 0, val);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations fops_keepalive = {
+ .open = simple_open,
+ .read = ath6kl_keepalive_read,
+ .write = ath6kl_keepalive_write,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout)
+{
+ ar->debug.disc_timeout = timeout;
+}
+
+static ssize_t ath6kl_disconnect_timeout_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ char buf[16];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.disc_timeout);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_disconnect_timeout_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ int ret;
+ u8 val;
+
+ ret = kstrtou8_from_user(user_buf, count, 0, &val);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, 0, val);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations fops_disconnect_timeout = {
+ .open = simple_open,
+ .read = ath6kl_disconnect_timeout_read,
+ .write = ath6kl_disconnect_timeout_write,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_create_qos_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct ath6kl_vif *vif;
+ char buf[200];
+ ssize_t len;
+ char *sptr, *token;
+ struct wmi_create_pstream_cmd pstream;
+ u32 val32;
+ u16 val16;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou8(token, 0, &pstream.user_pri))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou8(token, 0, &pstream.traffic_direc))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou8(token, 0, &pstream.traffic_class))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou8(token, 0, &pstream.traffic_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou8(token, 0, &pstream.voice_psc_cap))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.min_service_int = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.max_service_int = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.inactivity_int = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.suspension_int = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.service_start_time = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou8(token, 0, &pstream.tsid))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou16(token, 0, &val16))
+ return -EINVAL;
+ pstream.nominal_msdu = cpu_to_le16(val16);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou16(token, 0, &val16))
+ return -EINVAL;
+ pstream.max_msdu = cpu_to_le16(val16);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.min_data_rate = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.mean_data_rate = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.peak_data_rate = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.max_burst_size = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.delay_bound = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.min_phy_rate = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.sba = cpu_to_le32(val32);
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou32(token, 0, &val32))
+ return -EINVAL;
+ pstream.medium_time = cpu_to_le32(val32);
+
+ pstream.nominal_phy = le32_to_cpu(pstream.min_phy_rate) / 1000000;
+
+ ath6kl_wmi_create_pstream_cmd(ar->wmi, vif->fw_vif_idx, &pstream);
+
+ return count;
+}
+
+static const struct file_operations fops_create_qos = {
+ .write = ath6kl_create_qos_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_delete_qos_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct ath6kl_vif *vif;
+ char buf[100];
+ ssize_t len;
+ char *sptr, *token;
+ u8 traffic_class;
+ u8 tsid;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou8(token, 0, &traffic_class))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou8(token, 0, &tsid))
+ return -EINVAL;
+
+ ath6kl_wmi_delete_pstream_cmd(ar->wmi, vif->fw_vif_idx,
+ traffic_class, tsid);
+
+ return count;
+}
+
+static const struct file_operations fops_delete_qos = {
+ .write = ath6kl_delete_qos_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_bgscan_int_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct ath6kl_vif *vif;
+ u16 bgscan_int;
+ char buf[32];
+ ssize_t len;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ if (kstrtou16(buf, 0, &bgscan_int))
+ return -EINVAL;
+
+ if (bgscan_int == 0)
+ bgscan_int = 0xffff;
+
+ vif->bg_scan_period = bgscan_int;
+
+ ath6kl_wmi_scanparams_cmd(ar->wmi, 0, 0, 0, bgscan_int, 0, 0, 0, 3,
+ 0, 0, 0);
+
+ return count;
+}
+
+static const struct file_operations fops_bgscan_int = {
+ .write = ath6kl_bgscan_int_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_listen_int_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct ath6kl_vif *vif;
+ u16 listen_interval;
+ char buf[32];
+ ssize_t len;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ if (kstrtou16(buf, 0, &listen_interval))
+ return -EINVAL;
+
+ if ((listen_interval < 15) || (listen_interval > 3000))
+ return -EINVAL;
+
+ vif->listen_intvl_t = listen_interval;
+ ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
+ vif->listen_intvl_t, 0);
+
+ return count;
+}
+
+static ssize_t ath6kl_listen_int_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ struct ath6kl_vif *vif;
+ char buf[32];
+ int len;
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ len = scnprintf(buf, sizeof(buf), "%u\n", vif->listen_intvl_t);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_listen_int = {
+ .read = ath6kl_listen_int_read,
+ .write = ath6kl_listen_int_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_power_params_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath6kl *ar = file->private_data;
+ u8 buf[100];
+ unsigned int len = 0;
+ char *sptr, *token;
+ u16 idle_period, ps_poll_num, dtim,
+ tx_wakeup, num_tx;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou16(token, 0, &idle_period))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou16(token, 0, &ps_poll_num))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou16(token, 0, &dtim))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou16(token, 0, &tx_wakeup))
+ return -EINVAL;
+
+ token = strsep(&sptr, " ");
+ if (!token)
+ return -EINVAL;
+ if (kstrtou16(token, 0, &num_tx))
+ return -EINVAL;
+
+ ath6kl_wmi_pmparams_cmd(ar->wmi, 0, idle_period, ps_poll_num,
+ dtim, tx_wakeup, num_tx, 0);
+
+ return count;
+}
+
+static const struct file_operations fops_power_params = {
+ .write = ath6kl_power_params_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+void ath6kl_debug_init(struct ath6kl *ar)
+{
+ skb_queue_head_init(&ar->debug.fwlog_queue);
+ init_completion(&ar->debug.fwlog_completion);
/*
* Actually we are lying here but don't know how to read the mask
* value from the firmware.
*/
ar->debug.fwlog_mask = 0;
+}
+/*
+ * Initialisation needs to happen in two stages as fwlog events can come
+ * before cfg80211 is initialised, and debugfs depends on cfg80211
+ * initialisation.
+ */
+int ath6kl_debug_init_fs(struct ath6kl *ar)
+{
ar->debugfs_phy = debugfs_create_dir("ath6kl",
- ar->wdev->wiphy->debugfsdir);
- if (!ar->debugfs_phy) {
- vfree(ar->debug.fwlog_buf.buf);
- kfree(ar->debug.fwlog_tmp);
+ ar->wiphy->debugfsdir);
+ if (!ar->debugfs_phy)
return -ENOMEM;
- }
debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
&fops_tgt_stats);
- debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar,
- &fops_credit_dist_stats);
+ if (ar->hif_type == ATH6KL_HIF_TYPE_SDIO)
+ debugfs_create_file("credit_dist_stats", S_IRUSR,
+ ar->debugfs_phy, ar,
+ &fops_credit_dist_stats);
+
+ debugfs_create_file("endpoint_stats", S_IRUSR | S_IWUSR,
+ ar->debugfs_phy, ar, &fops_endpoint_stats);
debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
&fops_fwlog);
+ debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar,
+ &fops_fwlog_block);
+
debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
ar, &fops_fwlog_mask);
@@ -922,13 +1832,44 @@ int ath6kl_debug_init(struct ath6kl *ar)
debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar,
&fops_war_stats);
+ debugfs_create_file("roam_table", S_IRUSR, ar->debugfs_phy, ar,
+ &fops_roam_table);
+
+ debugfs_create_file("force_roam", S_IWUSR, ar->debugfs_phy, ar,
+ &fops_force_roam);
+
+ debugfs_create_file("roam_mode", S_IWUSR, ar->debugfs_phy, ar,
+ &fops_roam_mode);
+
+ debugfs_create_file("keepalive", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
+ &fops_keepalive);
+
+ debugfs_create_file("disconnect_timeout", S_IRUSR | S_IWUSR,
+ ar->debugfs_phy, ar, &fops_disconnect_timeout);
+
+ debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar,
+ &fops_create_qos);
+
+ debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar,
+ &fops_delete_qos);
+
+ debugfs_create_file("bgscan_interval", S_IWUSR,
+ ar->debugfs_phy, ar, &fops_bgscan_int);
+
+ debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR,
+ ar->debugfs_phy, ar, &fops_listen_int);
+
+ debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar,
+ &fops_power_params);
+
return 0;
}
void ath6kl_debug_cleanup(struct ath6kl *ar)
{
- vfree(ar->debug.fwlog_buf.buf);
- kfree(ar->debug.fwlog_tmp);
+ skb_queue_purge(&ar->debug.fwlog_queue);
+ complete(&ar->debug.fwlog_completion);
+ kfree(ar->debug.roam_tbl);
}
#endif
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h
index 9288a3ce1e3..e194c10d9f0 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.h
+++ b/drivers/net/wireless/ath/ath6kl/debug.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,19 +18,20 @@
#ifndef DEBUG_H
#define DEBUG_H
-#include "htc_hif.h"
+#include "hif.h"
+#include "trace.h"
enum ATH6K_DEBUG_MASK {
- ATH6KL_DBG_WLAN_CONNECT = BIT(0), /* wlan connect */
- ATH6KL_DBG_WLAN_SCAN = BIT(1), /* wlan scan */
+ ATH6KL_DBG_CREDIT = BIT(0),
+ /* hole */
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_HTC = BIT(5),
+ ATH6KL_DBG_HIF = BIT(6),
ATH6KL_DBG_IRQ = BIT(7), /* interrupt processing */
- ATH6KL_DBG_PM = BIT(8), /* power management */
- ATH6KL_DBG_WLAN_NODE = BIT(9), /* general wlan node tracing */
+ /* hole */
+ /* hole */
ATH6KL_DBG_WMI = BIT(10), /* wmi tracing */
ATH6KL_DBG_TRC = BIT(11), /* generic func tracing */
ATH6KL_DBG_SCATTER = BIT(12), /* hif scatter tracing */
@@ -40,49 +42,29 @@ enum ATH6K_DEBUG_MASK {
ATH6KL_DBG_SDIO_DUMP = BIT(17),
ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */
ATH6KL_DBG_WMI_DUMP = BIT(19),
+ ATH6KL_DBG_SUSPEND = BIT(20),
+ ATH6KL_DBG_USB = BIT(21),
+ ATH6KL_DBG_USB_BULK = BIT(22),
+ ATH6KL_DBG_RECOVERY = BIT(23),
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)
+__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...);
+__printf(1, 2) int ath6kl_info(const char *fmt, ...);
+__printf(1, 2) int ath6kl_err(const char *fmt, ...);
+__printf(1, 2) int ath6kl_warn(const char *fmt, ...);
enum ath6kl_war {
ATH6KL_WAR_INVALID_RATE,
};
#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 char *prefix,
- const void *buf, size_t len)
-{
- if (debug_mask & mask) {
- if (msg)
- ath6kl_dbg(mask, "%s\n", msg);
- print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
- }
-}
+void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...);
+void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len);
void ath6kl_dump_registers(struct ath6kl_device *dev,
struct ath6kl_irq_proc_registers *irq_proc_reg,
@@ -90,7 +72,12 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
void dump_cred_dist_stats(struct htc_target *target);
void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len);
void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war);
-int ath6kl_debug_init(struct ath6kl *ar);
+int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf,
+ size_t len);
+void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive);
+void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout);
+void ath6kl_debug_init(struct ath6kl *ar);
+int ath6kl_debug_init_fs(struct ath6kl *ar);
void ath6kl_debug_cleanup(struct ath6kl *ar);
#else
@@ -110,8 +97,8 @@ 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)
{
}
@@ -125,7 +112,26 @@ static inline void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
{
}
-static inline int ath6kl_debug_init(struct ath6kl *ar)
+static inline int ath6kl_debug_roam_tbl_event(struct ath6kl *ar,
+ const void *buf, size_t len)
+{
+ return 0;
+}
+
+static inline void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive)
+{
+}
+
+static inline void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar,
+ u8 timeout)
+{
+}
+
+static inline void ath6kl_debug_init(struct ath6kl *ar)
+{
+}
+
+static inline int ath6kl_debug_init_fs(struct ath6kl *ar)
{
return 0;
}
diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h
index d6c898f3d0b..8c9e72d5250 100644
--- a/drivers/net/wireless/ath/ath6kl/hif-ops.h
+++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,10 +19,16 @@
#define HIF_OPS_H
#include "hif.h"
+#include "debug.h"
static inline int hif_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
u32 len, u32 request)
{
+ ath6kl_dbg(ATH6KL_DBG_HIF,
+ "hif %s sync addr 0x%x buf 0x%p len %d request 0x%x\n",
+ (request & HIF_WRITE) ? "write" : "read",
+ addr, buf, len, request);
+
return ar->hif_ops->read_write_sync(ar, addr, buf, len, request);
}
@@ -29,16 +36,24 @@ static inline int hif_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
u32 length, u32 request,
struct htc_packet *packet)
{
+ ath6kl_dbg(ATH6KL_DBG_HIF,
+ "hif write async addr 0x%x buf 0x%p len %d request 0x%x\n",
+ address, buffer, length, request);
+
return ar->hif_ops->write_async(ar, address, buffer, length,
request, packet);
}
static inline void ath6kl_hif_irq_enable(struct ath6kl *ar)
{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif irq enable\n");
+
return ar->hif_ops->irq_enable(ar);
}
static inline void ath6kl_hif_irq_disable(struct ath6kl *ar)
{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif irq disable\n");
+
return ar->hif_ops->irq_disable(ar);
}
@@ -69,9 +84,104 @@ static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar)
return ar->hif_ops->cleanup_scatter(ar);
}
-static inline int ath6kl_hif_suspend(struct ath6kl *ar)
+static inline int ath6kl_hif_suspend(struct ath6kl *ar,
+ struct cfg80211_wowlan *wow)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif suspend\n");
+
+ return ar->hif_ops->suspend(ar, wow);
+}
+
+/*
+ * Read from the ATH6KL through its diagnostic window. No cooperation from
+ * the Target is required for this.
+ */
+static inline int ath6kl_hif_diag_read32(struct ath6kl *ar, u32 address,
+ u32 *value)
{
- return ar->hif_ops->suspend(ar);
+ return ar->hif_ops->diag_read32(ar, address, value);
+}
+
+/*
+ * Write to the ATH6KL through its diagnostic window. No cooperation from
+ * the Target is required for this.
+ */
+static inline int ath6kl_hif_diag_write32(struct ath6kl *ar, u32 address,
+ __le32 value)
+{
+ return ar->hif_ops->diag_write32(ar, address, value);
+}
+
+static inline int ath6kl_hif_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ return ar->hif_ops->bmi_read(ar, buf, len);
+}
+
+static inline int ath6kl_hif_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ return ar->hif_ops->bmi_write(ar, buf, len);
+}
+
+static inline int ath6kl_hif_resume(struct ath6kl *ar)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif resume\n");
+
+ return ar->hif_ops->resume(ar);
+}
+
+static inline int ath6kl_hif_power_on(struct ath6kl *ar)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif power on\n");
+
+ return ar->hif_ops->power_on(ar);
+}
+
+static inline int ath6kl_hif_power_off(struct ath6kl *ar)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif power off\n");
+
+ return ar->hif_ops->power_off(ar);
+}
+
+static inline void ath6kl_hif_stop(struct ath6kl *ar)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif stop\n");
+
+ ar->hif_ops->stop(ar);
+}
+
+static inline int ath6kl_hif_pipe_send(struct ath6kl *ar,
+ u8 pipe, struct sk_buff *hdr_buf,
+ struct sk_buff *buf)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe send\n");
+
+ return ar->hif_ops->pipe_send(ar, pipe, hdr_buf, buf);
+}
+
+static inline void ath6kl_hif_pipe_get_default(struct ath6kl *ar,
+ u8 *ul_pipe, u8 *dl_pipe)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n");
+
+ ar->hif_ops->pipe_get_default(ar, ul_pipe, dl_pipe);
+}
+
+static inline int ath6kl_hif_pipe_map_service(struct ath6kl *ar,
+ u16 service_id, u8 *ul_pipe,
+ u8 *dl_pipe)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n");
+
+ return ar->hif_ops->pipe_map_service(ar, service_id, ul_pipe, dl_pipe);
+}
+
+static inline u16 ath6kl_hif_pipe_get_free_queue_number(struct ath6kl *ar,
+ u8 pipe)
+{
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get free queue number\n");
+
+ return ar->hif_ops->pipe_get_free_queue_number(ar, pipe);
}
#endif
diff --git a/drivers/net/wireless/ath/ath6kl/htc_hif.c b/drivers/net/wireless/ath/ath6kl/hif.c
index 86b1cc7409c..18c070850a0 100644
--- a/drivers/net/wireless/ath/ath6kl/htc_hif.c
+++ b/drivers/net/wireless/ath/ath6kl/hif.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2007-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -13,18 +14,22 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "hif.h"
+
+#include <linux/export.h>
#include "core.h"
#include "target.h"
#include "hif-ops.h"
-#include "htc_hif.h"
#include "debug.h"
+#include "trace.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)
+static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req,
+ bool from_dma)
{
u8 *buf;
int i;
@@ -32,7 +37,6 @@ static int ath6kldev_cp_scat_dma_buf(struct hif_scatter_req *req, bool from_dma)
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);
@@ -46,12 +50,11 @@ static int ath6kldev_cp_scat_dma_buf(struct hif_scatter_req *req, bool from_dma)
return 0;
}
-int ath6kldev_rw_comp_handler(void *context, int status)
+int ath6kl_hif_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",
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif rw completion pkt 0x%p status %d\n",
packet, status);
packet->status = status;
@@ -59,30 +62,86 @@ int ath6kldev_rw_comp_handler(void *context, int status)
return 0;
}
+EXPORT_SYMBOL(ath6kl_hif_rw_comp_handler);
-static int ath6kldev_proc_dbg_intr(struct ath6kl_device *dev)
+#define REG_DUMP_COUNT_AR6003 60
+#define REGISTER_DUMP_LEN_MAX 60
+
+static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar)
{
- u32 dummy;
- int status;
+ __le32 regdump_val[REGISTER_DUMP_LEN_MAX];
+ u32 i, address, regdump_addr = 0;
+ int ret;
+
+ 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(ar->target_type, address);
+
+ /* read RAM location through diagnostic window */
+ ret = ath6kl_diag_read32(ar, address, &regdump_addr);
- ath6kl_err("target debug interrupt\n");
+ if (ret || !regdump_addr) {
+ ath6kl_warn("failed to get ptr to register dump area: %d\n",
+ ret);
+ return;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n",
+ regdump_addr);
+ regdump_addr = TARG_VTOP(ar->target_type, regdump_addr);
+
+ /* fetch register dump data */
+ ret = ath6kl_diag_read(ar, regdump_addr, (u8 *)&regdump_val[0],
+ REG_DUMP_COUNT_AR6003 * (sizeof(u32)));
+ if (ret) {
+ ath6kl_warn("failed to get register dump: %d\n", ret);
+ return;
+ }
- ath6kl_target_failure(dev->ar);
+ ath6kl_info("crash dump:\n");
+ ath6kl_info("hw 0x%x fw %s\n", ar->wiphy->hw_version,
+ ar->wiphy->fw_version);
+
+ BUILD_BUG_ON(REG_DUMP_COUNT_AR6003 % 4);
+
+ for (i = 0; i < REG_DUMP_COUNT_AR6003; i += 4) {
+ ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
+ i,
+ le32_to_cpu(regdump_val[i]),
+ le32_to_cpu(regdump_val[i + 1]),
+ le32_to_cpu(regdump_val[i + 2]),
+ le32_to_cpu(regdump_val[i + 3]));
+ }
+}
+
+static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
+{
+ u32 dummy;
+ int ret;
+
+ ath6kl_warn("firmware crashed\n");
/*
* read counter to clear the interrupt, the debug error interrupt is
* counter 0.
*/
- status = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
+ ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
(u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
- if (status)
- WARN_ON(1);
+ if (ret)
+ ath6kl_warn("Failed to clear debug interrupt: %d\n", ret);
- return status;
+ ath6kl_hif_dump_fw_crash(dev->ar);
+ ath6kl_read_fwlogs(dev->ar);
+ ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT);
+
+ return ret;
}
/* mailbox recv message polling */
-int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
+int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
int timeout)
{
struct ath6kl_irq_proc_registers *rg;
@@ -118,7 +177,7 @@ int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
/* delay a little */
mdelay(ATH6KL_TIME_QUANTUM);
- ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "retry mbox poll : %d\n", i);
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif retry mbox poll try %d\n", i);
}
if (i == 0) {
@@ -131,7 +190,7 @@ int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
* Target failure handler will be called in case of
* an assert.
*/
- ath6kldev_proc_dbg_intr(dev);
+ ath6kl_hif_proc_dbg_intr(dev);
}
return status;
@@ -141,11 +200,14 @@ int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
* 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)
+int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx)
{
struct ath6kl_irq_enable_reg regs;
int status = 0;
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif rx %s\n",
+ enable_rx ? "enable" : "disable");
+
/* take the lock to protect interrupt enable shadows */
spin_lock_bh(&dev->lock);
@@ -168,7 +230,7 @@ int ath6kldev_rx_control(struct ath6kl_device *dev, bool enable_rx)
return status;
}
-int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
+int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev,
struct hif_scatter_req *scat_req, bool read)
{
int status = 0;
@@ -185,14 +247,14 @@ int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
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",
+ ath6kl_dbg(ATH6KL_DBG_HIF,
+ "hif submit scatter request entries %d len %d mbox 0x%x %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);
+ status = ath6kl_hif_cp_scat_dma_buf(scat_req, false);
if (status) {
scat_req->status = status;
scat_req->complete(dev->ar->htc_target, scat_req);
@@ -207,13 +269,13 @@ int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
scat_req->status = status;
if (!status && scat_req->virt_scat)
scat_req->status =
- ath6kldev_cp_scat_dma_buf(scat_req, true);
+ ath6kl_hif_cp_scat_dma_buf(scat_req, true);
}
return status;
}
-static int ath6kldev_proc_counter_intr(struct ath6kl_device *dev)
+static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev)
{
u8 counter_int_status;
@@ -223,7 +285,7 @@ static int ath6kldev_proc_counter_intr(struct ath6kl_device *dev)
dev->irq_en_reg.cntr_int_status_en;
ath6kl_dbg(ATH6KL_DBG_IRQ,
- "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
+ "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
counter_int_status);
/*
@@ -232,12 +294,12 @@ static int ath6kldev_proc_counter_intr(struct ath6kl_device *dev)
* the debug assertion counter interrupt.
*/
if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK)
- return ath6kldev_proc_dbg_intr(dev);
+ return ath6kl_hif_proc_dbg_intr(dev);
return 0;
}
-static int ath6kldev_proc_err_intr(struct ath6kl_device *dev)
+static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev)
{
int status;
u8 error_int_status;
@@ -276,13 +338,12 @@ static int ath6kldev_proc_err_intr(struct ath6kl_device *dev)
status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS,
reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
- if (status)
- WARN_ON(1);
+ WARN_ON(status);
return status;
}
-static int ath6kldev_proc_cpu_intr(struct ath6kl_device *dev)
+static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev)
{
int status;
u8 cpu_int_status;
@@ -298,7 +359,7 @@ static int ath6kldev_proc_cpu_intr(struct ath6kl_device *dev)
}
ath6kl_dbg(ATH6KL_DBG_IRQ,
- "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
+ "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
cpu_int_status);
/* Clear the interrupt */
@@ -321,8 +382,7 @@ static int ath6kldev_proc_cpu_intr(struct ath6kl_device *dev)
status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS,
reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
- if (status)
- WARN_ON(1);
+ WARN_ON(status);
return status;
}
@@ -373,9 +433,10 @@ static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
if (status)
goto out;
- if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ))
- ath6kl_dump_registers(dev, &dev->irq_proc_reg,
- &dev->irq_en_reg);
+ ath6kl_dump_registers(dev, &dev->irq_proc_reg,
+ &dev->irq_en_reg);
+ trace_ath6kl_sdio_irq(&dev->irq_en_reg,
+ sizeof(dev->irq_en_reg));
/* Update only those registers that are enabled */
host_int_status = dev->irq_proc_reg.host_int_status &
@@ -417,7 +478,7 @@ static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
* we rapidly pull packets.
*/
status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt,
- &lk_ahd, &fetched);
+ lk_ahd, &fetched);
if (status)
goto out;
@@ -436,21 +497,21 @@ static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
if (MS(HOST_INT_STATUS_CPU, host_int_status)) {
/* CPU Interrupt */
- status = ath6kldev_proc_cpu_intr(dev);
+ status = ath6kl_hif_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);
+ status = ath6kl_hif_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);
+ status = ath6kl_hif_proc_counter_intr(dev);
out:
/*
@@ -479,9 +540,10 @@ out:
}
/* interrupt handler, kicks off all interrupt processing */
-int ath6kldev_intr_bh_handler(struct ath6kl *ar)
+int ath6kl_hif_intr_bh_handler(struct ath6kl *ar)
{
struct ath6kl_device *dev = ar->htc_target->dev;
+ unsigned long timeout;
int status = 0;
bool done = false;
@@ -495,7 +557,8 @@ int ath6kldev_intr_bh_handler(struct ath6kl *ar)
* IRQ processing is synchronous, interrupt status registers can be
* re-read.
*/
- while (!done) {
+ timeout = jiffies + msecs_to_jiffies(ATH6KL_HIF_COMMUNICATION_TIMEOUT);
+ while (time_before(jiffies, timeout) && !done) {
status = proc_pending_irqs(dev, &done);
if (status)
break;
@@ -503,8 +566,9 @@ int ath6kldev_intr_bh_handler(struct ath6kl *ar)
return status;
}
+EXPORT_SYMBOL(ath6kl_hif_intr_bh_handler);
-static int ath6kldev_enable_intrs(struct ath6kl_device *dev)
+static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev)
{
struct ath6kl_irq_enable_reg regs;
int status;
@@ -552,7 +616,7 @@ static int ath6kldev_enable_intrs(struct ath6kl_device *dev)
return status;
}
-int ath6kldev_disable_intrs(struct ath6kl_device *dev)
+int ath6kl_hif_disable_intrs(struct ath6kl_device *dev)
{
struct ath6kl_irq_enable_reg regs;
@@ -571,7 +635,7 @@ int ath6kldev_disable_intrs(struct ath6kl_device *dev)
}
/* enable device interrupts */
-int ath6kldev_unmask_intrs(struct ath6kl_device *dev)
+int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev)
{
int status = 0;
@@ -583,29 +647,29 @@ int ath6kldev_unmask_intrs(struct ath6kl_device *dev)
* target "soft" resets. The ATH6KL interrupt enables reset back to an
* "enabled" state when this happens.
*/
- ath6kldev_disable_intrs(dev);
+ ath6kl_hif_disable_intrs(dev);
/* unmask the host controller interrupts */
ath6kl_hif_irq_enable(dev->ar);
- status = ath6kldev_enable_intrs(dev);
+ status = ath6kl_hif_enable_intrs(dev);
return status;
}
/* disable all device interrupts */
-int ath6kldev_mask_intrs(struct ath6kl_device *dev)
+int ath6kl_hif_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_disable_intrs().
*/
ath6kl_hif_irq_disable(dev->ar);
- return ath6kldev_disable_intrs(dev);
+ return ath6kl_hif_disable_intrs(dev);
}
-int ath6kldev_setup(struct ath6kl_device *dev)
+int ath6kl_hif_setup(struct ath6kl_device *dev)
{
int status = 0;
@@ -621,21 +685,18 @@ int ath6kldev_setup(struct ath6kl_device *dev)
/* must be a power of 2 */
if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) {
WARN_ON(1);
+ status = -EINVAL;
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",
+ ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n",
dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr);
- ath6kl_dbg(ATH6KL_DBG_TRC,
- "hif interrupt processing is sync only\n");
-
- status = ath6kldev_disable_intrs(dev);
+ status = ath6kl_hif_disable_intrs(dev);
fail_setup:
return status;
-
}
diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h
index 797e2d1d9bf..dc6bd8cd9b8 100644
--- a/drivers/net/wireless/ath/ath6kl/hif.h
+++ b/drivers/net/wireless/ath/ath6kl/hif.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -35,6 +36,7 @@
#define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024)
#define MANUFACTURER_ID_AR6003_BASE 0x300
+#define MANUFACTURER_ID_AR6004_BASE 0x400
/* SDIO manufacturer ID and Codes */
#define MANUFACTURER_ID_ATH6KL_BASE_MASK 0xFF00
#define MANUFACTURER_CODE 0x271 /* Atheros */
@@ -59,6 +61,18 @@
/* mode to enable special 4-bit interrupt assertion without clock */
#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ (1 << 0)
+/* HTC runs over mailbox 0 */
+#define HTC_MAILBOX 0
+
+#define ATH6KL_TARGET_DEBUG_INTR_MASK 0x01
+
+/* FIXME: are these duplicates with MAX_SCATTER_ values in hif.h? */
+#define ATH6KL_SCATTER_ENTRIES_PER_REQ 16
+#define ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER (16 * 1024)
+#define ATH6KL_SCATTER_REQS 4
+
+#define ATH6KL_HIF_COMMUNICATION_TIMEOUT 1000
+
struct bus_request {
struct list_head list;
@@ -183,7 +197,38 @@ struct hif_scatter_req {
/* bounce buffer for upper layers to copy to/from */
u8 *virt_dma_buf;
- struct hif_scatter_item scat_list[1];
+ u32 scat_q_depth;
+
+ struct hif_scatter_item scat_list[0];
+};
+
+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 {
+ /* protects irq_proc_reg and irq_en_reg below */
+ spinlock_t lock;
+ struct ath6kl_irq_proc_registers irq_proc_reg;
+ struct ath6kl_irq_enable_reg irq_en_reg;
+ struct htc_target *htc_cnxt;
+ struct ath6kl *ar;
};
struct ath6kl_hif_ops {
@@ -202,7 +247,36 @@ struct ath6kl_hif_ops {
int (*scat_req_rw) (struct ath6kl *ar,
struct hif_scatter_req *scat_req);
void (*cleanup_scatter)(struct ath6kl *ar);
- int (*suspend)(struct ath6kl *ar);
+ int (*suspend)(struct ath6kl *ar, struct cfg80211_wowlan *wow);
+ int (*resume)(struct ath6kl *ar);
+ int (*diag_read32)(struct ath6kl *ar, u32 address, u32 *value);
+ int (*diag_write32)(struct ath6kl *ar, u32 address, __le32 value);
+ int (*bmi_read)(struct ath6kl *ar, u8 *buf, u32 len);
+ int (*bmi_write)(struct ath6kl *ar, u8 *buf, u32 len);
+ int (*power_on)(struct ath6kl *ar);
+ int (*power_off)(struct ath6kl *ar);
+ void (*stop)(struct ath6kl *ar);
+ int (*pipe_send)(struct ath6kl *ar, u8 pipe, struct sk_buff *hdr_buf,
+ struct sk_buff *buf);
+ void (*pipe_get_default)(struct ath6kl *ar, u8 *pipe_ul, u8 *pipe_dl);
+ int (*pipe_map_service)(struct ath6kl *ar, u16 service_id, u8 *pipe_ul,
+ u8 *pipe_dl);
+ u16 (*pipe_get_free_queue_number)(struct ath6kl *ar, u8 pipe);
};
+int ath6kl_hif_setup(struct ath6kl_device *dev);
+int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev);
+int ath6kl_hif_mask_intrs(struct ath6kl_device *dev);
+int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev,
+ u32 *lk_ahd, int timeout);
+int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx);
+int ath6kl_hif_disable_intrs(struct ath6kl_device *dev);
+
+int ath6kl_hif_rw_comp_handler(void *context, int status);
+int ath6kl_hif_intr_bh_handler(struct ath6kl *ar);
+
+/* Scatter Function and Definitions */
+int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev,
+ struct hif_scatter_req *scat_req, bool read);
+
#endif
diff --git a/drivers/net/wireless/ath/ath6kl/htc-ops.h b/drivers/net/wireless/ath/ath6kl/htc-ops.h
new file mode 100644
index 00000000000..2d4eed55cfd
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/htc-ops.h
@@ -0,0 +1,113 @@
+/*
+ * 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_OPS_H
+#define HTC_OPS_H
+
+#include "htc.h"
+#include "debug.h"
+
+static inline void *ath6kl_htc_create(struct ath6kl *ar)
+{
+ return ar->htc_ops->create(ar);
+}
+
+static inline int ath6kl_htc_wait_target(struct htc_target *target)
+{
+ return target->dev->ar->htc_ops->wait_target(target);
+}
+
+static inline int ath6kl_htc_start(struct htc_target *target)
+{
+ return target->dev->ar->htc_ops->start(target);
+}
+
+static inline int ath6kl_htc_conn_service(struct htc_target *target,
+ struct htc_service_connect_req *req,
+ struct htc_service_connect_resp *resp)
+{
+ return target->dev->ar->htc_ops->conn_service(target, req, resp);
+}
+
+static inline int ath6kl_htc_tx(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ return target->dev->ar->htc_ops->tx(target, packet);
+}
+
+static inline void ath6kl_htc_stop(struct htc_target *target)
+{
+ return target->dev->ar->htc_ops->stop(target);
+}
+
+static inline void ath6kl_htc_cleanup(struct htc_target *target)
+{
+ return target->dev->ar->htc_ops->cleanup(target);
+}
+
+static inline void ath6kl_htc_flush_txep(struct htc_target *target,
+ enum htc_endpoint_id endpoint,
+ u16 tag)
+{
+ return target->dev->ar->htc_ops->flush_txep(target, endpoint, tag);
+}
+
+static inline void ath6kl_htc_flush_rx_buf(struct htc_target *target)
+{
+ return target->dev->ar->htc_ops->flush_rx_buf(target);
+}
+
+static inline void ath6kl_htc_activity_changed(struct htc_target *target,
+ enum htc_endpoint_id endpoint,
+ bool active)
+{
+ return target->dev->ar->htc_ops->activity_changed(target, endpoint,
+ active);
+}
+
+static inline int ath6kl_htc_get_rxbuf_num(struct htc_target *target,
+ enum htc_endpoint_id endpoint)
+{
+ return target->dev->ar->htc_ops->get_rxbuf_num(target, endpoint);
+}
+
+static inline int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
+ struct list_head *pktq)
+{
+ return target->dev->ar->htc_ops->add_rxbuf_multiple(target, pktq);
+}
+
+static inline int ath6kl_htc_credit_setup(struct htc_target *target,
+ struct ath6kl_htc_credit_info *info)
+{
+ return target->dev->ar->htc_ops->credit_setup(target, info);
+}
+
+static inline void ath6kl_htc_tx_complete(struct ath6kl *ar,
+ struct sk_buff *skb)
+{
+ ar->htc_ops->tx_complete(ar, skb);
+}
+
+
+static inline void ath6kl_htc_rx_complete(struct ath6kl *ar,
+ struct sk_buff *skb, u8 pipe)
+{
+ ar->htc_ops->rx_complete(ar, skb, pipe);
+}
+
+
+#endif
diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h
index 8ce0c2c07de..14cab1403dd 100644
--- a/drivers/net/wireless/ath/ath6kl/htc.h
+++ b/drivers/net/wireless/ath/ath6kl/htc.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -24,6 +25,7 @@
/* send direction */
#define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0)
#define HTC_FLAGS_SEND_BUNDLE (1 << 1)
+#define HTC_FLAGS_TX_FIXUP_NETBUF (1 << 2)
/* receive direction */
#define HTC_FLG_RX_UNUSED (1 << 0)
@@ -55,6 +57,10 @@
#define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2
#define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4
#define HTC_CONN_FLGS_THRESH_MASK 0x3
+/* disable credit flow control on a specific service */
+#define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3)
+#define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8
+#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00U
/* connect response status codes */
#define HTC_SERVICE_SUCCESS 0
@@ -74,6 +80,7 @@
#define HTC_RECORD_LOOKAHEAD_BUNDLE 3
#define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0)
+#define HTC_SETUP_COMP_FLG_DISABLE_TX_CREDIT_FLOW (1 << 1)
#define MAKE_SERVICE_ID(group, index) \
(int)(((int)group << 8) | (int)(index))
@@ -87,6 +94,8 @@
#define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4)
#define WMI_MAX_SERVICES 5
+#define WMM_NUM_AC 4
+
/* reserved and used to flush ALL packets */
#define HTC_TX_PACKET_TAG_ALL 0
#define HTC_SERVICE_TX_PACKET_TAG 1
@@ -106,6 +115,8 @@
/* HTC operational parameters */
#define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */
+#define HTC_TARGET_RESPONSE_POLL_WAIT 10
+#define HTC_TARGET_RESPONSE_POLL_COUNT 200
#define HTC_TARGET_DEBUG_INTR_MASK 0x01
#define HTC_TARGET_CREDIT_INTR_MASK 0xF0
@@ -125,6 +136,7 @@
#define HTC_RECV_WAIT_BUFFERS (1 << 0)
#define HTC_OP_STATE_STOPPING (1 << 0)
+#define HTC_OP_STATE_SETUP_COMPLETE (1 << 1)
/*
* The frame header length and message formats defined herein were selected
@@ -308,6 +320,14 @@ struct htc_packet {
void (*completion) (struct htc_target *, struct htc_packet *);
struct htc_target *context;
+
+ /*
+ * optimization for network-oriented data, the HTC packet
+ * can pass the network buffer corresponding to the HTC packet
+ * lower layers may optimized the transfer knowing this is
+ * a network buffer
+ */
+ struct sk_buff *skb;
};
enum htc_send_full_action {
@@ -316,12 +336,14 @@ enum htc_send_full_action {
};
struct htc_ep_callbacks {
+ void (*tx_complete) (struct htc_target *, struct htc_packet *);
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);
+ void (*tx_comp_multi) (struct htc_target *, struct list_head *);
int rx_alloc_thresh;
int rx_refill_thresh;
};
@@ -393,7 +415,7 @@ struct htc_endpoint_credit_dist {
int cred_per_msg;
/* reserved for HTC use */
- void *htc_rsvd;
+ struct htc_endpoint *htc_ep;
/*
* current depth of TX queue , i.e. messages waiting for credits
@@ -414,9 +436,11 @@ enum htc_credit_dist_reason {
HTC_CREDIT_DIST_SEEK_CREDITS,
};
-struct htc_credit_state_info {
+struct ath6kl_htc_credit_info {
int total_avail_credits;
int cur_free_credits;
+
+ /* list of lowest priority endpoints */
struct list_head lowestpri_ep_dist;
};
@@ -496,6 +520,14 @@ struct htc_endpoint {
u8 seqno;
u32 conn_flags;
struct htc_endpoint_stats ep_st;
+ u16 tx_drop_packet_threshold;
+
+ struct {
+ u8 pipeid_ul;
+ u8 pipeid_dl;
+ struct list_head tx_lookup_queue;
+ bool tx_credit_flow_enabled;
+ } pipe;
};
struct htc_control_buffer {
@@ -503,20 +535,66 @@ struct htc_control_buffer {
u8 *buf;
};
+struct htc_pipe_txcredit_alloc {
+ u16 service_id;
+ u8 credit_alloc;
+};
+
+enum htc_send_queue_result {
+ HTC_SEND_QUEUE_OK = 0, /* packet was queued */
+ HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */
+};
+
+struct ath6kl_htc_ops {
+ void* (*create)(struct ath6kl *ar);
+ int (*wait_target)(struct htc_target *target);
+ int (*start)(struct htc_target *target);
+ int (*conn_service)(struct htc_target *target,
+ struct htc_service_connect_req *req,
+ struct htc_service_connect_resp *resp);
+ int (*tx)(struct htc_target *target, struct htc_packet *packet);
+ void (*stop)(struct htc_target *target);
+ void (*cleanup)(struct htc_target *target);
+ void (*flush_txep)(struct htc_target *target,
+ enum htc_endpoint_id endpoint, u16 tag);
+ void (*flush_rx_buf)(struct htc_target *target);
+ void (*activity_changed)(struct htc_target *target,
+ enum htc_endpoint_id endpoint,
+ bool active);
+ int (*get_rxbuf_num)(struct htc_target *target,
+ enum htc_endpoint_id endpoint);
+ int (*add_rxbuf_multiple)(struct htc_target *target,
+ struct list_head *pktq);
+ int (*credit_setup)(struct htc_target *target,
+ struct ath6kl_htc_credit_info *cred_info);
+ int (*tx_complete)(struct ath6kl *ar, struct sk_buff *skb);
+ int (*rx_complete)(struct ath6kl *ar, struct sk_buff *skb, u8 pipe);
+};
+
struct ath6kl_device;
/* our HTC target state */
struct htc_target {
struct htc_endpoint endpoint[ENDPOINT_MAX];
+
+ /* contains struct htc_endpoint_credit_dist */
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;
+ struct ath6kl_htc_credit_info *credit_info;
int tgt_creds;
unsigned int tgt_cred_sz;
+
+ /* protects free_ctrl_txbuf and free_ctrl_rxbuf */
spinlock_t htc_lock;
+
+ /* FIXME: does this protext rx_bufq and endpoint structures or what? */
spinlock_t rx_lock;
+
+ /* protects endpoint->txq */
spinlock_t tx_lock;
+
struct ath6kl_device *dev;
u32 htc_flags;
u32 rx_st_flags;
@@ -526,7 +604,7 @@ struct htc_target {
/* max messages per bundle for HTC */
int msg_per_bndl_max;
- bool tx_bndl_enable;
+ u32 tx_bndl_mask;
int rx_bndl_enable;
int max_rx_bndl_sz;
int max_tx_bndl_sz;
@@ -538,32 +616,21 @@ struct htc_target {
int max_xfer_szper_scatreq;
int chk_irq_status_cnt;
+
+ /* counts the number of Tx without bundling continously per AC */
+ u32 ac_tx_count[WMM_NUM_AC];
+
+ struct {
+ struct htc_packet *htc_packet_pool;
+ u8 ctrl_response_buf[HTC_MAX_CTRL_MSG_LEN];
+ int ctrl_response_len;
+ bool ctrl_response_valid;
+ struct htc_pipe_txcredit_alloc txcredit_alloc[ENDPOINT_MAX];
+ } pipe;
};
-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);
+ 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,
@@ -604,4 +671,7 @@ static inline int get_queue_depth(struct list_head *queue)
return depth;
}
+void ath6kl_htc_pipe_attach(struct ath6kl *ar);
+void ath6kl_htc_mbox_attach(struct ath6kl *ar);
+
#endif
diff --git a/drivers/net/wireless/ath/ath6kl/htc_hif.h b/drivers/net/wireless/ath/ath6kl/htc_hif.h
deleted file mode 100644
index 171ad63d89b..00000000000
--- a/drivers/net/wireless/ath/ath6kl/htc_hif.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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_ */
diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c
index f88a7c9e414..e481f14b987 100644
--- a/drivers/net/wireless/ath/ath6kl/htc.c
+++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2007-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -15,13 +16,335 @@
*/
#include "core.h"
-#include "htc_hif.h"
+#include "hif.h"
#include "debug.h"
#include "hif-ops.h"
+#include "trace.h"
+
#include <asm/unaligned.h>
#define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask))
+static void ath6kl_htc_mbox_cleanup(struct htc_target *target);
+static void ath6kl_htc_mbox_stop(struct htc_target *target);
+static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target,
+ struct list_head *pkt_queue);
+static void ath6kl_htc_set_credit_dist(struct htc_target *target,
+ struct ath6kl_htc_credit_info *cred_info,
+ u16 svc_pri_order[], int len);
+
+/* threshold to re-enable Tx bundling for an AC*/
+#define TX_RESUME_BUNDLE_THRESHOLD 1500
+
+/* Functions for Tx credit handling */
+static void ath6kl_credit_deposit(struct ath6kl_htc_credit_info *cred_info,
+ struct htc_endpoint_credit_dist *ep_dist,
+ int credits)
+{
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit deposit ep %d credits %d\n",
+ ep_dist->endpoint, credits);
+
+ ep_dist->credits += credits;
+ ep_dist->cred_assngd += credits;
+ cred_info->cur_free_credits -= credits;
+}
+
+static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info,
+ struct list_head *ep_list,
+ int tot_credits)
+{
+ struct htc_endpoint_credit_dist *cur_ep_dist;
+ int count;
+
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit init total %d\n", tot_credits);
+
+ 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_credit_deposit(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_credit_deposit(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;
+ }
+
+ /*
+ * 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
+ */
+ }
+
+ /*
+ * For ath6kl_credit_seek function,
+ * it use list_for_each_entry_reverse to walk around the whole ep list.
+ * Therefore assign this lowestpri_ep_dist after walk around the ep_list
+ */
+ cred_info->lowestpri_ep_dist = cur_ep_dist->list;
+
+ 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;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_CREDIT,
+ "credit ep %d svc_id %d credits %d per_msg %d norm %d min %d\n",
+ cur_ep_dist->endpoint,
+ cur_ep_dist->svc_id,
+ cur_ep_dist->credits,
+ cur_ep_dist->cred_per_msg,
+ cur_ep_dist->cred_norm,
+ cur_ep_dist->cred_min);
+ }
+}
+
+/* initialize and setup credit distribution */
+static int ath6kl_htc_mbox_credit_setup(struct htc_target *htc_target,
+ struct ath6kl_htc_credit_info *cred_info)
+{
+ u16 servicepriority[5];
+
+ memset(cred_info, 0, sizeof(struct ath6kl_htc_credit_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_target, cred_info, servicepriority, 5);
+
+ return 0;
+}
+
+/* reduce an ep's credits back to a set limit */
+static void ath6kl_credit_reduce(struct ath6kl_htc_credit_info *cred_info,
+ struct htc_endpoint_credit_dist *ep_dist,
+ int limit)
+{
+ int credits;
+
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit reduce ep %d limit %d\n",
+ ep_dist->endpoint, limit);
+
+ 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 ath6kl_credit_update(struct ath6kl_htc_credit_info *cred_info,
+ struct list_head *epdist_list)
+{
+ struct htc_endpoint_credit_dist *cur_list;
+
+ list_for_each_entry(cur_list, epdist_list, list) {
+ if (cur_list->endpoint == ENDPOINT_0)
+ continue;
+
+ if (cur_list->cred_to_dist > 0) {
+ cur_list->credits += cur_list->cred_to_dist;
+ cur_list->cred_to_dist = 0;
+
+ if (cur_list->credits > cur_list->cred_assngd)
+ ath6kl_credit_reduce(cred_info,
+ cur_list,
+ cur_list->cred_assngd);
+
+ if (cur_list->credits > cur_list->cred_norm)
+ ath6kl_credit_reduce(cred_info, cur_list,
+ cur_list->cred_norm);
+
+ if (!(cur_list->dist_flags & HTC_EP_ACTIVE)) {
+ if (cur_list->txq_depth == 0)
+ ath6kl_credit_reduce(cred_info,
+ cur_list, 0);
+ }
+ }
+ }
+}
+
+/*
+ * HTC has an endpoint that needs credits, ep_dist is the endpoint in
+ * question.
+ */
+static void ath6kl_credit_seek(struct ath6kl_htc_credit_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.
+ */
+ ath6kl_credit_reduce(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_credit_deposit(cred_info, ep_dist, credits);
+
+ ep_dist->seek_cred = 0;
+}
+
+/* redistribute credits based on activity change */
+static void ath6kl_credit_redistribute(struct ath6kl_htc_credit_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)
+ ath6kl_credit_reduce(info, curdist_list, 0);
+ else
+ ath6kl_credit_reduce(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.
+ */
+static void ath6kl_credit_distribute(struct ath6kl_htc_credit_info *cred_info,
+ struct list_head *ep_dist_list,
+ enum htc_credit_dist_reason reason)
+{
+ switch (reason) {
+ case HTC_CREDIT_DIST_SEND_COMPLETE:
+ ath6kl_credit_update(cred_info, ep_dist_list);
+ break;
+ case HTC_CREDIT_DIST_ACTIVITY_CHANGE:
+ ath6kl_credit_redistribute(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);
+}
+
static void ath6kl_htc_tx_buf_align(u8 **buf, unsigned long len)
{
u8 *align_addr;
@@ -102,12 +425,12 @@ static void htc_tx_comp_update(struct htc_target *target,
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);
+ ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx ctxt 0x%p dist 0x%p\n",
+ target->credit_info, &target->cred_dist_list);
- ath6k_credit_distribute(target->cred_dist_cntxt,
- &target->cred_dist_list,
- HTC_CREDIT_DIST_SEND_COMPLETE);
+ ath6kl_credit_distribute(target->credit_info,
+ &target->cred_dist_list,
+ HTC_CREDIT_DIST_SEND_COMPLETE);
spin_unlock_bh(&target->tx_lock);
}
@@ -118,11 +441,11 @@ static void htc_tx_complete(struct htc_endpoint *endpoint,
if (list_empty(txq))
return;
- ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
- "send complete ep %d, (%d pkts)\n",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx complete ep %d pkts %d\n",
endpoint->eid, get_queue_depth(txq));
- ath6kl_tx_complete(endpoint->target->dev->ar, txq);
+ ath6kl_tx_complete(endpoint->target, txq);
}
static void htc_tx_comp_handler(struct htc_target *target,
@@ -131,6 +454,9 @@ static void htc_tx_comp_handler(struct htc_target *target,
struct htc_endpoint *endpoint = &target->endpoint[packet->endpoint];
struct list_head container;
+ ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx complete seqno %d\n",
+ packet->info.tx.seqno);
+
htc_tx_comp_update(target, endpoint, packet);
INIT_LIST_HEAD(&container);
list_add_tail(&packet->list, &container);
@@ -148,9 +474,9 @@ static void htc_async_tx_scat_complete(struct htc_target *target,
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);
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx scat complete 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);
@@ -190,16 +516,13 @@ static int ath6kl_htc_tx_issue(struct htc_target *target,
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");
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx issue len %d seqno %d padded_len %d mbox 0x%X %s\n",
+ send_len, packet->info.tx.seqno, padded_len,
+ target->dev->ar->mbox_info.htc_addr,
+ sync ? "sync" : "async");
if (sync) {
status = hif_read_write_sync(target->dev->ar,
@@ -215,6 +538,8 @@ static int ath6kl_htc_tx_issue(struct htc_target *target,
packet->buf, padded_len,
HIF_WR_ASYNC_BLOCK_INC, packet);
+ trace_ath6kl_htc_tx(status, packet->endpoint, packet->buf, send_len);
+
return status;
}
@@ -223,11 +548,10 @@ static int htc_check_credits(struct htc_target *target,
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",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit check need %d got %d\n",
*req_cred, ep->cred_dist.credits);
if (ep->cred_dist.credits < *req_cred) {
@@ -237,16 +561,13 @@ static int htc_check_credits(struct htc_target *target,
/* 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);
+ ath6kl_credit_seek(target->credit_info, &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",
+ ath6kl_dbg(ATH6KL_DBG_CREDIT,
+ "credit not found for ep %d\n",
eid);
return -EINVAL;
}
@@ -260,17 +581,15 @@ static int htc_check_credits(struct htc_target *target,
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);
+ ath6kl_credit_seek(target->credit_info, &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");
+ ath6kl_dbg(ATH6KL_DBG_CREDIT,
+ "credit we need credits asap\n");
}
}
@@ -287,7 +606,6 @@ static void ath6kl_htc_tx_pkts_get(struct htc_target *target,
unsigned int len;
while (true) {
-
flags = 0;
if (list_empty(&endpoint->txq))
@@ -295,9 +613,9 @@ static void ath6kl_htc_tx_pkts_get(struct htc_target *target,
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));
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx got packet 0x%p queue depth %d\n",
+ packet, get_queue_depth(&endpoint->txq));
len = CALC_TXRX_PADDED_LEN(target,
packet->act_len + HTC_HDR_LENGTH);
@@ -367,6 +685,7 @@ static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target,
struct htc_packet *packet;
int i, len, rem_scat, cred_pad;
int status = 0;
+ u8 flags;
rem_scat = target->max_tx_bndl_sz;
@@ -393,9 +712,9 @@ static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target,
scat_req->scat_list[i].packet = packet;
/* prepare packet and flag message as part of a send bundle */
- ath6kl_htc_tx_prep_pkt(packet,
- packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE,
- cred_pad, packet->info.tx.seqno);
+ flags = packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE;
+ ath6kl_htc_tx_prep_pkt(packet, flags,
+ cred_pad, packet->info.tx.seqno);
/* Make sure the buffer is 4-byte aligned */
ath6kl_htc_tx_buf_align(&packet->buf,
packet->act_len + HTC_HDR_LENGTH);
@@ -404,9 +723,9 @@ static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target,
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);
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx adding (%d) pkt 0x%p seqno %d len %d remaining %d\n",
+ i, packet, packet->info.tx.seqno, len, rem_scat);
}
/* Roll back scatter setup in case of any failure */
@@ -439,8 +758,15 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
{
struct htc_target *target = endpoint->target;
struct hif_scatter_req *scat_req = NULL;
- int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0;
+ int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0, i;
+ struct htc_packet *packet;
int status;
+ u32 txb_mask;
+ u8 ac = WMM_NUM_AC;
+
+ if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) &&
+ (WMI_CONTROL_SVC != endpoint->svc_id))
+ ac = target->dev->ar->ep2ac_map[endpoint->eid];
while (true) {
status = 0;
@@ -455,12 +781,38 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
if (!scat_req) {
/* no scatter resources */
- ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
- "no more scatter resources\n");
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx no more scatter resources\n");
break;
}
- ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "pkts to scatter: %d\n",
+ if ((ac < WMM_NUM_AC) && (ac != WMM_AC_BK)) {
+ if (WMM_AC_BE == ac)
+ /*
+ * BE, BK have priorities and bit
+ * positions reversed
+ */
+ txb_mask = (1 << WMM_AC_BK);
+ else
+ /*
+ * any AC with priority lower than
+ * itself
+ */
+ txb_mask = ((1 << ac) - 1);
+
+ /*
+ * when the scatter request resources drop below a
+ * certain threshold, disable Tx bundling for all
+ * AC's with priority lower than the current requesting
+ * AC. Otherwise re-enable Tx bundling for them
+ */
+ if (scat_req->scat_q_depth < ATH6KL_SCATTER_REQS)
+ target->tx_bndl_mask &= ~txb_mask;
+ else
+ target->tx_bndl_mask |= txb_mask;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx pkts to scatter: %d\n",
n_scat);
scat_req->len = 0;
@@ -479,10 +831,17 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
n_sent_bundle++;
tot_pkts_bundle += scat_req->scat_entries;
- ath6kl_dbg(ATH6KL_DBG_HTC_SEND,
- "send scatter total bytes: %d , entries: %d\n",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx scatter bytes %d entries %d\n",
scat_req->len, scat_req->scat_entries);
- ath6kldev_submit_scat_req(target->dev, scat_req, false);
+
+ for (i = 0; i < scat_req->scat_entries; i++) {
+ packet = scat_req->scat_list[i].packet;
+ trace_ath6kl_htc_tx(packet->status, packet->endpoint,
+ packet->buf, packet->act_len);
+ }
+
+ ath6kl_hif_submit_scat_req(target->dev, scat_req, false);
if (status)
break;
@@ -490,8 +849,8 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
*sent_bundle = n_sent_bundle;
*n_bundle_pkts = tot_pkts_bundle;
- ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "%s (sent:%d)\n",
- __func__, n_sent_bundle);
+ ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx bundle sent %d pkts\n",
+ n_sent_bundle);
return;
}
@@ -503,6 +862,8 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
struct htc_packet *packet;
int bundle_sent;
int n_pkts_bundle;
+ u8 ac = WMM_NUM_AC;
+ int status;
spin_lock_bh(&target->tx_lock);
@@ -510,7 +871,7 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
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");
+ ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx busy\n");
return;
}
@@ -520,8 +881,11 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
*/
INIT_LIST_HEAD(&txq);
- while (true) {
+ if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) &&
+ (WMI_CONTROL_SVC != endpoint->svc_id))
+ ac = target->dev->ar->ep2ac_map[endpoint->eid];
+ while (true) {
if (list_empty(&endpoint->txq))
break;
@@ -537,15 +901,18 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
while (true) {
/* try to send a bundle on each pass */
- if ((target->tx_bndl_enable) &&
+ if ((target->tx_bndl_mask) &&
(get_queue_depth(&txq) >=
HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
int temp1 = 0, temp2 = 0;
- ath6kl_htc_tx_bundle(endpoint, &txq,
- &temp1, &temp2);
- bundle_sent += temp1;
- n_pkts_bundle += temp2;
+ /* check if bundling is enabled for an AC */
+ if (target->tx_bndl_mask & (1 << ac)) {
+ ath6kl_htc_tx_bundle(endpoint, &txq,
+ &temp1, &temp2);
+ bundle_sent += temp1;
+ n_pkts_bundle += temp2;
+ }
}
if (list_empty(&txq))
@@ -557,13 +924,38 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags,
0, packet->info.tx.seqno);
- ath6kl_htc_tx_issue(target, packet);
+ status = ath6kl_htc_tx_issue(target, packet);
+
+ if (status) {
+ packet->status = status;
+ packet->completion(packet->context, packet);
+ }
}
spin_lock_bh(&target->tx_lock);
endpoint->ep_st.tx_bundles += bundle_sent;
endpoint->ep_st.tx_pkt_bundled += n_pkts_bundle;
+
+ /*
+ * if an AC has bundling disabled and no tx bundling
+ * has occured continously for a certain number of TX,
+ * enable tx bundling for this AC
+ */
+ if (!bundle_sent) {
+ if (!(target->tx_bndl_mask & (1 << ac)) &&
+ (ac < WMM_NUM_AC)) {
+ if (++target->ac_tx_count[ac] >=
+ TX_RESUME_BUNDLE_THRESHOLD) {
+ target->ac_tx_count[ac] = 0;
+ target->tx_bndl_mask |= (1 << ac);
+ }
+ }
+ } else {
+ /* tx bundling will reset the counter */
+ if (ac < WMM_NUM_AC)
+ target->ac_tx_count[ac] = 0;
+ }
}
endpoint->tx_proc_cnt = 0;
@@ -588,15 +980,12 @@ static bool ath6kl_htc_tx_try(struct htc_target *target,
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,
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx overflow ep %d depth %d max %d\n",
+ endpoint->eid, 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;
@@ -625,12 +1014,12 @@ static void htc_chk_ep_txq(struct htc_target *target)
* are not modifying any state.
*/
list_for_each_entry(cred_dist, &target->cred_dist_list, list) {
- endpoint = (struct htc_endpoint *)cred_dist->htc_rsvd;
+ endpoint = cred_dist->htc_ep;
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",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc creds ep %d credits %d pkts %d\n",
cred_dist->endpoint,
endpoint->cred_dist.credits,
get_queue_depth(&endpoint->txq));
@@ -679,8 +1068,8 @@ static int htc_setup_tx_complete(struct htc_target *target)
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);
+ sizeof(struct htc_setup_comp_ext_msg),
+ ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
} else {
struct htc_setup_comp_msg *setup_comp;
@@ -688,8 +1077,8 @@ static int htc_setup_tx_complete(struct htc_target *target)
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);
+ sizeof(struct htc_setup_comp_msg),
+ ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
}
/* we want synchronous operation */
@@ -703,14 +1092,14 @@ static int htc_setup_tx_complete(struct htc_target *target)
return status;
}
-void ath6kl_htc_set_credit_dist(struct htc_target *target,
- struct htc_credit_state_info *cred_dist_cntxt,
+static void ath6kl_htc_set_credit_dist(struct htc_target *target,
+ struct ath6kl_htc_credit_info *credit_info,
u16 srvc_pri_order[], int list_len)
{
struct htc_endpoint *endpoint;
int i, ep;
- target->cred_dist_cntxt = cred_dist_cntxt;
+ target->credit_info = credit_info;
list_add_tail(&target->endpoint[ENDPOINT_0].cred_dist.list,
&target->cred_dist_list);
@@ -731,13 +1120,14 @@ void ath6kl_htc_set_credit_dist(struct htc_target *target,
}
}
-int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet)
+static int ath6kl_htc_mbox_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",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx ep id %d buf 0x%p len %d\n",
packet->endpoint, packet->buf, packet->act_len);
if (packet->endpoint >= ENDPOINT_MAX) {
@@ -759,7 +1149,7 @@ int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet)
}
/* flush endpoint TX queue */
-void ath6kl_htc_flush_txep(struct htc_target *target,
+static void ath6kl_htc_mbox_flush_txep(struct htc_target *target,
enum htc_endpoint_id eid, u16 tag)
{
struct htc_packet *packet, *tmp_pkt;
@@ -787,16 +1177,15 @@ void ath6kl_htc_flush_txep(struct htc_target *target,
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);
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx flushing 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)
@@ -811,12 +1200,13 @@ static void ath6kl_htc_flush_txep_all(struct htc_target *target)
if (endpoint->svc_id == 0)
/* not in use.. */
continue;
- ath6kl_htc_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL);
+ ath6kl_htc_mbox_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)
+static void ath6kl_htc_mbox_activity_changed(struct htc_target *target,
+ enum htc_endpoint_id eid,
+ bool active)
{
struct htc_endpoint *endpoint = &target->endpoint[eid];
bool dist = false;
@@ -844,12 +1234,13 @@ void ath6kl_htc_indicate_activity_change(struct htc_target *target,
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);
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc tx activity ctxt 0x%p dist 0x%p\n",
+ target->credit_info, &target->cred_dist_list);
- ath6k_credit_distribute(target->cred_dist_cntxt,
- &target->cred_dist_list,
- HTC_CREDIT_DIST_ACTIVITY_CHANGE);
+ ath6kl_credit_distribute(target->credit_info,
+ &target->cred_dist_list,
+ HTC_CREDIT_DIST_ACTIVITY_CHANGE);
}
spin_unlock_bh(&target->tx_lock);
@@ -883,7 +1274,7 @@ static int htc_add_rxbuf(struct htc_target *target, struct htc_packet *packet)
INIT_LIST_HEAD(&queue);
list_add_tail(&packet->list, &queue);
- return ath6kl_htc_add_rxbuf_multiple(target, &queue);
+ return ath6kl_htc_mbox_add_rxbuf_multiple(target, &queue);
}
static void htc_reclaim_rxbuf(struct htc_target *target,
@@ -919,15 +1310,15 @@ static int ath6kl_htc_rx_packet(struct htc_target *target,
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",
+ 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",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx 0x%p hdr 0x%x len %d mbox 0x%x\n",
packet, packet->info.rx.exp_hdr,
- padded_len, dev->ar->mbox_info.htc_addr, "sync");
+ padded_len, dev->ar->mbox_info.htc_addr);
status = hif_read_write_sync(dev->ar,
dev->ar->mbox_info.htc_addr,
@@ -990,13 +1381,14 @@ static int ath6kl_htc_rx_setup(struct htc_target *target,
sizeof(*htc_hdr));
if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) {
- ath6kl_warn("Rx buffer requested with invalid length\n");
+ ath6kl_warn("Rx buffer requested with invalid length htc_hdr:eid %d, flags 0x%x, len %d\n",
+ htc_hdr->eid, htc_hdr->flags,
+ le16_to_cpu(htc_hdr->payld_len));
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
@@ -1026,9 +1418,9 @@ static int ath6kl_htc_rx_setup(struct htc_target *target,
}
}
- if (list_empty(&ep->rx_bufq))
+ if (list_empty(&ep->rx_bufq)) {
packet = NULL;
- else {
+ } else {
packet = list_first_entry(&ep->rx_bufq,
struct htc_packet, list);
list_del(&packet->list);
@@ -1089,7 +1481,6 @@ static int ath6kl_htc_rx_alloc(struct htc_target *target,
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) {
@@ -1137,8 +1528,8 @@ static int ath6kl_htc_rx_alloc(struct htc_target *target,
}
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",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx bundle pkts %d\n",
n_msg);
} else
/* HTC header only indicates 1 message to fetch */
@@ -1189,10 +1580,10 @@ static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets)
if (packets->act_len > 0) {
ath6kl_err("htc_ctrl_rx, got message with len:%zu\n",
- packets->act_len + HTC_HDR_LENGTH);
+ packets->act_len + HTC_HDR_LENGTH);
- ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
- "Unexpected ENDPOINT 0 Message", "",
+ ath6kl_dbg_dump(ATH6KL_DBG_HTC,
+ "htc rx unexpected endpoint 0 message", "",
packets->buf - HTC_HDR_LENGTH,
packets->act_len + HTC_HDR_LENGTH);
}
@@ -1209,9 +1600,6 @@ static void htc_proc_cred_rpt(struct htc_target *target,
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++) {
@@ -1223,8 +1611,9 @@ static void htc_proc_cred_rpt(struct htc_target *target,
endpoint = &target->endpoint[rpt->eid];
- ath6kl_dbg(ATH6KL_DBG_HTC_SEND, " ep %d got %d credits\n",
- rpt->eid, rpt->credits);
+ ath6kl_dbg(ATH6KL_DBG_CREDIT,
+ "credit report ep %d credits %d\n",
+ rpt->eid, rpt->credits);
endpoint->ep_st.tx_cred_rpt += 1;
endpoint->ep_st.cred_retnd += rpt->credits;
@@ -1264,21 +1653,14 @@ static void htc_proc_cred_rpt(struct htc_target *target,
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);
+ ath6kl_credit_distribute(target->credit_info,
+ &target->cred_dist_list,
+ HTC_CREDIT_DIST_SEND_COMPLETE);
}
spin_unlock_bh(&target->tx_lock);
@@ -1317,17 +1699,17 @@ static int htc_parse_trailer(struct htc_target *target,
}
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",
+ if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) &&
+ next_lk_ahds) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx lk_ahd 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",
+ ath6kl_dbg_dump(ATH6KL_DBG_HTC,
+ "htc rx next look ahead",
"", next_lk_ahds, 4);
*n_lk_ahds = 1;
@@ -1346,7 +1728,7 @@ static int htc_parse_trailer(struct htc_target *target,
bundle_lkahd_rpt =
(struct htc_bundle_lkahd_rpt *) record_buf;
- ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Bundle lk_ahd",
+ ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bundle lk_ahd",
"", record_buf, record->len);
for (i = 0; i < len; i++) {
@@ -1365,7 +1747,6 @@ static int htc_parse_trailer(struct htc_target *target,
}
return 0;
-
}
static int htc_proc_trailer(struct htc_target *target,
@@ -1378,17 +1759,14 @@ static int htc_proc_trailer(struct htc_target *target,
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);
+ ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx trailer len %d\n", len);
+ ath6kl_dbg_dump(ATH6KL_DBG_HTC, NULL, "", buf, len);
orig_buf = buf;
orig_len = len;
status = 0;
while (len > 0) {
-
if (len < sizeof(struct htc_record_hdr)) {
status = -ENOMEM;
break;
@@ -1418,7 +1796,7 @@ static int htc_proc_trailer(struct htc_target *target,
}
if (status)
- ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD Recv Trailer",
+ ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bad trailer",
"", orig_buf, orig_len);
return status;
@@ -1436,9 +1814,6 @@ static int ath6kl_htc_rx_process_hdr(struct htc_target *target,
if (n_lkahds != NULL)
*n_lkahds = 0;
- ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", "htc ",
- packet->buf, packet->act_len);
-
/*
* NOTE: we cannot assume the alignment of buf, so we use the safe
* macros to retrieve 16 bit fields.
@@ -1480,9 +1855,9 @@ static int ath6kl_htc_rx_process_hdr(struct htc_target *target,
if (lk_ahd != packet->info.rx.exp_hdr) {
ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n",
__func__, packet, packet->info.rx.rx_flags);
- ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Expected Message lk_ahd",
+ ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx expected lk_ahd",
"", &packet->info.rx.exp_hdr, 4);
- ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Current Frame Header",
+ ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx current header",
"", (u8 *)&lk_ahd, sizeof(lk_ahd));
status = -ENOMEM;
goto fail_rx;
@@ -1518,15 +1893,8 @@ static int ath6kl_htc_rx_process_hdr(struct htc_target *target,
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);
- }
+ ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bad packet",
+ "", packet->buf, packet->act_len);
return status;
}
@@ -1534,9 +1902,10 @@ fail_rx:
static void ath6kl_htc_rx_complete(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",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx complete ep %d packet 0x%p\n",
endpoint->eid, packet);
+
endpoint->ep_cb.rx(endpoint->target, packet);
}
@@ -1571,9 +1940,9 @@ static int ath6kl_htc_rx_bundle(struct htc_target *target,
len = 0;
- ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
- "%s(): (numpackets: %d , actual : %d)\n",
- __func__, get_queue_depth(rxq), n_scat_pkt);
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx bundle depth %d pkts %d\n",
+ get_queue_depth(rxq), n_scat_pkt);
scat_req = hif_scatter_req_get(target->dev->ar);
@@ -1620,7 +1989,7 @@ static int ath6kl_htc_rx_bundle(struct htc_target *target,
scat_req->len = len;
scat_req->scat_entries = i;
- status = ath6kldev_submit_scat_req(target->dev, scat_req, true);
+ status = ath6kl_hif_submit_scat_req(target->dev, scat_req, true);
if (!status)
*n_pkt_fetched = i;
@@ -1643,15 +2012,19 @@ static int ath6kl_htc_rx_process_packets(struct htc_target *target,
int status = 0;
list_for_each_entry_safe(packet, tmp_pkt, comp_pktq, list) {
- list_del(&packet->list);
ep = &target->endpoint[packet->endpoint];
+ trace_ath6kl_htc_rx(packet->status, packet->endpoint,
+ packet->buf, packet->act_len);
+
/* process header for each of the recv packet */
status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds,
n_lk_ahd);
if (status)
return status;
+ list_del(&packet->list);
+
if (list_empty(comp_pktq)) {
/*
* Last packet's more packet flag is set
@@ -1686,11 +2059,15 @@ static int ath6kl_htc_rx_fetch(struct htc_target *target,
int fetched_pkts;
bool part_bundle = false;
int status = 0;
+ struct list_head tmp_rxq;
+ struct htc_packet *packet, *tmp_pkt;
/* now go fetch the list of HTC packets */
while (!list_empty(rx_pktq)) {
fetched_pkts = 0;
+ INIT_LIST_HEAD(&tmp_rxq);
+
if (target->rx_bndl_enable && (get_queue_depth(rx_pktq) > 1)) {
/*
* There are enough packets to attempt a
@@ -1698,28 +2075,26 @@ static int ath6kl_htc_rx_fetch(struct htc_target *target,
* allowed.
*/
status = ath6kl_htc_rx_bundle(target, rx_pktq,
- comp_pktq,
+ &tmp_rxq,
&fetched_pkts,
part_bundle);
if (status)
- return status;
+ goto fail_rx;
if (!list_empty(rx_pktq))
part_bundle = true;
+
+ list_splice_tail_init(&tmp_rxq, comp_pktq);
}
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))
+ if (!list_is_singular(rx_pktq))
/*
* look_aheads in all packet
* except the last one in the
@@ -1731,18 +2106,42 @@ static int ath6kl_htc_rx_fetch(struct htc_target *target,
/* go fetch the packet */
status = ath6kl_htc_rx_packet(target, packet,
packet->act_len);
+
+ list_move_tail(&packet->list, &tmp_rxq);
+
if (status)
- return status;
+ goto fail_rx;
- list_add_tail(&packet->list, comp_pktq);
+ list_splice_tail_init(&tmp_rxq, comp_pktq);
}
}
+ return 0;
+
+fail_rx:
+
+ /*
+ * Cleanup any packets we allocated but didn't use to
+ * actually fetch any packets.
+ */
+
+ list_for_each_entry_safe(packet, tmp_pkt, rx_pktq, list) {
+ list_del(&packet->list);
+ htc_reclaim_rxbuf(target, packet,
+ &target->endpoint[packet->endpoint]);
+ }
+
+ list_for_each_entry_safe(packet, tmp_pkt, &tmp_rxq, list) {
+ list_del(&packet->list);
+ htc_reclaim_rxbuf(target, packet,
+ &target->endpoint[packet->endpoint]);
+ }
+
return status;
}
int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
- u32 msg_look_ahead[], int *num_pkts)
+ u32 msg_look_ahead, int *num_pkts)
{
struct htc_packet *packets, *tmp_pkt;
struct htc_endpoint *endpoint;
@@ -1753,16 +2152,16 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
enum htc_endpoint_id id;
int n_fetched = 0;
+ INIT_LIST_HEAD(&comp_pktq);
*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));
+ look_aheads[0] = msg_look_ahead;
while (true) {
-
/*
* First lookahead sets the expected endpoint IDs for all
* packets in a bundle.
@@ -1827,15 +2226,6 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
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) {
@@ -1846,7 +2236,7 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
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);
+ ath6kl_hif_rx_control(target->dev, false);
}
}
@@ -1856,7 +2246,7 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
*/
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);
+ ath6kl_hif_rx_control(target->dev, false);
}
*num_pkts = n_fetched;
@@ -1874,12 +2264,12 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target)
struct htc_frame_hdr *htc_hdr;
u32 look_ahead;
- if (ath6kldev_poll_mboxmsg_rx(target->dev, &look_ahead,
- HTC_TARGET_RESPONSE_TIMEOUT))
+ if (ath6kl_hif_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);
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx wait ctrl look_ahead 0x%X\n", look_ahead);
htc_hdr = (struct htc_frame_hdr *)&look_ahead;
@@ -1905,6 +2295,9 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target)
if (ath6kl_htc_rx_packet(target, packet, packet->act_len))
goto fail_ctrl_rx;
+ trace_ath6kl_htc_rx(packet->status, packet->endpoint,
+ packet->buf, packet->act_len);
+
/* process receive header */
packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL);
@@ -1925,7 +2318,7 @@ fail_ctrl_rx:
return NULL;
}
-int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
+static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target,
struct list_head *pkt_queue)
{
struct htc_endpoint *endpoint;
@@ -1943,8 +2336,8 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
depth = get_queue_depth(pkt_queue);
- ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
- "htc_add_rxbuf_multiple: ep id: %d, cnt:%d, len: %d\n",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx add multiple ep id %d cnt %d len %d\n",
first_pkt->endpoint, depth, first_pkt->buf_len);
endpoint = &target->endpoint[first_pkt->endpoint];
@@ -1969,9 +2362,9 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
/* 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);
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx 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;
@@ -1982,12 +2375,12 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
if (rx_unblock && !(target->htc_flags & HTC_OP_STATE_STOPPING))
/* TODO : implement a buffer threshold count? */
- ath6kldev_rx_control(target->dev, true);
+ ath6kl_hif_rx_control(target->dev, true);
return status;
}
-void ath6kl_htc_flush_rx_buf(struct htc_target *target)
+static void ath6kl_htc_mbox_flush_rx_buf(struct htc_target *target)
{
struct htc_endpoint *endpoint;
struct htc_packet *packet, *tmp_pkt;
@@ -2004,18 +2397,32 @@ void ath6kl_htc_flush_rx_buf(struct htc_target *target)
&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",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc rx flush pkt 0x%p len %d ep %d\n",
packet, packet->buf_len,
packet->endpoint);
- dev_kfree_skb(packet->pkt_cntxt);
+ /*
+ * packets in rx_bufq of endpoint 0 have originally
+ * been queued from target->free_ctrl_rxbuf where
+ * packet and packet->buf_start are allocated
+ * separately using kmalloc(). For other endpoint
+ * rx_bufq, it is allocated as skb where packet is
+ * skb->head. Take care of this difference while freeing
+ * the memory.
+ */
+ if (packet->endpoint == ENDPOINT_0) {
+ kfree(packet->buf_start);
+ kfree(packet);
+ } else {
+ 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,
+static int ath6kl_htc_mbox_conn_service(struct htc_target *target,
struct htc_service_connect_req *conn_req,
struct htc_service_connect_resp *conn_resp)
{
@@ -2027,9 +2434,10 @@ int ath6kl_htc_conn_service(struct htc_target *target,
enum htc_endpoint_id assigned_ep = ENDPOINT_MAX;
unsigned int max_msg_sz = 0;
int status = 0;
+ u16 msg_id;
- ath6kl_dbg(ATH6KL_DBG_TRC,
- "htc_conn_service, target:0x%p service id:0x%X\n",
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "htc connect service target 0x%p service id 0x%x\n",
target, conn_req->svc_id);
if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) {
@@ -2070,9 +2478,10 @@ int ath6kl_htc_conn_service(struct htc_target *target,
}
resp_msg = (struct htc_conn_service_resp *)rx_pkt->buf;
+ msg_id = le16_to_cpu(resp_msg->msg_id);
- if ((le16_to_cpu(resp_msg->msg_id) != HTC_MSG_CONN_SVC_RESP_ID)
- || (rx_pkt->act_len < sizeof(*resp_msg))) {
+ if ((msg_id != HTC_MSG_CONN_SVC_RESP_ID) ||
+ (rx_pkt->act_len < sizeof(*resp_msg))) {
status = -ENOMEM;
goto fail_tx;
}
@@ -2090,7 +2499,8 @@ int ath6kl_htc_conn_service(struct htc_target *target,
max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz);
}
- if (assigned_ep >= ENDPOINT_MAX || !max_msg_sz) {
+ if (WARN_ON_ONCE(assigned_ep == ENDPOINT_UNUSED ||
+ assigned_ep >= ENDPOINT_MAX || !max_msg_sz)) {
status = -ENOMEM;
goto fail_tx;
}
@@ -2115,10 +2525,19 @@ int ath6kl_htc_conn_service(struct htc_target *target,
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.htc_ep = endpoint;
endpoint->cred_dist.endpoint = assigned_ep;
endpoint->cred_dist.cred_sz = target->tgt_cred_sz;
+ switch (endpoint->svc_id) {
+ case WMI_DATA_BK_SVC:
+ endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 3;
+ break;
+ default:
+ endpoint->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM;
+ break;
+ }
+
if (conn_req->max_rxmsg_sz) {
/*
* Override cred_per_msg calculation, this optimizes
@@ -2172,10 +2591,11 @@ static void reset_ep_state(struct htc_target *target)
}
/* reset distribution list */
+ /* FIXME: free existing entries */
INIT_LIST_HEAD(&target->cred_dist_list);
}
-int ath6kl_htc_get_rxbuf_num(struct htc_target *target,
+static int ath6kl_htc_mbox_get_rxbuf_num(struct htc_target *target,
enum htc_endpoint_id endpoint)
{
int num;
@@ -2201,8 +2621,8 @@ static void htc_setup_msg_bndl(struct htc_target *target)
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",
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "htc bundling allowed msg_per_bndl_max %d\n",
target->msg_per_bndl_max);
/* Max rx bundle size is limited by the max tx bundle size */
@@ -2211,11 +2631,12 @@ static void htc_setup_msg_bndl(struct htc_target *target)
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",
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "htc max_rx_bndl_sz %d max_tx_bndl_sz %d\n",
target->max_rx_bndl_sz, target->max_tx_bndl_sz);
if (target->max_tx_bndl_sz)
- target->tx_bndl_enable = true;
+ /* tx_bndl_mask is enabled per AC, each has 1 bit */
+ target->tx_bndl_mask = (1 << WMM_NUM_AC) - 1;
if (target->max_rx_bndl_sz)
target->rx_bndl_enable = true;
@@ -2230,11 +2651,11 @@ static void htc_setup_msg_bndl(struct htc_target *target)
* padding will spill into the next credit buffer
* which is fatal.
*/
- target->tx_bndl_enable = false;
+ target->tx_bndl_mask = 0;
}
}
-int ath6kl_htc_wait_target(struct htc_target *target)
+static int ath6kl_htc_mbox_wait_target(struct htc_target *target)
{
struct htc_packet *packet = NULL;
struct htc_ready_ext_msg *rdy_msg;
@@ -2265,8 +2686,8 @@ int ath6kl_htc_wait_target(struct htc_target *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",
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "htc target ready credits %d size %d\n",
target->tgt_creds, target->tgt_cred_sz);
/* check if this is an extended ready message */
@@ -2280,9 +2701,9 @@ int ath6kl_htc_wait_target(struct htc_target *target)
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);
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "htc using protocol %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);
@@ -2297,9 +2718,13 @@ int ath6kl_htc_wait_target(struct htc_target *target)
connect.svc_id = HTC_CTRL_RSVD_SVC;
/* connect fake service */
- status = ath6kl_htc_conn_service((void *)target, &connect, &resp);
+ status = ath6kl_htc_mbox_conn_service((void *)target, &connect, &resp);
if (status)
+ /*
+ * FIXME: this call doesn't make sense, the caller should
+ * call ath6kl_htc_mbox_cleanup() when it wants remove htc
+ */
ath6kl_hif_cleanup_scatter(target->dev->ar);
fail_wait_target:
@@ -2315,13 +2740,16 @@ fail_wait_target:
* Start HTC, enable interrupts and let the target know
* host has finished setup.
*/
-int ath6kl_htc_start(struct htc_target *target)
+static int ath6kl_htc_mbox_start(struct htc_target *target)
{
struct htc_packet *packet;
int status;
+ memset(&target->dev->irq_proc_reg, 0,
+ sizeof(target->dev->irq_proc_reg));
+
/* Disable interrupts at the chip level */
- ath6kldev_disable_intrs(target->dev);
+ ath6kl_hif_disable_intrs(target->dev);
target->htc_flags = 0;
target->rx_st_flags = 0;
@@ -2334,8 +2762,8 @@ int ath6kl_htc_start(struct htc_target *target)
}
/* 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);
+ ath6kl_credit_init(target->credit_info, &target->cred_dist_list,
+ target->tgt_creds);
dump_cred_dist_stats(target);
@@ -2346,16 +2774,55 @@ int ath6kl_htc_start(struct htc_target *target)
return status;
/* unmask interrupts */
- status = ath6kldev_unmask_intrs(target->dev);
+ status = ath6kl_hif_unmask_intrs(target->dev);
if (status)
- ath6kl_htc_stop(target);
+ ath6kl_htc_mbox_stop(target);
return status;
}
+static int ath6kl_htc_reset(struct htc_target *target)
+{
+ u32 block_size, ctrl_bufsz;
+ struct htc_packet *packet;
+ int i;
+
+ reset_ep_state(target);
+
+ block_size = target->dev->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)
+ return -ENOMEM;
+
+ packet->buf_start = kzalloc(ctrl_bufsz, GFP_KERNEL);
+ if (!packet->buf_start) {
+ kfree(packet);
+ return -ENOMEM;
+ }
+
+ 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);
+ }
+ }
+
+ return 0;
+}
+
/* htc_stop: stop interrupt reception, and flush all queued buffers */
-void ath6kl_htc_stop(struct htc_target *target)
+static void ath6kl_htc_mbox_stop(struct htc_target *target)
{
spin_lock_bh(&target->htc_lock);
target->htc_flags |= HTC_OP_STATE_STOPPING;
@@ -2366,21 +2833,19 @@ void ath6kl_htc_stop(struct htc_target *target)
* function returns all pending HIF I/O has completed, we can
* safely flush the queues.
*/
- ath6kldev_mask_intrs(target->dev);
+ ath6kl_hif_mask_intrs(target->dev);
ath6kl_htc_flush_txep_all(target);
- ath6kl_htc_flush_rx_buf(target);
+ ath6kl_htc_mbox_flush_rx_buf(target);
- reset_ep_state(target);
+ ath6kl_htc_reset(target);
}
-void *ath6kl_htc_create(struct ath6kl *ar)
+static void *ath6kl_htc_mbox_create(struct ath6kl *ar)
{
struct htc_target *target = NULL;
- struct htc_packet *packet;
- int status = 0, i = 0;
- u32 block_size, ctrl_bufsz;
+ int status = 0;
target = kzalloc(sizeof(*target), GFP_KERNEL);
if (!target) {
@@ -2392,7 +2857,7 @@ void *ath6kl_htc_create(struct ath6kl *ar)
if (!target->dev) {
ath6kl_err("unable to allocate memory\n");
status = -ENOMEM;
- goto fail_create_htc;
+ goto err_htc_cleanup;
}
spin_lock_init(&target->htc_lock);
@@ -2407,67 +2872,38 @@ void *ath6kl_htc_create(struct ath6kl *ar)
target->dev->htc_cnxt = target;
target->ep_waiting = ENDPOINT_MAX;
- reset_ep_state(target);
-
- status = ath6kldev_setup(target->dev);
-
+ status = ath6kl_hif_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;
+ goto err_htc_cleanup;
- packet->buf_start = kzalloc(ctrl_bufsz, GFP_KERNEL);
- if (!packet->buf_start) {
- kfree(packet);
- break;
- }
+ status = ath6kl_htc_reset(target);
+ if (status)
+ goto err_htc_cleanup;
- 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);
- }
+ return target;
-fail_create_htc:
- if (i != NUM_CONTROL_BUFFERS || status) {
- if (target) {
- ath6kl_htc_cleanup(target);
- target = NULL;
- }
- }
+err_htc_cleanup:
+ ath6kl_htc_mbox_cleanup(target);
- return target;
+ return NULL;
}
/* cleanup the HTC instance */
-void ath6kl_htc_cleanup(struct htc_target *target)
+static void ath6kl_htc_mbox_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) {
+ &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) {
+ &target->free_ctrl_rxbuf, list) {
list_del(&packet->list);
kfree(packet->buf_start);
kfree(packet);
@@ -2476,3 +2912,24 @@ void ath6kl_htc_cleanup(struct htc_target *target)
kfree(target->dev);
kfree(target);
}
+
+static const struct ath6kl_htc_ops ath6kl_htc_mbox_ops = {
+ .create = ath6kl_htc_mbox_create,
+ .wait_target = ath6kl_htc_mbox_wait_target,
+ .start = ath6kl_htc_mbox_start,
+ .conn_service = ath6kl_htc_mbox_conn_service,
+ .tx = ath6kl_htc_mbox_tx,
+ .stop = ath6kl_htc_mbox_stop,
+ .cleanup = ath6kl_htc_mbox_cleanup,
+ .flush_txep = ath6kl_htc_mbox_flush_txep,
+ .flush_rx_buf = ath6kl_htc_mbox_flush_rx_buf,
+ .activity_changed = ath6kl_htc_mbox_activity_changed,
+ .get_rxbuf_num = ath6kl_htc_mbox_get_rxbuf_num,
+ .add_rxbuf_multiple = ath6kl_htc_mbox_add_rxbuf_multiple,
+ .credit_setup = ath6kl_htc_mbox_credit_setup,
+};
+
+void ath6kl_htc_mbox_attach(struct ath6kl *ar)
+{
+ ar->htc_ops = &ath6kl_htc_mbox_ops;
+}
diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c
new file mode 100644
index 00000000000..756fe52a12c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c
@@ -0,0 +1,1711 @@
+/*
+ * 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 "debug.h"
+#include "hif-ops.h"
+
+#define HTC_PACKET_CONTAINER_ALLOCATION 32
+#define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH)
+
+static int ath6kl_htc_pipe_tx(struct htc_target *handle,
+ struct htc_packet *packet);
+static void ath6kl_htc_pipe_cleanup(struct htc_target *handle);
+
+/* htc pipe tx path */
+static inline void restore_tx_packet(struct htc_packet *packet)
+{
+ if (packet->info.tx.flags & HTC_FLAGS_TX_FIXUP_NETBUF) {
+ skb_pull(packet->skb, sizeof(struct htc_frame_hdr));
+ packet->info.tx.flags &= ~HTC_FLAGS_TX_FIXUP_NETBUF;
+ }
+}
+
+static void do_send_completion(struct htc_endpoint *ep,
+ struct list_head *queue_to_indicate)
+{
+ struct htc_packet *packet;
+
+ if (list_empty(queue_to_indicate)) {
+ /* nothing to indicate */
+ return;
+ }
+
+ if (ep->ep_cb.tx_comp_multi != NULL) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: calling ep %d, send complete multiple callback (%d pkts)\n",
+ __func__, ep->eid,
+ get_queue_depth(queue_to_indicate));
+ /*
+ * a multiple send complete handler is being used,
+ * pass the queue to the handler
+ */
+ ep->ep_cb.tx_comp_multi(ep->target, queue_to_indicate);
+ /*
+ * all packets are now owned by the callback,
+ * reset queue to be safe
+ */
+ INIT_LIST_HEAD(queue_to_indicate);
+ } else {
+ /* using legacy EpTxComplete */
+ do {
+ packet = list_first_entry(queue_to_indicate,
+ struct htc_packet, list);
+
+ list_del(&packet->list);
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: calling ep %d send complete callback on packet 0x%p\n",
+ __func__, ep->eid, packet);
+ ep->ep_cb.tx_complete(ep->target, packet);
+ } while (!list_empty(queue_to_indicate));
+ }
+}
+
+static void send_packet_completion(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ struct htc_endpoint *ep = &target->endpoint[packet->endpoint];
+ struct list_head container;
+
+ restore_tx_packet(packet);
+ INIT_LIST_HEAD(&container);
+ list_add_tail(&packet->list, &container);
+
+ /* do completion */
+ do_send_completion(ep, &container);
+}
+
+static void get_htc_packet_credit_based(struct htc_target *target,
+ struct htc_endpoint *ep,
+ struct list_head *queue)
+{
+ int credits_required;
+ int remainder;
+ u8 send_flags;
+ struct htc_packet *packet;
+ unsigned int transfer_len;
+
+ /* NOTE : the TX lock is held when this function is called */
+
+ /* loop until we can grab as many packets out of the queue as we can */
+ while (true) {
+ send_flags = 0;
+ if (list_empty(&ep->txq))
+ break;
+
+ /* get packet at head, but don't remove it */
+ packet = list_first_entry(&ep->txq, struct htc_packet, list);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: got head packet:0x%p , queue depth: %d\n",
+ __func__, packet, get_queue_depth(&ep->txq));
+
+ transfer_len = packet->act_len + HTC_HDR_LENGTH;
+
+ if (transfer_len <= target->tgt_cred_sz) {
+ credits_required = 1;
+ } else {
+ /* figure out how many credits this message requires */
+ credits_required = transfer_len / target->tgt_cred_sz;
+ remainder = transfer_len % target->tgt_cred_sz;
+
+ if (remainder)
+ credits_required++;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_HTC, "%s: creds required:%d got:%d\n",
+ __func__, credits_required, ep->cred_dist.credits);
+
+ if (ep->eid == ENDPOINT_0) {
+ /*
+ * endpoint 0 is special, it always has a credit and
+ * does not require credit based flow control
+ */
+ credits_required = 0;
+
+ } else {
+ if (ep->cred_dist.credits < credits_required)
+ break;
+
+ ep->cred_dist.credits -= credits_required;
+ ep->ep_st.cred_cosumd += credits_required;
+
+ /* check if we need credits back from the target */
+ if (ep->cred_dist.credits <
+ ep->cred_dist.cred_per_msg) {
+ /* tell the target we need credits ASAP! */
+ send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE;
+ ep->ep_st.cred_low_indicate += 1;
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: host needs credits\n",
+ __func__);
+ }
+ }
+
+ /* now we can fully dequeue */
+ packet = list_first_entry(&ep->txq, struct htc_packet, list);
+
+ list_del(&packet->list);
+ /* save the number of credits this packet consumed */
+ packet->info.tx.cred_used = credits_required;
+ /* save send flags */
+ packet->info.tx.flags = send_flags;
+ packet->info.tx.seqno = ep->seqno;
+ ep->seqno++;
+ /* queue this packet into the caller's queue */
+ list_add_tail(&packet->list, queue);
+ }
+}
+
+static void get_htc_packet(struct htc_target *target,
+ struct htc_endpoint *ep,
+ struct list_head *queue, int resources)
+{
+ struct htc_packet *packet;
+
+ /* NOTE : the TX lock is held when this function is called */
+
+ /* loop until we can grab as many packets out of the queue as we can */
+ while (resources) {
+ if (list_empty(&ep->txq))
+ break;
+
+ packet = list_first_entry(&ep->txq, struct htc_packet, list);
+ list_del(&packet->list);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: got packet:0x%p , new queue depth: %d\n",
+ __func__, packet, get_queue_depth(&ep->txq));
+ packet->info.tx.seqno = ep->seqno;
+ packet->info.tx.flags = 0;
+ packet->info.tx.cred_used = 0;
+ ep->seqno++;
+
+ /* queue this packet into the caller's queue */
+ list_add_tail(&packet->list, queue);
+ resources--;
+ }
+}
+
+static int htc_issue_packets(struct htc_target *target,
+ struct htc_endpoint *ep,
+ struct list_head *pkt_queue)
+{
+ int status = 0;
+ u16 payload_len;
+ struct sk_buff *skb;
+ struct htc_frame_hdr *htc_hdr;
+ struct htc_packet *packet;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: queue: 0x%p, pkts %d\n", __func__,
+ pkt_queue, get_queue_depth(pkt_queue));
+
+ while (!list_empty(pkt_queue)) {
+ packet = list_first_entry(pkt_queue, struct htc_packet, list);
+ list_del(&packet->list);
+
+ skb = packet->skb;
+ if (!skb) {
+ WARN_ON_ONCE(1);
+ status = -EINVAL;
+ break;
+ }
+
+ payload_len = packet->act_len;
+
+ /* setup HTC frame header */
+ htc_hdr = (struct htc_frame_hdr *) skb_push(skb,
+ sizeof(*htc_hdr));
+ if (!htc_hdr) {
+ WARN_ON_ONCE(1);
+ status = -EINVAL;
+ break;
+ }
+
+ packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF;
+
+ /* Endianess? */
+ put_unaligned((u16) payload_len, &htc_hdr->payld_len);
+ htc_hdr->flags = packet->info.tx.flags;
+ htc_hdr->eid = (u8) packet->endpoint;
+ htc_hdr->ctrl[0] = 0;
+ htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno;
+
+ spin_lock_bh(&target->tx_lock);
+
+ /* store in look up queue to match completions */
+ list_add_tail(&packet->list, &ep->pipe.tx_lookup_queue);
+ ep->ep_st.tx_issued += 1;
+ spin_unlock_bh(&target->tx_lock);
+
+ status = ath6kl_hif_pipe_send(target->dev->ar,
+ ep->pipe.pipeid_ul, NULL, skb);
+
+ if (status != 0) {
+ if (status != -ENOMEM) {
+ /* TODO: if more than 1 endpoint maps to the
+ * same PipeID, it is possible to run out of
+ * resources in the HIF layer.
+ * Don't emit the error
+ */
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: failed status:%d\n",
+ __func__, status);
+ }
+ spin_lock_bh(&target->tx_lock);
+ list_del(&packet->list);
+
+ /* reclaim credits */
+ ep->cred_dist.credits += packet->info.tx.cred_used;
+ spin_unlock_bh(&target->tx_lock);
+
+ /* put it back into the callers queue */
+ list_add(&packet->list, pkt_queue);
+ break;
+ }
+ }
+
+ if (status != 0) {
+ while (!list_empty(pkt_queue)) {
+ if (status != -ENOMEM) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: failed pkt:0x%p status:%d\n",
+ __func__, packet, status);
+ }
+
+ packet = list_first_entry(pkt_queue,
+ struct htc_packet, list);
+ list_del(&packet->list);
+ packet->status = status;
+ send_packet_completion(target, packet);
+ }
+ }
+
+ return status;
+}
+
+static enum htc_send_queue_result htc_try_send(struct htc_target *target,
+ struct htc_endpoint *ep,
+ struct list_head *txq)
+{
+ struct list_head send_queue; /* temp queue to hold packets */
+ struct htc_packet *packet, *tmp_pkt;
+ struct ath6kl *ar = target->dev->ar;
+ enum htc_send_full_action action;
+ int tx_resources, overflow, txqueue_depth, i, good_pkts;
+ u8 pipeid;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC, "%s: (queue:0x%p depth:%d)\n",
+ __func__, txq,
+ (txq == NULL) ? 0 : get_queue_depth(txq));
+
+ /* init the local send queue */
+ INIT_LIST_HEAD(&send_queue);
+
+ /*
+ * txq equals to NULL means
+ * caller didn't provide a queue, just wants us to
+ * check queues and send
+ */
+ if (txq != NULL) {
+ if (list_empty(txq)) {
+ /* empty queue */
+ return HTC_SEND_QUEUE_DROP;
+ }
+
+ spin_lock_bh(&target->tx_lock);
+ txqueue_depth = get_queue_depth(&ep->txq);
+ spin_unlock_bh(&target->tx_lock);
+
+ if (txqueue_depth >= ep->max_txq_depth) {
+ /* we've already overflowed */
+ overflow = get_queue_depth(txq);
+ } else {
+ /* get how much we will overflow by */
+ overflow = txqueue_depth;
+ overflow += get_queue_depth(txq);
+ /* get how much we will overflow the TX queue by */
+ overflow -= ep->max_txq_depth;
+ }
+
+ /* if overflow is negative or zero, we are okay */
+ if (overflow > 0) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: Endpoint %d, TX queue will overflow :%d, Tx Depth:%d, Max:%d\n",
+ __func__, ep->eid, overflow, txqueue_depth,
+ ep->max_txq_depth);
+ }
+ if ((overflow <= 0) ||
+ (ep->ep_cb.tx_full == NULL)) {
+ /*
+ * all packets will fit or caller did not provide send
+ * full indication handler -- just move all of them
+ * to the local send_queue object
+ */
+ list_splice_tail_init(txq, &send_queue);
+ } else {
+ good_pkts = get_queue_depth(txq) - overflow;
+ if (good_pkts < 0) {
+ WARN_ON_ONCE(1);
+ return HTC_SEND_QUEUE_DROP;
+ }
+
+ /* we have overflowed, and a callback is provided */
+ /* dequeue all non-overflow packets to the sendqueue */
+ for (i = 0; i < good_pkts; i++) {
+ /* pop off caller's queue */
+ packet = list_first_entry(txq,
+ struct htc_packet,
+ list);
+ /* move to local queue */
+ list_move_tail(&packet->list, &send_queue);
+ }
+
+ /*
+ * the caller's queue has all the packets that won't fit
+ * walk through the caller's queue and indicate each to
+ * the send full handler
+ */
+ list_for_each_entry_safe(packet, tmp_pkt,
+ txq, list) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: Indicat overflowed TX pkts: %p\n",
+ __func__, packet);
+ action = ep->ep_cb.tx_full(ep->target, packet);
+ if (action == HTC_SEND_FULL_DROP) {
+ /* callback wants the packet dropped */
+ ep->ep_st.tx_dropped += 1;
+
+ /* leave this one in the caller's queue
+ * for cleanup */
+ } else {
+ /* callback wants to keep this packet,
+ * move from caller's queue to the send
+ * queue */
+ list_move_tail(&packet->list,
+ &send_queue);
+ }
+ }
+
+ if (list_empty(&send_queue)) {
+ /* no packets made it in, caller will cleanup */
+ return HTC_SEND_QUEUE_DROP;
+ }
+ }
+ }
+
+ if (!ep->pipe.tx_credit_flow_enabled) {
+ tx_resources =
+ ath6kl_hif_pipe_get_free_queue_number(ar,
+ ep->pipe.pipeid_ul);
+ } else {
+ tx_resources = 0;
+ }
+
+ spin_lock_bh(&target->tx_lock);
+ if (!list_empty(&send_queue)) {
+ /* transfer packets to tail */
+ list_splice_tail_init(&send_queue, &ep->txq);
+ if (!list_empty(&send_queue)) {
+ WARN_ON_ONCE(1);
+ spin_unlock_bh(&target->tx_lock);
+ return HTC_SEND_QUEUE_DROP;
+ }
+ INIT_LIST_HEAD(&send_queue);
+ }
+
+ /* increment tx processing count on entry */
+ ep->tx_proc_cnt++;
+
+ if (ep->tx_proc_cnt > 1) {
+ /*
+ * Another thread or task is draining the TX queues on this
+ * endpoint that thread will reset the tx processing count
+ * when the queue is drained.
+ */
+ ep->tx_proc_cnt--;
+ spin_unlock_bh(&target->tx_lock);
+ return HTC_SEND_QUEUE_OK;
+ }
+
+ /***** beyond this point only 1 thread may enter ******/
+
+ /*
+ * Now drain the endpoint TX queue for transmission as long as we have
+ * enough transmit resources.
+ */
+ while (true) {
+ if (get_queue_depth(&ep->txq) == 0)
+ break;
+
+ if (ep->pipe.tx_credit_flow_enabled) {
+ /*
+ * Credit based mechanism provides flow control
+ * based on target transmit resource availability,
+ * we assume that the HIF layer will always have
+ * bus resources greater than target transmit
+ * resources.
+ */
+ get_htc_packet_credit_based(target, ep, &send_queue);
+ } else {
+ /*
+ * Get all packets for this endpoint that we can
+ * for this pass.
+ */
+ get_htc_packet(target, ep, &send_queue, tx_resources);
+ }
+
+ if (get_queue_depth(&send_queue) == 0) {
+ /*
+ * Didn't get packets due to out of resources or TX
+ * queue was drained.
+ */
+ break;
+ }
+
+ spin_unlock_bh(&target->tx_lock);
+
+ /* send what we can */
+ htc_issue_packets(target, ep, &send_queue);
+
+ if (!ep->pipe.tx_credit_flow_enabled) {
+ pipeid = ep->pipe.pipeid_ul;
+ tx_resources =
+ ath6kl_hif_pipe_get_free_queue_number(ar, pipeid);
+ }
+
+ spin_lock_bh(&target->tx_lock);
+ }
+
+ /* done with this endpoint, we can clear the count */
+ ep->tx_proc_cnt = 0;
+ spin_unlock_bh(&target->tx_lock);
+
+ return HTC_SEND_QUEUE_OK;
+}
+
+/* htc control packet manipulation */
+static void destroy_htc_txctrl_packet(struct htc_packet *packet)
+{
+ struct sk_buff *skb;
+ skb = packet->skb;
+ dev_kfree_skb(skb);
+ kfree(packet);
+}
+
+static struct htc_packet *build_htc_txctrl_packet(void)
+{
+ struct htc_packet *packet = NULL;
+ struct sk_buff *skb;
+
+ packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL);
+ if (packet == NULL)
+ return NULL;
+
+ skb = __dev_alloc_skb(HTC_CONTROL_BUFFER_SIZE, GFP_KERNEL);
+
+ if (skb == NULL) {
+ kfree(packet);
+ return NULL;
+ }
+ packet->skb = skb;
+
+ return packet;
+}
+
+static void htc_free_txctrl_packet(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ destroy_htc_txctrl_packet(packet);
+}
+
+static struct htc_packet *htc_alloc_txctrl_packet(struct htc_target *target)
+{
+ return build_htc_txctrl_packet();
+}
+
+static void htc_txctrl_complete(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ htc_free_txctrl_packet(target, packet);
+}
+
+#define MAX_MESSAGE_SIZE 1536
+
+static int htc_setup_target_buffer_assignments(struct htc_target *target)
+{
+ int status, credits, credit_per_maxmsg, i;
+ struct htc_pipe_txcredit_alloc *entry;
+ unsigned int hif_usbaudioclass = 0;
+
+ credit_per_maxmsg = MAX_MESSAGE_SIZE / target->tgt_cred_sz;
+ if (MAX_MESSAGE_SIZE % target->tgt_cred_sz)
+ credit_per_maxmsg++;
+
+ /* TODO, this should be configured by the caller! */
+
+ credits = target->tgt_creds;
+ entry = &target->pipe.txcredit_alloc[0];
+
+ status = -ENOMEM;
+
+ /* FIXME: hif_usbaudioclass is always zero */
+ if (hif_usbaudioclass) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: For USB Audio Class- Total:%d\n",
+ __func__, credits);
+ entry++;
+ entry++;
+ /* Setup VO Service To have Max Credits */
+ entry->service_id = WMI_DATA_VO_SVC;
+ entry->credit_alloc = (credits - 6);
+ if (entry->credit_alloc == 0)
+ entry->credit_alloc++;
+
+ credits -= (int) entry->credit_alloc;
+ if (credits <= 0)
+ return status;
+
+ entry++;
+ entry->service_id = WMI_CONTROL_SVC;
+ entry->credit_alloc = credit_per_maxmsg;
+ credits -= (int) entry->credit_alloc;
+ if (credits <= 0)
+ return status;
+
+ /* leftovers go to best effort */
+ entry++;
+ entry++;
+ entry->service_id = WMI_DATA_BE_SVC;
+ entry->credit_alloc = (u8) credits;
+ status = 0;
+ } else {
+ entry++;
+ entry->service_id = WMI_DATA_VI_SVC;
+ entry->credit_alloc = credits / 4;
+ if (entry->credit_alloc == 0)
+ entry->credit_alloc++;
+
+ credits -= (int) entry->credit_alloc;
+ if (credits <= 0)
+ return status;
+
+ entry++;
+ entry->service_id = WMI_DATA_VO_SVC;
+ entry->credit_alloc = credits / 4;
+ if (entry->credit_alloc == 0)
+ entry->credit_alloc++;
+
+ credits -= (int) entry->credit_alloc;
+ if (credits <= 0)
+ return status;
+
+ entry++;
+ entry->service_id = WMI_CONTROL_SVC;
+ entry->credit_alloc = credit_per_maxmsg;
+ credits -= (int) entry->credit_alloc;
+ if (credits <= 0)
+ return status;
+
+ entry++;
+ entry->service_id = WMI_DATA_BK_SVC;
+ entry->credit_alloc = credit_per_maxmsg;
+ credits -= (int) entry->credit_alloc;
+ if (credits <= 0)
+ return status;
+
+ /* leftovers go to best effort */
+ entry++;
+ entry->service_id = WMI_DATA_BE_SVC;
+ entry->credit_alloc = (u8) credits;
+ status = 0;
+ }
+
+ if (status == 0) {
+ for (i = 0; i < ENDPOINT_MAX; i++) {
+ if (target->pipe.txcredit_alloc[i].service_id != 0) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "HTC Service Index : %d TX : 0x%2.2X : alloc:%d\n",
+ i,
+ target->pipe.txcredit_alloc[i].
+ service_id,
+ target->pipe.txcredit_alloc[i].
+ credit_alloc);
+ }
+ }
+ }
+ return status;
+}
+
+/* process credit reports and call distribution function */
+static void htc_process_credit_report(struct htc_target *target,
+ struct htc_credit_report *rpt,
+ int num_entries,
+ enum htc_endpoint_id from_ep)
+{
+ int total_credits = 0, i;
+ struct htc_endpoint *ep;
+
+ /* lock out TX while we update credits */
+ spin_lock_bh(&target->tx_lock);
+
+ for (i = 0; i < num_entries; i++, rpt++) {
+ if (rpt->eid >= ENDPOINT_MAX) {
+ WARN_ON_ONCE(1);
+ spin_unlock_bh(&target->tx_lock);
+ return;
+ }
+
+ ep = &target->endpoint[rpt->eid];
+ ep->cred_dist.credits += rpt->credits;
+
+ if (ep->cred_dist.credits && get_queue_depth(&ep->txq)) {
+ spin_unlock_bh(&target->tx_lock);
+ htc_try_send(target, ep, NULL);
+ spin_lock_bh(&target->tx_lock);
+ }
+
+ total_credits += rpt->credits;
+ }
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "Report indicated %d credits to distribute\n",
+ total_credits);
+
+ spin_unlock_bh(&target->tx_lock);
+}
+
+/* flush endpoint TX queue */
+static void htc_flush_tx_endpoint(struct htc_target *target,
+ struct htc_endpoint *ep, u16 tag)
+{
+ struct htc_packet *packet;
+
+ spin_lock_bh(&target->tx_lock);
+ while (get_queue_depth(&ep->txq)) {
+ packet = list_first_entry(&ep->txq, struct htc_packet, list);
+ list_del(&packet->list);
+ packet->status = 0;
+ send_packet_completion(target, packet);
+ }
+ spin_unlock_bh(&target->tx_lock);
+}
+
+/*
+ * In the adapted HIF layer, struct sk_buff * are passed between HIF and HTC,
+ * since upper layers expects struct htc_packet containers we use the completed
+ * skb and lookup it's corresponding HTC packet buffer from a lookup list.
+ * This is extra overhead that can be fixed by re-aligning HIF interfaces with
+ * HTC.
+ */
+static struct htc_packet *htc_lookup_tx_packet(struct htc_target *target,
+ struct htc_endpoint *ep,
+ struct sk_buff *skb)
+{
+ struct htc_packet *packet, *tmp_pkt, *found_packet = NULL;
+
+ spin_lock_bh(&target->tx_lock);
+
+ /*
+ * interate from the front of tx lookup queue
+ * this lookup should be fast since lower layers completes in-order and
+ * so the completed packet should be at the head of the list generally
+ */
+ list_for_each_entry_safe(packet, tmp_pkt, &ep->pipe.tx_lookup_queue,
+ list) {
+ /* check for removal */
+ if (skb == packet->skb) {
+ /* found it */
+ list_del(&packet->list);
+ found_packet = packet;
+ break;
+ }
+ }
+
+ spin_unlock_bh(&target->tx_lock);
+
+ return found_packet;
+}
+
+static int ath6kl_htc_pipe_tx_complete(struct ath6kl *ar, struct sk_buff *skb)
+{
+ struct htc_target *target = ar->htc_target;
+ struct htc_frame_hdr *htc_hdr;
+ struct htc_endpoint *ep;
+ struct htc_packet *packet;
+ u8 ep_id, *netdata;
+ u32 netlen;
+
+ netdata = skb->data;
+ netlen = skb->len;
+
+ htc_hdr = (struct htc_frame_hdr *) netdata;
+
+ ep_id = htc_hdr->eid;
+ ep = &target->endpoint[ep_id];
+
+ packet = htc_lookup_tx_packet(target, ep, skb);
+ if (packet == NULL) {
+ /* may have already been flushed and freed */
+ ath6kl_err("HTC TX lookup failed!\n");
+ } else {
+ /* will be giving this buffer back to upper layers */
+ packet->status = 0;
+ send_packet_completion(target, packet);
+ }
+ skb = NULL;
+
+ if (!ep->pipe.tx_credit_flow_enabled) {
+ /*
+ * note: when using TX credit flow, the re-checking of queues
+ * happens when credits flow back from the target. in the
+ * non-TX credit case, we recheck after the packet completes
+ */
+ htc_try_send(target, ep, NULL);
+ }
+
+ return 0;
+}
+
+static int htc_send_packets_multiple(struct htc_target *target,
+ struct list_head *pkt_queue)
+{
+ struct htc_endpoint *ep;
+ struct htc_packet *packet, *tmp_pkt;
+
+ if (list_empty(pkt_queue))
+ return -EINVAL;
+
+ /* get first packet to find out which ep the packets will go into */
+ packet = list_first_entry(pkt_queue, struct htc_packet, list);
+
+ if (packet->endpoint >= ENDPOINT_MAX) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+ ep = &target->endpoint[packet->endpoint];
+
+ htc_try_send(target, ep, pkt_queue);
+
+ /* do completion on any packets that couldn't get in */
+ if (!list_empty(pkt_queue)) {
+ list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) {
+ packet->status = -ENOMEM;
+ }
+
+ do_send_completion(ep, pkt_queue);
+ }
+
+ return 0;
+}
+
+/* htc pipe rx path */
+static struct htc_packet *alloc_htc_packet_container(struct htc_target *target)
+{
+ struct htc_packet *packet;
+ spin_lock_bh(&target->rx_lock);
+
+ if (target->pipe.htc_packet_pool == NULL) {
+ spin_unlock_bh(&target->rx_lock);
+ return NULL;
+ }
+
+ packet = target->pipe.htc_packet_pool;
+ target->pipe.htc_packet_pool = (struct htc_packet *) packet->list.next;
+
+ spin_unlock_bh(&target->rx_lock);
+
+ packet->list.next = NULL;
+ return packet;
+}
+
+static void free_htc_packet_container(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ struct list_head *lh;
+
+ spin_lock_bh(&target->rx_lock);
+
+ if (target->pipe.htc_packet_pool == NULL) {
+ target->pipe.htc_packet_pool = packet;
+ packet->list.next = NULL;
+ } else {
+ lh = (struct list_head *) target->pipe.htc_packet_pool;
+ packet->list.next = lh;
+ target->pipe.htc_packet_pool = packet;
+ }
+
+ spin_unlock_bh(&target->rx_lock);
+}
+
+static int htc_process_trailer(struct htc_target *target, u8 *buffer,
+ int len, enum htc_endpoint_id from_ep)
+{
+ struct htc_credit_report *report;
+ struct htc_record_hdr *record;
+ u8 *record_buf, *orig_buf;
+ int orig_len, status;
+
+ orig_buf = buffer;
+ orig_len = len;
+ status = 0;
+
+ while (len > 0) {
+ if (len < sizeof(struct htc_record_hdr)) {
+ status = -EINVAL;
+ break;
+ }
+
+ /* these are byte aligned structs */
+ record = (struct htc_record_hdr *) buffer;
+ len -= sizeof(struct htc_record_hdr);
+ buffer += sizeof(struct htc_record_hdr);
+
+ if (record->len > len) {
+ /* no room left in buffer for record */
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "invalid length: %d (id:%d) buffer has: %d bytes left\n",
+ record->len, record->rec_id, len);
+ status = -EINVAL;
+ break;
+ }
+
+ /* start of record follows the header */
+ record_buf = buffer;
+
+ switch (record->rec_id) {
+ case HTC_RECORD_CREDITS:
+ if (record->len < sizeof(struct htc_credit_report)) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ report = (struct htc_credit_report *) record_buf;
+ htc_process_credit_report(target, report,
+ record->len / sizeof(*report),
+ from_ep);
+ break;
+ default:
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "unhandled record: id:%d length:%d\n",
+ record->rec_id, record->len);
+ break;
+ }
+
+ if (status != 0)
+ break;
+
+ /* advance buffer past this record for next time around */
+ buffer += record->len;
+ len -= record->len;
+ }
+
+ return status;
+}
+
+static void do_recv_completion(struct htc_endpoint *ep,
+ struct list_head *queue_to_indicate)
+{
+ struct htc_packet *packet;
+
+ if (list_empty(queue_to_indicate)) {
+ /* nothing to indicate */
+ return;
+ }
+
+ /* using legacy EpRecv */
+ while (!list_empty(queue_to_indicate)) {
+ packet = list_first_entry(queue_to_indicate,
+ struct htc_packet, list);
+ list_del(&packet->list);
+ ep->ep_cb.rx(ep->target, packet);
+ }
+
+ return;
+}
+
+static void recv_packet_completion(struct htc_target *target,
+ struct htc_endpoint *ep,
+ struct htc_packet *packet)
+{
+ struct list_head container;
+ INIT_LIST_HEAD(&container);
+ list_add_tail(&packet->list, &container);
+
+ /* do completion */
+ do_recv_completion(ep, &container);
+}
+
+static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb,
+ u8 pipeid)
+{
+ struct htc_target *target = ar->htc_target;
+ u8 *netdata, *trailer, hdr_info;
+ struct htc_frame_hdr *htc_hdr;
+ u32 netlen, trailerlen = 0;
+ struct htc_packet *packet;
+ struct htc_endpoint *ep;
+ u16 payload_len;
+ int status = 0;
+
+ /*
+ * ar->htc_target can be NULL due to a race condition that can occur
+ * during driver initialization(we do 'ath6kl_hif_power_on' before
+ * initializing 'ar->htc_target' via 'ath6kl_htc_create').
+ * 'ath6kl_hif_power_on' assigns 'ath6kl_recv_complete' as
+ * usb_complete_t/callback function for 'usb_fill_bulk_urb'.
+ * Thus the possibility of ar->htc_target being NULL
+ * via ath6kl_recv_complete -> ath6kl_usb_io_comp_work.
+ */
+ if (WARN_ON_ONCE(!target)) {
+ ath6kl_err("Target not yet initialized\n");
+ status = -EINVAL;
+ goto free_skb;
+ }
+
+
+ netdata = skb->data;
+ netlen = skb->len;
+
+ htc_hdr = (struct htc_frame_hdr *) netdata;
+
+ if (htc_hdr->eid >= ENDPOINT_MAX) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "HTC Rx: invalid EndpointID=%d\n",
+ htc_hdr->eid);
+ status = -EINVAL;
+ goto free_skb;
+ }
+ ep = &target->endpoint[htc_hdr->eid];
+
+ payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len));
+
+ if (netlen < (payload_len + HTC_HDR_LENGTH)) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "HTC Rx: insufficient length, got:%d expected =%u\n",
+ netlen, payload_len + HTC_HDR_LENGTH);
+ status = -EINVAL;
+ goto free_skb;
+ }
+
+ /* get flags to check for trailer */
+ hdr_info = htc_hdr->flags;
+ if (hdr_info & HTC_FLG_RX_TRAILER) {
+ /* extract the trailer length */
+ hdr_info = htc_hdr->ctrl[0];
+ if ((hdr_info < sizeof(struct htc_record_hdr)) ||
+ (hdr_info > payload_len)) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "invalid header: payloadlen should be %d, CB[0]: %d\n",
+ payload_len, hdr_info);
+ status = -EINVAL;
+ goto free_skb;
+ }
+
+ trailerlen = hdr_info;
+ /* process trailer after hdr/apps payload */
+ trailer = (u8 *) htc_hdr + HTC_HDR_LENGTH +
+ payload_len - hdr_info;
+ status = htc_process_trailer(target, trailer, hdr_info,
+ htc_hdr->eid);
+ if (status != 0)
+ goto free_skb;
+ }
+
+ if (((int) payload_len - (int) trailerlen) <= 0) {
+ /* zero length packet with trailer, just drop these */
+ goto free_skb;
+ }
+
+ if (htc_hdr->eid == ENDPOINT_0) {
+ /* handle HTC control message */
+ if (target->htc_flags & HTC_OP_STATE_SETUP_COMPLETE) {
+ /*
+ * fatal: target should not send unsolicited
+ * messageson the endpoint 0
+ */
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "HTC ignores Rx Ctrl after setup complete\n");
+ status = -EINVAL;
+ goto free_skb;
+ }
+
+ /* remove HTC header */
+ skb_pull(skb, HTC_HDR_LENGTH);
+
+ netdata = skb->data;
+ netlen = skb->len;
+
+ spin_lock_bh(&target->rx_lock);
+
+ target->pipe.ctrl_response_valid = true;
+ target->pipe.ctrl_response_len = min_t(int, netlen,
+ HTC_MAX_CTRL_MSG_LEN);
+ memcpy(target->pipe.ctrl_response_buf, netdata,
+ target->pipe.ctrl_response_len);
+
+ spin_unlock_bh(&target->rx_lock);
+
+ dev_kfree_skb(skb);
+ skb = NULL;
+
+ goto free_skb;
+ }
+
+ /*
+ * TODO: the message based HIF architecture allocates net bufs
+ * for recv packets since it bridges that HIF to upper layers,
+ * which expects HTC packets, we form the packets here
+ */
+ packet = alloc_htc_packet_container(target);
+ if (packet == NULL) {
+ status = -ENOMEM;
+ goto free_skb;
+ }
+
+ packet->status = 0;
+ packet->endpoint = htc_hdr->eid;
+ packet->pkt_cntxt = skb;
+
+ /* TODO: for backwards compatibility */
+ packet->buf = skb_push(skb, 0) + HTC_HDR_LENGTH;
+ packet->act_len = netlen - HTC_HDR_LENGTH - trailerlen;
+
+ /*
+ * TODO: this is a hack because the driver layer will set the
+ * actual len of the skb again which will just double the len
+ */
+ skb_trim(skb, 0);
+
+ recv_packet_completion(target, ep, packet);
+
+ /* recover the packet container */
+ free_htc_packet_container(target, packet);
+ skb = NULL;
+
+free_skb:
+ dev_kfree_skb(skb);
+
+ return status;
+}
+
+static void htc_flush_rx_queue(struct htc_target *target,
+ struct htc_endpoint *ep)
+{
+ struct list_head container;
+ struct htc_packet *packet;
+
+ spin_lock_bh(&target->rx_lock);
+
+ while (1) {
+ if (list_empty(&ep->rx_bufq))
+ break;
+
+ packet = list_first_entry(&ep->rx_bufq,
+ struct htc_packet, list);
+ list_del(&packet->list);
+
+ spin_unlock_bh(&target->rx_lock);
+ packet->status = -ECANCELED;
+ packet->act_len = 0;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "Flushing RX packet:0x%p, length:%d, ep:%d\n",
+ packet, packet->buf_len,
+ packet->endpoint);
+
+ INIT_LIST_HEAD(&container);
+ list_add_tail(&packet->list, &container);
+
+ /* give the packet back */
+ do_recv_completion(ep, &container);
+ spin_lock_bh(&target->rx_lock);
+ }
+
+ spin_unlock_bh(&target->rx_lock);
+}
+
+/* polling routine to wait for a control packet to be received */
+static int htc_wait_recv_ctrl_message(struct htc_target *target)
+{
+ int count = HTC_TARGET_RESPONSE_POLL_COUNT;
+
+ while (count > 0) {
+ spin_lock_bh(&target->rx_lock);
+
+ if (target->pipe.ctrl_response_valid) {
+ target->pipe.ctrl_response_valid = false;
+ spin_unlock_bh(&target->rx_lock);
+ break;
+ }
+
+ spin_unlock_bh(&target->rx_lock);
+
+ count--;
+
+ msleep_interruptible(HTC_TARGET_RESPONSE_POLL_WAIT);
+ }
+
+ if (count <= 0) {
+ ath6kl_warn("htc pipe control receive timeout!\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void htc_rxctrl_complete(struct htc_target *context,
+ struct htc_packet *packet)
+{
+ /* TODO, can't really receive HTC control messages yet.... */
+ ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function\n", __func__);
+}
+
+/* htc pipe initialization */
+static void reset_endpoint_states(struct htc_target *target)
+{
+ struct htc_endpoint *ep;
+ int i;
+
+ for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+ ep = &target->endpoint[i];
+ ep->svc_id = 0;
+ ep->len_max = 0;
+ ep->max_txq_depth = 0;
+ ep->eid = i;
+ INIT_LIST_HEAD(&ep->txq);
+ INIT_LIST_HEAD(&ep->pipe.tx_lookup_queue);
+ INIT_LIST_HEAD(&ep->rx_bufq);
+ ep->target = target;
+ ep->pipe.tx_credit_flow_enabled = true;
+ }
+}
+
+/* start HTC, this is called after all services are connected */
+static int htc_config_target_hif_pipe(struct htc_target *target)
+{
+ return 0;
+}
+
+/* htc service functions */
+static u8 htc_get_credit_alloc(struct htc_target *target, u16 service_id)
+{
+ u8 allocation = 0;
+ int i;
+
+ for (i = 0; i < ENDPOINT_MAX; i++) {
+ if (target->pipe.txcredit_alloc[i].service_id == service_id)
+ allocation =
+ target->pipe.txcredit_alloc[i].credit_alloc;
+ }
+
+ if (allocation == 0) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "HTC Service TX : 0x%2.2X : allocation is zero!\n",
+ service_id);
+ }
+
+ return allocation;
+}
+
+static int ath6kl_htc_pipe_conn_service(struct htc_target *target,
+ struct htc_service_connect_req *conn_req,
+ struct htc_service_connect_resp *conn_resp)
+{
+ struct ath6kl *ar = target->dev->ar;
+ struct htc_packet *packet = NULL;
+ struct htc_conn_service_resp *resp_msg;
+ struct htc_conn_service_msg *conn_msg;
+ enum htc_endpoint_id assigned_epid = ENDPOINT_MAX;
+ bool disable_credit_flowctrl = false;
+ unsigned int max_msg_size = 0;
+ struct htc_endpoint *ep;
+ int length, status = 0;
+ struct sk_buff *skb;
+ u8 tx_alloc;
+ u16 flags;
+
+ if (conn_req->svc_id == 0) {
+ WARN_ON_ONCE(1);
+ status = -EINVAL;
+ goto free_packet;
+ }
+
+ if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) {
+ /* special case for pseudo control service */
+ assigned_epid = ENDPOINT_0;
+ max_msg_size = HTC_MAX_CTRL_MSG_LEN;
+ tx_alloc = 0;
+
+ } else {
+ tx_alloc = htc_get_credit_alloc(target, conn_req->svc_id);
+ if (tx_alloc == 0) {
+ status = -ENOMEM;
+ goto free_packet;
+ }
+
+ /* allocate a packet to send to the target */
+ packet = htc_alloc_txctrl_packet(target);
+
+ if (packet == NULL) {
+ WARN_ON_ONCE(1);
+ status = -ENOMEM;
+ goto free_packet;
+ }
+
+ skb = packet->skb;
+ length = sizeof(struct htc_conn_service_msg);
+
+ /* assemble connect service message */
+ conn_msg = (struct htc_conn_service_msg *) skb_put(skb,
+ length);
+ if (conn_msg == NULL) {
+ WARN_ON_ONCE(1);
+ status = -EINVAL;
+ goto free_packet;
+ }
+
+ memset(conn_msg, 0,
+ sizeof(struct htc_conn_service_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 &
+ ~HTC_CONN_FLGS_SET_RECV_ALLOC_MASK);
+
+ /* tell target desired recv alloc for this ep */
+ flags = tx_alloc << HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT;
+ conn_msg->conn_flags |= cpu_to_le16(flags);
+
+ if (conn_req->conn_flags &
+ HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL) {
+ disable_credit_flowctrl = true;
+ }
+
+ set_htc_pkt_info(packet, NULL, (u8 *) conn_msg,
+ length,
+ ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
+
+ status = ath6kl_htc_pipe_tx(target, packet);
+
+ /* we don't own it anymore */
+ packet = NULL;
+ if (status != 0)
+ goto free_packet;
+
+ /* wait for response */
+ status = htc_wait_recv_ctrl_message(target);
+ if (status != 0)
+ goto free_packet;
+
+ /* we controlled the buffer creation so it has to be
+ * properly aligned
+ */
+ resp_msg = (struct htc_conn_service_resp *)
+ target->pipe.ctrl_response_buf;
+
+ if (resp_msg->msg_id != cpu_to_le16(HTC_MSG_CONN_SVC_RESP_ID) ||
+ (target->pipe.ctrl_response_len < sizeof(*resp_msg))) {
+ /* this message is not valid */
+ WARN_ON_ONCE(1);
+ status = -EINVAL;
+ goto free_packet;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "%s: service 0x%X conn resp: status: %d ep: %d\n",
+ __func__, resp_msg->svc_id, resp_msg->status,
+ resp_msg->eid);
+
+ conn_resp->resp_code = resp_msg->status;
+ /* check response status */
+ if (resp_msg->status != HTC_SERVICE_SUCCESS) {
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "Target failed service 0x%X connect request (status:%d)\n",
+ resp_msg->svc_id, resp_msg->status);
+ status = -EINVAL;
+ goto free_packet;
+ }
+
+ assigned_epid = (enum htc_endpoint_id) resp_msg->eid;
+ max_msg_size = le16_to_cpu(resp_msg->max_msg_sz);
+ }
+
+ /* the rest are parameter checks so set the error status */
+ status = -EINVAL;
+
+ if (assigned_epid >= ENDPOINT_MAX) {
+ WARN_ON_ONCE(1);
+ goto free_packet;
+ }
+
+ if (max_msg_size == 0) {
+ WARN_ON_ONCE(1);
+ goto free_packet;
+ }
+
+ ep = &target->endpoint[assigned_epid];
+ ep->eid = assigned_epid;
+ if (ep->svc_id != 0) {
+ /* endpoint already in use! */
+ WARN_ON_ONCE(1);
+ goto free_packet;
+ }
+
+ /* return assigned endpoint to caller */
+ conn_resp->endpoint = assigned_epid;
+ conn_resp->len_max = max_msg_size;
+
+ /* setup the endpoint */
+ ep->svc_id = conn_req->svc_id; /* this marks ep in use */
+ ep->max_txq_depth = conn_req->max_txq_depth;
+ ep->len_max = max_msg_size;
+ ep->cred_dist.credits = tx_alloc;
+ ep->cred_dist.cred_sz = target->tgt_cred_sz;
+ ep->cred_dist.cred_per_msg = max_msg_size / target->tgt_cred_sz;
+ if (max_msg_size % target->tgt_cred_sz)
+ ep->cred_dist.cred_per_msg++;
+
+ /* copy all the callbacks */
+ ep->ep_cb = conn_req->ep_cb;
+
+ /* initialize tx_drop_packet_threshold */
+ ep->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM;
+
+ status = ath6kl_hif_pipe_map_service(ar, ep->svc_id,
+ &ep->pipe.pipeid_ul,
+ &ep->pipe.pipeid_dl);
+ if (status != 0)
+ goto free_packet;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "SVC Ready: 0x%4.4X: ULpipe:%d DLpipe:%d id:%d\n",
+ ep->svc_id, ep->pipe.pipeid_ul,
+ ep->pipe.pipeid_dl, ep->eid);
+
+ if (disable_credit_flowctrl && ep->pipe.tx_credit_flow_enabled) {
+ ep->pipe.tx_credit_flow_enabled = false;
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "SVC: 0x%4.4X ep:%d TX flow control off\n",
+ ep->svc_id, assigned_epid);
+ }
+
+free_packet:
+ if (packet != NULL)
+ htc_free_txctrl_packet(target, packet);
+ return status;
+}
+
+/* htc export functions */
+static void *ath6kl_htc_pipe_create(struct ath6kl *ar)
+{
+ int status = 0;
+ struct htc_endpoint *ep = NULL;
+ struct htc_target *target = NULL;
+ struct htc_packet *packet;
+ int i;
+
+ target = kzalloc(sizeof(struct htc_target), GFP_KERNEL);
+ if (target == NULL) {
+ ath6kl_err("htc create unable to allocate memory\n");
+ status = -ENOMEM;
+ goto fail_htc_create;
+ }
+
+ spin_lock_init(&target->htc_lock);
+ spin_lock_init(&target->rx_lock);
+ spin_lock_init(&target->tx_lock);
+
+ reset_endpoint_states(target);
+
+ for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) {
+ packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL);
+
+ if (packet != NULL)
+ free_htc_packet_container(target, packet);
+ }
+
+ target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL);
+ if (!target->dev) {
+ ath6kl_err("unable to allocate memory\n");
+ status = -ENOMEM;
+ goto fail_htc_create;
+ }
+ target->dev->ar = ar;
+ target->dev->htc_cnxt = target;
+
+ /* Get HIF default pipe for HTC message exchange */
+ ep = &target->endpoint[ENDPOINT_0];
+
+ ath6kl_hif_pipe_get_default(ar, &ep->pipe.pipeid_ul,
+ &ep->pipe.pipeid_dl);
+
+ return target;
+
+fail_htc_create:
+ if (status != 0) {
+ if (target != NULL)
+ ath6kl_htc_pipe_cleanup(target);
+
+ target = NULL;
+ }
+ return target;
+}
+
+/* cleanup the HTC instance */
+static void ath6kl_htc_pipe_cleanup(struct htc_target *target)
+{
+ struct htc_packet *packet;
+
+ while (true) {
+ packet = alloc_htc_packet_container(target);
+ if (packet == NULL)
+ break;
+ kfree(packet);
+ }
+
+ kfree(target->dev);
+
+ /* kfree our instance */
+ kfree(target);
+}
+
+static int ath6kl_htc_pipe_start(struct htc_target *target)
+{
+ struct sk_buff *skb;
+ struct htc_setup_comp_ext_msg *setup;
+ struct htc_packet *packet;
+
+ htc_config_target_hif_pipe(target);
+
+ /* allocate a buffer to send */
+ packet = htc_alloc_txctrl_packet(target);
+ if (packet == NULL) {
+ WARN_ON_ONCE(1);
+ return -ENOMEM;
+ }
+
+ skb = packet->skb;
+
+ /* assemble setup complete message */
+ setup = (struct htc_setup_comp_ext_msg *) skb_put(skb,
+ sizeof(*setup));
+ memset(setup, 0, sizeof(struct htc_setup_comp_ext_msg));
+ setup->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID);
+
+ ath6kl_dbg(ATH6KL_DBG_HTC, "HTC using TX credit flow control\n");
+
+ set_htc_pkt_info(packet, NULL, (u8 *) setup,
+ sizeof(struct htc_setup_comp_ext_msg),
+ ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
+
+ target->htc_flags |= HTC_OP_STATE_SETUP_COMPLETE;
+
+ return ath6kl_htc_pipe_tx(target, packet);
+}
+
+static void ath6kl_htc_pipe_stop(struct htc_target *target)
+{
+ int i;
+ struct htc_endpoint *ep;
+
+ /* cleanup endpoints */
+ for (i = 0; i < ENDPOINT_MAX; i++) {
+ ep = &target->endpoint[i];
+ htc_flush_rx_queue(target, ep);
+ htc_flush_tx_endpoint(target, ep, HTC_TX_PACKET_TAG_ALL);
+ }
+
+ reset_endpoint_states(target);
+ target->htc_flags &= ~HTC_OP_STATE_SETUP_COMPLETE;
+}
+
+static int ath6kl_htc_pipe_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 int ath6kl_htc_pipe_tx(struct htc_target *target,
+ struct htc_packet *packet)
+{
+ struct list_head queue;
+
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "%s: endPointId: %d, buffer: 0x%p, length: %d\n",
+ __func__, packet->endpoint, packet->buf,
+ packet->act_len);
+
+ INIT_LIST_HEAD(&queue);
+ list_add_tail(&packet->list, &queue);
+
+ return htc_send_packets_multiple(target, &queue);
+}
+
+static int ath6kl_htc_pipe_wait_target(struct htc_target *target)
+{
+ struct htc_ready_ext_msg *ready_msg;
+ struct htc_service_connect_req connect;
+ struct htc_service_connect_resp resp;
+ int status = 0;
+
+ status = htc_wait_recv_ctrl_message(target);
+
+ if (status != 0)
+ return status;
+
+ if (target->pipe.ctrl_response_len < sizeof(*ready_msg)) {
+ ath6kl_warn("invalid htc pipe ready msg len: %d\n",
+ target->pipe.ctrl_response_len);
+ return -ECOMM;
+ }
+
+ ready_msg = (struct htc_ready_ext_msg *) target->pipe.ctrl_response_buf;
+
+ if (ready_msg->ver2_0_info.msg_id != cpu_to_le16(HTC_MSG_READY_ID)) {
+ ath6kl_warn("invalid htc pipe ready msg: 0x%x\n",
+ ready_msg->ver2_0_info.msg_id);
+ return -ECOMM;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_HTC,
+ "Target Ready! : transmit resources : %d size:%d\n",
+ ready_msg->ver2_0_info.cred_cnt,
+ ready_msg->ver2_0_info.cred_sz);
+
+ target->tgt_creds = le16_to_cpu(ready_msg->ver2_0_info.cred_cnt);
+ target->tgt_cred_sz = le16_to_cpu(ready_msg->ver2_0_info.cred_sz);
+
+ if ((target->tgt_creds == 0) || (target->tgt_cred_sz == 0))
+ return -ECOMM;
+
+ htc_setup_target_buffer_assignments(target);
+
+ /* setup our pseudo HTC control endpoint connection */
+ memset(&connect, 0, sizeof(connect));
+ memset(&resp, 0, sizeof(resp));
+ connect.ep_cb.tx_complete = htc_txctrl_complete;
+ connect.ep_cb.rx = htc_rxctrl_complete;
+ connect.max_txq_depth = NUM_CONTROL_TX_BUFFERS;
+ connect.svc_id = HTC_CTRL_RSVD_SVC;
+
+ /* connect fake service */
+ status = ath6kl_htc_pipe_conn_service(target, &connect, &resp);
+
+ return status;
+}
+
+static void ath6kl_htc_pipe_flush_txep(struct htc_target *target,
+ enum htc_endpoint_id endpoint, u16 tag)
+{
+ struct htc_endpoint *ep = &target->endpoint[endpoint];
+
+ if (ep->svc_id == 0) {
+ WARN_ON_ONCE(1);
+ /* not in use.. */
+ return;
+ }
+
+ htc_flush_tx_endpoint(target, ep, tag);
+}
+
+static int ath6kl_htc_pipe_add_rxbuf_multiple(struct htc_target *target,
+ struct list_head *pkt_queue)
+{
+ struct htc_packet *packet, *tmp_pkt, *first;
+ struct htc_endpoint *ep;
+ int status = 0;
+
+ if (list_empty(pkt_queue))
+ return -EINVAL;
+
+ first = list_first_entry(pkt_queue, struct htc_packet, list);
+
+ if (first->endpoint >= ENDPOINT_MAX) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_HTC, "%s: epid: %d, cnt:%d, len: %d\n",
+ __func__, first->endpoint, get_queue_depth(pkt_queue),
+ first->buf_len);
+
+ ep = &target->endpoint[first->endpoint];
+
+ spin_lock_bh(&target->rx_lock);
+
+ /* store receive packets */
+ list_splice_tail_init(pkt_queue, &ep->rx_bufq);
+
+ spin_unlock_bh(&target->rx_lock);
+
+ if (status != 0) {
+ /* walk through queue and mark each one canceled */
+ list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) {
+ packet->status = -ECANCELED;
+ }
+
+ do_recv_completion(ep, pkt_queue);
+ }
+
+ return status;
+}
+
+static void ath6kl_htc_pipe_activity_changed(struct htc_target *target,
+ enum htc_endpoint_id ep,
+ bool active)
+{
+ /* TODO */
+}
+
+static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target)
+{
+ /* TODO */
+}
+
+static int ath6kl_htc_pipe_credit_setup(struct htc_target *target,
+ struct ath6kl_htc_credit_info *info)
+{
+ return 0;
+}
+
+static const struct ath6kl_htc_ops ath6kl_htc_pipe_ops = {
+ .create = ath6kl_htc_pipe_create,
+ .wait_target = ath6kl_htc_pipe_wait_target,
+ .start = ath6kl_htc_pipe_start,
+ .conn_service = ath6kl_htc_pipe_conn_service,
+ .tx = ath6kl_htc_pipe_tx,
+ .stop = ath6kl_htc_pipe_stop,
+ .cleanup = ath6kl_htc_pipe_cleanup,
+ .flush_txep = ath6kl_htc_pipe_flush_txep,
+ .flush_rx_buf = ath6kl_htc_pipe_flush_rx_buf,
+ .activity_changed = ath6kl_htc_pipe_activity_changed,
+ .get_rxbuf_num = ath6kl_htc_pipe_get_rxbuf_num,
+ .add_rxbuf_multiple = ath6kl_htc_pipe_add_rxbuf_multiple,
+ .credit_setup = ath6kl_htc_pipe_credit_setup,
+ .tx_complete = ath6kl_htc_pipe_tx_complete,
+ .rx_complete = ath6kl_htc_pipe_rx_complete,
+};
+
+void ath6kl_htc_pipe_attach(struct ath6kl *ar)
+{
+ ar->htc_ops = &ath6kl_htc_pipe_ops;
+}
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index c1d2366704b..d5ef211f261 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -15,20 +16,157 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/export.h>
#include <linux/of.h>
#include <linux/mmc/sdio_func.h>
+#include <linux/vmalloc.h>
+
#include "core.h"
#include "cfg80211.h"
#include "target.h"
#include "debug.h"
#include "hif-ops.h"
-
-unsigned int debug_mask;
-static unsigned int testmode;
-
-module_param(debug_mask, uint, 0644);
-module_param(testmode, uint, 0644);
+#include "htc-ops.h"
+
+static const struct ath6kl_hw hw_list[] = {
+ {
+ .id = AR6003_HW_2_0_VERSION,
+ .name = "ar6003 hw 2.0",
+ .dataset_patch_addr = 0x57e884,
+ .app_load_addr = 0x543180,
+ .board_ext_data_addr = 0x57e500,
+ .reserved_ram_size = 6912,
+ .refclk_hz = 26000000,
+ .uarttx_pin = 8,
+ .flags = ATH6KL_HW_SDIO_CRC_ERROR_WAR,
+
+ /* hw2.0 needs override address hardcoded */
+ .app_start_override_addr = 0x944C00,
+
+ .fw = {
+ .dir = AR6003_HW_2_0_FW_DIR,
+ .otp = AR6003_HW_2_0_OTP_FILE,
+ .fw = AR6003_HW_2_0_FIRMWARE_FILE,
+ .tcmd = AR6003_HW_2_0_TCMD_FIRMWARE_FILE,
+ .patch = AR6003_HW_2_0_PATCH_FILE,
+ },
+
+ .fw_board = AR6003_HW_2_0_BOARD_DATA_FILE,
+ .fw_default_board = AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE,
+ },
+ {
+ .id = AR6003_HW_2_1_1_VERSION,
+ .name = "ar6003 hw 2.1.1",
+ .dataset_patch_addr = 0x57ff74,
+ .app_load_addr = 0x1234,
+ .board_ext_data_addr = 0x542330,
+ .reserved_ram_size = 512,
+ .refclk_hz = 26000000,
+ .uarttx_pin = 8,
+ .testscript_addr = 0x57ef74,
+ .flags = ATH6KL_HW_SDIO_CRC_ERROR_WAR,
+
+ .fw = {
+ .dir = AR6003_HW_2_1_1_FW_DIR,
+ .otp = AR6003_HW_2_1_1_OTP_FILE,
+ .fw = AR6003_HW_2_1_1_FIRMWARE_FILE,
+ .tcmd = AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE,
+ .patch = AR6003_HW_2_1_1_PATCH_FILE,
+ .utf = AR6003_HW_2_1_1_UTF_FIRMWARE_FILE,
+ .testscript = AR6003_HW_2_1_1_TESTSCRIPT_FILE,
+ },
+
+ .fw_board = AR6003_HW_2_1_1_BOARD_DATA_FILE,
+ .fw_default_board = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE,
+ },
+ {
+ .id = AR6004_HW_1_0_VERSION,
+ .name = "ar6004 hw 1.0",
+ .dataset_patch_addr = 0x57e884,
+ .app_load_addr = 0x1234,
+ .board_ext_data_addr = 0x437000,
+ .reserved_ram_size = 19456,
+ .board_addr = 0x433900,
+ .refclk_hz = 26000000,
+ .uarttx_pin = 11,
+ .flags = ATH6KL_HW_64BIT_RATES |
+ ATH6KL_HW_AP_INACTIVITY_MINS,
+
+ .fw = {
+ .dir = AR6004_HW_1_0_FW_DIR,
+ .fw = AR6004_HW_1_0_FIRMWARE_FILE,
+ },
+
+ .fw_board = AR6004_HW_1_0_BOARD_DATA_FILE,
+ .fw_default_board = AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE,
+ },
+ {
+ .id = AR6004_HW_1_1_VERSION,
+ .name = "ar6004 hw 1.1",
+ .dataset_patch_addr = 0x57e884,
+ .app_load_addr = 0x1234,
+ .board_ext_data_addr = 0x437000,
+ .reserved_ram_size = 11264,
+ .board_addr = 0x43d400,
+ .refclk_hz = 40000000,
+ .uarttx_pin = 11,
+ .flags = ATH6KL_HW_64BIT_RATES |
+ ATH6KL_HW_AP_INACTIVITY_MINS,
+ .fw = {
+ .dir = AR6004_HW_1_1_FW_DIR,
+ .fw = AR6004_HW_1_1_FIRMWARE_FILE,
+ },
+
+ .fw_board = AR6004_HW_1_1_BOARD_DATA_FILE,
+ .fw_default_board = AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE,
+ },
+ {
+ .id = AR6004_HW_1_2_VERSION,
+ .name = "ar6004 hw 1.2",
+ .dataset_patch_addr = 0x436ecc,
+ .app_load_addr = 0x1234,
+ .board_ext_data_addr = 0x437000,
+ .reserved_ram_size = 9216,
+ .board_addr = 0x435c00,
+ .refclk_hz = 40000000,
+ .uarttx_pin = 11,
+ .flags = ATH6KL_HW_64BIT_RATES |
+ ATH6KL_HW_AP_INACTIVITY_MINS,
+
+ .fw = {
+ .dir = AR6004_HW_1_2_FW_DIR,
+ .fw = AR6004_HW_1_2_FIRMWARE_FILE,
+ },
+ .fw_board = AR6004_HW_1_2_BOARD_DATA_FILE,
+ .fw_default_board = AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE,
+ },
+ {
+ .id = AR6004_HW_1_3_VERSION,
+ .name = "ar6004 hw 1.3",
+ .dataset_patch_addr = 0x437860,
+ .app_load_addr = 0x1234,
+ .board_ext_data_addr = 0x437000,
+ .reserved_ram_size = 7168,
+ .board_addr = 0x436400,
+ .refclk_hz = 40000000,
+ .uarttx_pin = 11,
+ .flags = ATH6KL_HW_64BIT_RATES |
+ ATH6KL_HW_AP_INACTIVITY_MINS |
+ ATH6KL_HW_MAP_LP_ENDPOINT,
+
+ .fw = {
+ .dir = AR6004_HW_1_3_FW_DIR,
+ .fw = AR6004_HW_1_3_FIRMWARE_FILE,
+ },
+
+ .fw_board = AR6004_HW_1_3_BOARD_DATA_FILE,
+ .fw_default_board = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE,
+ },
+};
/*
* Include definitions here that can be used to tune the WLAN module
@@ -55,7 +193,6 @@ module_param(testmode, uint, 0644);
*/
#define WLAN_CONFIG_DISCONNECT_TIMEOUT 10
-#define CONFIG_AR600x_DEBUG_UART_TX_PIN 8
#define ATH6KL_DATA_OFFSET 64
struct sk_buff *ath6kl_buf_alloc(int size)
@@ -64,8 +201,8 @@ struct sk_buff *ath6kl_buf_alloc(int size)
u16 reserved;
/* Add chacheline space at front and back of buffer */
- reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET +
- sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES;
+ reserved = roundup((2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET +
+ sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES, 4);
skb = dev_alloc_skb(size + reserved);
if (skb)
@@ -73,37 +210,21 @@ struct sk_buff *ath6kl_buf_alloc(int size)
return skb;
}
-void ath6kl_init_profile_info(struct ath6kl *ar)
+void ath6kl_init_profile_info(struct ath6kl_vif *vif)
{
- 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_crypto_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;
- }
+ vif->ssid_len = 0;
+ memset(vif->ssid, 0, sizeof(vif->ssid));
+
+ vif->dot11_auth_mode = OPEN_AUTH;
+ vif->auth_mode = NONE_AUTH;
+ vif->prwise_crypto = NONE_CRYPT;
+ vif->prwise_crypto_len = 0;
+ vif->grp_crypto = NONE_CRYPT;
+ vif->grp_crypto_len = 0;
+ memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list));
+ memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
+ memset(vif->bssid, 0, sizeof(vif->bssid));
+ vif->bss_ch = 0;
}
static int ath6kl_set_host_app_area(struct ath6kl *ar)
@@ -120,7 +241,7 @@ static int ath6kl_set_host_app_area(struct ath6kl *ar)
return -EIO;
address = TARG_VTOP(ar->target_type, data);
- host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION;
+ host_app_area.wmi_protocol_ver = cpu_to_le32(WMI_PROTOCOL_VERSION);
if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area,
sizeof(struct host_app_area)))
return -EIO;
@@ -186,6 +307,7 @@ static int ath6kl_init_service_ep(struct ath6kl *ar)
memset(&connect, 0, sizeof(connect));
/* these fields are the same for all service endpoints */
+ connect.ep_cb.tx_comp_multi = ath6kl_tx_complete;
connect.ep_cb.rx = ath6kl_rx;
connect.ep_cb.rx_refill = ath6kl_rx_refill;
connect.ep_cb.tx_full = ath6kl_tx_queue_full;
@@ -239,7 +361,7 @@ static int ath6kl_init_service_ep(struct ath6kl *ar)
if (ath6kl_connectservice(ar, &connect, "WMI DATA BK"))
return -EIO;
- /* connect to Video service, map this to to HI PRI */
+ /* connect to Video service, map this to HI PRI */
connect.svc_id = WMI_DATA_VI_SVC;
if (ath6kl_connectservice(ar, &connect, "WMI DATA VI"))
return -EIO;
@@ -258,40 +380,12 @@ static int ath6kl_init_service_ep(struct ath6kl *ar)
return 0;
}
-static void ath6kl_init_control_info(struct ath6kl *ar)
+void ath6kl_init_control_info(struct ath6kl_vif *vif)
{
- 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;
- ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
-
- 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);
+ ath6kl_init_profile_info(vif);
+ vif->def_txkey_index = 0;
+ memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list));
+ vif->ch_hint = 0;
}
/*
@@ -310,11 +404,7 @@ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val,
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);
+ status = ath6kl_bmi_write_hi32(ar, hi_mbox_io_block_sz, blk_size);
if (status) {
ath6kl_err("bmi_write_memory for IO block size failed\n");
goto out;
@@ -326,11 +416,8 @@ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val,
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);
+ status = ath6kl_bmi_write_hi32(ar, hi_mbox_isr_yield_limit,
+ mbox_isr_yield_val);
if (status) {
ath6kl_err("bmi_write_memory for yield limit failed\n");
goto out;
@@ -341,64 +428,8 @@ 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(ar->target_type, address);
-
- /* read RAM location through diagnostic window */
- status = ath6kl_diag_read32(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(ar->target_type, regdump_loc);
-
- /* fetch register dump data */
- status = ath6kl_diag_read(ar, regdump_loc, (u8 *)&regdump_val[0],
- REG_DUMP_COUNT_AR6003 * (sizeof(u32)));
-
- 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)
+static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx)
{
- 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;
int ret;
/*
@@ -406,84 +437,135 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar)
* 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;
+ ret = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx,
+ ar->rx_meta_ver, 0, 0);
+ if (ret) {
+ ath6kl_err("unable to set the rx frame format: %d\n", ret);
+ return ret;
}
- 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_PS_FAIL_EVT_IN_SCAN) {
+ ret = ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1,
+ IGNORE_PS_FAIL_DURING_SCAN);
+ if (ret) {
+ ath6kl_err("unable to set power save fail event policy: %d\n",
+ ret);
+ return ret;
}
+ }
- 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 (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) {
+ ret = ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0,
+ WMI_FOLLOW_BARKER_IN_ERP);
+ if (ret) {
+ ath6kl_err("unable to set barker preamble policy: %d\n",
+ ret);
+ return ret;
}
+ }
- if (ath6kl_wmi_set_keepalive_cmd(ar->wmi,
- WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) {
- ath6kl_err("unable to set keep alive interval\n");
- status = -EIO;
+ ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx,
+ WLAN_CONFIG_KEEP_ALIVE_INTERVAL);
+ if (ret) {
+ ath6kl_err("unable to set keep alive interval: %d\n", ret);
+ return ret;
}
- if (ath6kl_wmi_disctimeout_cmd(ar->wmi,
- WLAN_CONFIG_DISCONNECT_TIMEOUT)) {
- ath6kl_err("unable to set disconnect timeout\n");
- status = -EIO;
+ ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, idx,
+ WLAN_CONFIG_DISCONNECT_TIMEOUT);
+ if (ret) {
+ ath6kl_err("unable to set disconnect timeout: %d\n", ret);
+ return ret;
}
- 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;
+ if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) {
+ ret = ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED);
+ if (ret) {
+ ath6kl_err("unable to set txop bursting: %d\n", ret);
+ return ret;
}
+ }
- if (ar->p2p) {
- ret = ath6kl_wmi_info_req_cmd(ar->wmi,
+ if (ar->p2p && (ar->vif_max == 1 || idx)) {
+ ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx,
P2P_FLAG_CAPABILITIES_REQ |
P2P_FLAG_MACADDR_REQ |
P2P_FLAG_HMODEL_REQ);
if (ret) {
- ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P "
- "capabilities (%d) - assuming P2P not "
- "supported\n", ret);
- ar->p2p = 0;
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "failed to request P2P capabilities (%d) - assuming P2P not supported\n",
+ ret);
+ ar->p2p = false;
}
}
- if (ar->p2p) {
+ if (ar->p2p && (ar->vif_max == 1 || idx)) {
/* Enable Probe Request reporting for P2P */
- ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true);
+ ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true);
if (ret) {
- ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe "
- "Request reporting (%d)\n", ret);
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "failed to enable Probe Request reporting (%d)\n",
+ ret);
}
}
- return status;
+ return ret;
}
int ath6kl_configure_target(struct ath6kl *ar)
{
u32 param, ram_reserved_size;
- u8 fw_iftype;
+ u8 fw_iftype, fw_mode = 0, fw_submode = 0;
+ int i, status;
- fw_iftype = ath6kl_get_fw_iftype(ar);
- if (fw_iftype == 0xff)
- return -EINVAL;
+ param = !!(ar->conf_flags & ATH6KL_CONF_UART_DEBUG);
+ if (ath6kl_bmi_write_hi32(ar, hi_serial_enable, param)) {
+ ath6kl_err("bmi_write_memory for uart debug failed\n");
+ return -EIO;
+ }
+
+ /*
+ * Note: Even though the firmware interface type is
+ * chosen as BSS_STA for all three interfaces, can
+ * be configured to IBSS/AP as long as the fw submode
+ * remains normal mode (0 - AP, STA and IBSS). But
+ * due to an target assert in firmware only one interface is
+ * configured for now.
+ */
+ fw_iftype = HI_OPTION_FW_MODE_BSS_STA;
+
+ for (i = 0; i < ar->vif_max; i++)
+ fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS);
+
+ /*
+ * Submodes when fw does not support dynamic interface
+ * switching:
+ * vif[0] - AP/STA/IBSS
+ * vif[1] - "P2P dev"/"P2P GO"/"P2P Client"
+ * vif[2] - "P2P dev"/"P2P GO"/"P2P Client"
+ * Otherwise, All the interface are initialized to p2p dev.
+ */
+
+ if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+ ar->fw_capabilities)) {
+ for (i = 0; i < ar->vif_max; i++)
+ fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
+ (i * HI_OPTION_FW_SUBMODE_BITS);
+ } else {
+ for (i = 0; i < ar->max_norm_iface; i++)
+ fw_submode |= HI_OPTION_FW_SUBMODE_NONE <<
+ (i * HI_OPTION_FW_SUBMODE_BITS);
- /* 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) {
+ for (i = ar->max_norm_iface; i < ar->vif_max; i++)
+ fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
+ (i * HI_OPTION_FW_SUBMODE_BITS);
+
+ if (ar->p2p && ar->vif_max == 1)
+ fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV;
+ }
+
+ if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest,
+ HTC_PROTOCOL_VERSION) != 0) {
ath6kl_err("bmi_write_memory for htc version failed\n");
return -EIO;
}
@@ -491,28 +573,19 @@ int ath6kl_configure_target(struct ath6kl *ar)
/* 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) {
+ if (ath6kl_bmi_read_hi32(ar, hi_option_flag, &param) != 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);
- if (ar->p2p && fw_iftype == HI_OPTION_FW_MODE_BSS_STA) {
- param |= HI_OPTION_FW_SUBMODE_P2PDEV <<
- HI_OPTION_FW_SUBMODE_SHIFT;
- }
+ param |= (ar->vif_max << HI_OPTION_NUM_DEV_SHIFT);
+ param |= fw_mode << HI_OPTION_FW_MODE_SHIFT;
+ param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT;
+
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) {
+ if (ath6kl_bmi_write_hi32(ar, hi_option_flag, param) != 0) {
ath6kl_err("bmi_write_memory for setting fwmode failed\n");
return -EIO;
}
@@ -528,21 +601,20 @@ int ath6kl_configure_target(struct ath6kl *ar)
* but possible in theory.
*/
- param = ar->hw.board_ext_data_addr;
- ram_reserved_size = ar->hw.reserved_ram_size;
+ if (ar->target_type == TARGET_TYPE_AR6003) {
+ param = ar->hw.board_ext_data_addr;
+ ram_reserved_size = ar->hw.reserved_ram_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_hi32(ar, hi_board_ext_data, param) != 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;
+ if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz,
+ ram_reserved_size) != 0) {
+ ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n");
+ return -EIO;
+ }
}
/* set the block size for the target */
@@ -550,69 +622,16 @@ int ath6kl_configure_target(struct ath6kl *ar)
/* 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;
-
- if (ath6kl_debug_init(ar)) {
- ath6kl_err("Failed to initialize debugfs\n");
- ath6kl_cfg80211_deinit(ar);
- return NULL;
- }
-
- 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;
-
- 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;
-}
+ /* Configure GPIO AR600x UART */
+ status = ath6kl_bmi_write_hi32(ar, hi_dbg_uart_txpin,
+ ar->hw.uarttx_pin);
+ if (status)
+ return status;
-int ath6kl_unavail_ev(struct ath6kl *ar)
-{
- ath6kl_destroy(ar->net_dev, 1);
+ /* Configure target refclk_hz */
+ status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, ar->hw.refclk_hz);
+ if (status)
+ return status;
return 0;
}
@@ -640,21 +659,6 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename,
}
#ifdef CONFIG_OF
-static const char *get_target_ver_dir(const struct ath6kl *ar)
-{
- switch (ar->version.target_ver) {
- case AR6003_REV1_VERSION:
- return "ath6k/AR6003/hw1.0";
- case AR6003_REV2_VERSION:
- return "ath6k/AR6003/hw2.0";
- case AR6003_REV3_VERSION:
- return "ath6k/AR6003/hw2.1.1";
- }
- ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__,
- ar->version.target_ver);
- return NULL;
-}
-
/*
* Check the device tree for a board-id and use it to construct
* the pathname to the firmware file. Used (for now) to find a
@@ -677,7 +681,7 @@ static bool check_device_tree(struct ath6kl *ar)
continue;
}
snprintf(board_filename, sizeof(board_filename),
- "%s/bdata.%s.bin", get_target_ver_dir(ar), board_id);
+ "%s/bdata.%s.bin", ar->hw.fw.dir, board_id);
ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board,
&ar->fw_board_len);
@@ -705,17 +709,10 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
if (ar->fw_board != NULL)
return 0;
- switch (ar->version.target_ver) {
- case AR6003_REV2_VERSION:
- filename = AR6003_REV2_BOARD_DATA_FILE;
- break;
- case AR6004_REV1_VERSION:
- filename = AR6004_REV1_BOARD_DATA_FILE;
- break;
- default:
- filename = AR6003_REV3_BOARD_DATA_FILE;
- break;
- }
+ if (WARN_ON(ar->hw.fw_board == NULL))
+ return -EINVAL;
+
+ filename = ar->hw.fw_board;
ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
&ar->fw_board_len);
@@ -733,17 +730,7 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",
filename, ret);
- switch (ar->version.target_ver) {
- case AR6003_REV2_VERSION:
- filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE;
- break;
- case AR6004_REV1_VERSION:
- filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE;
- break;
- default:
- filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE;
- break;
- }
+ filename = ar->hw.fw_default_board;
ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
&ar->fw_board_len);
@@ -761,25 +748,21 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
static int ath6kl_fetch_otp_file(struct ath6kl *ar)
{
- const char *filename;
+ char filename[100];
int ret;
if (ar->fw_otp != NULL)
return 0;
- switch (ar->version.target_ver) {
- case AR6003_REV2_VERSION:
- filename = AR6003_REV2_OTP_FILE;
- break;
- case AR6004_REV1_VERSION:
- ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n");
+ if (ar->hw.fw.otp == NULL) {
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "no OTP file configured for this hw\n");
return 0;
- break;
- default:
- filename = AR6003_REV3_OTP_FILE;
- break;
}
+ snprintf(filename, sizeof(filename), "%s/%s",
+ ar->hw.fw.dir, ar->hw.fw.otp);
+
ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
&ar->fw_otp_len);
if (ret) {
@@ -791,49 +774,61 @@ static int ath6kl_fetch_otp_file(struct ath6kl *ar)
return 0;
}
-static int ath6kl_fetch_fw_file(struct ath6kl *ar)
+static int ath6kl_fetch_testmode_file(struct ath6kl *ar)
{
- const char *filename;
+ char filename[100];
int ret;
- if (ar->fw != NULL)
+ if (ar->testmode == 0)
return 0;
- if (testmode) {
- switch (ar->version.target_ver) {
- case AR6003_REV2_VERSION:
- filename = AR6003_REV2_TCMD_FIRMWARE_FILE;
- break;
- case AR6003_REV3_VERSION:
- filename = AR6003_REV3_TCMD_FIRMWARE_FILE;
- break;
- case AR6004_REV1_VERSION:
- ath6kl_warn("testmode not supported with ar6004\n");
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "testmode %d\n", ar->testmode);
+
+ if (ar->testmode == 2) {
+ if (ar->hw.fw.utf == NULL) {
+ ath6kl_warn("testmode 2 not supported\n");
return -EOPNOTSUPP;
- default:
- ath6kl_warn("unknown target version: 0x%x\n",
- ar->version.target_ver);
- return -EINVAL;
}
- set_bit(TESTMODE, &ar->flag);
+ snprintf(filename, sizeof(filename), "%s/%s",
+ ar->hw.fw.dir, ar->hw.fw.utf);
+ } else {
+ if (ar->hw.fw.tcmd == NULL) {
+ ath6kl_warn("testmode 1 not supported\n");
+ return -EOPNOTSUPP;
+ }
- goto get_fw;
+ snprintf(filename, sizeof(filename), "%s/%s",
+ ar->hw.fw.dir, ar->hw.fw.tcmd);
}
- switch (ar->version.target_ver) {
- case AR6003_REV2_VERSION:
- filename = AR6003_REV2_FIRMWARE_FILE;
- break;
- case AR6004_REV1_VERSION:
- filename = AR6004_REV1_FIRMWARE_FILE;
- break;
- default:
- filename = AR6003_REV3_FIRMWARE_FILE;
- break;
+ set_bit(TESTMODE, &ar->flag);
+
+ ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
+ if (ret) {
+ ath6kl_err("Failed to get testmode %d firmware file %s: %d\n",
+ ar->testmode, filename, ret);
+ return ret;
}
-get_fw:
+ return 0;
+}
+
+static int ath6kl_fetch_fw_file(struct ath6kl *ar)
+{
+ char filename[100];
+ int ret;
+
+ if (ar->fw != NULL)
+ return 0;
+
+ /* FIXME: remove WARN_ON() as we won't support FW API 1 for long */
+ if (WARN_ON(ar->hw.fw.fw == NULL))
+ return -EINVAL;
+
+ snprintf(filename, sizeof(filename), "%s/%s",
+ ar->hw.fw.dir, ar->hw.fw.fw);
+
ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
if (ret) {
ath6kl_err("Failed to get firmware file %s: %d\n",
@@ -846,30 +841,52 @@ get_fw:
static int ath6kl_fetch_patch_file(struct ath6kl *ar)
{
- const char *filename;
+ char filename[100];
int ret;
- switch (ar->version.target_ver) {
- case AR6003_REV2_VERSION:
- filename = AR6003_REV2_PATCH_FILE;
- break;
- case AR6004_REV1_VERSION:
- /* FIXME: implement for AR6004 */
+ if (ar->fw_patch != NULL)
return 0;
- break;
- default:
- filename = AR6003_REV3_PATCH_FILE;
- break;
+
+ if (ar->hw.fw.patch == NULL)
+ return 0;
+
+ snprintf(filename, sizeof(filename), "%s/%s",
+ ar->hw.fw.dir, ar->hw.fw.patch);
+
+ ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
+ &ar->fw_patch_len);
+ if (ret) {
+ ath6kl_err("Failed to get patch file %s: %d\n",
+ filename, ret);
+ return ret;
}
- 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;
- }
+ return 0;
+}
+
+static int ath6kl_fetch_testscript_file(struct ath6kl *ar)
+{
+ char filename[100];
+ int ret;
+
+ if (ar->testmode != 2)
+ return 0;
+
+ if (ar->fw_testscript != NULL)
+ return 0;
+
+ if (ar->hw.fw.testscript == NULL)
+ return 0;
+
+ snprintf(filename, sizeof(filename), "%s/%s",
+ ar->hw.fw.dir, ar->hw.fw.testscript);
+
+ ret = ath6kl_get_fw(ar, filename, &ar->fw_testscript,
+ &ar->fw_testscript_len);
+ if (ret) {
+ ath6kl_err("Failed to get testscript file %s: %d\n",
+ filename, ret);
+ return ret;
}
return 0;
@@ -891,32 +908,24 @@ static int ath6kl_fetch_fw_api1(struct ath6kl *ar)
if (ret)
return ret;
+ ret = ath6kl_fetch_testscript_file(ar);
+ if (ret)
+ return ret;
+
return 0;
}
-static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
+static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
{
size_t magic_len, len, ie_len;
const struct firmware *fw;
struct ath6kl_fw_ie *hdr;
- const char *filename;
+ char filename[100];
const u8 *data;
int ret, ie_id, i, index, bit;
__le32 *val;
- switch (ar->version.target_ver) {
- case AR6003_REV2_VERSION:
- filename = AR6003_REV2_FIRMWARE_2_FILE;
- break;
- case AR6003_REV3_VERSION:
- filename = AR6003_REV3_FIRMWARE_2_FILE;
- break;
- case AR6004_REV1_VERSION:
- filename = AR6004_REV1_FIRMWARE_2_FILE;
- break;
- default:
- return -EOPNOTSUPP;
- }
+ snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, name);
ret = request_firmware(&fw, filename, ar->dev);
if (ret)
@@ -958,9 +967,17 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
}
switch (ie_id) {
+ case ATH6KL_FW_IE_FW_VERSION:
+ strlcpy(ar->wiphy->fw_version, data,
+ sizeof(ar->wiphy->fw_version));
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "found fw version %s\n",
+ ar->wiphy->fw_version);
+ break;
case ATH6KL_FW_IE_OTP_IMAGE:
ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n",
- ie_len);
+ ie_len);
ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL);
@@ -973,20 +990,25 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
break;
case ATH6KL_FW_IE_FW_IMAGE:
ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n",
- ie_len);
+ ie_len);
- ar->fw = kmemdup(data, ie_len, GFP_KERNEL);
+ /* in testmode we already might have a fw file */
+ if (ar->fw != NULL)
+ break;
+
+ ar->fw = vmalloc(ie_len);
if (ar->fw == NULL) {
ret = -ENOMEM;
goto out;
}
+ memcpy(ar->fw, data, ie_len);
ar->fw_len = ie_len;
break;
case ATH6KL_FW_IE_PATCH_IMAGE:
ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n",
- ie_len);
+ ie_len);
ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL);
@@ -1011,9 +1033,12 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
ie_len);
for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) {
- index = ALIGN(i, 8) / 8;
+ index = i / 8;
bit = i % 8;
+ if (index == ie_len)
+ break;
+
if (data[index] & (1 << bit))
__set_bit(i, ar->fw_capabilities);
}
@@ -1030,9 +1055,34 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
ar->hw.dataset_patch_addr = le32_to_cpup(val);
ath6kl_dbg(ATH6KL_DBG_BOOT,
- "found patch address ie 0x%d\n",
+ "found patch address ie 0x%x\n",
ar->hw.dataset_patch_addr);
break;
+ case ATH6KL_FW_IE_BOARD_ADDR:
+ if (ie_len != sizeof(*val))
+ break;
+
+ val = (__le32 *) data;
+ ar->hw.board_addr = le32_to_cpup(val);
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "found board address ie 0x%x\n",
+ ar->hw.board_addr);
+ break;
+ case ATH6KL_FW_IE_VIF_MAX:
+ if (ie_len != sizeof(*val))
+ break;
+
+ val = (__le32 *) data;
+ ar->vif_max = min_t(unsigned int, le32_to_cpup(val),
+ ATH6KL_VIF_MAX);
+
+ if (ar->vif_max > 1 && !ar->p2p)
+ ar->max_norm_iface = 2;
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "found vif max ie %d\n", ar->vif_max);
+ break;
default:
ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n",
le32_to_cpup(&hdr->id));
@@ -1050,7 +1100,7 @@ out:
return ret;
}
-static int ath6kl_fetch_firmwares(struct ath6kl *ar)
+int ath6kl_init_fetch_firmwares(struct ath6kl *ar)
{
int ret;
@@ -1058,17 +1108,36 @@ static int ath6kl_fetch_firmwares(struct ath6kl *ar)
if (ret)
return ret;
- ret = ath6kl_fetch_fw_api2(ar);
+ ret = ath6kl_fetch_testmode_file(ar);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API4_FILE);
if (ret == 0) {
- ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n");
- return 0;
+ ar->fw_api = 4;
+ goto out;
+ }
+
+ ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API3_FILE);
+ if (ret == 0) {
+ ar->fw_api = 3;
+ goto out;
+ }
+
+ ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API2_FILE);
+ if (ret == 0) {
+ ar->fw_api = 2;
+ goto out;
}
ret = ath6kl_fetch_fw_api1(ar);
if (ret)
return ret;
- ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n");
+ ar->fw_api = 1;
+
+out:
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api %d\n", ar->fw_api);
return 0;
}
@@ -1087,26 +1156,19 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
* For AR6004, host determine Target RAM address for
* writing board data.
*/
- if (ar->target_type == TARGET_TYPE_AR6004) {
- board_address = AR6004_REV1_BOARD_DATA_ADDRESS;
- ath6kl_bmi_write(ar,
- ath6kl_get_hi_item_addr(ar,
- HI_ITEM(hi_board_data)),
- (u8 *) &board_address, 4);
+ if (ar->hw.board_addr != 0) {
+ board_address = ar->hw.board_addr;
+ ath6kl_bmi_write_hi32(ar, hi_board_data,
+ board_address);
} else {
- ath6kl_bmi_read(ar,
- ath6kl_get_hi_item_addr(ar,
- HI_ITEM(hi_board_data)),
- (u8 *) &board_address, 4);
+ ath6kl_bmi_read_hi32(ar, hi_board_data, &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_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address);
- if (board_ext_address == 0) {
+ if (ar->target_type == TARGET_TYPE_AR6003 &&
+ board_ext_address == 0) {
ath6kl_err("Failed to get board file target address.\n");
return -EINVAL;
}
@@ -1115,6 +1177,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
case TARGET_TYPE_AR6003:
board_data_size = AR6003_BOARD_DATA_SZ;
board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ;
+ if (ar->fw_board_len > (board_data_size + board_ext_data_size))
+ board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ_V2;
break;
case TARGET_TYPE_AR6004:
board_data_size = AR6004_BOARD_DATA_SZ;
@@ -1126,9 +1190,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
break;
}
- if (ar->fw_board_len == (board_data_size +
- board_ext_data_size)) {
-
+ if (board_ext_address &&
+ ar->fw_board_len == (board_data_size + board_ext_data_size)) {
/* write extended board data */
ath6kl_dbg(ATH6KL_DBG_BOOT,
"writing extended board data to 0x%x (%d B)\n",
@@ -1146,10 +1209,7 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
/* record that extended board data is initialized */
param = (board_ext_data_size << 16) | 1;
- ath6kl_bmi_write(ar,
- ath6kl_get_hi_item_addr(ar,
- HI_ITEM(hi_board_ext_data_config)),
- (unsigned char *) &param, 4);
+ ath6kl_bmi_write_hi32(ar, hi_board_ext_data_config, param);
}
if (ar->fw_board_len < board_data_size) {
@@ -1170,11 +1230,7 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
}
/* 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);
+ ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, 1);
return ret;
}
@@ -1182,10 +1238,11 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
static int ath6kl_upload_otp(struct ath6kl *ar)
{
u32 address, param;
+ bool from_hw = false;
int ret;
- if (WARN_ON(ar->fw_otp == NULL))
- return -ENOENT;
+ if (ar->fw_otp == NULL)
+ return 0;
address = ar->hw.app_load_addr;
@@ -1200,25 +1257,27 @@ static int ath6kl_upload_otp(struct ath6kl *ar)
}
/* read firmware start address */
- ret = ath6kl_bmi_read(ar,
- ath6kl_get_hi_item_addr(ar,
- HI_ITEM(hi_app_start)),
- (u8 *) &address, sizeof(address));
+ ret = ath6kl_bmi_read_hi32(ar, hi_app_start, &address);
if (ret) {
ath6kl_err("Failed to read hi_app_start: %d\n", ret);
return ret;
}
- ar->hw.app_start_override_addr = address;
+ if (ar->hw.app_start_override_addr == 0) {
+ ar->hw.app_start_override_addr = address;
+ from_hw = true;
+ }
- ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr 0x%x\n",
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr%s 0x%x\n",
+ from_hw ? " (from hw)" : "",
ar->hw.app_start_override_addr);
/* execute the OTP code */
- ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", address);
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n",
+ ar->hw.app_start_override_addr);
param = 0;
- ath6kl_bmi_execute(ar, address, &param);
+ ath6kl_bmi_execute(ar, ar->hw.app_start_override_addr, &param);
return ret;
}
@@ -1229,7 +1288,7 @@ static int ath6kl_upload_firmware(struct ath6kl *ar)
int ret;
if (WARN_ON(ar->fw == NULL))
- return -ENOENT;
+ return 0;
address = ar->hw.app_load_addr;
@@ -1256,11 +1315,11 @@ static int ath6kl_upload_firmware(struct ath6kl *ar)
static int ath6kl_upload_patch(struct ath6kl *ar)
{
- u32 address, param;
+ u32 address;
int ret;
- if (WARN_ON(ar->fw_patch == NULL))
- return -ENOENT;
+ if (ar->fw_patch == NULL)
+ return 0;
address = ar->hw.dataset_patch_addr;
@@ -1273,11 +1332,37 @@ static int ath6kl_upload_patch(struct ath6kl *ar)
return ret;
}
- param = address;
- ath6kl_bmi_write(ar,
- ath6kl_get_hi_item_addr(ar,
- HI_ITEM(hi_dset_list_head)),
- (unsigned char *) &param, 4);
+ ath6kl_bmi_write_hi32(ar, hi_dset_list_head, address);
+
+ return 0;
+}
+
+static int ath6kl_upload_testscript(struct ath6kl *ar)
+{
+ u32 address;
+ int ret;
+
+ if (ar->testmode != 2)
+ return 0;
+
+ if (ar->fw_testscript == NULL)
+ return 0;
+
+ address = ar->hw.testscript_addr;
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "writing testscript to 0x%x (%zd B)\n",
+ address, ar->fw_testscript_len);
+
+ ret = ath6kl_bmi_write(ar, address, ar->fw_testscript,
+ ar->fw_testscript_len);
+ if (ret) {
+ ath6kl_err("Failed to write testscript file: %d\n", ret);
+ return ret;
+ }
+
+ ath6kl_bmi_write_hi32(ar, hi_ota_testscript, address);
+ ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096);
+ ath6kl_bmi_write_hi32(ar, hi_test_apps_related, 1);
return 0;
}
@@ -1288,7 +1373,7 @@ static int ath6kl_init_upload(struct ath6kl *ar)
int status = 0;
if (ar->target_type != TARGET_TYPE_AR6003 &&
- ar->target_type != TARGET_TYPE_AR6004)
+ ar->target_type != TARGET_TYPE_AR6004)
return -EINVAL;
/* temporarily disable system sleep */
@@ -1345,9 +1430,15 @@ static int ath6kl_init_upload(struct ath6kl *ar)
return status;
/* WAR to avoid SDIO CRC err */
- if (ar->version.target_ver == AR6003_REV2_VERSION) {
+ if (ar->hw.flags & ATH6KL_HW_SDIO_CRC_ERROR_WAR) {
ath6kl_err("temporary war to avoid sdio crc error\n");
+ param = 0x28;
+ address = GPIO_BASE_ADDRESS + GPIO_PIN9_ADDRESS;
+ status = ath6kl_bmi_reg_write(ar, address, param);
+ if (status)
+ return status;
+
param = 0x20;
address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS;
@@ -1390,6 +1481,11 @@ static int ath6kl_init_upload(struct ath6kl *ar)
if (status)
return status;
+ /* Download the test script */
+ status = ath6kl_upload_testscript(ar);
+ if (status)
+ return status;
+
/* Restore system sleep */
address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS;
status = ath6kl_bmi_reg_write(ar, address, sleep);
@@ -1402,43 +1498,29 @@ static int ath6kl_init_upload(struct ath6kl *ar)
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_hw_params(struct ath6kl *ar)
+int ath6kl_init_hw_params(struct ath6kl *ar)
{
- switch (ar->version.target_ver) {
- case AR6003_REV2_VERSION:
- ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
- ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS;
- ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
- ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE;
- break;
- case AR6003_REV3_VERSION:
- ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS;
- ar->hw.app_load_addr = 0x1234;
- ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
- ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE;
- break;
- case AR6004_REV1_VERSION:
- ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
- ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS;
- ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS;
- ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE;
- break;
- default:
+ const struct ath6kl_hw *uninitialized_var(hw);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
+ hw = &hw_list[i];
+
+ if (hw->id == ar->version.target_ver)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(hw_list)) {
ath6kl_err("Unsupported hardware version: 0x%x\n",
ar->version.target_ver);
return -EINVAL;
}
+ ar->hw = *hw;
+
ath6kl_dbg(ATH6KL_DBG_BOOT,
"target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n",
ar->version.target_ver, ar->target_type,
@@ -1447,75 +1529,165 @@ static int ath6kl_init_hw_params(struct ath6kl *ar)
"app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x",
ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr,
ar->hw.reserved_ram_size);
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "refclk_hz %d uarttx_pin %d",
+ ar->hw.refclk_hz, ar->hw.uarttx_pin);
return 0;
}
-static int ath6kl_init(struct net_device *dev)
+static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type)
{
- struct ath6kl *ar = ath6kl_priv(dev);
- int status = 0;
- s32 timeleft;
+ switch (type) {
+ case ATH6KL_HIF_TYPE_SDIO:
+ return "sdio";
+ case ATH6KL_HIF_TYPE_USB:
+ return "usb";
+ }
- if (!ar)
- return -EIO;
+ return NULL;
+}
- /* Do we need to finish the BMI phase */
- if (ath6kl_bmi_done(ar)) {
- status = -EIO;
- goto ath6kl_init_done;
+
+static const struct fw_capa_str_map {
+ int id;
+ const char *name;
+} fw_capa_map[] = {
+ { ATH6KL_FW_CAPABILITY_HOST_P2P, "host-p2p" },
+ { ATH6KL_FW_CAPABILITY_SCHED_SCAN, "sched-scan" },
+ { ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, "sta-p2pdev-duplex" },
+ { ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, "inactivity-timeout" },
+ { ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, "rsn-cap-override" },
+ { ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, "wow-mc-filter" },
+ { ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, "bmiss-enhance" },
+ { ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, "sscan-match-list" },
+ { ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, "rssi-scan-thold" },
+ { ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, "custom-mac-addr" },
+ { ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, "tx-err-notify" },
+ { ATH6KL_FW_CAPABILITY_REGDOMAIN, "regdomain" },
+ { ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, "sched-scan-v2" },
+ { ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, "hb-poll" },
+};
+
+static const char *ath6kl_init_get_fw_capa_name(unsigned int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_capa_map); i++) {
+ if (fw_capa_map[i].id == id)
+ return fw_capa_map[i].name;
}
- /* 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;
+ return "<unknown>";
+}
+
+static void ath6kl_init_get_fwcaps(struct ath6kl *ar, char *buf, size_t buf_len)
+{
+ u8 *data = (u8 *) ar->fw_capabilities;
+ size_t trunc_len, len = 0;
+ int i, index, bit;
+ char *trunc = "...";
+
+ for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) {
+ index = i / 8;
+ bit = i % 8;
+
+ if (index >= sizeof(ar->fw_capabilities) * 4)
+ break;
+
+ if (buf_len - len < 4) {
+ ath6kl_warn("firmware capability buffer too small!\n");
+
+ /* add "..." to the end of string */
+ trunc_len = strlen(trunc) + 1;
+ strncpy(buf + buf_len - trunc_len, trunc, trunc_len);
+
+ return;
+ }
+
+ if (data[index] & (1 << bit)) {
+ len += scnprintf(buf + len, buf_len - len, "%s,",
+ ath6kl_init_get_fw_capa_name(i));
+ }
}
- ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi);
+ /* overwrite the last comma */
+ if (len > 0)
+ len--;
+
+ buf[len] = '\0';
+}
+
+static int ath6kl_init_hw_reset(struct ath6kl *ar)
+{
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "cold resetting the device");
+
+ return ath6kl_diag_write32(ar, RESET_CONTROL_ADDRESS,
+ cpu_to_le32(RESET_CONTROL_COLD_RST));
+}
+
+static int __ath6kl_init_hw_start(struct ath6kl *ar)
+{
+ long timeleft;
+ int ret, i;
+ char buf[200];
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "hw start\n");
+
+ ret = ath6kl_hif_power_on(ar);
+ if (ret)
+ return ret;
+
+ ret = ath6kl_configure_target(ar);
+ if (ret)
+ goto err_power_off;
+
+ ret = ath6kl_init_upload(ar);
+ if (ret)
+ goto err_power_off;
+
+ /* Do we need to finish the BMI phase */
+ ret = ath6kl_bmi_done(ar);
+ if (ret)
+ goto err_power_off;
/*
* 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;
+ ret = ath6kl_htc_wait_target(ar->htc_target);
+
+ if (ret == -ETIMEDOUT) {
+ /*
+ * Most likely USB target is in odd state after reboot and
+ * needs a reset. A cold reset makes the whole device
+ * disappear from USB bus and initialisation starts from
+ * beginning.
+ */
+ ath6kl_warn("htc wait target timed out, resetting device\n");
+ ath6kl_init_hw_reset(ar);
+ goto err_power_off;
+ } else if (ret) {
+ ath6kl_err("htc wait target failed: %d\n", ret);
+ goto err_power_off;
+ }
+
+ ret = ath6kl_init_service_ep(ar);
+ if (ret) {
+ ath6kl_err("Endpoint service initilisation failed: %d\n", ret);
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);
+ ath6kl_htc_credit_setup(ar->htc_target, &ar->credit_state_info);
/* start HTC */
- status = ath6kl_htc_start(ar->htc_target);
-
- if (status) {
+ ret = ath6kl_htc_start(ar->htc_target);
+ if (ret) {
+ /* FIXME: call this */
ath6kl_cookie_cleanup(ar);
- goto err_rxbuf_cleanup;
+ goto err_cleanup_scatter;
}
/* Wait for Wmi event to be ready */
@@ -1523,138 +1695,121 @@ static int ath6kl_init(struct net_device *dev)
test_bit(WMI_READY,
&ar->flag),
WMI_TIMEOUT);
+ if (timeleft <= 0) {
+ clear_bit(WMI_READY, &ar->flag);
+ ath6kl_err("wmi is not ready or wait was interrupted: %ld\n",
+ timeleft);
+ ret = -EIO;
+ goto err_htc_stop;
+ }
ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
+ if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) {
+ ath6kl_info("%s %s fw %s api %d%s\n",
+ ar->hw.name,
+ ath6kl_init_get_hif_name(ar->hif_type),
+ ar->wiphy->fw_version,
+ ar->fw_api,
+ test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
+ ath6kl_init_get_fwcaps(ar, buf, sizeof(buf));
+ ath6kl_info("firmware supports: %s\n", buf);
+ }
+
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;
+ ret = -EIO;
goto err_htc_stop;
}
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__);
/* communicate the wmi protocol verision to the target */
+ /* FIXME: return error */
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;
-
- ar->wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+ for (i = 0; i < ar->vif_max; i++) {
+ ret = ath6kl_target_config_wlan_params(ar, i);
+ if (ret)
+ goto err_htc_stop;
+ }
- status = ath6kl_target_config_wlan_params(ar);
- if (!status)
- goto ath6kl_init_done;
+ return 0;
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:
- ath6kl_wmi_shutdown(ar->wmi);
- clear_bit(WMI_ENABLED, &ar->flag);
- ar->wmi = NULL;
+err_power_off:
+ ath6kl_hif_power_off(ar);
-ath6kl_init_done:
- return status;
+ return ret;
}
-int ath6kl_core_init(struct ath6kl *ar)
+int ath6kl_init_hw_start(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;
+ int err;
- 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);
+ err = __ath6kl_init_hw_start(ar);
+ if (err)
+ return err;
+ ar->state = ATH6KL_STATE_ON;
+ return 0;
+}
- ret = ath6kl_init_hw_params(ar);
- if (ret)
- goto err_bmi_cleanup;
+static int __ath6kl_init_hw_stop(struct ath6kl *ar)
+{
+ int ret;
- ret = ath6kl_configure_target(ar);
- if (ret)
- goto err_bmi_cleanup;
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "hw stop\n");
- ar->htc_target = ath6kl_htc_create(ar);
+ ath6kl_htc_stop(ar->htc_target);
- if (!ar->htc_target) {
- ret = -ENOMEM;
- goto err_bmi_cleanup;
- }
+ ath6kl_hif_stop(ar);
- 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;
- }
+ ath6kl_bmi_reset(ar);
- ret = ath6kl_fetch_firmwares(ar);
+ ret = ath6kl_hif_power_off(ar);
if (ret)
- goto err_htc_cleanup;
+ ath6kl_warn("failed to power off hif: %d\n", ret);
- ret = ath6kl_init_upload(ar);
- if (ret)
- goto err_htc_cleanup;
+ return 0;
+}
- ret = ath6kl_init(ar->net_dev);
- if (ret)
- goto err_htc_cleanup;
+int ath6kl_init_hw_stop(struct ath6kl *ar)
+{
+ int err;
- /* 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;
- }
+ err = __ath6kl_init_hw_stop(ar);
+ if (err)
+ return err;
+ ar->state = ATH6KL_STATE_OFF;
+ return 0;
+}
- set_bit(NETDEV_REGISTERED, &ar->flag);
+void ath6kl_init_hw_restart(struct ath6kl *ar)
+{
+ clear_bit(WMI_READY, &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);
+ ath6kl_cfg80211_stop_all(ar);
- return ret;
+ if (__ath6kl_init_hw_stop(ar)) {
+ ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to stop during fw error recovery\n");
+ return;
+ }
-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;
+ if (__ath6kl_init_hw_start(ar)) {
+ ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n");
+ return;
+ }
}
void ath6kl_stop_txrx(struct ath6kl *ar)
{
- struct net_device *ndev = ar->net_dev;
-
- if (!ndev)
- return;
+ struct ath6kl_vif *vif, *tmp_vif;
+ int i;
set_bit(DESTROY_IN_PROGRESS, &ar->flag);
@@ -1663,65 +1818,49 @@ void ath6kl_stop_txrx(struct ath6kl *ar)
return;
}
- if (ar->wlan_pwr_state != WLAN_POWER_STATE_CUT_PWR)
- ath6kl_stop_endpoint(ndev, false, true);
+ for (i = 0; i < AP_MAX_NUM_STA; i++)
+ aggr_reset_state(ar->sta_list[i].aggr_conn);
- 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;
+ spin_lock_bh(&ar->list_lock);
+ list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) {
+ list_del(&vif->list);
+ spin_unlock_bh(&ar->list_lock);
+ ath6kl_cfg80211_vif_stop(vif, test_bit(WMI_READY, &ar->flag));
+ rtnl_lock();
+ ath6kl_cfg80211_vif_cleanup(vif);
+ rtnl_unlock();
+ spin_lock_bh(&ar->list_lock);
}
+ spin_unlock_bh(&ar->list_lock);
- ar = ath6kl_priv(dev);
+ clear_bit(WMI_READY, &ar->flag);
- destroy_workqueue(ar->ath6kl_wq);
+ if (ar->fw_recovery.enable)
+ del_timer_sync(&ar->fw_recovery.hb_timer);
- 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);
-
- ath6kl_debug_cleanup(ar);
+ /*
+ * 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.
+ */
+ ath6kl_wmi_shutdown(ar->wmi);
- if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) {
- unregister_netdev(dev);
- clear_bit(NETDEV_REGISTERED, &ar->flag);
+ clear_bit(WMI_ENABLED, &ar->flag);
+ if (ar->htc_target) {
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__);
+ ath6kl_htc_stop(ar->htc_target);
}
- free_netdev(dev);
-
- kfree(ar->fw_board);
- kfree(ar->fw_otp);
- kfree(ar->fw);
- kfree(ar->fw_patch);
+ /*
+ * Try to reset the device if we can. The driver may have been
+ * configure NOT to reset the target during a debug session.
+ */
+ ath6kl_init_hw_reset(ar);
- ath6kl_cfg80211_deinit(ar);
+ up(&ar->sem);
}
+EXPORT_SYMBOL(ath6kl_stop_txrx);
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index 30b5a53db9e..d56554674da 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,18 +15,24 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#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 *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr)
{
+ struct ath6kl *ar = vif->ar;
struct ath6kl_sta *conn = NULL;
u8 i, max_conn;
- max_conn = (ar->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0;
+ if (is_zero_ether_addr(node_addr))
+ return NULL;
+
+ max_conn = (vif->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) {
@@ -51,9 +58,11 @@ struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid)
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)
+static void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u16 aid,
+ u8 *wpaie, size_t ielen, u8 keymgmt,
+ u8 ucipher, u8 auth, u8 apsd_info)
{
+ struct ath6kl *ar = vif->ar;
struct ath6kl_sta *sta;
u8 free_slot;
@@ -67,18 +76,31 @@ static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie,
sta->keymgmt = keymgmt;
sta->ucipher = ucipher;
sta->auth = auth;
+ sta->apsd_info = apsd_info;
ar->sta_list_index = ar->sta_list_index | (1 << free_slot);
ar->ap_stats.sta[free_slot].aid = cpu_to_le32(aid);
+ aggr_conn_init(vif, vif->aggr_cntxt, sta->aggr_conn);
}
static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
{
struct ath6kl_sta *sta = &ar->sta_list[i];
+ struct ath6kl_mgmt_buff *entry, *tmp;
/* empty the queued pkts in the PS queue if any */
spin_lock_bh(&sta->psq_lock);
skb_queue_purge(&sta->psq);
+ skb_queue_purge(&sta->apsdq);
+
+ if (sta->mgmt_psq_len != 0) {
+ list_for_each_entry_safe(entry, tmp, &sta->mgmt_psq, list) {
+ kfree(entry);
+ }
+ INIT_LIST_HEAD(&sta->mgmt_psq);
+ sta->mgmt_psq_len = 0;
+ }
+
spin_unlock_bh(&sta->psq_lock);
memset(&ar->ap_stats.sta[sta->aid - 1], 0,
@@ -89,7 +111,7 @@ static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
sta->sta_flags = 0;
ar->sta_list_index = ar->sta_list_index & ~(1 << i);
-
+ aggr_reset_state(sta->aggr_conn);
}
static u8 ath6kl_remove_sta(struct ath6kl *ar, u8 *mac, u16 reason)
@@ -174,64 +196,6 @@ void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *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;
- s32 i;
- __le32 addr_val;
-
- /*
- * Write bytes 1,2,3 of the register to set the upper address bytes,
- * the LSB is written last to initiate the access cycle
- */
-
- for (i = 1; i <= 3; i++) {
- /*
- * Fill the buffer with the address byte value we want to
- * hit 4 times. No need to worry about endianness as the
- * same byte is copied to all four bytes of addr_val at
- * any time.
- */
- memset((u8 *)&addr_val, ((u8 *)&addr)[i], 4);
-
- /*
- * Hit each byte of the register address with a 4-byte
- * write operation to the same address, this is a harmless
- * operation.
- */
- status = hif_read_write_sync(ar, reg_addr + i, (u8 *)&addr_val,
- 4, HIF_WR_SYNC_BYTE_FIX);
- if (status)
- break;
- }
-
- if (status) {
- ath6kl_err("failed to write initial bytes of 0x%x to window reg: 0x%X\n",
- addr, reg_addr);
- return status;
- }
-
- /*
- * Write the address register again, this time write the whole
- * 4-byte value. The effect here is that the LSB write causes the
- * cycle to start, the extra 3 byte write to bytes 1,2,3 has no
- * effect since we are writing the same values again
- */
- addr_val = cpu_to_le32(addr);
- status = hif_read_write_sync(ar, reg_addr,
- (u8 *)&(addr_val),
- 4, HIF_WR_SYNC_BYTE_INC);
-
- if (status) {
- ath6kl_err("failed to write 0x%x to window reg: 0x%X\n",
- addr, reg_addr);
- return status;
- }
-
- return 0;
-}
-
/*
* Read from the hardware through its diagnostic window. No cooperation
* from the firmware is required for this.
@@ -240,14 +204,7 @@ int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value)
{
int ret;
- /* set window register to start read cycle */
- ret = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, address);
- if (ret)
- return ret;
-
- /* read the data */
- ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) value,
- sizeof(*value), HIF_RD_SYNC_BYTE_INC);
+ ret = ath6kl_hif_diag_read32(ar, address, value);
if (ret) {
ath6kl_warn("failed to read32 through diagnose window: %d\n",
ret);
@@ -265,18 +222,15 @@ int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value)
{
int ret;
- /* set write data */
- ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) &value,
- sizeof(value), HIF_WR_SYNC_BYTE_INC);
+ ret = ath6kl_hif_diag_write32(ar, address, value);
+
if (ret) {
ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n",
address, value);
return ret;
}
- /* set window register, which starts the write cycle */
- return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
- address);
+ return 0;
}
int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length)
@@ -319,7 +273,7 @@ int ath6kl_read_fwlogs(struct ath6kl *ar)
struct ath6kl_dbglog_hdr debug_hdr;
struct ath6kl_dbglog_buf debug_buf;
u32 address, length, dropped, firstbuf, debug_hdr_addr;
- int ret = 0, loop;
+ int ret, loop;
u8 *buf;
buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL);
@@ -342,13 +296,17 @@ int ath6kl_read_fwlogs(struct ath6kl *ar)
}
address = TARG_VTOP(ar->target_type, debug_hdr_addr);
- ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr));
+ ret = ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr));
+ if (ret)
+ goto out;
address = TARG_VTOP(ar->target_type,
le32_to_cpu(debug_hdr.dbuf_addr));
firstbuf = address;
dropped = le32_to_cpu(debug_hdr.dropped);
- ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+ ret = ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+ if (ret)
+ goto out;
loop = 100;
@@ -371,7 +329,8 @@ int ath6kl_read_fwlogs(struct ath6kl *ar)
address = TARG_VTOP(ar->target_type,
le32_to_cpu(debug_buf.next));
- ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+ ret = ath6kl_diag_read(ar, address, &debug_buf,
+ sizeof(debug_buf));
if (ret)
goto out;
@@ -389,138 +348,33 @@ out:
return ret;
}
-/* FIXME: move to a better place, target.h? */
-#define AR6003_RESET_CONTROL_ADDRESS 0x00004000
-#define AR6004_RESET_CONTROL_ADDRESS 0x00004000
-
-static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
- bool wait_fot_compltn, bool cold_reset)
-{
- int status = 0;
- u32 address;
- __le32 data;
-
- if (target_type != TARGET_TYPE_AR6003 &&
- target_type != TARGET_TYPE_AR6004)
- return;
-
- data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) :
- cpu_to_le32(RESET_CONTROL_MBOX_RST);
-
- switch (target_type) {
- case TARGET_TYPE_AR6003:
- address = AR6003_RESET_CONTROL_ADDRESS;
- break;
- case TARGET_TYPE_AR6004:
- address = AR6004_RESET_CONTROL_ADDRESS;
- break;
- default:
- address = AR6003_RESET_CONTROL_ADDRESS;
- break;
- }
-
- status = ath6kl_diag_write32(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)
+static void ath6kl_install_static_wep_keys(struct ath6kl_vif *vif)
{
u8 index;
u8 keyusage;
- for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) {
- if (ar->wep_key_list[index].key_len) {
+ for (index = 0; index <= WMI_MAX_KEY_INDEX; index++) {
+ if (vif->wep_key_list[index].key_len) {
keyusage = GROUP_USAGE;
- if (index == ar->def_txkey_index)
+ if (index == vif->def_txkey_index)
keyusage |= TX_USAGE;
- ath6kl_wmi_addkey_cmd(ar->wmi,
+ ath6kl_wmi_addkey_cmd(vif->ar->wmi, vif->fw_vif_idx,
index,
WEP_CRYPT,
keyusage,
- ar->wep_key_list[index].key_len,
- NULL,
- ar->wep_key_list[index].key,
+ vif->wep_key_list[index].key_len,
+ NULL, 0,
+ vif->wep_key_list[index].key,
KEY_OP_INIT_VAL, NULL,
NO_SYNC_WMIFLAG);
}
}
}
-void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel)
+void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel)
{
+ struct ath6kl *ar = vif->ar;
struct ath6kl_req_key *ik;
int res;
u8 key_rsc[ATH6KL_KEY_SEQ_LEN];
@@ -529,39 +383,46 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel)
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel);
- switch (ar->auth_mode) {
+ switch (vif->auth_mode) {
case NONE_AUTH:
- if (ar->prwise_crypto == WEP_CRYPT)
- ath6kl_install_static_wep_keys(ar);
- break;
+ if (vif->prwise_crypto == WEP_CRYPT)
+ ath6kl_install_static_wep_keys(vif);
+ if (!ik->valid || ik->key_type != WAPI_CRYPT)
+ break;
+ /* for WAPI, we need to set the delayed group key, continue: */
case WPA_PSK_AUTH:
case WPA2_PSK_AUTH:
case (WPA_PSK_AUTH | WPA2_PSK_AUTH):
if (!ik->valid)
break;
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for "
- "the initial group key for AP mode\n");
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "Delayed addkey for the initial group key for AP mode\n");
memset(key_rsc, 0, sizeof(key_rsc));
res = ath6kl_wmi_addkey_cmd(
- ar->wmi, ik->key_index, ik->key_type,
- GROUP_USAGE, ik->key_len, key_rsc, ik->key,
+ ar->wmi, vif->fw_vif_idx, ik->key_index, ik->key_type,
+ GROUP_USAGE, ik->key_len, key_rsc, ATH6KL_KEY_SEQ_LEN,
+ ik->key,
KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG);
if (res) {
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed "
- "addkey failed: %d\n", res);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ "Delayed addkey failed: %d\n", res);
}
break;
}
- ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
- set_bit(CONNECTED, &ar->flag);
- netif_carrier_on(ar->net_dev);
+ if (ar->last_ch != channel)
+ /* we actually don't know the phymode, default to HT20 */
+ ath6kl_cfg80211_ch_switch_notify(vif, channel, WMI_11G_HT20);
+
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0);
+ set_bit(CONNECTED, &vif->flags);
+ netif_carrier_on(vif->ndev);
}
-void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
+void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr,
u8 keymgmt, u8 ucipher, u8 auth,
- u8 assoc_req_len, u8 *assoc_info)
+ u8 assoc_req_len, u8 *assoc_info, u8 apsd_info)
{
u8 *ies = NULL, *wpa_ie = NULL, *pos;
size_t ies_len = 0;
@@ -600,13 +461,25 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
wpa_ie = pos; /* WPS IE */
break; /* overrides WPA/RSN IE */
}
+ } else if (pos[0] == 0x44 && wpa_ie == NULL) {
+ /*
+ * Note: WAPI Parameter Set IE re-uses Element ID that
+ * was officially allocated for BSS AC Access Delay. As
+ * such, we need to be a bit more careful on when
+ * parsing the frame. However, BSS AC Access Delay
+ * element is not supposed to be included in
+ * (Re)Association Request frames, so this should not
+ * cause problems.
+ */
+ wpa_ie = pos; /* WAPI IE */
+ break;
}
pos += 2 + pos[1];
}
- ath6kl_add_new_sta(ar, mac_addr, aid, wpa_ie,
+ ath6kl_add_new_sta(vif, mac_addr, aid, wpa_ie,
wpa_ie ? 2 + wpa_ie[1] : 0,
- keymgmt, ucipher, auth);
+ keymgmt, ucipher, auth, apsd_info);
/* send event to application */
memset(&sinfo, 0, sizeof(sinfo));
@@ -617,462 +490,195 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
sinfo.assoc_req_ies_len = ies_len;
sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
- cfg80211_new_sta(ar->net_dev, mac_addr, &sinfo, GFP_KERNEL);
+ cfg80211_new_sta(vif->ndev, mac_addr, &sinfo, GFP_KERNEL);
- netif_wake_queue(ar->net_dev);
+ netif_wake_queue(vif->ndev);
}
-/* Functions for Tx credit handling */
-void ath6k_credit_init(struct htc_credit_state_info *cred_info,
- struct list_head *ep_list,
- int tot_credits)
+void disconnect_timer_handler(unsigned long ptr)
{
- 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;
- }
+ struct net_device *dev = (struct net_device *)ptr;
+ struct ath6kl_vif *vif = netdev_priv(dev);
- 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;
+ ath6kl_init_profile_info(vif);
+ ath6kl_disconnect(vif);
+}
+void ath6kl_disconnect(struct ath6kl_vif *vif)
+{
+ if (test_bit(CONNECTED, &vif->flags) ||
+ test_bit(CONNECT_PEND, &vif->flags)) {
+ ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
/*
- * 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
+ * Disconnect command is issued, clear the connect pending
+ * flag. The connected flag will be cleared in
+ * disconnect event notification.
*/
- }
-
- 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;
-
- }
+ clear_bit(CONNECT_PEND, &vif->flags);
}
}
-/* 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;
-}
+/* WMI Event handlers */
-/* 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)
+void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver,
+ enum wmi_phy_cap cap)
{
- int credits;
-
- ep_dist->cred_assngd = limit;
+ struct ath6kl *ar = devt;
- if (ep_dist->credits <= limit)
- return;
+ memcpy(ar->mac_addr, datap, ETH_ALEN);
- credits = ep_dist->credits - limit;
- ep_dist->credits -= credits;
- cred_info->cur_free_credits += credits;
-}
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "ready event mac addr %pM sw_ver 0x%x abi_ver 0x%x cap 0x%x\n",
+ ar->mac_addr, sw_ver, abi_ver, cap);
-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);
- }
- }
+ ar->version.wlan_ver = sw_ver;
+ ar->version.abi_ver = abi_ver;
+ ar->hw.cap = cap;
+
+ if (strlen(ar->wiphy->fw_version) == 0) {
+ snprintf(ar->wiphy->fw_version,
+ sizeof(ar->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);
}
-/*
- * 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)
+void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status)
{
- 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;
- }
+ struct ath6kl *ar = vif->ar;
+ bool aborted = false;
- credits = min(cred_info->cur_free_credits, ep_dist->seek_cred);
+ if (status != WMI_SCAN_STATUS_SUCCESS)
+ aborted = true;
-out:
- /* did we find some credits? */
- if (credits)
- ath6kl_deposit_credit_to_ep(cred_info, ep_dist, credits);
-
- ep_dist->seek_cred = 0;
-}
+ ath6kl_cfg80211_scan_complete_event(vif, aborted);
-/* 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;
+ if (!ar->usr_bss_filter) {
+ clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+ NONE_BSS_FILTER, 0);
}
- WARN_ON(cred_info->cur_free_credits > cred_info->total_avail_credits);
- WARN_ON(cred_info->cur_free_credits < 0);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status);
}
-void disconnect_timer_handler(unsigned long ptr)
+static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel)
{
- struct net_device *dev = (struct net_device *)ptr;
- struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl *ar = vif->ar;
- ath6kl_init_profile_info(ar);
- ath6kl_disconnect(ar);
-}
+ vif->profile.ch = cpu_to_le16(channel);
-void ath6kl_disconnect(struct ath6kl *ar)
-{
- if (test_bit(CONNECTED, &ar->flag) ||
- test_bit(CONNECT_PEND, &ar->flag)) {
- ath6kl_wmi_disconnect_cmd(ar->wmi);
+ switch (vif->nw_type) {
+ case AP_NETWORK:
/*
- * Disconnect command is issued, clear the connect pending
- * flag. The connected flag will be cleared in
- * disconnect event notification.
+ * reconfigure any saved RSN IE capabilites in the beacon /
+ * probe response to stay in sync with the supplicant.
*/
- clear_bit(CONNECT_PEND, &ar->flag);
- }
-}
-
-void ath6kl_deep_sleep_enable(struct ath6kl *ar)
-{
- switch (ar->sme_state) {
- case SME_CONNECTING:
- cfg80211_connect_result(ar->net_dev, ar->bssid, NULL, 0,
- NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
- break;
- case SME_CONNECTED:
+ if (vif->rsn_capab &&
+ test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+ ar->fw_capabilities))
+ ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
+ WLAN_EID_RSN, WMI_RSN_IE_CAPB,
+ (const u8 *) &vif->rsn_capab,
+ sizeof(vif->rsn_capab));
+
+ return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx,
+ &vif->profile);
default:
- /*
- * FIXME: oddly enough smeState is in DISCONNECTED during
- * suspend, why? Need to send disconnected event in that
- * state.
- */
- cfg80211_disconnected(ar->net_dev, 0, NULL, 0, GFP_KERNEL);
- break;
+ ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type);
+ return -ENOTSUPP;
}
-
- if (test_bit(CONNECTED, &ar->flag) ||
- test_bit(CONNECT_PEND, &ar->flag))
- ath6kl_wmi_disconnect_cmd(ar->wmi);
-
- ar->sme_state = SME_DISCONNECTED;
-
- /* disable scanning */
- if (ath6kl_wmi_scanparams_cmd(ar->wmi, 0xFFFF, 0, 0, 0, 0, 0, 0, 0,
- 0, 0) != 0)
- printk(KERN_WARNING "ath6kl: failed to disable scan "
- "during suspend\n");
-
- ath6kl_cfg80211_scan_complete_event(ar, -ECANCELED);
}
-/* WMI Event handlers */
-
-static const char *get_hw_id_string(u32 id)
+static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel)
{
- 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";
- }
-}
+ struct ath6kl_vif *vif;
+ int res = 0;
-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);
+ if (!ar->want_ch_switch)
+ return;
- ath6kl_info("hw %s fw %s%s\n",
- get_hw_id_string(ar->wdev->wiphy->hw_version),
- ar->wdev->wiphy->fw_version,
- test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
-}
+ spin_lock_bh(&ar->list_lock);
+ list_for_each_entry(vif, &ar->vif_list, list) {
+ if (ar->want_ch_switch & (1 << vif->fw_vif_idx))
+ res = ath6kl_commit_ch_switch(vif, channel);
-void ath6kl_scan_complete_evt(struct ath6kl *ar, int status)
-{
- ath6kl_cfg80211_scan_complete_event(ar, status);
+ /* if channel switch failed, oh well we tried */
+ ar->want_ch_switch &= ~(1 << vif->fw_vif_idx);
- if (!ar->usr_bss_filter) {
- clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
- ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+ if (res)
+ ath6kl_err("channel switch failed nw_type %d res %d\n",
+ vif->nw_type, res);
}
-
- ath6kl_dbg(ATH6KL_DBG_WLAN_SCAN, "scan complete: %d\n", status);
+ spin_unlock_bh(&ar->list_lock);
}
-void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid,
+void ath6kl_connect_event(struct ath6kl_vif *vif, 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;
+ struct ath6kl *ar = vif->ar;
- ath6kl_cfg80211_connect_event(ar, channel, bssid,
+ ath6kl_cfg80211_connect_event(vif, 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;
+ memcpy(vif->bssid, bssid, sizeof(vif->bssid));
+ vif->bss_ch = channel;
- if ((ar->nw_type == INFRA_NETWORK))
- ath6kl_wmi_listeninterval_cmd(ar->wmi, ar->listen_intvl_t,
- ar->listen_intvl_b);
+ if ((vif->nw_type == INFRA_NETWORK)) {
+ ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
+ vif->listen_intvl_t, 0);
+ ath6kl_check_ch_switch(ar, channel);
+ }
- netif_wake_queue(ar->net_dev);
+ netif_wake_queue(vif->ndev);
/* 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);
+ spin_lock_bh(&vif->if_lock);
+ set_bit(CONNECTED, &vif->flags);
+ clear_bit(CONNECT_PEND, &vif->flags);
+ netif_carrier_on(vif->ndev);
+ spin_unlock_bh(&vif->if_lock);
- aggr_reset_state(ar->aggr_cntxt);
- ar->reconnect_flag = 0;
+ aggr_reset_state(vif->aggr_cntxt->aggr_conn);
+ vif->reconnect_flag = 0;
- if ((ar->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable) {
+ if ((vif->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) {
- set_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
- ath6kl_wmi_bssfilter_cmd(ar->wmi, CURRENT_BSS_FILTER, 0);
+ set_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+ CURRENT_BSS_FILTER, 0);
}
}
-void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast)
+void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast)
{
struct ath6kl_sta *sta;
+ struct ath6kl *ar = vif->ar;
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) {
+ if (vif->nw_type == AP_NETWORK) {
sta = ath6kl_find_sta_by_aid(ar, (keyid >> 2));
if (!sta)
return;
@@ -1081,19 +687,20 @@ void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast)
"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,
+ cfg80211_michael_mic_failure(vif->ndev, sta->mac,
NL80211_KEYTYPE_PAIRWISE, keyid,
tsc, GFP_KERNEL);
- } else
- ath6kl_cfg80211_tkip_micerr_event(ar, keyid, ismcast);
-
+ } else {
+ ath6kl_cfg80211_tkip_micerr_event(vif, keyid, ismcast);
+ }
}
-static void ath6kl_update_target_stats(struct ath6kl *ar, u8 *ptr, u32 len)
+static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
{
struct wmi_target_stats *tgt_stats =
(struct wmi_target_stats *) ptr;
- struct target_stats *stats = &ar->target_stats;
+ struct ath6kl *ar = vif->ar;
+ struct target_stats *stats = &vif->target_stats;
struct tkip_ccmp_stats *ccmp_stats;
u8 ac;
@@ -1189,8 +796,12 @@ static void ath6kl_update_target_stats(struct ath6kl *ar, u8 *ptr, u32 len)
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);
+ stats->arp_received = le32_to_cpu(tgt_stats->arp_stats.arp_received);
+ stats->arp_replied = le32_to_cpu(tgt_stats->arp_stats.arp_replied);
+ stats->arp_matched = le32_to_cpu(tgt_stats->arp_stats.arp_matched);
+
+ if (test_bit(STATS_UPDATE_PEND, &vif->flags)) {
+ clear_bit(STATS_UPDATE_PEND, &vif->flags);
wake_up(&ar->event_wq);
}
}
@@ -1200,14 +811,15 @@ 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)
+void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len)
{
struct wmi_ap_mode_stat *p = (struct wmi_ap_mode_stat *) ptr;
+ struct ath6kl *ar = vif->ar;
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 (vif->nw_type == AP_NETWORK) {
if (len < sizeof(*p))
return;
@@ -1226,7 +838,7 @@ void ath6kl_tgt_stats_event(struct ath6kl *ar, u8 *ptr, u32 len)
}
} else {
- ath6kl_update_target_stats(ar, ptr, len);
+ ath6kl_update_target_stats(vif, ptr, len);
}
}
@@ -1245,11 +857,13 @@ void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr)
wake_up(&ar->event_wq);
}
-void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid)
+void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
{
struct ath6kl_sta *conn;
struct sk_buff *skb;
bool psq_empty = false;
+ struct ath6kl *ar = vif->ar;
+ struct ath6kl_mgmt_buff *mgmt_buf;
conn = ath6kl_find_sta_by_aid(ar, aid);
@@ -1260,7 +874,7 @@ void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid)
* becomes empty update the PVB for this station.
*/
spin_lock_bh(&conn->psq_lock);
- psq_empty = skb_queue_empty(&conn->psq);
+ psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
spin_unlock_bh(&conn->psq_lock);
if (psq_empty)
@@ -1268,25 +882,42 @@ void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid)
return;
spin_lock_bh(&conn->psq_lock);
- skb = skb_dequeue(&conn->psq);
- spin_unlock_bh(&conn->psq_lock);
+ if (conn->mgmt_psq_len > 0) {
+ mgmt_buf = list_first_entry(&conn->mgmt_psq,
+ struct ath6kl_mgmt_buff, list);
+ list_del(&mgmt_buf->list);
+ conn->mgmt_psq_len--;
+ spin_unlock_bh(&conn->psq_lock);
+
+ conn->sta_flags |= STA_PS_POLLED;
+ ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx,
+ mgmt_buf->id, mgmt_buf->freq,
+ mgmt_buf->wait, mgmt_buf->buf,
+ mgmt_buf->len, mgmt_buf->no_cck);
+ conn->sta_flags &= ~STA_PS_POLLED;
+ kfree(mgmt_buf);
+ } else {
+ 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;
+ conn->sta_flags |= STA_PS_POLLED;
+ ath6kl_data_tx(skb, vif->ndev);
+ conn->sta_flags &= ~STA_PS_POLLED;
+ }
spin_lock_bh(&conn->psq_lock);
- psq_empty = skb_queue_empty(&conn->psq);
+ psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
spin_unlock_bh(&conn->psq_lock);
if (psq_empty)
- ath6kl_wmi_set_pvb_cmd(ar->wmi, conn->aid, 0);
+ ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, conn->aid, 0);
}
-void ath6kl_dtimexpiry_event(struct ath6kl *ar)
+void ath6kl_dtimexpiry_event(struct ath6kl_vif *vif)
{
bool mcastq_empty = false;
struct sk_buff *skb;
+ struct ath6kl *ar = vif->ar;
/*
* If there are no associated STAs, ignore the DTIM expiry event.
@@ -1308,31 +939,53 @@ void ath6kl_dtimexpiry_event(struct ath6kl *ar)
return;
/* set the STA flag to dtim_expired for the frame to go out */
- set_bit(DTIM_EXPIRED, &ar->flag);
+ set_bit(DTIM_EXPIRED, &vif->flags);
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);
+ ath6kl_data_tx(skb, vif->ndev);
spin_lock_bh(&ar->mcastpsq_lock);
}
spin_unlock_bh(&ar->mcastpsq_lock);
- clear_bit(DTIM_EXPIRED, &ar->flag);
+ clear_bit(DTIM_EXPIRED, &vif->flags);
/* clear the LSB of the BitMapCtl field of the TIM IE */
- ath6kl_wmi_set_pvb_cmd(ar->wmi, MCAST_AID, 0);
+ ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, MCAST_AID, 0);
}
-void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
+void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid,
u8 assoc_resp_len, u8 *assoc_info,
u16 prot_reason_status)
{
- unsigned long flags;
+ struct ath6kl *ar = vif->ar;
+
+ if (vif->nw_type == AP_NETWORK) {
+ /* disconnect due to other STA vif switching channels */
+ if (reason == BSS_DISCONNECTED &&
+ prot_reason_status == WMI_AP_REASON_STA_ROAM) {
+ ar->want_ch_switch |= 1 << vif->fw_vif_idx;
+ /* bail back to this channel if STA vif fails connect */
+ ar->last_ch = le16_to_cpu(vif->profile.ch);
+ }
+
+ if (prot_reason_status == WMI_AP_REASON_MAX_STA) {
+ /* send max client reached notification to user space */
+ cfg80211_conn_failed(vif->ndev, bssid,
+ NL80211_CONN_FAIL_MAX_CLIENTS,
+ GFP_KERNEL);
+ }
+
+ if (prot_reason_status == WMI_AP_REASON_ACL) {
+ /* send blocked client notification to user space */
+ cfg80211_conn_failed(vif->ndev, bssid,
+ NL80211_CONN_FAIL_BLOCKED_CLIENT,
+ GFP_KERNEL);
+ }
- if (ar->nw_type == AP_NETWORK) {
if (!ath6kl_remove_sta(ar, bssid, prot_reason_status))
return;
@@ -1344,31 +997,31 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
/* 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);
+ ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
+ MCAST_AID, 0);
}
if (!is_broadcast_ether_addr(bssid)) {
/* send event to application */
- cfg80211_del_sta(ar->net_dev, bssid, GFP_KERNEL);
+ cfg80211_del_sta(vif->ndev, bssid, GFP_KERNEL);
}
- if (memcmp(ar->net_dev->dev_addr, bssid, ETH_ALEN) == 0) {
- memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
- clear_bit(CONNECTED, &ar->flag);
+ if (memcmp(vif->ndev->dev_addr, bssid, ETH_ALEN) == 0) {
+ memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list));
+ clear_bit(CONNECTED, &vif->flags);
}
return;
}
- ath6kl_cfg80211_disconnect_event(ar, reason, bssid,
- assoc_resp_len, assoc_info,
- prot_reason_status);
+ ath6kl_cfg80211_disconnect_event(vif, reason, bssid,
+ assoc_resp_len, assoc_info,
+ prot_reason_status);
- aggr_reset_state(ar->aggr_cntxt);
+ aggr_reset_state(vif->aggr_cntxt->aggr_conn);
- del_timer(&ar->disconnect_timer);
+ del_timer(&vif->disconnect_timer);
- ath6kl_dbg(ATH6KL_DBG_WLAN_CONNECT,
- "disconnect reason is %d\n", reason);
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "disconnect reason is %d\n", reason);
/*
* If the event is due to disconnect cmd from the host, only they
@@ -1377,101 +1030,278 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
*/
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);
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+ NONE_BSS_FILTER, 0);
} else {
- set_bit(CONNECT_PEND, &ar->flag);
+ set_bit(CONNECT_PEND, &vif->flags);
if (((reason == ASSOC_FAILED) &&
- (prot_reason_status == 0x11)) ||
- ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0)
- && (ar->reconnect_flag == 1))) {
- set_bit(CONNECTED, &ar->flag);
+ (prot_reason_status == 0x11)) ||
+ ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) &&
+ (vif->reconnect_flag == 1))) {
+ set_bit(CONNECTED, &vif->flags);
return;
}
}
+ /* restart disconnected concurrent vifs waiting for new channel */
+ ath6kl_check_ch_switch(ar, ar->last_ch);
+
/* 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);
+ spin_lock_bh(&vif->if_lock);
+ clear_bit(CONNECTED, &vif->flags);
+ netif_carrier_off(vif->ndev);
+ spin_unlock_bh(&vif->if_lock);
- if ((reason != CSERV_DISCONNECT) || (ar->reconnect_flag != 1))
- ar->reconnect_flag = 0;
+ if ((reason != CSERV_DISCONNECT) || (vif->reconnect_flag != 1))
+ vif->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;
+ netif_stop_queue(vif->ndev);
+ memset(vif->bssid, 0, sizeof(vif->bssid));
+ vif->bss_ch = 0;
ath6kl_tx_data_cleanup(ar);
}
-static int ath6kl_open(struct net_device *dev)
+struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar)
{
- struct ath6kl *ar = ath6kl_priv(dev);
- unsigned long flags;
+ struct ath6kl_vif *vif;
+
+ spin_lock_bh(&ar->list_lock);
+ if (list_empty(&ar->vif_list)) {
+ spin_unlock_bh(&ar->list_lock);
+ return NULL;
+ }
- spin_lock_irqsave(&ar->lock, flags);
+ vif = list_first_entry(&ar->vif_list, struct ath6kl_vif, list);
- set_bit(WLAN_ENABLED, &ar->flag);
+ spin_unlock_bh(&ar->list_lock);
+
+ return vif;
+}
+
+static int ath6kl_open(struct net_device *dev)
+{
+ struct ath6kl_vif *vif = netdev_priv(dev);
- if (test_bit(CONNECTED, &ar->flag)) {
+ set_bit(WLAN_ENABLED, &vif->flags);
+
+ if (test_bit(CONNECTED, &vif->flags)) {
netif_carrier_on(dev);
netif_wake_queue(dev);
- } else
+ } 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);
+ struct ath6kl_vif *vif = netdev_priv(dev);
netif_stop_queue(dev);
- ath6kl_disconnect(ar);
+ ath6kl_cfg80211_stop(vif);
- 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);
+ clear_bit(WLAN_ENABLED, &vif->flags);
return 0;
}
static struct net_device_stats *ath6kl_get_stats(struct net_device *dev)
{
- struct ath6kl *ar = ath6kl_priv(dev);
+ struct ath6kl_vif *vif = netdev_priv(dev);
+
+ return &vif->net_stats;
+}
+
+static int ath6kl_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct ath6kl_vif *vif = netdev_priv(dev);
+ struct ath6kl *ar = vif->ar;
+ int err = 0;
+
+ if ((features & NETIF_F_RXCSUM) &&
+ (ar->rx_meta_ver != WMI_META_VERSION_2)) {
+ ar->rx_meta_ver = WMI_META_VERSION_2;
+ err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi,
+ vif->fw_vif_idx,
+ ar->rx_meta_ver, 0, 0);
+ if (err) {
+ dev->features = features & ~NETIF_F_RXCSUM;
+ return err;
+ }
+ } else if (!(features & NETIF_F_RXCSUM) &&
+ (ar->rx_meta_ver == WMI_META_VERSION_2)) {
+ ar->rx_meta_ver = 0;
+ err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi,
+ vif->fw_vif_idx,
+ ar->rx_meta_ver, 0, 0);
+ if (err) {
+ dev->features = features | NETIF_F_RXCSUM;
+ return err;
+ }
+ }
+
+ return err;
+}
+
+static void ath6kl_set_multicast_list(struct net_device *ndev)
+{
+ struct ath6kl_vif *vif = netdev_priv(ndev);
+ bool mc_all_on = false;
+ int mc_count = netdev_mc_count(ndev);
+ struct netdev_hw_addr *ha;
+ bool found;
+ struct ath6kl_mc_filter *mc_filter, *tmp;
+ struct list_head mc_filter_new;
+ int ret;
+
+ if (!test_bit(WMI_READY, &vif->ar->flag) ||
+ !test_bit(WLAN_ENABLED, &vif->flags))
+ return;
+
+ /* Enable multicast-all filter. */
+ mc_all_on = !!(ndev->flags & IFF_PROMISC) ||
+ !!(ndev->flags & IFF_ALLMULTI) ||
+ !!(mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST);
+
+ if (mc_all_on)
+ set_bit(NETDEV_MCAST_ALL_ON, &vif->flags);
+ else
+ clear_bit(NETDEV_MCAST_ALL_ON, &vif->flags);
+
+ if (test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
+ vif->ar->fw_capabilities)) {
+ mc_all_on = mc_all_on || (vif->ar->state == ATH6KL_STATE_ON);
+ }
+
+ if (!(ndev->flags & IFF_MULTICAST)) {
+ mc_all_on = false;
+ set_bit(NETDEV_MCAST_ALL_OFF, &vif->flags);
+ } else {
+ clear_bit(NETDEV_MCAST_ALL_OFF, &vif->flags);
+ }
+
+ /* Enable/disable "multicast-all" filter*/
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast-all filter\n",
+ mc_all_on ? "enabling" : "disabling");
+
+ ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx,
+ mc_all_on);
+ if (ret) {
+ ath6kl_warn("Failed to %s multicast-all receive\n",
+ mc_all_on ? "enable" : "disable");
+ return;
+ }
+
+ if (test_bit(NETDEV_MCAST_ALL_ON, &vif->flags))
+ return;
- return &ar->net_stats;
+ /* Keep the driver and firmware mcast list in sync. */
+ list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
+ found = false;
+ netdev_for_each_mc_addr(ha, ndev) {
+ if (memcmp(ha->addr, mc_filter->hw_addr,
+ ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ /*
+ * Delete the filter which was previously set
+ * but not in the new request.
+ */
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "Removing %pM from multicast filter\n",
+ mc_filter->hw_addr);
+ ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi,
+ vif->fw_vif_idx, mc_filter->hw_addr,
+ false);
+ if (ret) {
+ ath6kl_warn("Failed to remove multicast filter:%pM\n",
+ mc_filter->hw_addr);
+ return;
+ }
+
+ list_del(&mc_filter->list);
+ kfree(mc_filter);
+ }
+ }
+
+ INIT_LIST_HEAD(&mc_filter_new);
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ found = false;
+ list_for_each_entry(mc_filter, &vif->mc_filter, list) {
+ if (memcmp(ha->addr, mc_filter->hw_addr,
+ ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ mc_filter = kzalloc(sizeof(struct ath6kl_mc_filter),
+ GFP_ATOMIC);
+ if (!mc_filter) {
+ WARN_ON(1);
+ goto out;
+ }
+
+ memcpy(mc_filter->hw_addr, ha->addr,
+ ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE);
+ /* Set the multicast filter */
+ ath6kl_dbg(ATH6KL_DBG_TRC,
+ "Adding %pM to multicast filter list\n",
+ mc_filter->hw_addr);
+ ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi,
+ vif->fw_vif_idx, mc_filter->hw_addr,
+ true);
+ if (ret) {
+ ath6kl_warn("Failed to add multicast filter :%pM\n",
+ mc_filter->hw_addr);
+ kfree(mc_filter);
+ goto out;
+ }
+
+ list_add_tail(&mc_filter->list, &mc_filter_new);
+ }
+ }
+
+out:
+ list_splice_tail(&mc_filter_new, &vif->mc_filter);
}
-static struct net_device_ops ath6kl_netdev_ops = {
+static const 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,
+ .ndo_set_features = ath6kl_set_features,
+ .ndo_set_rx_mode = ath6kl_set_multicast_list,
};
void init_netdev(struct net_device *dev)
{
dev->netdev_ops = &ath6kl_netdev_ops;
+ dev->destructor = free_netdev;
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 + ATH6KL_HTC_ALIGN_BYTES;
+ dev->needed_headroom += roundup(sizeof(struct ath6kl_llc_snap_hdr) +
+ sizeof(struct wmi_data_hdr) +
+ HTC_HDR_LENGTH +
+ WMI_MAX_TX_META_SZ +
+ ATH6KL_HTC_ALIGN_BYTES, 4);
+
+ dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
return;
}
diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c
new file mode 100644
index 00000000000..3a8d5e97dc8
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/recovery.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "cfg80211.h"
+#include "debug.h"
+
+static void ath6kl_recovery_work(struct work_struct *work)
+{
+ struct ath6kl *ar = container_of(work, struct ath6kl,
+ fw_recovery.recovery_work);
+
+ ar->state = ATH6KL_STATE_RECOVERY;
+
+ del_timer_sync(&ar->fw_recovery.hb_timer);
+
+ ath6kl_init_hw_restart(ar);
+
+ ar->state = ATH6KL_STATE_ON;
+ clear_bit(WMI_CTRL_EP_FULL, &ar->flag);
+
+ ar->fw_recovery.err_reason = 0;
+
+ if (ar->fw_recovery.hb_poll)
+ mod_timer(&ar->fw_recovery.hb_timer, jiffies +
+ msecs_to_jiffies(ar->fw_recovery.hb_poll));
+}
+
+void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason)
+{
+ if (!ar->fw_recovery.enable)
+ return;
+
+ ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n",
+ reason);
+
+ set_bit(reason, &ar->fw_recovery.err_reason);
+
+ if (!test_bit(RECOVERY_CLEANUP, &ar->flag) &&
+ ar->state != ATH6KL_STATE_RECOVERY)
+ queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work);
+}
+
+void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie)
+{
+ if (cookie == ar->fw_recovery.seq_num)
+ ar->fw_recovery.hb_pending = false;
+}
+
+static void ath6kl_recovery_hb_timer(unsigned long data)
+{
+ struct ath6kl *ar = (struct ath6kl *) data;
+ int err;
+
+ if (test_bit(RECOVERY_CLEANUP, &ar->flag) ||
+ (ar->state == ATH6KL_STATE_RECOVERY))
+ return;
+
+ if (ar->fw_recovery.hb_pending)
+ ar->fw_recovery.hb_misscnt++;
+ else
+ ar->fw_recovery.hb_misscnt = 0;
+
+ if (ar->fw_recovery.hb_misscnt > ATH6KL_HB_RESP_MISS_THRES) {
+ ar->fw_recovery.hb_misscnt = 0;
+ ar->fw_recovery.seq_num = 0;
+ ar->fw_recovery.hb_pending = false;
+ ath6kl_recovery_err_notify(ar, ATH6KL_FW_HB_RESP_FAILURE);
+ return;
+ }
+
+ ar->fw_recovery.seq_num++;
+ ar->fw_recovery.hb_pending = true;
+
+ err = ath6kl_wmi_get_challenge_resp_cmd(ar->wmi,
+ ar->fw_recovery.seq_num, 0);
+ if (err)
+ ath6kl_warn("Failed to send hb challenge request, err:%d\n",
+ err);
+
+ mod_timer(&ar->fw_recovery.hb_timer, jiffies +
+ msecs_to_jiffies(ar->fw_recovery.hb_poll));
+}
+
+void ath6kl_recovery_init(struct ath6kl *ar)
+{
+ struct ath6kl_fw_recovery *recovery = &ar->fw_recovery;
+
+ clear_bit(RECOVERY_CLEANUP, &ar->flag);
+ INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work);
+ recovery->seq_num = 0;
+ recovery->hb_misscnt = 0;
+ ar->fw_recovery.hb_pending = false;
+ ar->fw_recovery.hb_timer.function = ath6kl_recovery_hb_timer;
+ ar->fw_recovery.hb_timer.data = (unsigned long) ar;
+ init_timer_deferrable(&ar->fw_recovery.hb_timer);
+
+ if (ar->fw_recovery.hb_poll)
+ mod_timer(&ar->fw_recovery.hb_timer, jiffies +
+ msecs_to_jiffies(ar->fw_recovery.hb_poll));
+}
+
+void ath6kl_recovery_cleanup(struct ath6kl *ar)
+{
+ if (!ar->fw_recovery.enable)
+ return;
+
+ set_bit(RECOVERY_CLEANUP, &ar->flag);
+
+ del_timer_sync(&ar->fw_recovery.hb_timer);
+ cancel_work_sync(&ar->fw_recovery.recovery_work);
+}
+
+void ath6kl_recovery_suspend(struct ath6kl *ar)
+{
+ if (!ar->fw_recovery.enable)
+ return;
+
+ ath6kl_recovery_cleanup(ar);
+
+ if (!ar->fw_recovery.err_reason)
+ return;
+
+ /* Process pending fw error detection */
+ ar->fw_recovery.err_reason = 0;
+ WARN_ON(ar->state != ATH6KL_STATE_ON);
+ ar->state = ATH6KL_STATE_RECOVERY;
+ ath6kl_init_hw_restart(ar);
+ ar->state = ATH6KL_STATE_ON;
+}
+
+void ath6kl_recovery_resume(struct ath6kl *ar)
+{
+ if (!ar->fw_recovery.enable)
+ return;
+
+ clear_bit(RECOVERY_CLEANUP, &ar->flag);
+
+ if (!ar->fw_recovery.hb_poll)
+ return;
+
+ ar->fw_recovery.hb_pending = false;
+ ar->fw_recovery.seq_num = 0;
+ ar->fw_recovery.hb_misscnt = 0;
+ mod_timer(&ar->fw_recovery.hb_timer,
+ jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll));
+}
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c
index f1dc311ee0c..339d89f14d3 100644
--- a/drivers/net/wireless/ath/ath6kl/sdio.c
+++ b/drivers/net/wireless/ath/ath6kl/sdio.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +15,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/module.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
@@ -21,15 +23,17 @@
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sd.h>
-#include "htc_hif.h"
+#include "hif.h"
#include "hif-ops.h"
#include "target.h"
#include "debug.h"
#include "cfg80211.h"
+#include "trace.h"
struct ath6kl_sdio {
struct sdio_func *func;
+ /* protects access to bus_req_freeq */
spinlock_t lock;
/* free list */
@@ -39,17 +43,29 @@ struct ath6kl_sdio {
struct bus_request bus_req[BUS_REQUEST_MAX_NUM];
struct ath6kl *ar;
+
u8 *dma_buffer;
+ /* protects access to dma_buffer */
+ struct mutex dma_buffer_mutex;
+
/* scatter request list head */
struct list_head scat_req;
+ atomic_t irq_handling;
+ wait_queue_head_t irq_wq;
+
+ /* protects access to scat_req */
spinlock_t scat_lock;
+
+ bool scatter_enabled;
+
bool is_disabled;
- atomic_t irq_handling;
const struct sdio_device_id *id;
struct work_struct wr_async_work;
struct list_head wr_asyncq;
+
+ /* protects access to wr_asyncq */
spinlock_t wr_async_lock;
};
@@ -134,6 +150,8 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
{
int ret = 0;
+ sdio_claim_host(func);
+
if (request & HIF_WRITE) {
/* FIXME: looks like ugly workaround for something */
if (addr >= HIF_MBOX_BASE_ADDR &&
@@ -155,23 +173,26 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
ret = sdio_memcpy_fromio(func, buf, addr, len);
}
+ sdio_release_host(func);
+
ath6kl_dbg(ATH6KL_DBG_SDIO, "%s addr 0x%x%s buf 0x%p len %d\n",
request & HIF_WRITE ? "wr" : "rd", addr,
request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len);
ath6kl_dbg_dump(ATH6KL_DBG_SDIO_DUMP, NULL, "sdio ", buf, len);
+ trace_ath6kl_sdio(addr, request, buf, 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);
+ spin_lock_bh(&ar_sdio->lock);
if (list_empty(&ar_sdio->bus_req_freeq)) {
- spin_unlock_irqrestore(&ar_sdio->lock, flag);
+ spin_unlock_bh(&ar_sdio->lock);
return NULL;
}
@@ -179,7 +200,7 @@ static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio)
struct bus_request, list);
list_del(&bus_req->list);
- spin_unlock_irqrestore(&ar_sdio->lock, flag);
+ spin_unlock_bh(&ar_sdio->lock);
ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n",
__func__, bus_req);
@@ -189,14 +210,12 @@ static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio)
static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio,
struct bus_request *bus_req)
{
- unsigned long flag;
-
ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n",
__func__, bus_req);
- spin_lock_irqsave(&ar_sdio->lock, flag);
+ spin_lock_bh(&ar_sdio->lock);
list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
- spin_unlock_irqrestore(&ar_sdio->lock, flag);
+ spin_unlock_bh(&ar_sdio->lock);
}
static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req,
@@ -290,10 +309,21 @@ static int ath6kl_sdio_scat_rw(struct ath6kl_sdio *ar_sdio,
mmc_req.cmd = &cmd;
mmc_req.data = &data;
+ sdio_claim_host(ar_sdio->func);
+
mmc_set_data_timeout(&data, ar_sdio->func->card);
+
+ trace_ath6kl_sdio_scat(scat_req->addr,
+ scat_req->req,
+ scat_req->len,
+ scat_req->scat_entries,
+ scat_req->scat_list);
+
/* synchronous call to process request */
mmc_wait_for_req(ar_sdio->func->card->host, &mmc_req);
+ sdio_release_host(ar_sdio->func);
+
status = cmd.error ? cmd.error : data.error;
scat_complete:
@@ -315,17 +345,17 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
{
struct hif_scatter_req *s_req;
struct bus_request *bus_req;
- int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz;
+ int i, scat_req_sz, scat_list_sz, size;
u8 *virt_buf;
- scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item);
+ scat_list_sz = n_scat_entry * 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;
+ size = sizeof(struct scatterlist) * n_scat_entry;
else
- buf_sz = 2 * L1_CACHE_BYTES +
- ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
+ size = 2 * L1_CACHE_BYTES +
+ ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
for (i = 0; i < n_scat_req; i++) {
/* allocate the scatter request */
@@ -334,7 +364,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
return -ENOMEM;
if (virt_scat) {
- virt_buf = kzalloc(buf_sz, GFP_KERNEL);
+ virt_buf = kzalloc(size, GFP_KERNEL);
if (!virt_buf) {
kfree(s_req);
return -ENOMEM;
@@ -344,7 +374,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
(u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf);
} else {
/* allocate sglist */
- s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL);
+ s_req->sgentries = kzalloc(size, GFP_KERNEL);
if (!s_req->sgentries) {
kfree(s_req);
@@ -388,17 +418,23 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
if (buf_needs_bounce(buf)) {
if (!ar_sdio->dma_buffer)
return -ENOMEM;
+ mutex_lock(&ar_sdio->dma_buffer_mutex);
tbuf = ar_sdio->dma_buffer;
- memcpy(tbuf, buf, len);
+
+ if (request & HIF_WRITE)
+ memcpy(tbuf, buf, len);
+
bounced = true;
- } else
+ } 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);
+
+ if (bounced)
+ mutex_unlock(&ar_sdio->dma_buffer_mutex);
return ret;
}
@@ -406,9 +442,9 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio,
struct bus_request *req)
{
- if (req->scat_req)
+ if (req->scat_req) {
ath6kl_sdio_scat_rw(ar_sdio, req);
- else {
+ } else {
void *context;
int status;
@@ -417,29 +453,25 @@ static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio,
req->request);
context = req->packet;
ath6kl_sdio_free_bus_req(ar_sdio, req);
- ath6kldev_rw_comp_handler(context, status);
+ ath6kl_hif_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);
+ spin_lock_bh(&ar_sdio->wr_async_lock);
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);
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
__ath6kl_sdio_write_async(ar_sdio, req);
- spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+ spin_lock_bh(&ar_sdio->wr_async_lock);
}
- spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
-
- sdio_release_host(ar_sdio->func);
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
}
static void ath6kl_sdio_irq_handler(struct sdio_func *func)
@@ -451,27 +483,32 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func)
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);
+ status = ath6kl_hif_intr_bh_handler(ar_sdio->ar);
sdio_claim_host(ar_sdio->func);
+
atomic_set(&ar_sdio->irq_handling, 0);
+ wake_up(&ar_sdio->irq_wq);
+
WARN_ON(status && status != -ECANCELED);
}
-static int ath6kl_sdio_power_on(struct ath6kl_sdio *ar_sdio)
+static int ath6kl_sdio_power_on(struct ath6kl *ar)
{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
struct sdio_func *func = ar_sdio->func;
int ret = 0;
if (!ar_sdio->is_disabled)
return 0;
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "sdio power on\n");
+
sdio_claim_host(func);
ret = sdio_enable_func(func);
@@ -494,13 +531,16 @@ static int ath6kl_sdio_power_on(struct ath6kl_sdio *ar_sdio)
return ret;
}
-static int ath6kl_sdio_power_off(struct ath6kl_sdio *ar_sdio)
+static int ath6kl_sdio_power_off(struct ath6kl *ar)
{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
int ret;
if (ar_sdio->is_disabled)
return 0;
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "sdio power off\n");
+
/* Disable the card */
sdio_claim_host(ar_sdio->func);
ret = sdio_disable_func(ar_sdio->func);
@@ -520,11 +560,10 @@ static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
{
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)
+ if (WARN_ON_ONCE(!bus_req))
return -ENOMEM;
bus_req->address = address;
@@ -533,9 +572,9 @@ static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer,
bus_req->request = request;
bus_req->packet = packet;
- spin_lock_irqsave(&ar_sdio->wr_async_lock, flags);
+ spin_lock_bh(&ar_sdio->wr_async_lock);
list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq);
- spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work);
return 0;
@@ -556,6 +595,13 @@ static void ath6kl_sdio_irq_enable(struct ath6kl *ar)
sdio_release_host(ar_sdio->func);
}
+static bool ath6kl_sdio_is_on_irq(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+
+ return !atomic_read(&ar_sdio->irq_handling);
+}
+
static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
@@ -563,10 +609,14 @@ static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
sdio_claim_host(ar_sdio->func);
- /* Mask our function IRQ */
- while (atomic_read(&ar_sdio->irq_handling)) {
+ if (atomic_read(&ar_sdio->irq_handling)) {
sdio_release_host(ar_sdio->func);
- schedule_timeout(HZ / 10);
+
+ ret = wait_event_interruptible(ar_sdio->irq_wq,
+ ath6kl_sdio_is_on_irq(ar));
+ if (ret)
+ return;
+
sdio_claim_host(ar_sdio->func);
}
@@ -581,17 +631,18 @@ 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);
+ spin_lock_bh(&ar_sdio->scat_lock);
if (!list_empty(&ar_sdio->scat_req)) {
node = list_first_entry(&ar_sdio->scat_req,
struct hif_scatter_req, list);
list_del(&node->list);
+
+ node->scat_q_depth = get_queue_depth(&ar_sdio->scat_req);
}
- spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+ spin_unlock_bh(&ar_sdio->scat_lock);
return node;
}
@@ -600,14 +651,12 @@ 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);
+ spin_lock_bh(&ar_sdio->scat_lock);
list_add_tail(&s_req->list, &ar_sdio->scat_req);
- spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
-
+ spin_unlock_bh(&ar_sdio->scat_lock);
}
/* scatter gather read write request */
@@ -617,23 +666,20 @@ static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar,
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);
+ "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);
+ spin_lock_bh(&ar_sdio->wr_async_lock);
list_add_tail(&scat_req->busrequest->list, &ar_sdio->wr_asyncq);
- spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags);
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work);
}
@@ -645,23 +691,27 @@ 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);
+ spin_lock_bh(&ar_sdio->scat_lock);
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);
+ spin_unlock_bh(&ar_sdio->scat_lock);
+ /*
+ * FIXME: should we also call completion handler with
+ * ath6kl_hif_rw_comp_handler() with status -ECANCELED so
+ * that the packet is properly freed?
+ */
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_lock_bh(&ar_sdio->scat_lock);
}
- spin_unlock_irqrestore(&ar_sdio->scat_lock, flag);
+ spin_unlock_bh(&ar_sdio->scat_lock);
}
/* setup of HIF scatter resources */
@@ -669,9 +719,14 @@ 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;
+ int ret = 0;
bool virt_scat = false;
+ if (ar_sdio->scatter_enabled)
+ return 0;
+
+ ar_sdio->scatter_enabled = true;
+
/* 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",
@@ -686,8 +741,8 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
MAX_SCATTER_REQUESTS, virt_scat);
if (!ret) {
- ath6kl_dbg(ATH6KL_DBG_SCATTER,
- "hif-scatter enabled: max scatter req : %d entries: %d\n",
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "hif-scatter enabled requests %d entries %d\n",
MAX_SCATTER_REQUESTS,
MAX_SCATTER_ENTRIES_PER_REQ);
@@ -711,8 +766,8 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
return ret;
}
- ath6kl_dbg(ATH6KL_DBG_SCATTER,
- "Vitual scatter enabled, max_scat_req:%d, entries:%d\n",
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "virtual scatter enabled requests %d entries %d\n",
ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ);
target->max_scat_entries = ATH6KL_SCATTER_ENTRIES_PER_REQ;
@@ -723,7 +778,46 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
return 0;
}
-static int ath6kl_sdio_suspend(struct ath6kl *ar)
+static int ath6kl_sdio_config(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct sdio_func *func = ar_sdio->func;
+ int ret;
+
+ 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);
+ goto out;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "4-bit async irq mode enabled\n");
+ }
+
+ /* give us some time to enable, in ms */
+ func->enable_timeout = 100;
+
+ 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);
+ goto out;
+ }
+
+out:
+ sdio_release_host(func);
+
+ return ret;
+}
+
+static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
struct sdio_func *func = ar_sdio->func;
@@ -732,25 +826,419 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar)
flags = sdio_get_host_pm_caps(func);
- if (!(flags & MMC_PM_KEEP_POWER))
- /* as host doesn't support keep power we need to bail out */
- ath6kl_dbg(ATH6KL_DBG_SDIO,
- "func %d doesn't support MMC_PM_KEEP_POWER\n",
- func->num);
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags);
+
+ if (!(flags & MMC_PM_WAKE_SDIO_IRQ) ||
+ !(flags & MMC_PM_KEEP_POWER))
return -EINVAL;
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret) {
- printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n",
- ret);
+ ath6kl_err("set sdio keep pwr flag failed: %d\n", ret);
+ return ret;
+ }
+
+ /* sdio irq wakes up host */
+ ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+ if (ret)
+ ath6kl_err("set sdio wake irq flag failed: %d\n", ret);
+
+ return ret;
+}
+
+static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct sdio_func *func = ar_sdio->func;
+ mmc_pm_flag_t flags;
+ bool try_deepsleep = false;
+ int ret;
+
+ if (ar->suspend_mode == WLAN_POWER_STATE_WOW ||
+ (!ar->suspend_mode && wow)) {
+ ret = ath6kl_set_sdio_pm_caps(ar);
+ if (ret)
+ goto cut_pwr;
+
+ ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow);
+ if (ret && ret != -ENOTCONN)
+ ath6kl_err("wow suspend failed: %d\n", ret);
+
+ if (ret &&
+ (!ar->wow_suspend_mode ||
+ ar->wow_suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP))
+ try_deepsleep = true;
+ else if (ret &&
+ ar->wow_suspend_mode == WLAN_POWER_STATE_CUT_PWR)
+ goto cut_pwr;
+ if (!ret)
+ return 0;
+ }
+
+ if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP ||
+ !ar->suspend_mode || try_deepsleep) {
+ flags = sdio_get_host_pm_caps(func);
+ if (!(flags & MMC_PM_KEEP_POWER))
+ goto cut_pwr;
+
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret)
+ goto cut_pwr;
+
+ /*
+ * Workaround to support Deep Sleep with MSM, set the host pm
+ * flag as MMC_PM_WAKE_SDIO_IRQ to allow SDCC deiver to disable
+ * the sdc2_clock and internally allows MSM to enter
+ * TCXO shutdown properly.
+ */
+ if ((flags & MMC_PM_WAKE_SDIO_IRQ)) {
+ ret = sdio_set_host_pm_flags(func,
+ MMC_PM_WAKE_SDIO_IRQ);
+ if (ret)
+ goto cut_pwr;
+ }
+
+ ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP,
+ NULL);
+ if (ret)
+ goto cut_pwr;
+
+ return 0;
+ }
+
+cut_pwr:
+ if (func->card && func->card->host)
+ func->card->host->pm_flags &= ~MMC_PM_KEEP_POWER;
+
+ return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL);
+}
+
+static int ath6kl_sdio_resume(struct ath6kl *ar)
+{
+ switch (ar->state) {
+ case ATH6KL_STATE_OFF:
+ case ATH6KL_STATE_CUTPOWER:
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND,
+ "sdio resume configuring sdio\n");
+
+ /* need to set sdio settings after power is cut from sdio */
+ ath6kl_sdio_config(ar);
+ break;
+
+ case ATH6KL_STATE_ON:
+ break;
+
+ case ATH6KL_STATE_DEEPSLEEP:
+ break;
+
+ case ATH6KL_STATE_WOW:
+ break;
+
+ case ATH6KL_STATE_SUSPENDING:
+ break;
+
+ case ATH6KL_STATE_RESUMING:
+ break;
+
+ case ATH6KL_STATE_RECOVERY:
+ break;
+ }
+
+ ath6kl_cfg80211_resume(ar);
+
+ return 0;
+}
+
+/* set the window address register (using 4-byte register access ). */
+static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
+{
+ int status;
+ u8 addr_val[4];
+ s32 i;
+
+ /*
+ * Write bytes 1,2,3 of the register to set the upper address bytes,
+ * the LSB is written last to initiate the access cycle
+ */
+
+ for (i = 1; i <= 3; i++) {
+ /*
+ * Fill the buffer with the address byte value we want to
+ * hit 4 times.
+ */
+ memset(addr_val, ((u8 *)&addr)[i], 4);
+
+ /*
+ * Hit each byte of the register address with a 4-byte
+ * write operation to the same address, this is a harmless
+ * operation.
+ */
+ status = ath6kl_sdio_read_write_sync(ar, reg_addr + i, addr_val,
+ 4, HIF_WR_SYNC_BYTE_FIX);
+ if (status)
+ break;
+ }
+
+ if (status) {
+ ath6kl_err("%s: failed to write initial bytes of 0x%x to window reg: 0x%X\n",
+ __func__, addr, reg_addr);
+ return status;
+ }
+
+ /*
+ * Write the address register again, this time write the whole
+ * 4-byte value. The effect here is that the LSB write causes the
+ * cycle to start, the extra 3 byte write to bytes 1,2,3 has no
+ * effect since we are writing the same values again
+ */
+ status = ath6kl_sdio_read_write_sync(ar, reg_addr, (u8 *)(&addr),
+ 4, HIF_WR_SYNC_BYTE_INC);
+
+ if (status) {
+ ath6kl_err("%s: failed to write 0x%x to window reg: 0x%X\n",
+ __func__, addr, reg_addr);
+ return status;
+ }
+
+ return 0;
+}
+
+static int ath6kl_sdio_diag_read32(struct ath6kl *ar, u32 address, u32 *data)
+{
+ int status;
+
+ /* set window register to start read cycle */
+ status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS,
+ address);
+
+ if (status)
+ return status;
+
+ /* read the data */
+ status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS,
+ (u8 *)data, sizeof(u32), HIF_RD_SYNC_BYTE_INC);
+ if (status) {
+ ath6kl_err("%s: failed to read from window data addr\n",
+ __func__);
+ return status;
+ }
+
+ return status;
+}
+
+static int ath6kl_sdio_diag_write32(struct ath6kl *ar, u32 address,
+ __le32 data)
+{
+ int status;
+ u32 val = (__force u32) data;
+
+ /* set write data */
+ status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS,
+ (u8 *) &val, sizeof(u32), HIF_WR_SYNC_BYTE_INC);
+ if (status) {
+ ath6kl_err("%s: failed to write 0x%x to window data addr\n",
+ __func__, data);
+ return status;
+ }
+
+ /* set window register, which starts the write cycle */
+ return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
+ address);
+}
+
+static int ath6kl_sdio_bmi_credits(struct ath6kl *ar)
+{
+ u32 addr;
+ unsigned long timeout;
+ int ret;
+
+ ar->bmi.cmd_credits = 0;
+
+ /* Read the counter register to get the command credits */
+ addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4;
+
+ timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
+ while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
+ /*
+ * Hit the credit counter with a 4-byte access, the first byte
+ * read will hit the counter and cause a decrement, while the
+ * remaining 3 bytes has no effect. The rationale behind this
+ * is to make all HIF accesses 4-byte aligned.
+ */
+ ret = ath6kl_sdio_read_write_sync(ar, addr,
+ (u8 *)&ar->bmi.cmd_credits, 4,
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_err("Unable to decrement the command credit count register: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* The counter is only 8 bits.
+ * Ignore anything in the upper 3 bytes
+ */
+ ar->bmi.cmd_credits &= 0xFF;
+ }
+
+ if (!ar->bmi.cmd_credits) {
+ ath6kl_err("bmi communication timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar)
+{
+ unsigned long timeout;
+ u32 rx_word = 0;
+ int ret = 0;
+
+ timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
+ while ((time_before(jiffies, timeout)) && !rx_word) {
+ ret = ath6kl_sdio_read_write_sync(ar,
+ RX_LOOKAHEAD_VALID_ADDRESS,
+ (u8 *)&rx_word, sizeof(rx_word),
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n");
+ return ret;
+ }
+
+ /* all we really want is one bit */
+ rx_word &= (1 << ENDPOINT1);
+ }
+
+ if (!rx_word) {
+ ath6kl_err("bmi_recv_buf FIFO empty\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ath6kl_sdio_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ int ret;
+ u32 addr;
+
+ ret = ath6kl_sdio_bmi_credits(ar);
+ if (ret)
+ return ret;
+
+ addr = ar->mbox_info.htc_addr;
+
+ ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len,
+ HIF_WR_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_err("unable to send the bmi data to the device\n");
return ret;
}
- ath6kl_deep_sleep_enable(ar);
+ return 0;
+}
+
+static int ath6kl_sdio_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ int ret;
+ u32 addr;
+
+ /*
+ * During normal bootup, small reads may be required.
+ * Rather than issue an HIF Read and then wait as the Target
+ * adds successive bytes to the FIFO, we wait here until
+ * we know that response data is available.
+ *
+ * This allows us to cleanly timeout on an unexpected
+ * Target failure rather than risk problems at the HIF level.
+ * In particular, this avoids SDIO timeouts and possibly garbage
+ * data on some host controllers. And on an interconnect
+ * such as Compact Flash (as well as some SDIO masters) which
+ * does not provide any indication on data timeout, it avoids
+ * a potential hang or garbage response.
+ *
+ * Synchronization is more difficult for reads larger than the
+ * size of the MBOX FIFO (128B), because the Target is unable
+ * to push the 129th byte of data until AFTER the Host posts an
+ * HIF Read and removes some FIFO data. So for large reads the
+ * Host proceeds to post an HIF Read BEFORE all the data is
+ * actually available to read. Fortunately, large BMI reads do
+ * not occur in practice -- they're supported for debug/development.
+ *
+ * So Host/Target BMI synchronization is divided into these cases:
+ * CASE 1: length < 4
+ * Should not happen
+ *
+ * CASE 2: 4 <= length <= 128
+ * Wait for first 4 bytes to be in FIFO
+ * If CONSERVATIVE_BMI_READ is enabled, also wait for
+ * a BMI command credit, which indicates that the ENTIRE
+ * response is available in the the FIFO
+ *
+ * CASE 3: length > 128
+ * Wait for the first 4 bytes to be in FIFO
+ *
+ * For most uses, a small timeout should be sufficient and we will
+ * usually see a response quickly; but there may be some unusual
+ * (debug) cases of BMI_EXECUTE where we want an larger timeout.
+ * For now, we use an unbounded busy loop while waiting for
+ * BMI_EXECUTE.
+ *
+ * If BMI_EXECUTE ever needs to support longer-latency execution,
+ * especially in production, this code needs to be enhanced to sleep
+ * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently
+ * a function of Host processor speed.
+ */
+ if (len >= 4) { /* NB: Currently, always true */
+ ret = ath6kl_bmi_get_rx_lkahd(ar);
+ if (ret)
+ return ret;
+ }
+
+ addr = ar->mbox_info.htc_addr;
+ ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len,
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath6kl_err("Unable to read the bmi data from the device: %d\n",
+ ret);
+ return ret;
+ }
return 0;
}
+static void ath6kl_sdio_stop(struct ath6kl *ar)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct bus_request *req, *tmp_req;
+ void *context;
+
+ /* FIXME: make sure that wq is not queued again */
+
+ cancel_work_sync(&ar_sdio->wr_async_work);
+
+ spin_lock_bh(&ar_sdio->wr_async_lock);
+
+ list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
+ list_del(&req->list);
+
+ if (req->scat_req) {
+ /* this is a scatter gather request */
+ req->scat_req->status = -ECANCELED;
+ req->scat_req->complete(ar_sdio->ar->htc_target,
+ req->scat_req);
+ } else {
+ context = req->packet;
+ ath6kl_sdio_free_bus_req(ar_sdio, req);
+ ath6kl_hif_rw_comp_handler(context, -ECANCELED);
+ }
+ }
+
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
+
+ WARN_ON(get_queue_depth(&ar_sdio->scat_req) != 4);
+}
+
static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
.read_write_sync = ath6kl_sdio_read_write_sync,
.write_async = ath6kl_sdio_write_async,
@@ -762,8 +1250,47 @@ static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
.scat_req_rw = ath6kl_sdio_async_rw_scatter,
.cleanup_scatter = ath6kl_sdio_cleanup_scatter,
.suspend = ath6kl_sdio_suspend,
+ .resume = ath6kl_sdio_resume,
+ .diag_read32 = ath6kl_sdio_diag_read32,
+ .diag_write32 = ath6kl_sdio_diag_write32,
+ .bmi_read = ath6kl_sdio_bmi_read,
+ .bmi_write = ath6kl_sdio_bmi_write,
+ .power_on = ath6kl_sdio_power_on,
+ .power_off = ath6kl_sdio_power_off,
+ .stop = ath6kl_sdio_stop,
};
+#ifdef CONFIG_PM_SLEEP
+
+/*
+ * Empty handlers so that mmc subsystem doesn't remove us entirely during
+ * suspend. We instead follow cfg80211 suspend/resume handlers.
+ */
+static int ath6kl_sdio_pm_suspend(struct device *device)
+{
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio pm suspend\n");
+
+ return 0;
+}
+
+static int ath6kl_sdio_pm_resume(struct device *device)
+{
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio pm resume\n");
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ath6kl_sdio_pm_ops, ath6kl_sdio_pm_suspend,
+ ath6kl_sdio_pm_resume);
+
+#define ATH6KL_SDIO_PM_OPS (&ath6kl_sdio_pm_ops)
+
+#else
+
+#define ATH6KL_SDIO_PM_OPS NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
static int ath6kl_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
@@ -772,8 +1299,8 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
struct ath6kl *ar;
int count;
- ath6kl_dbg(ATH6KL_DBG_SDIO,
- "new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
func->num, func->vendor, func->device,
func->max_blksize, func->cur_blksize);
@@ -796,6 +1323,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
spin_lock_init(&ar_sdio->lock);
spin_lock_init(&ar_sdio->scat_lock);
spin_lock_init(&ar_sdio->wr_async_lock);
+ mutex_init(&ar_sdio->dma_buffer_mutex);
INIT_LIST_HEAD(&ar_sdio->scat_req);
INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
@@ -803,10 +1331,12 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work);
+ init_waitqueue_head(&ar_sdio->irq_wq);
+
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);
+ ar = ath6kl_core_create(&ar_sdio->func->dev);
if (!ar) {
ath6kl_err("Failed to alloc ath6kl core\n");
ret = -ENOMEM;
@@ -814,62 +1344,29 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
}
ar_sdio->ar = ar;
+ ar->hif_type = ATH6KL_HIF_TYPE_SDIO;
ar->hif_priv = ar_sdio;
ar->hif_ops = &ath6kl_sdio_ops;
+ ar->bmi.max_data_size = 256;
ath6kl_sdio_set_mbox_info(ar);
- 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_cfg80211;
- }
-
- ath6kl_dbg(ATH6KL_DBG_SDIO, "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_cfg80211;
-
- sdio_claim_host(func);
-
- ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE);
+ ret = ath6kl_sdio_config(ar);
if (ret) {
- ath6kl_err("Set sdio block size %d failed: %d)\n",
- HIF_MBOX_BLOCK_SIZE, ret);
- sdio_release_host(func);
- goto err_off;
+ ath6kl_err("Failed to config sdio: %d\n", ret);
+ goto err_core_alloc;
}
- sdio_release_host(func);
-
- ret = ath6kl_core_init(ar);
+ ret = ath6kl_core_init(ar, ATH6KL_HTC_TYPE_MBOX);
if (ret) {
ath6kl_err("Failed to init ath6kl core\n");
- goto err_off;
+ goto err_core_alloc;
}
return ret;
-err_off:
- ath6kl_sdio_power_off(ar_sdio);
-err_cfg80211:
- ath6kl_cfg80211_deinit(ar_sdio->ar);
+err_core_alloc:
+ ath6kl_core_destroy(ar_sdio->ar);
err_dma:
kfree(ar_sdio->dma_buffer);
err_hif:
@@ -882,8 +1379,8 @@ static void ath6kl_sdio_remove(struct sdio_func *func)
{
struct ath6kl_sdio *ar_sdio;
- ath6kl_dbg(ATH6KL_DBG_SDIO,
- "removed func %d vendor 0x%x device 0x%x\n",
+ ath6kl_dbg(ATH6KL_DBG_BOOT,
+ "sdio removed func %d vendor 0x%x device 0x%x\n",
func->num, func->vendor, func->device);
ar_sdio = sdio_get_drvdata(func);
@@ -891,9 +1388,8 @@ static void ath6kl_sdio_remove(struct sdio_func *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);
+ ath6kl_core_cleanup(ar_sdio->ar);
+ ath6kl_core_destroy(ar_sdio->ar);
kfree(ar_sdio->dma_buffer);
kfree(ar_sdio);
@@ -902,6 +1398,8 @@ static void ath6kl_sdio_remove(struct sdio_func *func)
static const struct sdio_device_id ath6kl_sdio_devices[] = {
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
+ {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
+ {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
{},
};
@@ -912,6 +1410,7 @@ static struct sdio_driver ath6kl_sdio_driver = {
.id_table = ath6kl_sdio_devices,
.probe = ath6kl_sdio_probe,
.remove = ath6kl_sdio_remove,
+ .drv.pm = ATH6KL_SDIO_PM_OPS,
};
static int __init ath6kl_sdio_init(void)
@@ -937,13 +1436,25 @@ 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);
+MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_OTP_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_PATCH_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_OTP_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_PATCH_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_2_FW_DIR "/" AR6004_HW_1_2_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_3_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h
index c9a76051f04..d5eeeae7711 100644
--- a/drivers/net/wireless/ath/ath6kl/target.h
+++ b/drivers/net/wireless/ath/ath6kl/target.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2010 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -19,11 +20,12 @@
#define AR6003_BOARD_DATA_SZ 1024
#define AR6003_BOARD_EXT_DATA_SZ 768
+#define AR6003_BOARD_EXT_DATA_SZ_V2 1024
-#define AR6004_BOARD_DATA_SZ 7168
+#define AR6004_BOARD_DATA_SZ 6144
#define AR6004_BOARD_EXT_DATA_SZ 0
-#define RESET_CONTROL_ADDRESS 0x00000000
+#define RESET_CONTROL_ADDRESS 0x00004000
#define RESET_CONTROL_COLD_RST 0x00000100
#define RESET_CONTROL_MBOX_RST 0x00000004
@@ -43,6 +45,7 @@
#define LPO_CAL_ENABLE_S 20
#define LPO_CAL_ENABLE 0x00100000
+#define GPIO_PIN9_ADDRESS 0x0000004c
#define GPIO_PIN10_ADDRESS 0x00000050
#define GPIO_PIN11_ADDRESS 0x00000054
#define GPIO_PIN12_ADDRESS 0x00000058
@@ -286,7 +289,7 @@ struct host_interest {
u32 hi_hp_rx_traffic_ratio; /* 0xd8 */
/* test applications flags */
- u32 hi_test_apps_related ; /* 0xdc */
+ u32 hi_test_apps_related; /* 0xdc */
/* location of test script */
u32 hi_ota_testscript; /* 0xe0 */
/* location of CAL data */
@@ -320,7 +323,10 @@ struct host_interest {
| (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2)
|------------------------------------------------------------------------------|
*/
+#define HI_OPTION_FW_MODE_BITS 0x2
#define HI_OPTION_FW_MODE_SHIFT 0xC
+
+#define HI_OPTION_FW_SUBMODE_BITS 0x2
#define HI_OPTION_FW_SUBMODE_SHIFT 0x14
/* Convert a Target virtual address into a Target physical address */
@@ -331,20 +337,6 @@ struct host_interest {
(((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \
(((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : 0))
-#define AR6003_REV2_APP_LOAD_ADDRESS 0x543180
-#define AR6003_REV2_BOARD_EXT_DATA_ADDRESS 0x57E500
-#define AR6003_REV2_DATASET_PATCH_ADDRESS 0x57e884
-#define AR6003_REV2_RAM_RESERVE_SIZE 6912
-
-#define AR6003_REV3_APP_LOAD_ADDRESS 0x545000
-#define AR6003_REV3_BOARD_EXT_DATA_ADDRESS 0x542330
-#define AR6003_REV3_DATASET_PATCH_ADDRESS 0x57FF74
-#define AR6003_REV3_RAM_RESERVE_SIZE 512
-
-#define AR6004_REV1_BOARD_DATA_ADDRESS 0x435400
-#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS 0x437000
-#define AR6004_REV1_RAM_RESERVE_SIZE 11264
-
#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500
struct ath6kl_dbglog_buf {
diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c
index 381eb66a605..d67170ea103 100644
--- a/drivers/net/wireless/ath/ath6kl/testmode.c
+++ b/drivers/net/wireless/ath/ath6kl/testmode.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -15,6 +16,7 @@
*/
#include "testmode.h"
+#include "debug.h"
#include <net/netlink.h>
@@ -30,7 +32,7 @@ enum ath6kl_tm_attr {
enum ath6kl_tm_cmd {
ATH6KL_TM_CMD_TCMD = 0,
- ATH6KL_TM_CMD_RX_REPORT = 1,
+ ATH6KL_TM_CMD_RX_REPORT = 1, /* not used anymore */
};
#define ATH6KL_TM_DATA_MAX_LEN 5000
@@ -41,84 +43,35 @@ static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = {
.len = ATH6KL_TM_DATA_MAX_LEN },
};
-void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len)
+void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len)
{
- if (down_interruptible(&ar->sem))
- return;
-
- kfree(ar->tm.rx_report);
-
- ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL);
- ar->tm.rx_report_len = buf_len;
-
- up(&ar->sem);
-
- wake_up(&ar->event_wq);
-}
-
-static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len,
- struct sk_buff *skb)
-{
- int ret = 0;
- long left;
-
- if (down_interruptible(&ar->sem))
- return -ERESTARTSYS;
-
- if (!test_bit(WMI_READY, &ar->flag)) {
- ret = -EIO;
- goto out;
- }
-
- if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
- ret = -EBUSY;
- goto out;
- }
-
- if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) {
- up(&ar->sem);
- return -EIO;
- }
-
- left = wait_event_interruptible_timeout(ar->event_wq,
- ar->tm.rx_report != NULL,
- WMI_TIMEOUT);
+ struct sk_buff *skb;
- if (left == 0) {
- ret = -ETIMEDOUT;
- goto out;
- } else if (left < 0) {
- ret = left;
- goto out;
- }
+ if (!buf || buf_len == 0)
+ return;
- if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) {
- ret = -EINVAL;
- goto out;
+ skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_KERNEL);
+ if (!skb) {
+ ath6kl_warn("failed to allocate testmode rx skb!\n");
+ return;
}
-
- NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len,
- ar->tm.rx_report);
-
- kfree(ar->tm.rx_report);
- ar->tm.rx_report = NULL;
-
-out:
- up(&ar->sem);
-
- return ret;
+ if (nla_put_u32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD) ||
+ nla_put(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf))
+ goto nla_put_failure;
+ cfg80211_testmode_event(skb, GFP_KERNEL);
+ return;
nla_put_failure:
- ret = -ENOBUFS;
- goto out;
+ kfree_skb(skb);
+ ath6kl_warn("nla_put failed on testmode rx skb!\n");
}
-int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len)
{
struct ath6kl *ar = wiphy_priv(wiphy);
struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1];
- int err, buf_len, reply_len;
- struct sk_buff *skb;
+ int err, buf_len;
void *buf;
err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len,
@@ -143,24 +96,6 @@ int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
break;
case ATH6KL_TM_CMD_RX_REPORT:
- if (!tb[ATH6KL_TM_ATTR_DATA])
- return -EINVAL;
-
- buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
- buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
-
- reply_len = nla_total_size(ATH6KL_TM_DATA_MAX_LEN);
- skb = cfg80211_testmode_alloc_reply_skb(wiphy, reply_len);
- if (!skb)
- return -ENOMEM;
-
- err = ath6kl_tm_rx_report(ar, buf, buf_len, skb);
- if (err < 0) {
- kfree_skb(skb);
- return err;
- }
-
- return cfg80211_testmode_reply(skb);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/wireless/ath/ath6kl/testmode.h b/drivers/net/wireless/ath/ath6kl/testmode.h
index 43dffcc11fb..9fbcdec3e20 100644
--- a/drivers/net/wireless/ath/ath6kl/testmode.h
+++ b/drivers/net/wireless/ath/ath6kl/testmode.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,17 +19,20 @@
#ifdef CONFIG_NL80211_TESTMODE
-void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len);
-int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len);
+void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len);
+int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len);
#else
-static inline void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf,
- size_t buf_len)
+static inline void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf,
+ size_t buf_len)
{
}
-static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+static inline int ath6kl_tm_cmd(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ void *data, int len)
{
return 0;
}
diff --git a/drivers/net/wireless/ath/ath6kl/trace.c b/drivers/net/wireless/ath/ath6kl/trace.c
new file mode 100644
index 00000000000..e7d64b1285c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/trace.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL(ath6kl_sdio);
+EXPORT_TRACEPOINT_SYMBOL(ath6kl_sdio_scat);
diff --git a/drivers/net/wireless/ath/ath6kl/trace.h b/drivers/net/wireless/ath/ath6kl/trace.h
new file mode 100644
index 00000000000..1a1ea7881b4
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/trace.h
@@ -0,0 +1,332 @@
+#if !defined(_ATH6KL_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+
+#include <net/cfg80211.h>
+#include <linux/skbuff.h>
+#include <linux/tracepoint.h>
+#include "wmi.h"
+#include "hif.h"
+
+#if !defined(_ATH6KL_TRACE_H)
+static inline unsigned int ath6kl_get_wmi_id(void *buf, size_t buf_len)
+{
+ struct wmi_cmd_hdr *hdr = buf;
+
+ if (buf_len < sizeof(*hdr))
+ return 0;
+
+ return le16_to_cpu(hdr->cmd_id);
+}
+#endif /* __ATH6KL_TRACE_H */
+
+#define _ATH6KL_TRACE_H
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_ATH6KL_TRACING)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_ATH6KL_TRACING || __CHECKER__ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ath6kl
+
+TRACE_EVENT(ath6kl_wmi_cmd,
+ TP_PROTO(void *buf, size_t buf_len),
+
+ TP_ARGS(buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, id)
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->id = ath6kl_get_wmi_id(buf, buf_len);
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "id %d len %zd",
+ __entry->id, __entry->buf_len
+ )
+);
+
+TRACE_EVENT(ath6kl_wmi_event,
+ TP_PROTO(void *buf, size_t buf_len),
+
+ TP_ARGS(buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, id)
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->id = ath6kl_get_wmi_id(buf, buf_len);
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "id %d len %zd",
+ __entry->id, __entry->buf_len
+ )
+);
+
+TRACE_EVENT(ath6kl_sdio,
+ TP_PROTO(unsigned int addr, int flags,
+ void *buf, size_t buf_len),
+
+ TP_ARGS(addr, flags, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, tx)
+ __field(unsigned int, addr)
+ __field(int, flags)
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->addr = addr;
+ __entry->flags = flags;
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+
+ if (flags & HIF_WRITE)
+ __entry->tx = 1;
+ else
+ __entry->tx = 0;
+ ),
+
+ TP_printk(
+ "%s addr 0x%x flags 0x%x len %zd\n",
+ __entry->tx ? "tx" : "rx",
+ __entry->addr,
+ __entry->flags,
+ __entry->buf_len
+ )
+);
+
+TRACE_EVENT(ath6kl_sdio_scat,
+ TP_PROTO(unsigned int addr, int flags, unsigned int total_len,
+ unsigned int entries, struct hif_scatter_item *list),
+
+ TP_ARGS(addr, flags, total_len, entries, list),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, tx)
+ __field(unsigned int, addr)
+ __field(int, flags)
+ __field(unsigned int, entries)
+ __field(size_t, total_len)
+ __dynamic_array(unsigned int, len_array, entries)
+ __dynamic_array(u8, data, total_len)
+ ),
+
+ TP_fast_assign(
+ unsigned int *len_array;
+ int i, offset = 0;
+ size_t len;
+
+ __entry->addr = addr;
+ __entry->flags = flags;
+ __entry->entries = entries;
+ __entry->total_len = total_len;
+
+ if (flags & HIF_WRITE)
+ __entry->tx = 1;
+ else
+ __entry->tx = 0;
+
+ len_array = __get_dynamic_array(len_array);
+
+ for (i = 0; i < entries; i++) {
+ len = list[i].len;
+
+ memcpy((u8 *) __get_dynamic_array(data) + offset,
+ list[i].buf, len);
+
+ len_array[i] = len;
+ offset += len;
+ }
+ ),
+
+ TP_printk(
+ "%s addr 0x%x flags 0x%x entries %d total_len %zd\n",
+ __entry->tx ? "tx" : "rx",
+ __entry->addr,
+ __entry->flags,
+ __entry->entries,
+ __entry->total_len
+ )
+);
+
+TRACE_EVENT(ath6kl_sdio_irq,
+ TP_PROTO(void *buf, size_t buf_len),
+
+ TP_ARGS(buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "irq len %zd\n", __entry->buf_len
+ )
+);
+
+TRACE_EVENT(ath6kl_htc_rx,
+ TP_PROTO(int status, int endpoint, void *buf,
+ size_t buf_len),
+
+ TP_ARGS(status, endpoint, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(int, status)
+ __field(int, endpoint)
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->status = status;
+ __entry->endpoint = endpoint;
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "status %d endpoint %d len %zd\n",
+ __entry->status,
+ __entry->endpoint,
+ __entry->buf_len
+ )
+);
+
+TRACE_EVENT(ath6kl_htc_tx,
+ TP_PROTO(int status, int endpoint, void *buf,
+ size_t buf_len),
+
+ TP_ARGS(status, endpoint, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(int, status)
+ __field(int, endpoint)
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->status = status;
+ __entry->endpoint = endpoint;
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "status %d endpoint %d len %zd\n",
+ __entry->status,
+ __entry->endpoint,
+ __entry->buf_len
+ )
+);
+
+#define ATH6KL_MSG_MAX 200
+
+DECLARE_EVENT_CLASS(ath6kl_log_event,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf),
+ TP_STRUCT__entry(
+ __dynamic_array(char, msg, ATH6KL_MSG_MAX)
+ ),
+ TP_fast_assign(
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ ATH6KL_MSG_MAX,
+ vaf->fmt,
+ *vaf->va) >= ATH6KL_MSG_MAX);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(ath6kl_log_event, ath6kl_log_err,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath6kl_log_event, ath6kl_log_warn,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath6kl_log_event, ath6kl_log_info,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+TRACE_EVENT(ath6kl_log_dbg,
+ TP_PROTO(unsigned int level, struct va_format *vaf),
+ TP_ARGS(level, vaf),
+ TP_STRUCT__entry(
+ __field(unsigned int, level)
+ __dynamic_array(char, msg, ATH6KL_MSG_MAX)
+ ),
+ TP_fast_assign(
+ __entry->level = level;
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ ATH6KL_MSG_MAX,
+ vaf->fmt,
+ *vaf->va) >= ATH6KL_MSG_MAX);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+
+TRACE_EVENT(ath6kl_log_dbg_dump,
+ TP_PROTO(const char *msg, const char *prefix,
+ const void *buf, size_t buf_len),
+
+ TP_ARGS(msg, prefix, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __string(msg, msg)
+ __string(prefix, prefix)
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __assign_str(msg, msg);
+ __assign_str(prefix, prefix);
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "%s/%s\n", __get_str(prefix), __get_str(msg)
+ )
+);
+
+#endif /* _ ATH6KL_TRACE_H || TRACE_HEADER_MULTI_READ*/
+
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
index a7117074f81..40432fe7a5d 100644
--- a/drivers/net/wireless/ath/ath6kl/txrx.c
+++ b/drivers/net/wireless/ath/ath6kl/txrx.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,8 +15,29 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "core.h"
#include "debug.h"
+#include "htc-ops.h"
+#include "trace.h"
+
+/*
+ * tid - tid_mux0..tid_mux3
+ * aid - tid_mux4..tid_mux7
+ */
+#define ATH6KL_TID_MASK 0xf
+#define ATH6KL_AID_SHIFT 4
+
+static inline u8 ath6kl_get_tid(u8 tid_mux)
+{
+ return tid_mux & ATH6KL_TID_MASK;
+}
+
+static inline u8 ath6kl_get_aid(u8 tid_mux)
+{
+ return tid_mux >> ATH6KL_AID_SHIFT;
+}
static u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev,
u32 *map_no)
@@ -77,12 +99,120 @@ static u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev,
return ar->node_map[ep_map].ep_id;
}
-static bool ath6kl_powersave_ap(struct ath6kl *ar, struct sk_buff *skb,
- bool *more_data)
+static bool ath6kl_process_uapsdq(struct ath6kl_sta *conn,
+ struct ath6kl_vif *vif,
+ struct sk_buff *skb,
+ u32 *flags)
+{
+ struct ath6kl *ar = vif->ar;
+ bool is_apsdq_empty = false;
+ struct ethhdr *datap = (struct ethhdr *) skb->data;
+ u8 up = 0, traffic_class, *ip_hdr;
+ u16 ether_type;
+ struct ath6kl_llc_snap_hdr *llc_hdr;
+
+ if (conn->sta_flags & STA_PS_APSD_TRIGGER) {
+ /*
+ * This tx is because of a uAPSD trigger, determine
+ * more and EOSP bit. Set EOSP if queue is empty
+ * or sufficient frames are delivered for this trigger.
+ */
+ spin_lock_bh(&conn->psq_lock);
+ if (!skb_queue_empty(&conn->apsdq))
+ *flags |= WMI_DATA_HDR_FLAGS_MORE;
+ else if (conn->sta_flags & STA_PS_APSD_EOSP)
+ *flags |= WMI_DATA_HDR_FLAGS_EOSP;
+ *flags |= WMI_DATA_HDR_FLAGS_UAPSD;
+ spin_unlock_bh(&conn->psq_lock);
+ return false;
+ } else if (!conn->apsd_info) {
+ return false;
+ }
+
+ if (test_bit(WMM_ENABLED, &vif->flags)) {
+ ether_type = be16_to_cpu(datap->h_proto);
+ if (is_ethertype(ether_type)) {
+ /* packet is in DIX format */
+ ip_hdr = (u8 *)(datap + 1);
+ } else {
+ /* packet is in 802.3 format */
+ llc_hdr = (struct ath6kl_llc_snap_hdr *)
+ (datap + 1);
+ ether_type = be16_to_cpu(llc_hdr->eth_type);
+ ip_hdr = (u8 *)(llc_hdr + 1);
+ }
+
+ if (ether_type == IP_ETHERTYPE)
+ up = ath6kl_wmi_determine_user_priority(
+ ip_hdr, 0);
+ }
+
+ traffic_class = ath6kl_wmi_get_traffic_class(up);
+
+ if ((conn->apsd_info & (1 << traffic_class)) == 0)
+ return false;
+
+ /* Queue the frames if the STA is sleeping */
+ spin_lock_bh(&conn->psq_lock);
+ is_apsdq_empty = skb_queue_empty(&conn->apsdq);
+ skb_queue_tail(&conn->apsdq, 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_apsdq_empty) {
+ ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi,
+ vif->fw_vif_idx,
+ conn->aid, 1, 0);
+ }
+ *flags |= WMI_DATA_HDR_FLAGS_UAPSD;
+
+ return true;
+}
+
+static bool ath6kl_process_psq(struct ath6kl_sta *conn,
+ struct ath6kl_vif *vif,
+ struct sk_buff *skb,
+ u32 *flags)
+{
+ bool is_psq_empty = false;
+ struct ath6kl *ar = vif->ar;
+
+ if (conn->sta_flags & STA_PS_POLLED) {
+ spin_lock_bh(&conn->psq_lock);
+ if (!skb_queue_empty(&conn->psq))
+ *flags |= WMI_DATA_HDR_FLAGS_MORE;
+ spin_unlock_bh(&conn->psq_lock);
+ return false;
+ }
+
+ /* 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,
+ vif->fw_vif_idx,
+ conn->aid, 1);
+ return true;
+}
+
+static bool ath6kl_powersave_ap(struct ath6kl_vif *vif, struct sk_buff *skb,
+ u32 *flags)
{
struct ethhdr *datap = (struct ethhdr *) skb->data;
struct ath6kl_sta *conn = NULL;
- bool ps_queued = false, is_psq_empty = false;
+ bool ps_queued = false;
+ struct ath6kl *ar = vif->ar;
if (is_multicast_ether_addr(datap->h_dest)) {
u8 ctr = 0;
@@ -100,7 +230,7 @@ static bool ath6kl_powersave_ap(struct ath6kl *ar, struct sk_buff *skb,
* If this transmit is not because of a Dtim Expiry
* q it.
*/
- if (!test_bit(DTIM_EXPIRED, &ar->flag)) {
+ if (!test_bit(DTIM_EXPIRED, &vif->flags)) {
bool is_mcastq_empty = false;
spin_lock_bh(&ar->mcastpsq_lock);
@@ -116,6 +246,7 @@ static bool ath6kl_powersave_ap(struct ath6kl *ar, struct sk_buff *skb,
*/
if (is_mcastq_empty)
ath6kl_wmi_set_pvb_cmd(ar->wmi,
+ vif->fw_vif_idx,
MCAST_AID, 1);
ps_queued = true;
@@ -126,12 +257,12 @@ static bool ath6kl_powersave_ap(struct ath6kl *ar, struct sk_buff *skb,
*/
spin_lock_bh(&ar->mcastpsq_lock);
if (!skb_queue_empty(&ar->mcastpsq))
- *more_data = true;
+ *flags |= WMI_DATA_HDR_FLAGS_MORE;
spin_unlock_bh(&ar->mcastpsq_lock);
}
}
} else {
- conn = ath6kl_find_sta(ar, datap->h_dest);
+ conn = ath6kl_find_sta(vif, datap->h_dest);
if (!conn) {
dev_kfree_skb(skb);
@@ -140,36 +271,13 @@ static bool ath6kl_powersave_ap(struct ath6kl *ar, struct sk_buff *skb,
}
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);
- }
+ ps_queued = ath6kl_process_uapsdq(conn,
+ vif, skb, flags);
+ if (!(*flags & WMI_DATA_HDR_FLAGS_UAPSD))
+ ps_queued = ath6kl_process_psq(conn,
+ vif, skb, flags);
}
}
-
return ps_queued;
}
@@ -182,6 +290,19 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb,
int status = 0;
struct ath6kl_cookie *cookie = NULL;
+ trace_ath6kl_wmi_cmd(skb->data, skb->len);
+
+ if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) {
+ dev_kfree_skb(skb);
+ return -EACCES;
+ }
+
+ if (WARN_ON_ONCE(eid == ENDPOINT_UNUSED ||
+ eid >= ENDPOINT_MAX)) {
+ status = -EINVAL;
+ goto fail_ctrl_tx;
+ }
+
spin_lock_bh(&ar->lock);
ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
@@ -196,8 +317,9 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb,
cookie = NULL;
ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n",
skb, skb->len);
- } else
+ } else {
cookie = ath6kl_alloc_cookie(ar);
+ }
if (cookie == NULL) {
spin_unlock_bh(&ar->lock);
@@ -216,6 +338,7 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb,
cookie->map_no = 0;
set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
eid, ATH6KL_CONTROL_PKT_TAG);
+ cookie->htc_pkt.skb = skb;
/*
* This interface is asynchronous, if there is an error, cleanup
@@ -235,35 +358,56 @@ 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;
+ struct ath6kl_vif *vif = netdev_priv(dev);
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;
+ u8 ac = 99; /* initialize to unmapped ac */
+ bool chk_adhoc_ps_mapping = false;
int ret;
+ struct wmi_tx_meta_v2 meta_v2;
+ void *meta;
+ u8 csum_start = 0, csum_dest = 0, csum = skb->ip_summed;
+ u8 meta_ver = 0;
+ u32 flags = 0;
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(CONNECTED, &vif->flags))
+ goto fail_tx;
+
+ if (WARN_ON_ONCE(ar->state != ATH6KL_STATE_ON))
+ goto fail_tx;
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))
+ if (vif->nw_type == AP_NETWORK) {
+ if (ath6kl_powersave_ap(vif, skb, &flags))
return 0;
}
if (test_bit(WMI_ENABLED, &ar->flag)) {
+ if ((dev->features & NETIF_F_IP_CSUM) &&
+ (csum == CHECKSUM_PARTIAL)) {
+ csum_start = skb->csum_start -
+ (skb_network_header(skb) - skb->head) +
+ sizeof(struct ath6kl_llc_snap_hdr);
+ csum_dest = skb->csum_offset + csum_start;
+ }
+
if (skb_headroom(skb) < dev->needed_headroom) {
- WARN_ON(1);
- goto fail_tx;
+ struct sk_buff *tmp_skb = skb;
+
+ skb = skb_realloc_headroom(skb, dev->needed_headroom);
+ kfree_skb(tmp_skb);
+ if (skb == NULL) {
+ vif->net_stats.tx_dropped++;
+ return 0;
+ }
}
if (ath6kl_wmi_dix_2_dot3(ar->wmi, skb)) {
@@ -271,24 +415,45 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
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");
+ if ((dev->features & NETIF_F_IP_CSUM) &&
+ (csum == CHECKSUM_PARTIAL)) {
+ meta_v2.csum_start = csum_start;
+ meta_v2.csum_dest = csum_dest;
+
+ /* instruct target to calculate checksum */
+ meta_v2.csum_flags = WMI_META_V2_FLAG_CSUM_OFFLOAD;
+ meta_ver = WMI_META_VERSION_2;
+ meta = &meta_v2;
+ } else {
+ meta_ver = 0;
+ meta = NULL;
+ }
+
+ ret = ath6kl_wmi_data_hdr_add(ar->wmi, skb,
+ DATA_MSGTYPE, flags, 0,
+ meta_ver,
+ meta, vif->fw_vif_idx);
+
+ if (ret) {
+ ath6kl_warn("failed to add wmi data header:%d\n"
+ , ret);
goto fail_tx;
}
- if ((ar->nw_type == ADHOC_NETWORK) &&
- ar->ibss_ps_enable && test_bit(CONNECTED, &ar->flag))
+ if ((vif->nw_type == ADHOC_NETWORK) &&
+ ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags))
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);
+ ret = ath6kl_wmi_implicit_create_pstream(ar->wmi,
+ vif->fw_vif_idx, skb,
+ 0, test_bit(WMM_ENABLED, &vif->flags), &ac);
if (ret)
goto fail_tx;
}
- } else
+ } else {
goto fail_tx;
+ }
spin_lock_bh(&ar->lock);
@@ -339,6 +504,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
cookie->map_no = map_no;
set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
eid, htc_tag);
+ cookie->htc_pkt.skb = skb;
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ",
skb->data, skb->len);
@@ -354,8 +520,8 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
fail_tx:
dev_kfree_skb(skb);
- ar->net_stats.tx_dropped++;
- ar->net_stats.tx_aborted_errors++;
+ vif->net_stats.tx_dropped++;
+ vif->net_stats.tx_aborted_errors++;
return 0;
}
@@ -419,14 +585,16 @@ void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active)
notify_htc:
/* notify HTC, this may cause credit distribution changes */
- ath6kl_htc_indicate_activity_change(ar->htc_target, eid, active);
+ ath6kl_htc_activity_changed(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;
+ struct ath6kl_vif *vif;
enum htc_endpoint_id endpoint = packet->endpoint;
+ enum htc_send_full_action action = HTC_SEND_FULL_KEEP;
if (endpoint == ar->ctrl_ep) {
/*
@@ -435,23 +603,14 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
* 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;
+ ath6kl_recovery_err_notify(ar, ATH6KL_FW_EP_FULL);
+ return action;
}
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;
+ return action;
/*
* The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for
@@ -459,29 +618,40 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
*/
if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] <
ar->hiac_stream_active_pri &&
- ar->cookie_count <= MAX_HI_COOKIE_NUM)
+ ar->cookie_count <=
+ target->endpoint[endpoint].tx_drop_packet_threshold)
/*
* Give preference to the highest priority stream by
* dropping the packets which overflowed.
*/
- return HTC_SEND_FULL_DROP;
+ action = 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);
+ /* FIXME: Locking */
+ spin_lock_bh(&ar->list_lock);
+ list_for_each_entry(vif, &ar->vif_list, list) {
+ if (vif->nw_type == ADHOC_NETWORK ||
+ action != HTC_SEND_FULL_DROP) {
+ spin_unlock_bh(&ar->list_lock);
+
+ set_bit(NETQ_STOPPED, &vif->flags);
+ netif_stop_queue(vif->ndev);
- return HTC_SEND_FULL_KEEP;
+ return action;
+ }
+ }
+ spin_unlock_bh(&ar->list_lock);
+
+ return action;
}
/* TODO this needs to be looked at */
-static void ath6kl_tx_clear_node_map(struct ath6kl *ar,
+static void ath6kl_tx_clear_node_map(struct ath6kl_vif *vif,
enum htc_endpoint_id eid, u32 map_no)
{
+ struct ath6kl *ar = vif->ar;
u32 i;
- if (ar->nw_type != ADHOC_NETWORK)
+ if (vif->nw_type != ADHOC_NETWORK)
return;
if (!ar->ibss_ps_enable)
@@ -512,9 +682,10 @@ static void ath6kl_tx_clear_node_map(struct ath6kl *ar,
}
}
-void ath6kl_tx_complete(void *context, struct list_head *packet_queue)
+void ath6kl_tx_complete(struct htc_target *target,
+ struct list_head *packet_queue)
{
- struct ath6kl *ar = context;
+ struct ath6kl *ar = target->dev->ar;
struct sk_buff_head skb_queue;
struct htc_packet *packet;
struct sk_buff *skb;
@@ -523,7 +694,9 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue)
int status;
enum htc_endpoint_id eid;
bool wake_event = false;
- bool flushing = false;
+ bool flushing[ATH6KL_VIF_MAX] = {false};
+ u8 if_idx;
+ struct ath6kl_vif *vif;
skb_queue_head_init(&skb_queue);
@@ -532,29 +705,35 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue)
/* reap completed packets */
while (!list_empty(packet_queue)) {
-
packet = list_first_entry(packet_queue, struct htc_packet,
list);
list_del(&packet->list);
+ if (WARN_ON_ONCE(packet->endpoint == ENDPOINT_UNUSED ||
+ packet->endpoint >= ENDPOINT_MAX))
+ continue;
+
ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt;
- if (!ath6kl_cookie)
- goto fatal;
+ if (WARN_ON_ONCE(!ath6kl_cookie))
+ continue;
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;
+ if (WARN_ON_ONCE(!skb || !skb->data)) {
+ dev_kfree_skb(skb);
+ ath6kl_free_cookie(ar, ath6kl_cookie);
+ continue;
+ }
__skb_queue_tail(&skb_queue, skb);
- if (!status && (packet->act_len != skb->len))
- goto fatal;
+ if (WARN_ON_ONCE(!status && (packet->act_len != skb->len))) {
+ ath6kl_free_cookie(ar, ath6kl_cookie);
+ continue;
+ }
ar->tx_pending[eid]--;
@@ -569,15 +748,30 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue)
wake_event = true;
}
+ if (eid == ar->ctrl_ep) {
+ if_idx = wmi_cmd_hdr_get_if_idx(
+ (struct wmi_cmd_hdr *) packet->buf);
+ } else {
+ if_idx = wmi_data_hdr_get_if_idx(
+ (struct wmi_data_hdr *) packet->buf);
+ }
+
+ vif = ath6kl_get_vif_by_index(ar, if_idx);
+ if (!vif) {
+ ath6kl_free_cookie(ar, ath6kl_cookie);
+ continue;
+ }
+
if (status) {
if (status == -ECANCELED)
/* a packet was flushed */
- flushing = true;
+ flushing[if_idx] = true;
+
+ vif->net_stats.tx_errors++;
- ar->net_stats.tx_errors++;
+ if (status != -ENOSPC && status != -ECANCELED)
+ ath6kl_warn("tx complete error: %d\n", status);
- 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,
@@ -588,37 +782,39 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue)
__func__, skb, packet->buf, packet->act_len,
eid, "OK");
- flushing = false;
- ar->net_stats.tx_packets++;
- ar->net_stats.tx_bytes += skb->len;
+ flushing[if_idx] = false;
+ vif->net_stats.tx_packets++;
+ vif->net_stats.tx_bytes += skb->len;
}
- ath6kl_tx_clear_node_map(ar, eid, map_no);
+ ath6kl_tx_clear_node_map(vif, eid, map_no);
ath6kl_free_cookie(ar, ath6kl_cookie);
- if (test_bit(NETQ_STOPPED, &ar->flag))
- clear_bit(NETQ_STOPPED, &ar->flag);
+ if (test_bit(NETQ_STOPPED, &vif->flags))
+ clear_bit(NETQ_STOPPED, &vif->flags);
}
spin_unlock_bh(&ar->lock);
__skb_queue_purge(&skb_queue);
- if (test_bit(CONNECTED, &ar->flag)) {
- if (!flushing)
- netif_wake_queue(ar->net_dev);
+ /* FIXME: Locking */
+ spin_lock_bh(&ar->list_lock);
+ list_for_each_entry(vif, &ar->vif_list, list) {
+ if (test_bit(CONNECTED, &vif->flags) &&
+ !flushing[vif->fw_vif_idx]) {
+ spin_unlock_bh(&ar->list_lock);
+ netif_wake_queue(vif->ndev);
+ spin_lock_bh(&ar->list_lock);
+ }
}
+ spin_unlock_bh(&ar->list_lock);
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)
@@ -670,10 +866,12 @@ 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);
+ if (skb_queue_len(&p_aggr->rx_amsdu_freeq) <
+ (AGGR_NUM_OF_FREE_NETBUFS >> 2))
+ ath6kl_alloc_netbufs(&p_aggr->rx_amsdu_freeq,
+ AGGR_NUM_OF_FREE_NETBUFS);
- skb = skb_dequeue(&p_aggr->free_q);
+ skb = skb_dequeue(&p_aggr->rx_amsdu_freeq);
return skb;
}
@@ -705,10 +903,14 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint)
break;
packet = (struct htc_packet *) skb->head;
- if (!IS_ALIGNED((unsigned long) skb->data, 4))
+ if (!IS_ALIGNED((unsigned long) skb->data, 4)) {
+ size_t len = skb_headlen(skb);
skb->data = PTR_ALIGN(skb->data - 4, 4);
+ skb_set_tail_pointer(skb, len);
+ }
set_htc_rxpkt_info(packet, skb, skb->data,
- ATH6KL_BUFFER_SIZE, endpoint);
+ ATH6KL_BUFFER_SIZE, endpoint);
+ packet->skb = skb;
list_add_tail(&packet->list, &queue);
}
@@ -727,10 +929,15 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count)
return;
packet = (struct htc_packet *) skb->head;
- if (!IS_ALIGNED((unsigned long) skb->data, 4))
+ if (!IS_ALIGNED((unsigned long) skb->data, 4)) {
+ size_t len = skb_headlen(skb);
skb->data = PTR_ALIGN(skb->data - 4, 4);
+ skb_set_tail_pointer(skb, len);
+ }
set_htc_rxpkt_info(packet, skb, skb->data,
ATH6KL_AMSDU_BUFFER_SIZE, 0);
+ packet->skb = skb;
+
spin_lock_bh(&ar->lock);
list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue);
spin_unlock_bh(&ar->lock);
@@ -841,7 +1048,7 @@ static void aggr_slice_amsdu(struct aggr_info *p_aggr,
dev_kfree_skb(skb);
}
-static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid,
+static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid,
u16 seq_no, u8 order)
{
struct sk_buff *skb;
@@ -850,12 +1057,10 @@ static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid,
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];
+ rxtid = &agg_conn->rx_tid[tid];
+ stats = &agg_conn->stat[tid];
+ spin_lock_bh(&rxtid->lock);
idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
/*
@@ -874,8 +1079,6 @@ static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid,
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))
@@ -883,12 +1086,14 @@ static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid,
if (node->skb) {
if (node->is_amsdu)
- aggr_slice_amsdu(p_aggr, rxtid, node->skb);
+ aggr_slice_amsdu(agg_conn->aggr_info, rxtid,
+ node->skb);
else
skb_queue_tail(&rxtid->q, node->skb);
node->skb = NULL;
- } else
+ } 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);
@@ -899,10 +1104,10 @@ static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid,
stats->num_delivered += skb_queue_len(&rxtid->q);
while ((skb = skb_dequeue(&rxtid->q)))
- ath6kl_deliver_frames_to_nw_stack(p_aggr->dev, skb);
+ ath6kl_deliver_frames_to_nw_stack(agg_conn->dev, skb);
}
-static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid,
+static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid,
u16 seq_no,
bool is_amsdu, struct sk_buff *frame)
{
@@ -914,18 +1119,18 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid,
bool is_queued = false;
u16 extended_end;
- rxtid = &agg_info->rx_tid[tid];
- stats = &agg_info->stat[tid];
+ rxtid = &agg_conn->rx_tid[tid];
+ stats = &agg_conn->stat[tid];
stats->num_into_aggr++;
if (!rxtid->aggr) {
if (is_amsdu) {
- aggr_slice_amsdu(agg_info, rxtid, frame);
+ aggr_slice_amsdu(agg_conn->aggr_info, rxtid, frame);
is_queued = true;
stats->num_amsdu++;
while ((skb = skb_dequeue(&rxtid->q)))
- ath6kl_deliver_frames_to_nw_stack(agg_info->dev,
+ ath6kl_deliver_frames_to_nw_stack(agg_conn->dev,
skb);
}
return is_queued;
@@ -945,12 +1150,14 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid,
(cur < end || cur > extended_end)) ||
((end > extended_end) && (cur > extended_end) &&
(cur < end))) {
- aggr_deque_frms(agg_info, tid, 0, 0);
+ aggr_deque_frms(agg_conn, tid, 0, 0);
+ spin_lock_bh(&rxtid->lock);
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);
+ spin_unlock_bh(&rxtid->lock);
} else {
/*
* Dequeue only those frames that are outside the
@@ -962,7 +1169,7 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid,
st = ATH6KL_MAX_SEQ_NO -
(rxtid->hold_q_sz - 2 - cur);
- aggr_deque_frms(agg_info, tid, st, 0);
+ aggr_deque_frms(agg_conn, tid, st, 0);
}
stats->num_oow++;
@@ -1001,32 +1208,101 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid,
spin_unlock_bh(&rxtid->lock);
- aggr_deque_frms(agg_info, tid, 0, 1);
+ aggr_deque_frms(agg_conn, 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;
- }
+ if (agg_conn->timer_scheduled)
+ return is_queued;
+
+ spin_lock_bh(&rxtid->lock);
+ 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_conn->timer_scheduled = true;
+ mod_timer(&agg_conn->timer,
+ (jiffies + (HZ * AGGR_RX_TIMEOUT) / 1000));
+ rxtid->timer_mon = true;
+ break;
}
+ }
+ spin_unlock_bh(&rxtid->lock);
return is_queued;
}
+static void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif,
+ struct ath6kl_sta *conn)
+{
+ struct ath6kl *ar = vif->ar;
+ bool is_apsdq_empty, is_apsdq_empty_at_start;
+ u32 num_frames_to_deliver, flags;
+ struct sk_buff *skb = NULL;
+
+ /*
+ * If the APSD q for this STA is not empty, dequeue and
+ * send a pkt from the head of the q. Also update the
+ * More data bit in the WMI_DATA_HDR if there are
+ * more pkts for this STA in the APSD q.
+ * If there are no more pkts for this STA,
+ * update the APSD bitmap for this STA.
+ */
+
+ num_frames_to_deliver = (conn->apsd_info >> ATH6KL_APSD_NUM_OF_AC) &
+ ATH6KL_APSD_FRAME_MASK;
+ /*
+ * Number of frames to send in a service period is
+ * indicated by the station
+ * in the QOS_INFO of the association request
+ * If it is zero, send all frames
+ */
+ if (!num_frames_to_deliver)
+ num_frames_to_deliver = ATH6KL_APSD_ALL_FRAME;
+
+ spin_lock_bh(&conn->psq_lock);
+ is_apsdq_empty = skb_queue_empty(&conn->apsdq);
+ spin_unlock_bh(&conn->psq_lock);
+ is_apsdq_empty_at_start = is_apsdq_empty;
+
+ while ((!is_apsdq_empty) && (num_frames_to_deliver)) {
+ spin_lock_bh(&conn->psq_lock);
+ skb = skb_dequeue(&conn->apsdq);
+ is_apsdq_empty = skb_queue_empty(&conn->apsdq);
+ spin_unlock_bh(&conn->psq_lock);
+
+ /*
+ * Set the STA flag to Trigger delivery,
+ * so that the frame will go out
+ */
+ conn->sta_flags |= STA_PS_APSD_TRIGGER;
+ num_frames_to_deliver--;
+
+ /* Last frame in the service period, set EOSP or queue empty */
+ if ((is_apsdq_empty) || (!num_frames_to_deliver))
+ conn->sta_flags |= STA_PS_APSD_EOSP;
+
+ ath6kl_data_tx(skb, vif->ndev);
+ conn->sta_flags &= ~(STA_PS_APSD_TRIGGER);
+ conn->sta_flags &= ~(STA_PS_APSD_EOSP);
+ }
+
+ if (is_apsdq_empty) {
+ if (is_apsdq_empty_at_start)
+ flags = WMI_AP_APSD_NO_DELIVERY_FRAMES;
+ else
+ flags = 0;
+
+ ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi,
+ vif->fw_vif_idx,
+ conn->aid, 0, flags);
+ }
+
+ return;
+}
+
void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
{
struct ath6kl *ar = target->dev->ar;
@@ -1035,22 +1311,50 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
struct wmi_data_hdr *dhdr;
int min_hdr_len;
u8 meta_type, dot11_hdr = 0;
+ u8 pad_before_data_start;
int status = packet->status;
enum htc_endpoint_id ept = packet->endpoint;
bool is_amsdu, prev_ps, ps_state = false;
+ bool trig_state = false;
struct ath6kl_sta *conn = NULL;
struct sk_buff *skb1 = NULL;
struct ethhdr *datap = NULL;
+ struct ath6kl_vif *vif;
+ struct aggr_info_conn *aggr_conn;
u16 seq_no, offset;
- u8 tid;
+ u8 tid, if_idx;
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++;
+ if (status || packet->act_len < HTC_HDR_LENGTH) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ skb_put(skb, packet->act_len + HTC_HDR_LENGTH);
+ skb_pull(skb, HTC_HDR_LENGTH);
+
+ ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ",
+ skb->data, skb->len);
+
+ if (ept == ar->ctrl_ep) {
+ if (test_bit(WMI_ENABLED, &ar->flag)) {
+ ath6kl_check_wow_status(ar);
+ ath6kl_wmi_control_rx(ar->wmi, skb);
+ return;
+ }
+ if_idx =
+ wmi_cmd_hdr_get_if_idx((struct wmi_cmd_hdr *) skb->data);
+ } else {
+ if_idx =
+ wmi_data_hdr_get_if_idx((struct wmi_data_hdr *) skb->data);
+ }
+
+ vif = ath6kl_get_vif_by_index(ar, if_idx);
+ if (!vif) {
dev_kfree_skb(skb);
return;
}
@@ -1059,32 +1363,23 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
* 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;
+ spin_lock_bh(&vif->if_lock);
- spin_unlock_bh(&ar->lock);
-
- skb_put(skb, packet->act_len + HTC_HDR_LENGTH);
- skb_pull(skb, HTC_HDR_LENGTH);
+ vif->net_stats.rx_packets++;
+ vif->net_stats.rx_bytes += packet->act_len;
- ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ",
- skb->data, skb->len);
+ spin_unlock_bh(&vif->if_lock);
- skb->dev = ar->net_dev;
+ skb->dev = vif->ndev;
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);
+ ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb);
return;
}
- if (ept == ar->ctrl_ep) {
- ath6kl_wmi_control_rx(ar->wmi, skb);
- return;
- }
+ ath6kl_check_wow_status(ar);
min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) +
sizeof(struct ath6kl_llc_snap_hdr);
@@ -1096,24 +1391,25 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
* 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 &&
+ if (vif->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++;
+ vif->net_stats.rx_errors++;
+ vif->net_stats.rx_length_errors++;
dev_kfree_skb(skb);
return;
}
/* Get the Power save state of the STA */
- if (ar->nw_type == AP_NETWORK) {
+ if (vif->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);
+ trig_state = !!(le16_to_cpu(dhdr->info3) & WMI_DATA_HDR_TRIG);
switch (meta_type) {
case 0:
@@ -1129,7 +1425,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
}
datap = (struct ethhdr *) (skb->data + offset);
- conn = ath6kl_find_sta(ar, datap->h_source);
+ conn = ath6kl_find_sta(vif, datap->h_source);
if (!conn) {
dev_kfree_skb(skb);
@@ -1152,20 +1448,64 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
else
conn->sta_flags &= ~STA_PS_SLEEP;
+ /* Accept trigger only when the station is in sleep */
+ if ((conn->sta_flags & STA_PS_SLEEP) && trig_state)
+ ath6kl_uapsd_trigger_frame_rx(vif, conn);
+
if (prev_ps ^ !!(conn->sta_flags & STA_PS_SLEEP)) {
if (!(conn->sta_flags & STA_PS_SLEEP)) {
struct sk_buff *skbuff = NULL;
+ bool is_apsdq_empty;
+ struct ath6kl_mgmt_buff *mgmt;
+ u8 idx;
spin_lock_bh(&conn->psq_lock);
- while ((skbuff = skb_dequeue(&conn->psq))
- != NULL) {
+ while (conn->mgmt_psq_len > 0) {
+ mgmt = list_first_entry(
+ &conn->mgmt_psq,
+ struct ath6kl_mgmt_buff,
+ list);
+ list_del(&mgmt->list);
+ conn->mgmt_psq_len--;
+ spin_unlock_bh(&conn->psq_lock);
+ idx = vif->fw_vif_idx;
+
+ ath6kl_wmi_send_mgmt_cmd(ar->wmi,
+ idx,
+ mgmt->id,
+ mgmt->freq,
+ mgmt->wait,
+ mgmt->buf,
+ mgmt->len,
+ mgmt->no_cck);
+
+ kfree(mgmt);
+ spin_lock_bh(&conn->psq_lock);
+ }
+ conn->mgmt_psq_len = 0;
+ while ((skbuff = skb_dequeue(&conn->psq))) {
+ spin_unlock_bh(&conn->psq_lock);
+ ath6kl_data_tx(skbuff, vif->ndev);
+ spin_lock_bh(&conn->psq_lock);
+ }
+
+ is_apsdq_empty = skb_queue_empty(&conn->apsdq);
+ while ((skbuff = skb_dequeue(&conn->apsdq))) {
spin_unlock_bh(&conn->psq_lock);
- ath6kl_data_tx(skbuff, ar->net_dev);
+ ath6kl_data_tx(skbuff, vif->ndev);
spin_lock_bh(&conn->psq_lock);
}
spin_unlock_bh(&conn->psq_lock);
+
+ if (!is_apsdq_empty)
+ ath6kl_wmi_set_apsd_bfrd_traf(
+ ar->wmi,
+ vif->fw_vif_idx,
+ conn->aid, 0, 0);
+
/* Clear the PVB for this STA */
- ath6kl_wmi_set_pvb_cmd(ar->wmi, conn->aid, 0);
+ ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
+ conn->aid, 0);
}
}
@@ -1183,6 +1523,10 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
seq_no = wmi_data_hdr_get_seqno(dhdr);
meta_type = wmi_data_hdr_get_meta(dhdr);
dot11_hdr = wmi_data_hdr_get_dot11(dhdr);
+ pad_before_data_start =
+ (le16_to_cpu(dhdr->info3) >> WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT)
+ & WMI_DATA_HDR_PAD_BEFORE_DATA_MASK;
+
skb_pull(skb, sizeof(struct wmi_data_hdr));
switch (meta_type) {
@@ -1201,6 +1545,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
break;
}
+ skb_pull(skb, pad_before_data_start);
+
if (dot11_hdr)
status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb);
else if (!is_amsdu)
@@ -1215,12 +1561,12 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
return;
}
- if (!(ar->net_dev->flags & IFF_UP)) {
+ if (!(vif->ndev->flags & IFF_UP)) {
dev_kfree_skb(skb);
return;
}
- if (ar->nw_type == AP_NETWORK) {
+ if (vif->nw_type == AP_NETWORK) {
datap = (struct ethhdr *) skb->data;
if (is_multicast_ether_addr(datap->h_dest))
/*
@@ -1235,8 +1581,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
* 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);
+ conn = ath6kl_find_sta(vif, datap->h_dest);
if (conn && ar->intra_bss) {
skb1 = skb;
@@ -1247,32 +1592,50 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
}
}
if (skb1)
- ath6kl_data_tx(skb1, ar->net_dev);
+ ath6kl_data_tx(skb1, vif->ndev);
+
+ if (skb == NULL) {
+ /* nothing to deliver up the stack */
+ return;
+ }
}
datap = (struct ethhdr *) skb->data;
- if (is_unicast_ether_addr(datap->h_dest) &&
- aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no,
- is_amsdu, skb))
- /* aggregation code will handle the skb */
- return;
+ if (is_unicast_ether_addr(datap->h_dest)) {
+ if (vif->nw_type == AP_NETWORK) {
+ conn = ath6kl_find_sta(vif, datap->h_source);
+ if (!conn)
+ return;
+ aggr_conn = conn->aggr_conn;
+ } else {
+ aggr_conn = vif->aggr_cntxt->aggr_conn;
+ }
+
+ if (aggr_process_recv_frm(aggr_conn, tid, seq_no,
+ is_amsdu, skb)) {
+ /* aggregation code will handle the skb */
+ return;
+ }
+ } else if (!is_broadcast_ether_addr(datap->h_dest)) {
+ vif->net_stats.multicast++;
+ }
- ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb);
+ ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb);
}
static void aggr_timeout(unsigned long arg)
{
u8 i, j;
- struct aggr_info *p_aggr = (struct aggr_info *) arg;
+ struct aggr_info_conn *aggr_conn = (struct aggr_info_conn *) 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];
+ rxtid = &aggr_conn->rx_tid[i];
+ stats = &aggr_conn->stat[i];
- if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress)
+ if (!rxtid->aggr || !rxtid->timer_mon)
continue;
stats->num_timeouts++;
@@ -1281,50 +1644,50 @@ static void aggr_timeout(unsigned long arg)
rxtid->seq_next,
((rxtid->seq_next + rxtid->hold_q_sz-1) &
ATH6KL_MAX_SEQ_NO));
- aggr_deque_frms(p_aggr, i, 0, 0);
+ aggr_deque_frms(aggr_conn, i, 0, 0);
}
- p_aggr->timer_scheduled = false;
+ aggr_conn->timer_scheduled = false;
for (i = 0; i < NUM_OF_TIDS; i++) {
- rxtid = &p_aggr->rx_tid[i];
+ rxtid = &aggr_conn->rx_tid[i];
if (rxtid->aggr && rxtid->hold_q) {
+ spin_lock_bh(&rxtid->lock);
for (j = 0; j < rxtid->hold_q_sz; j++) {
if (rxtid->hold_q[j].skb) {
- p_aggr->timer_scheduled = true;
+ aggr_conn->timer_scheduled = true;
rxtid->timer_mon = true;
- rxtid->progress = false;
break;
}
}
+ spin_unlock_bh(&rxtid->lock);
if (j >= rxtid->hold_q_sz)
rxtid->timer_mon = false;
}
}
- if (p_aggr->timer_scheduled)
- mod_timer(&p_aggr->timer,
+ if (aggr_conn->timer_scheduled)
+ mod_timer(&aggr_conn->timer,
jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT));
}
-static void aggr_delete_tid_state(struct aggr_info *p_aggr, u8 tid)
+static void aggr_delete_tid_state(struct aggr_info_conn *aggr_conn, u8 tid)
{
struct rxtid *rxtid;
struct rxtid_stats *stats;
- if (!p_aggr || tid >= NUM_OF_TIDS)
+ if (!aggr_conn || tid >= NUM_OF_TIDS)
return;
- rxtid = &p_aggr->rx_tid[tid];
- stats = &p_aggr->stat[tid];
+ rxtid = &aggr_conn->rx_tid[tid];
+ stats = &aggr_conn->stat[tid];
if (rxtid->aggr)
- aggr_deque_frms(p_aggr, tid, 0, 0);
+ aggr_deque_frms(aggr_conn, tid, 0, 0);
rxtid->aggr = false;
- rxtid->progress = false;
rxtid->timer_mon = false;
rxtid->win_sz = 0;
rxtid->seq_next = 0;
@@ -1336,25 +1699,41 @@ static void aggr_delete_tid_state(struct aggr_info *p_aggr, u8 tid)
memset(stats, 0, sizeof(struct rxtid_stats));
}
-void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no, u8 win_sz)
+void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no,
+ u8 win_sz)
{
- struct aggr_info *p_aggr = ar->aggr_cntxt;
+ struct ath6kl_sta *sta;
+ struct aggr_info_conn *aggr_conn = NULL;
struct rxtid *rxtid;
struct rxtid_stats *stats;
u16 hold_q_size;
+ u8 tid, aid;
+
+ if (vif->nw_type == AP_NETWORK) {
+ aid = ath6kl_get_aid(tid_mux);
+ sta = ath6kl_find_sta_by_aid(vif->ar, aid);
+ if (sta)
+ aggr_conn = sta->aggr_conn;
+ } else {
+ aggr_conn = vif->aggr_cntxt->aggr_conn;
+ }
+
+ if (!aggr_conn)
+ return;
- if (!p_aggr)
+ tid = ath6kl_get_tid(tid_mux);
+ if (tid >= NUM_OF_TIDS)
return;
- rxtid = &p_aggr->rx_tid[tid];
- stats = &p_aggr->stat[tid];
+ rxtid = &aggr_conn->rx_tid[tid];
+ stats = &aggr_conn->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);
+ aggr_delete_tid_state(aggr_conn, tid);
rxtid->seq_next = seq_no;
hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q);
@@ -1370,61 +1749,98 @@ void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no, u8 win_sz)
rxtid->aggr = true;
}
-struct aggr_info *aggr_init(struct net_device *dev)
+void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info,
+ struct aggr_info_conn *aggr_conn)
{
- 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);
+ aggr_conn->aggr_sz = AGGR_SZ_DEFAULT;
+ aggr_conn->dev = vif->ndev;
+ init_timer(&aggr_conn->timer);
+ aggr_conn->timer.function = aggr_timeout;
+ aggr_conn->timer.data = (unsigned long) aggr_conn;
+ aggr_conn->aggr_info = aggr_info;
- ath6kl_alloc_netbufs(&p_aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS);
+ aggr_conn->timer_scheduled = false;
for (i = 0; i < NUM_OF_TIDS; i++) {
- rxtid = &p_aggr->rx_tid[i];
+ rxtid = &aggr_conn->rx_tid[i];
rxtid->aggr = false;
- rxtid->progress = false;
rxtid->timer_mon = false;
skb_queue_head_init(&rxtid->q);
spin_lock_init(&rxtid->lock);
}
+}
+
+struct aggr_info *aggr_init(struct ath6kl_vif *vif)
+{
+ struct aggr_info *p_aggr = NULL;
+
+ 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_conn = kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL);
+ if (!p_aggr->aggr_conn) {
+ ath6kl_err("failed to alloc memory for connection specific aggr info\n");
+ kfree(p_aggr);
+ return NULL;
+ }
+
+ aggr_conn_init(vif, p_aggr, p_aggr->aggr_conn);
+
+ skb_queue_head_init(&p_aggr->rx_amsdu_freeq);
+ ath6kl_alloc_netbufs(&p_aggr->rx_amsdu_freeq, AGGR_NUM_OF_FREE_NETBUFS);
return p_aggr;
}
-void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid)
+void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid_mux)
{
- struct aggr_info *p_aggr = ar->aggr_cntxt;
+ struct ath6kl_sta *sta;
struct rxtid *rxtid;
+ struct aggr_info_conn *aggr_conn = NULL;
+ u8 tid, aid;
+
+ if (vif->nw_type == AP_NETWORK) {
+ aid = ath6kl_get_aid(tid_mux);
+ sta = ath6kl_find_sta_by_aid(vif->ar, aid);
+ if (sta)
+ aggr_conn = sta->aggr_conn;
+ } else {
+ aggr_conn = vif->aggr_cntxt->aggr_conn;
+ }
+
+ if (!aggr_conn)
+ return;
- if (!p_aggr)
+ tid = ath6kl_get_tid(tid_mux);
+ if (tid >= NUM_OF_TIDS)
return;
- rxtid = &p_aggr->rx_tid[tid];
+ rxtid = &aggr_conn->rx_tid[tid];
if (rxtid->aggr)
- aggr_delete_tid_state(p_aggr, tid);
+ aggr_delete_tid_state(aggr_conn, tid);
}
-void aggr_reset_state(struct aggr_info *aggr_info)
+void aggr_reset_state(struct aggr_info_conn *aggr_conn)
{
u8 tid;
+ if (!aggr_conn)
+ return;
+
+ if (aggr_conn->timer_scheduled) {
+ del_timer(&aggr_conn->timer);
+ aggr_conn->timer_scheduled = false;
+ }
+
for (tid = 0; tid < NUM_OF_TIDS; tid++)
- aggr_delete_tid_state(aggr_info, tid);
+ aggr_delete_tid_state(aggr_conn, tid);
}
/* clean up our amsdu buffer list */
@@ -1451,28 +1867,11 @@ void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar)
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);
+ aggr_reset_state(aggr_info->aggr_conn);
+ skb_queue_purge(&aggr_info->rx_amsdu_freeq);
+ kfree(aggr_info->aggr_conn);
kfree(aggr_info);
}
diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c
new file mode 100644
index 00000000000..3afc5a463d0
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/usb.c
@@ -0,0 +1,1264 @@
+/*
+ * Copyright (c) 2007-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "debug.h"
+#include "core.h"
+
+/* constants */
+#define TX_URB_COUNT 32
+#define RX_URB_COUNT 32
+#define ATH6KL_USB_RX_BUFFER_SIZE 4096
+
+/* tx/rx pipes for usb */
+enum ATH6KL_USB_PIPE_ID {
+ ATH6KL_USB_PIPE_TX_CTRL = 0,
+ ATH6KL_USB_PIPE_TX_DATA_LP,
+ ATH6KL_USB_PIPE_TX_DATA_MP,
+ ATH6KL_USB_PIPE_TX_DATA_HP,
+ ATH6KL_USB_PIPE_RX_CTRL,
+ ATH6KL_USB_PIPE_RX_DATA,
+ ATH6KL_USB_PIPE_RX_DATA2,
+ ATH6KL_USB_PIPE_RX_INT,
+ ATH6KL_USB_PIPE_MAX
+};
+
+#define ATH6KL_USB_PIPE_INVALID ATH6KL_USB_PIPE_MAX
+
+struct ath6kl_usb_pipe {
+ struct list_head urb_list_head;
+ struct usb_anchor urb_submitted;
+ u32 urb_alloc;
+ u32 urb_cnt;
+ u32 urb_cnt_thresh;
+ unsigned int usb_pipe_handle;
+ u32 flags;
+ u8 ep_address;
+ u8 logical_pipe_num;
+ struct ath6kl_usb *ar_usb;
+ u16 max_packet_size;
+ struct work_struct io_complete_work;
+ struct sk_buff_head io_comp_queue;
+ struct usb_endpoint_descriptor *ep_desc;
+};
+
+#define ATH6KL_USB_PIPE_FLAG_TX (1 << 0)
+
+/* usb device object */
+struct ath6kl_usb {
+ /* protects pipe->urb_list_head and pipe->urb_cnt */
+ spinlock_t cs_lock;
+
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ struct ath6kl_usb_pipe pipes[ATH6KL_USB_PIPE_MAX];
+ u8 *diag_cmd_buffer;
+ u8 *diag_resp_buffer;
+ struct ath6kl *ar;
+};
+
+/* usb urb object */
+struct ath6kl_urb_context {
+ struct list_head link;
+ struct ath6kl_usb_pipe *pipe;
+ struct sk_buff *skb;
+ struct ath6kl *ar;
+};
+
+/* USB endpoint definitions */
+#define ATH6KL_USB_EP_ADDR_APP_CTRL_IN 0x81
+#define ATH6KL_USB_EP_ADDR_APP_DATA_IN 0x82
+#define ATH6KL_USB_EP_ADDR_APP_DATA2_IN 0x83
+#define ATH6KL_USB_EP_ADDR_APP_INT_IN 0x84
+
+#define ATH6KL_USB_EP_ADDR_APP_CTRL_OUT 0x01
+#define ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT 0x02
+#define ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT 0x03
+#define ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT 0x04
+
+/* diagnostic command defnitions */
+#define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD 1
+#define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP 2
+#define ATH6KL_USB_CONTROL_REQ_DIAG_CMD 3
+#define ATH6KL_USB_CONTROL_REQ_DIAG_RESP 4
+
+#define ATH6KL_USB_CTRL_DIAG_CC_READ 0
+#define ATH6KL_USB_CTRL_DIAG_CC_WRITE 1
+
+struct ath6kl_usb_ctrl_diag_cmd_write {
+ __le32 cmd;
+ __le32 address;
+ __le32 value;
+ __le32 _pad[1];
+} __packed;
+
+struct ath6kl_usb_ctrl_diag_cmd_read {
+ __le32 cmd;
+ __le32 address;
+} __packed;
+
+struct ath6kl_usb_ctrl_diag_resp_read {
+ __le32 value;
+} __packed;
+
+/* function declarations */
+static void ath6kl_usb_recv_complete(struct urb *urb);
+
+#define ATH6KL_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02)
+#define ATH6KL_USB_IS_INT_EP(attr) (((attr) & 3) == 0x03)
+#define ATH6KL_USB_IS_ISOC_EP(attr) (((attr) & 3) == 0x01)
+#define ATH6KL_USB_IS_DIR_IN(addr) ((addr) & 0x80)
+
+/* pipe/urb operations */
+static struct ath6kl_urb_context *
+ath6kl_usb_alloc_urb_from_pipe(struct ath6kl_usb_pipe *pipe)
+{
+ struct ath6kl_urb_context *urb_context = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags);
+ if (!list_empty(&pipe->urb_list_head)) {
+ urb_context =
+ list_first_entry(&pipe->urb_list_head,
+ struct ath6kl_urb_context, link);
+ list_del(&urb_context->link);
+ pipe->urb_cnt--;
+ }
+ spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags);
+
+ return urb_context;
+}
+
+static void ath6kl_usb_free_urb_to_pipe(struct ath6kl_usb_pipe *pipe,
+ struct ath6kl_urb_context *urb_context)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags);
+ pipe->urb_cnt++;
+
+ list_add(&urb_context->link, &pipe->urb_list_head);
+ spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags);
+}
+
+static void ath6kl_usb_cleanup_recv_urb(struct ath6kl_urb_context *urb_context)
+{
+ dev_kfree_skb(urb_context->skb);
+ urb_context->skb = NULL;
+
+ ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context);
+}
+
+static inline struct ath6kl_usb *ath6kl_usb_priv(struct ath6kl *ar)
+{
+ return ar->hif_priv;
+}
+
+/* pipe resource allocation/cleanup */
+static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe,
+ int urb_cnt)
+{
+ struct ath6kl_urb_context *urb_context;
+ int status = 0, i;
+
+ INIT_LIST_HEAD(&pipe->urb_list_head);
+ init_usb_anchor(&pipe->urb_submitted);
+
+ for (i = 0; i < urb_cnt; i++) {
+ urb_context = kzalloc(sizeof(struct ath6kl_urb_context),
+ GFP_KERNEL);
+ if (urb_context == NULL) {
+ status = -ENOMEM;
+ goto fail_alloc_pipe_resources;
+ }
+
+ urb_context->pipe = pipe;
+
+ /*
+ * we are only allocate the urb contexts here, the actual URB
+ * is allocated from the kernel as needed to do a transaction
+ */
+ pipe->urb_alloc++;
+ ath6kl_usb_free_urb_to_pipe(pipe, urb_context);
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_USB,
+ "ath6kl usb: alloc resources lpipe:%d hpipe:0x%X urbs:%d\n",
+ pipe->logical_pipe_num, pipe->usb_pipe_handle,
+ pipe->urb_alloc);
+
+fail_alloc_pipe_resources:
+ return status;
+}
+
+static void ath6kl_usb_free_pipe_resources(struct ath6kl_usb_pipe *pipe)
+{
+ struct ath6kl_urb_context *urb_context;
+
+ if (pipe->ar_usb == NULL) {
+ /* nothing allocated for this pipe */
+ return;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_USB,
+ "ath6kl usb: free resources lpipe:%d"
+ "hpipe:0x%X urbs:%d avail:%d\n",
+ pipe->logical_pipe_num, pipe->usb_pipe_handle,
+ pipe->urb_alloc, pipe->urb_cnt);
+
+ if (pipe->urb_alloc != pipe->urb_cnt) {
+ ath6kl_dbg(ATH6KL_DBG_USB,
+ "ath6kl usb: urb leak! lpipe:%d"
+ "hpipe:0x%X urbs:%d avail:%d\n",
+ pipe->logical_pipe_num, pipe->usb_pipe_handle,
+ pipe->urb_alloc, pipe->urb_cnt);
+ }
+
+ while (true) {
+ urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe);
+ if (urb_context == NULL)
+ break;
+ kfree(urb_context);
+ }
+}
+
+static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb)
+{
+ int i;
+
+ for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++)
+ ath6kl_usb_free_pipe_resources(&ar_usb->pipes[i]);
+}
+
+static u8 ath6kl_usb_get_logical_pipe_num(struct ath6kl_usb *ar_usb,
+ u8 ep_address, int *urb_count)
+{
+ u8 pipe_num = ATH6KL_USB_PIPE_INVALID;
+
+ switch (ep_address) {
+ case ATH6KL_USB_EP_ADDR_APP_CTRL_IN:
+ pipe_num = ATH6KL_USB_PIPE_RX_CTRL;
+ *urb_count = RX_URB_COUNT;
+ break;
+ case ATH6KL_USB_EP_ADDR_APP_DATA_IN:
+ pipe_num = ATH6KL_USB_PIPE_RX_DATA;
+ *urb_count = RX_URB_COUNT;
+ break;
+ case ATH6KL_USB_EP_ADDR_APP_INT_IN:
+ pipe_num = ATH6KL_USB_PIPE_RX_INT;
+ *urb_count = RX_URB_COUNT;
+ break;
+ case ATH6KL_USB_EP_ADDR_APP_DATA2_IN:
+ pipe_num = ATH6KL_USB_PIPE_RX_DATA2;
+ *urb_count = RX_URB_COUNT;
+ break;
+ case ATH6KL_USB_EP_ADDR_APP_CTRL_OUT:
+ pipe_num = ATH6KL_USB_PIPE_TX_CTRL;
+ *urb_count = TX_URB_COUNT;
+ break;
+ case ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT:
+ pipe_num = ATH6KL_USB_PIPE_TX_DATA_LP;
+ *urb_count = TX_URB_COUNT;
+ break;
+ case ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT:
+ pipe_num = ATH6KL_USB_PIPE_TX_DATA_MP;
+ *urb_count = TX_URB_COUNT;
+ break;
+ case ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT:
+ pipe_num = ATH6KL_USB_PIPE_TX_DATA_HP;
+ *urb_count = TX_URB_COUNT;
+ break;
+ default:
+ /* note: there may be endpoints not currently used */
+ break;
+ }
+
+ return pipe_num;
+}
+
+static int ath6kl_usb_setup_pipe_resources(struct ath6kl_usb *ar_usb)
+{
+ struct usb_interface *interface = ar_usb->interface;
+ struct usb_host_interface *iface_desc = interface->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint;
+ struct ath6kl_usb_pipe *pipe;
+ int i, urbcount, status = 0;
+ u8 pipe_num;
+
+ ath6kl_dbg(ATH6KL_DBG_USB, "setting up USB Pipes using interface\n");
+
+ /* walk decriptors and setup pipes */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) {
+ ath6kl_dbg(ATH6KL_DBG_USB,
+ "%s Bulk Ep:0x%2.2X maxpktsz:%d\n",
+ ATH6KL_USB_IS_DIR_IN
+ (endpoint->bEndpointAddress) ?
+ "RX" : "TX", endpoint->bEndpointAddress,
+ le16_to_cpu(endpoint->wMaxPacketSize));
+ } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) {
+ ath6kl_dbg(ATH6KL_DBG_USB,
+ "%s Int Ep:0x%2.2X maxpktsz:%d interval:%d\n",
+ ATH6KL_USB_IS_DIR_IN
+ (endpoint->bEndpointAddress) ?
+ "RX" : "TX", endpoint->bEndpointAddress,
+ le16_to_cpu(endpoint->wMaxPacketSize),
+ endpoint->bInterval);
+ } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) {
+ /* TODO for ISO */
+ ath6kl_dbg(ATH6KL_DBG_USB,
+ "%s ISOC Ep:0x%2.2X maxpktsz:%d interval:%d\n",
+ ATH6KL_USB_IS_DIR_IN
+ (endpoint->bEndpointAddress) ?
+ "RX" : "TX", endpoint->bEndpointAddress,
+ le16_to_cpu(endpoint->wMaxPacketSize),
+ endpoint->bInterval);
+ }
+ urbcount = 0;
+
+ pipe_num =
+ ath6kl_usb_get_logical_pipe_num(ar_usb,
+ endpoint->bEndpointAddress,
+ &urbcount);
+ if (pipe_num == ATH6KL_USB_PIPE_INVALID)
+ continue;
+
+ pipe = &ar_usb->pipes[pipe_num];
+ if (pipe->ar_usb != NULL) {
+ /* hmmm..pipe was already setup */
+ continue;
+ }
+
+ pipe->ar_usb = ar_usb;
+ pipe->logical_pipe_num = pipe_num;
+ pipe->ep_address = endpoint->bEndpointAddress;
+ pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize);
+
+ if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) {
+ if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) {
+ pipe->usb_pipe_handle =
+ usb_rcvbulkpipe(ar_usb->udev,
+ pipe->ep_address);
+ } else {
+ pipe->usb_pipe_handle =
+ usb_sndbulkpipe(ar_usb->udev,
+ pipe->ep_address);
+ }
+ } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) {
+ if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) {
+ pipe->usb_pipe_handle =
+ usb_rcvintpipe(ar_usb->udev,
+ pipe->ep_address);
+ } else {
+ pipe->usb_pipe_handle =
+ usb_sndintpipe(ar_usb->udev,
+ pipe->ep_address);
+ }
+ } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) {
+ /* TODO for ISO */
+ if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) {
+ pipe->usb_pipe_handle =
+ usb_rcvisocpipe(ar_usb->udev,
+ pipe->ep_address);
+ } else {
+ pipe->usb_pipe_handle =
+ usb_sndisocpipe(ar_usb->udev,
+ pipe->ep_address);
+ }
+ }
+
+ pipe->ep_desc = endpoint;
+
+ if (!ATH6KL_USB_IS_DIR_IN(pipe->ep_address))
+ pipe->flags |= ATH6KL_USB_PIPE_FLAG_TX;
+
+ status = ath6kl_usb_alloc_pipe_resources(pipe, urbcount);
+ if (status != 0)
+ break;
+ }
+
+ return status;
+}
+
+/* pipe operations */
+static void ath6kl_usb_post_recv_transfers(struct ath6kl_usb_pipe *recv_pipe,
+ int buffer_length)
+{
+ struct ath6kl_urb_context *urb_context;
+ struct urb *urb;
+ int usb_status;
+
+ while (true) {
+ urb_context = ath6kl_usb_alloc_urb_from_pipe(recv_pipe);
+ if (urb_context == NULL)
+ break;
+
+ urb_context->skb = dev_alloc_skb(buffer_length);
+ if (urb_context->skb == NULL)
+ goto err_cleanup_urb;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (urb == NULL)
+ goto err_cleanup_urb;
+
+ usb_fill_bulk_urb(urb,
+ recv_pipe->ar_usb->udev,
+ recv_pipe->usb_pipe_handle,
+ urb_context->skb->data,
+ buffer_length,
+ ath6kl_usb_recv_complete, urb_context);
+
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "ath6kl usb: bulk recv submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n",
+ recv_pipe->logical_pipe_num,
+ recv_pipe->usb_pipe_handle, recv_pipe->ep_address,
+ buffer_length, urb_context->skb);
+
+ usb_anchor_urb(urb, &recv_pipe->urb_submitted);
+ usb_status = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (usb_status) {
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "ath6kl usb : usb bulk recv failed %d\n",
+ usb_status);
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ goto err_cleanup_urb;
+ }
+ usb_free_urb(urb);
+ }
+ return;
+
+err_cleanup_urb:
+ ath6kl_usb_cleanup_recv_urb(urb_context);
+ return;
+}
+
+static void ath6kl_usb_flush_all(struct ath6kl_usb *ar_usb)
+{
+ int i;
+
+ for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) {
+ if (ar_usb->pipes[i].ar_usb != NULL)
+ usb_kill_anchored_urbs(&ar_usb->pipes[i].urb_submitted);
+ }
+
+ /*
+ * Flushing any pending I/O may schedule work this call will block
+ * until all scheduled work runs to completion.
+ */
+ flush_scheduled_work();
+}
+
+static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *ar_usb)
+{
+ /*
+ * note: control pipe is no longer used
+ * ar_usb->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_cnt_thresh =
+ * ar_usb->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_alloc/2;
+ * ath6kl_usb_post_recv_transfers(&ar_usb->
+ * pipes[ATH6KL_USB_PIPE_RX_CTRL],
+ * ATH6KL_USB_RX_BUFFER_SIZE);
+ */
+
+ ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = 1;
+
+ ath6kl_usb_post_recv_transfers(&ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA],
+ ATH6KL_USB_RX_BUFFER_SIZE);
+}
+
+/* hif usb rx/tx completion functions */
+static void ath6kl_usb_recv_complete(struct urb *urb)
+{
+ struct ath6kl_urb_context *urb_context = urb->context;
+ struct ath6kl_usb_pipe *pipe = urb_context->pipe;
+ struct sk_buff *skb = NULL;
+ int status = 0;
+
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "%s: recv pipe: %d, stat:%d, len:%d urb:0x%p\n", __func__,
+ pipe->logical_pipe_num, urb->status, urb->actual_length,
+ urb);
+
+ if (urb->status != 0) {
+ status = -EIO;
+ switch (urb->status) {
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /*
+ * no need to spew these errors when device
+ * removed or urb killed due to driver shutdown
+ */
+ status = -ECANCELED;
+ break;
+ default:
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n",
+ __func__, pipe->logical_pipe_num,
+ pipe->ep_address, urb->status);
+ break;
+ }
+ goto cleanup_recv_urb;
+ }
+
+ if (urb->actual_length == 0)
+ goto cleanup_recv_urb;
+
+ skb = urb_context->skb;
+
+ /* we are going to pass it up */
+ urb_context->skb = NULL;
+ skb_put(skb, urb->actual_length);
+
+ /* note: queue implements a lock */
+ skb_queue_tail(&pipe->io_comp_queue, skb);
+ schedule_work(&pipe->io_complete_work);
+
+cleanup_recv_urb:
+ ath6kl_usb_cleanup_recv_urb(urb_context);
+
+ if (status == 0 &&
+ pipe->urb_cnt >= pipe->urb_cnt_thresh) {
+ /* our free urbs are piling up, post more transfers */
+ ath6kl_usb_post_recv_transfers(pipe, ATH6KL_USB_RX_BUFFER_SIZE);
+ }
+}
+
+static void ath6kl_usb_usb_transmit_complete(struct urb *urb)
+{
+ struct ath6kl_urb_context *urb_context = urb->context;
+ struct ath6kl_usb_pipe *pipe = urb_context->pipe;
+ struct sk_buff *skb;
+
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "%s: pipe: %d, stat:%d, len:%d\n",
+ __func__, pipe->logical_pipe_num, urb->status,
+ urb->actual_length);
+
+ if (urb->status != 0) {
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "%s: pipe: %d, failed:%d\n",
+ __func__, pipe->logical_pipe_num, urb->status);
+ }
+
+ skb = urb_context->skb;
+ urb_context->skb = NULL;
+ ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context);
+
+ /* note: queue implements a lock */
+ skb_queue_tail(&pipe->io_comp_queue, skb);
+ schedule_work(&pipe->io_complete_work);
+}
+
+static void ath6kl_usb_io_comp_work(struct work_struct *work)
+{
+ struct ath6kl_usb_pipe *pipe = container_of(work,
+ struct ath6kl_usb_pipe,
+ io_complete_work);
+ struct ath6kl_usb *ar_usb;
+ struct sk_buff *skb;
+
+ ar_usb = pipe->ar_usb;
+
+ while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+ if (pipe->flags & ATH6KL_USB_PIPE_FLAG_TX) {
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "ath6kl usb xmit callback buf:0x%p\n", skb);
+ ath6kl_core_tx_complete(ar_usb->ar, skb);
+ } else {
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "ath6kl usb recv callback buf:0x%p\n", skb);
+ ath6kl_core_rx_complete(ar_usb->ar, skb,
+ pipe->logical_pipe_num);
+ }
+ }
+}
+
+#define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write))
+#define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read))
+
+static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb)
+{
+ ath6kl_usb_flush_all(ar_usb);
+
+ ath6kl_usb_cleanup_pipe_resources(ar_usb);
+
+ usb_set_intfdata(ar_usb->interface, NULL);
+
+ kfree(ar_usb->diag_cmd_buffer);
+ kfree(ar_usb->diag_resp_buffer);
+
+ kfree(ar_usb);
+}
+
+static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface)
+{
+ struct usb_device *dev = interface_to_usbdev(interface);
+ struct ath6kl_usb *ar_usb;
+ struct ath6kl_usb_pipe *pipe;
+ int status = 0;
+ int i;
+
+ ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL);
+ if (ar_usb == NULL)
+ goto fail_ath6kl_usb_create;
+
+ usb_set_intfdata(interface, ar_usb);
+ spin_lock_init(&(ar_usb->cs_lock));
+ ar_usb->udev = dev;
+ ar_usb->interface = interface;
+
+ for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) {
+ pipe = &ar_usb->pipes[i];
+ INIT_WORK(&pipe->io_complete_work,
+ ath6kl_usb_io_comp_work);
+ skb_queue_head_init(&pipe->io_comp_queue);
+ }
+
+ ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL);
+ if (ar_usb->diag_cmd_buffer == NULL) {
+ status = -ENOMEM;
+ goto fail_ath6kl_usb_create;
+ }
+
+ ar_usb->diag_resp_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_RESP,
+ GFP_KERNEL);
+ if (ar_usb->diag_resp_buffer == NULL) {
+ status = -ENOMEM;
+ goto fail_ath6kl_usb_create;
+ }
+
+ status = ath6kl_usb_setup_pipe_resources(ar_usb);
+
+fail_ath6kl_usb_create:
+ if (status != 0) {
+ ath6kl_usb_destroy(ar_usb);
+ ar_usb = NULL;
+ }
+ return ar_usb;
+}
+
+static void ath6kl_usb_device_detached(struct usb_interface *interface)
+{
+ struct ath6kl_usb *ar_usb;
+
+ ar_usb = usb_get_intfdata(interface);
+ if (ar_usb == NULL)
+ return;
+
+ ath6kl_stop_txrx(ar_usb->ar);
+
+ /* Delay to wait for the target to reboot */
+ mdelay(20);
+ ath6kl_core_cleanup(ar_usb->ar);
+ ath6kl_usb_destroy(ar_usb);
+}
+
+/* exported hif usb APIs for htc pipe */
+static void hif_start(struct ath6kl *ar)
+{
+ struct ath6kl_usb *device = ath6kl_usb_priv(ar);
+ int i;
+
+ ath6kl_usb_start_recv_pipes(device);
+
+ /* set the TX resource avail threshold for each TX pipe */
+ for (i = ATH6KL_USB_PIPE_TX_CTRL;
+ i <= ATH6KL_USB_PIPE_TX_DATA_HP; i++) {
+ device->pipes[i].urb_cnt_thresh =
+ device->pipes[i].urb_alloc / 2;
+ }
+}
+
+static int ath6kl_usb_send(struct ath6kl *ar, u8 PipeID,
+ struct sk_buff *hdr_skb, struct sk_buff *skb)
+{
+ struct ath6kl_usb *device = ath6kl_usb_priv(ar);
+ struct ath6kl_usb_pipe *pipe = &device->pipes[PipeID];
+ struct ath6kl_urb_context *urb_context;
+ int usb_status, status = 0;
+ struct urb *urb;
+ u8 *data;
+ u32 len;
+
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK, "+%s pipe : %d, buf:0x%p\n",
+ __func__, PipeID, skb);
+
+ urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe);
+
+ if (urb_context == NULL) {
+ /*
+ * TODO: it is possible to run out of urbs if
+ * 2 endpoints map to the same pipe ID
+ */
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "%s pipe:%d no urbs left. URB Cnt : %d\n",
+ __func__, PipeID, pipe->urb_cnt);
+ status = -ENOMEM;
+ goto fail_hif_send;
+ }
+
+ urb_context->skb = skb;
+
+ data = skb->data;
+ len = skb->len;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (urb == NULL) {
+ status = -ENOMEM;
+ ath6kl_usb_free_urb_to_pipe(urb_context->pipe,
+ urb_context);
+ goto fail_hif_send;
+ }
+
+ usb_fill_bulk_urb(urb,
+ device->udev,
+ pipe->usb_pipe_handle,
+ data,
+ len,
+ ath6kl_usb_usb_transmit_complete, urb_context);
+
+ if ((len % pipe->max_packet_size) == 0) {
+ /* hit a max packet boundary on this pipe */
+ urb->transfer_flags |= URB_ZERO_PACKET;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n",
+ pipe->logical_pipe_num, pipe->usb_pipe_handle,
+ pipe->ep_address, len);
+
+ usb_anchor_urb(urb, &pipe->urb_submitted);
+ usb_status = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (usb_status) {
+ ath6kl_dbg(ATH6KL_DBG_USB_BULK,
+ "ath6kl usb : usb bulk transmit failed %d\n",
+ usb_status);
+ usb_unanchor_urb(urb);
+ ath6kl_usb_free_urb_to_pipe(urb_context->pipe,
+ urb_context);
+ status = -EINVAL;
+ }
+ usb_free_urb(urb);
+
+fail_hif_send:
+ return status;
+}
+
+static void hif_stop(struct ath6kl *ar)
+{
+ struct ath6kl_usb *device = ath6kl_usb_priv(ar);
+
+ ath6kl_usb_flush_all(device);
+}
+
+static void ath6kl_usb_get_default_pipe(struct ath6kl *ar,
+ u8 *ul_pipe, u8 *dl_pipe)
+{
+ *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL;
+ *dl_pipe = ATH6KL_USB_PIPE_RX_CTRL;
+}
+
+static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id,
+ u8 *ul_pipe, u8 *dl_pipe)
+{
+ int status = 0;
+
+ switch (svc_id) {
+ case HTC_CTRL_RSVD_SVC:
+ case WMI_CONTROL_SVC:
+ *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL;
+ /* due to large control packets, shift to data pipe */
+ *dl_pipe = ATH6KL_USB_PIPE_RX_DATA;
+ break;
+ case WMI_DATA_BE_SVC:
+ case WMI_DATA_BK_SVC:
+ *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP;
+ /*
+ * Disable rxdata2 directly, it will be enabled
+ * if FW enable rxdata2
+ */
+ *dl_pipe = ATH6KL_USB_PIPE_RX_DATA;
+ break;
+ case WMI_DATA_VI_SVC:
+
+ if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT)
+ *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP;
+ else
+ *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP;
+ /*
+ * Disable rxdata2 directly, it will be enabled
+ * if FW enable rxdata2
+ */
+ *dl_pipe = ATH6KL_USB_PIPE_RX_DATA;
+ break;
+ case WMI_DATA_VO_SVC:
+
+ if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT)
+ *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP;
+ else
+ *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP;
+ /*
+ * Disable rxdata2 directly, it will be enabled
+ * if FW enable rxdata2
+ */
+ *dl_pipe = ATH6KL_USB_PIPE_RX_DATA;
+ break;
+ default:
+ status = -EPERM;
+ break;
+ }
+
+ return status;
+}
+
+static u16 ath6kl_usb_get_free_queue_number(struct ath6kl *ar, u8 pipe_id)
+{
+ struct ath6kl_usb *device = ath6kl_usb_priv(ar);
+
+ return device->pipes[pipe_id].urb_cnt;
+}
+
+static void hif_detach_htc(struct ath6kl *ar)
+{
+ struct ath6kl_usb *device = ath6kl_usb_priv(ar);
+
+ ath6kl_usb_flush_all(device);
+}
+
+static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb,
+ u8 req, u16 value, u16 index, void *data,
+ u32 size)
+{
+ u8 *buf = NULL;
+ int ret;
+
+ if (size > 0) {
+ buf = kmemdup(data, size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ }
+
+ /* note: if successful returns number of bytes transfered */
+ ret = usb_control_msg(ar_usb->udev,
+ usb_sndctrlpipe(ar_usb->udev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, value, index, buf,
+ size, 1000);
+
+ if (ret < 0) {
+ ath6kl_warn("Failed to submit usb control message: %d\n", ret);
+ kfree(buf);
+ return ret;
+ }
+
+ kfree(buf);
+
+ return 0;
+}
+
+static int ath6kl_usb_submit_ctrl_in(struct ath6kl_usb *ar_usb,
+ u8 req, u16 value, u16 index, void *data,
+ u32 size)
+{
+ u8 *buf = NULL;
+ int ret;
+
+ if (size > 0) {
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ }
+
+ /* note: if successful returns number of bytes transfered */
+ ret = usb_control_msg(ar_usb->udev,
+ usb_rcvctrlpipe(ar_usb->udev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, value, index, buf,
+ size, 2 * HZ);
+
+ if (ret < 0) {
+ ath6kl_warn("Failed to read usb control message: %d\n", ret);
+ kfree(buf);
+ return ret;
+ }
+
+ memcpy((u8 *) data, buf, size);
+
+ kfree(buf);
+
+ return 0;
+}
+
+static int ath6kl_usb_ctrl_msg_exchange(struct ath6kl_usb *ar_usb,
+ u8 req_val, u8 *req_buf, u32 req_len,
+ u8 resp_val, u8 *resp_buf, u32 *resp_len)
+{
+ int ret;
+
+ /* send command */
+ ret = ath6kl_usb_submit_ctrl_out(ar_usb, req_val, 0, 0,
+ req_buf, req_len);
+
+ if (ret != 0)
+ return ret;
+
+ if (resp_buf == NULL) {
+ /* no expected response */
+ return ret;
+ }
+
+ /* get response */
+ ret = ath6kl_usb_submit_ctrl_in(ar_usb, resp_val, 0, 0,
+ resp_buf, *resp_len);
+
+ return ret;
+}
+
+static int ath6kl_usb_diag_read32(struct ath6kl *ar, u32 address, u32 *data)
+{
+ struct ath6kl_usb *ar_usb = ar->hif_priv;
+ struct ath6kl_usb_ctrl_diag_resp_read *resp;
+ struct ath6kl_usb_ctrl_diag_cmd_read *cmd;
+ u32 resp_len;
+ int ret;
+
+ cmd = (struct ath6kl_usb_ctrl_diag_cmd_read *) ar_usb->diag_cmd_buffer;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->cmd = ATH6KL_USB_CTRL_DIAG_CC_READ;
+ cmd->address = cpu_to_le32(address);
+ resp_len = sizeof(*resp);
+
+ ret = ath6kl_usb_ctrl_msg_exchange(ar_usb,
+ ATH6KL_USB_CONTROL_REQ_DIAG_CMD,
+ (u8 *) cmd,
+ sizeof(struct ath6kl_usb_ctrl_diag_cmd_write),
+ ATH6KL_USB_CONTROL_REQ_DIAG_RESP,
+ ar_usb->diag_resp_buffer, &resp_len);
+
+ if (ret) {
+ ath6kl_warn("diag read32 failed: %d\n", ret);
+ return ret;
+ }
+
+ resp = (struct ath6kl_usb_ctrl_diag_resp_read *)
+ ar_usb->diag_resp_buffer;
+
+ *data = le32_to_cpu(resp->value);
+
+ return ret;
+}
+
+static int ath6kl_usb_diag_write32(struct ath6kl *ar, u32 address, __le32 data)
+{
+ struct ath6kl_usb *ar_usb = ar->hif_priv;
+ struct ath6kl_usb_ctrl_diag_cmd_write *cmd;
+ int ret;
+
+ cmd = (struct ath6kl_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer;
+
+ memset(cmd, 0, sizeof(struct ath6kl_usb_ctrl_diag_cmd_write));
+ cmd->cmd = cpu_to_le32(ATH6KL_USB_CTRL_DIAG_CC_WRITE);
+ cmd->address = cpu_to_le32(address);
+ cmd->value = data;
+
+ ret = ath6kl_usb_ctrl_msg_exchange(ar_usb,
+ ATH6KL_USB_CONTROL_REQ_DIAG_CMD,
+ (u8 *) cmd,
+ sizeof(*cmd),
+ 0, NULL, NULL);
+ if (ret) {
+ ath6kl_warn("diag_write32 failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath6kl_usb_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ struct ath6kl_usb *ar_usb = ar->hif_priv;
+ int ret;
+
+ /* get response */
+ ret = ath6kl_usb_submit_ctrl_in(ar_usb,
+ ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP,
+ 0, 0, buf, len);
+ if (ret) {
+ ath6kl_err("Unable to read the bmi data from the device: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
+{
+ struct ath6kl_usb *ar_usb = ar->hif_priv;
+ int ret;
+
+ /* send command */
+ ret = ath6kl_usb_submit_ctrl_out(ar_usb,
+ ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD,
+ 0, 0, buf, len);
+ if (ret) {
+ ath6kl_err("unable to send the bmi data to the device: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath6kl_usb_power_on(struct ath6kl *ar)
+{
+ hif_start(ar);
+ return 0;
+}
+
+static int ath6kl_usb_power_off(struct ath6kl *ar)
+{
+ hif_detach_htc(ar);
+ return 0;
+}
+
+static void ath6kl_usb_stop(struct ath6kl *ar)
+{
+ hif_stop(ar);
+}
+
+static void ath6kl_usb_cleanup_scatter(struct ath6kl *ar)
+{
+ /*
+ * USB doesn't support it. Just return.
+ */
+ return;
+}
+
+static int ath6kl_usb_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+ /*
+ * cfg80211 suspend/WOW currently not supported for USB.
+ */
+ return 0;
+}
+
+static int ath6kl_usb_resume(struct ath6kl *ar)
+{
+ /*
+ * cfg80211 resume currently not supported for USB.
+ */
+ return 0;
+}
+
+static const struct ath6kl_hif_ops ath6kl_usb_ops = {
+ .diag_read32 = ath6kl_usb_diag_read32,
+ .diag_write32 = ath6kl_usb_diag_write32,
+ .bmi_read = ath6kl_usb_bmi_read,
+ .bmi_write = ath6kl_usb_bmi_write,
+ .power_on = ath6kl_usb_power_on,
+ .power_off = ath6kl_usb_power_off,
+ .stop = ath6kl_usb_stop,
+ .pipe_send = ath6kl_usb_send,
+ .pipe_get_default = ath6kl_usb_get_default_pipe,
+ .pipe_map_service = ath6kl_usb_map_service_pipe,
+ .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number,
+ .cleanup_scatter = ath6kl_usb_cleanup_scatter,
+ .suspend = ath6kl_usb_suspend,
+ .resume = ath6kl_usb_resume,
+};
+
+/* ath6kl usb driver registered functions */
+static int ath6kl_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(interface);
+ struct ath6kl *ar;
+ struct ath6kl_usb *ar_usb = NULL;
+ int vendor_id, product_id;
+ int ret = 0;
+
+ usb_get_dev(dev);
+
+ vendor_id = le16_to_cpu(dev->descriptor.idVendor);
+ product_id = le16_to_cpu(dev->descriptor.idProduct);
+
+ ath6kl_dbg(ATH6KL_DBG_USB, "vendor_id = %04x\n", vendor_id);
+ ath6kl_dbg(ATH6KL_DBG_USB, "product_id = %04x\n", product_id);
+
+ if (interface->cur_altsetting)
+ ath6kl_dbg(ATH6KL_DBG_USB, "USB Interface %d\n",
+ interface->cur_altsetting->desc.bInterfaceNumber);
+
+
+ if (dev->speed == USB_SPEED_HIGH)
+ ath6kl_dbg(ATH6KL_DBG_USB, "USB 2.0 Host\n");
+ else
+ ath6kl_dbg(ATH6KL_DBG_USB, "USB 1.1 Host\n");
+
+ ar_usb = ath6kl_usb_create(interface);
+
+ if (ar_usb == NULL) {
+ ret = -ENOMEM;
+ goto err_usb_put;
+ }
+
+ ar = ath6kl_core_create(&ar_usb->udev->dev);
+ if (ar == NULL) {
+ ath6kl_err("Failed to alloc ath6kl core\n");
+ ret = -ENOMEM;
+ goto err_usb_destroy;
+ }
+
+ ar->hif_priv = ar_usb;
+ ar->hif_type = ATH6KL_HIF_TYPE_USB;
+ ar->hif_ops = &ath6kl_usb_ops;
+ ar->mbox_info.block_size = 16;
+ ar->bmi.max_data_size = 252;
+
+ ar_usb->ar = ar;
+
+ ret = ath6kl_core_init(ar, ATH6KL_HTC_TYPE_PIPE);
+ if (ret) {
+ ath6kl_err("Failed to init ath6kl core: %d\n", ret);
+ goto err_core_free;
+ }
+
+ return ret;
+
+err_core_free:
+ ath6kl_core_destroy(ar);
+err_usb_destroy:
+ ath6kl_usb_destroy(ar_usb);
+err_usb_put:
+ usb_put_dev(dev);
+
+ return ret;
+}
+
+static void ath6kl_usb_remove(struct usb_interface *interface)
+{
+ usb_put_dev(interface_to_usbdev(interface));
+ ath6kl_usb_device_detached(interface);
+}
+
+#ifdef CONFIG_PM
+
+static int ath6kl_usb_pm_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct ath6kl_usb *device;
+ device = usb_get_intfdata(interface);
+
+ ath6kl_usb_flush_all(device);
+ return 0;
+}
+
+static int ath6kl_usb_pm_resume(struct usb_interface *interface)
+{
+ struct ath6kl_usb *device;
+ device = usb_get_intfdata(interface);
+
+ ath6kl_usb_post_recv_transfers(&device->pipes[ATH6KL_USB_PIPE_RX_DATA],
+ ATH6KL_USB_RX_BUFFER_SIZE);
+ ath6kl_usb_post_recv_transfers(&device->pipes[ATH6KL_USB_PIPE_RX_DATA2],
+ ATH6KL_USB_RX_BUFFER_SIZE);
+
+ return 0;
+}
+
+static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf)
+{
+ if (usb_get_intfdata(intf))
+ ath6kl_usb_remove(intf);
+ return 0;
+}
+
+#else
+
+#define ath6kl_usb_pm_suspend NULL
+#define ath6kl_usb_pm_resume NULL
+#define ath6kl_usb_pm_reset_resume NULL
+
+#endif
+
+/* table of devices that work with this driver */
+static struct usb_device_id ath6kl_usb_ids[] = {
+ {USB_DEVICE(0x0cf3, 0x9374)},
+ { /* Terminating entry */ },
+};
+
+MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids);
+
+static struct usb_driver ath6kl_usb_driver = {
+ .name = "ath6kl_usb",
+ .probe = ath6kl_usb_probe,
+ .suspend = ath6kl_usb_pm_suspend,
+ .resume = ath6kl_usb_pm_resume,
+ .reset_resume = ath6kl_usb_pm_reset_resume,
+ .disconnect = ath6kl_usb_remove,
+ .id_table = ath6kl_usb_ids,
+ .supports_autosuspend = true,
+ .disable_hub_initiated_lpm = 1,
+};
+
+static int ath6kl_usb_init(void)
+{
+ int ret;
+
+ ret = usb_register(&ath6kl_usb_driver);
+ if (ret) {
+ ath6kl_err("usb registration failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ath6kl_usb_exit(void)
+{
+ usb_deregister(&ath6kl_usb_driver);
+}
+
+module_init(ath6kl_usb_init);
+module_exit(ath6kl_usb_exit);
+
+MODULE_AUTHOR("Atheros Communications, Inc.");
+MODULE_DESCRIPTION("Driver support for Atheros AR600x USB devices");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_2_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_FIRMWARE_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_3_BOARD_DATA_FILE);
+MODULE_FIRMWARE(AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index a7de23cbd2c..4d7f9e4712e 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -15,13 +16,15 @@
*/
#include <linux/ip.h>
+#include <linux/in.h>
#include "core.h"
#include "debug.h"
#include "testmode.h"
+#include "trace.h"
#include "../regd.h"
#include "../regd_common.h"
-static int ath6kl_wmi_sync_point(struct wmi *wmi);
+static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx);
static const s32 wmi_rate_tbl[][2] = {
/* {W/O SGI, with SGI} */
@@ -81,6 +84,26 @@ enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi)
return wmi->ep_id;
}
+struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx)
+{
+ struct ath6kl_vif *vif, *found = NULL;
+
+ if (WARN_ON(if_idx > (ar->vif_max - 1)))
+ return NULL;
+
+ /* FIXME: Locking */
+ spin_lock_bh(&ar->list_lock);
+ list_for_each_entry(vif, &ar->vif_list, list) {
+ if (vif->fw_vif_idx == if_idx) {
+ found = vif;
+ break;
+ }
+ }
+ spin_unlock_bh(&ar->list_lock);
+
+ return found;
+}
+
/* 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.
@@ -106,7 +129,7 @@ int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb)
if (!is_ethertype(be16_to_cpu(type))) {
ath6kl_dbg(ATH6KL_DBG_WMI,
- "%s: pkt is already in 802.3 format\n", __func__);
+ "%s: pkt is already in 802.3 format\n", __func__);
return 0;
}
@@ -160,14 +183,14 @@ static int ath6kl_wmi_meta_add(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,
+ u8 msg_type, u32 flags,
enum wmi_data_hdr_data_type data_type,
- u8 meta_ver, void *tx_meta_info)
+ u8 meta_ver, void *tx_meta_info, u8 if_idx)
{
struct wmi_data_hdr *data_hdr;
int ret;
- if (WARN_ON(skb == NULL))
+ if (WARN_ON(skb == NULL || (if_idx > wmi->parent_dev->vif_max - 1)))
return -EINVAL;
if (tx_meta_info) {
@@ -184,17 +207,19 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
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;
+ if (flags & WMI_DATA_HDR_FLAGS_MORE)
+ data_hdr->info |= WMI_DATA_HDR_MORE;
- data_hdr->info2 = cpu_to_le16(meta_ver << WMI_DATA_HDR_META_SHIFT);
- data_hdr->info3 = 0;
+ if (flags & WMI_DATA_HDR_FLAGS_EOSP)
+ data_hdr->info3 |= cpu_to_le16(WMI_DATA_HDR_EOSP);
+
+ data_hdr->info2 |= cpu_to_le16(meta_ver << WMI_DATA_HDR_META_SHIFT);
+ data_hdr->info3 |= cpu_to_le16(if_idx & WMI_DATA_HDR_IF_IDX_MASK);
return 0;
}
-static u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri)
+u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri)
{
struct iphdr *ip_hdr = (struct iphdr *) pkt;
u8 ip_pri;
@@ -216,7 +241,13 @@ static u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri)
return ip_pri;
}
-int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
+u8 ath6kl_wmi_get_traffic_class(u8 user_priority)
+{
+ return up_to_ac[user_priority & 0x7];
+}
+
+int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx,
+ struct sk_buff *skb,
u32 layer2_priority, bool wmm_enabled,
u8 *ac)
{
@@ -258,11 +289,24 @@ int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) +
sizeof(struct ath6kl_llc_snap_hdr),
layer2_priority);
- } else
+ } else {
usr_pri = layer2_priority & 0x7;
+ }
+
+ /*
+ * Queue the EAPOL frames in the same WMM_AC_VO queue
+ * as that of management frames.
+ */
+ if (skb->protocol == cpu_to_be16(ETH_P_PAE))
+ usr_pri = WMI_VOICE_USER_PRIORITY;
}
- /* workaround for WMM S5 */
+ /*
+ * workaround for WMM S5
+ *
+ * FIXME: wmi->traffic_class is always 100 so this test doesn't
+ * make sense
+ */
if ((wmi->traffic_class == WMM_AC_VI) &&
((usr_pri == 5) || (usr_pri == 4)))
usr_pri = 1;
@@ -284,7 +328,7 @@ int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
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);
+ ath6kl_wmi_create_pstream_cmd(wmi, if_idx, &cmd);
}
*ac = traffic_class;
@@ -316,8 +360,9 @@ int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
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))
+ } 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);
@@ -393,9 +438,6 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
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));
@@ -410,13 +452,14 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
}
static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap,
- int len)
+ int len, struct ath6kl_vif *vif)
{
struct wmi_remain_on_chnl_event *ev;
u32 freq;
u32 dur;
struct ieee80211_channel *chan;
struct ath6kl *ar = wmi->parent_dev;
+ u32 id;
if (len < sizeof(*ev))
return -EINVAL;
@@ -426,26 +469,30 @@ static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap,
dur = le32_to_cpu(ev->duration);
ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n",
freq, dur);
- chan = ieee80211_get_channel(ar->wdev->wiphy, freq);
+ chan = ieee80211_get_channel(ar->wiphy, freq);
if (!chan) {
- ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: Unknown channel "
- "(freq=%u)\n", freq);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "remain_on_chnl: Unknown channel (freq=%u)\n",
+ freq);
return -EINVAL;
}
- cfg80211_ready_on_channel(ar->net_dev, 1, chan, NL80211_CHAN_NO_HT,
+ id = vif->last_roc_id;
+ cfg80211_ready_on_channel(&vif->wdev, id, chan,
dur, GFP_ATOMIC);
return 0;
}
static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi,
- u8 *datap, int len)
+ u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_cancel_remain_on_chnl_event *ev;
u32 freq;
u32 dur;
struct ieee80211_channel *chan;
struct ath6kl *ar = wmi->parent_dev;
+ u32 id;
if (len < sizeof(*ev))
return -EINVAL;
@@ -453,25 +500,32 @@ static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi,
ev = (struct wmi_cancel_remain_on_chnl_event *) datap;
freq = le32_to_cpu(ev->freq);
dur = le32_to_cpu(ev->duration);
- ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u "
- "status=%u\n", freq, dur, ev->status);
- chan = ieee80211_get_channel(ar->wdev->wiphy, freq);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "cancel_remain_on_chnl: freq=%u dur=%u status=%u\n",
+ freq, dur, ev->status);
+ chan = ieee80211_get_channel(ar->wiphy, freq);
if (!chan) {
- ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: Unknown "
- "channel (freq=%u)\n", freq);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "cancel_remain_on_chnl: Unknown channel (freq=%u)\n",
+ freq);
return -EINVAL;
}
- cfg80211_remain_on_channel_expired(ar->net_dev, 1, chan,
- NL80211_CHAN_NO_HT, GFP_ATOMIC);
+ if (vif->last_cancel_roc_id &&
+ vif->last_cancel_roc_id + 1 == vif->last_roc_id)
+ id = vif->last_cancel_roc_id; /* event for cancel command */
+ else
+ id = vif->last_roc_id; /* timeout on uncanceled r-o-c */
+ vif->last_cancel_roc_id = 0;
+ cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, GFP_ATOMIC);
return 0;
}
-static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_tx_status_event *ev;
u32 id;
- struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(*ev))
return -EINVAL;
@@ -481,7 +535,7 @@ static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len)
ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n",
id, ev->ack_status);
if (wmi->last_mgmt_tx_frame) {
- cfg80211_mgmt_tx_status(ar->net_dev, id,
+ cfg80211_mgmt_tx_status(&vif->wdev, id,
wmi->last_mgmt_tx_frame,
wmi->last_mgmt_tx_frame_len,
!!ev->ack_status, GFP_ATOMIC);
@@ -493,12 +547,12 @@ static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len)
return 0;
}
-static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_p2p_rx_probe_req_event *ev;
u32 freq;
u16 dlen;
- struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(*ev))
return -EINVAL;
@@ -507,16 +561,17 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len)
freq = le32_to_cpu(ev->freq);
dlen = le16_to_cpu(ev->len);
if (datap + len < ev->data + dlen) {
- ath6kl_err("invalid wmi_p2p_rx_probe_req_event: "
- "len=%d dlen=%u\n", len, dlen);
+ ath6kl_err("invalid wmi_p2p_rx_probe_req_event: len=%d dlen=%u\n",
+ len, dlen);
return -EINVAL;
}
- ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u freq=%u "
- "probe_req_report=%d\n",
- dlen, freq, ar->probe_req_report);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "rx_probe_req: len=%u freq=%u probe_req_report=%d\n",
+ dlen, freq, vif->probe_req_report);
- if (ar->probe_req_report || ar->nw_type == AP_NETWORK)
- cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC);
+ if (vif->probe_req_report || vif->nw_type == AP_NETWORK)
+ cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0,
+ GFP_ATOMIC);
return 0;
}
@@ -536,12 +591,12 @@ static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len)
return 0;
}
-static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_rx_action_event *ev;
u32 freq;
u16 dlen;
- struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(*ev))
return -EINVAL;
@@ -550,12 +605,12 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len)
freq = le32_to_cpu(ev->freq);
dlen = le16_to_cpu(ev->len);
if (datap + len < ev->data + dlen) {
- ath6kl_err("invalid wmi_rx_action_event: "
- "len=%d dlen=%u\n", len, dlen);
+ ath6kl_err("invalid wmi_rx_action_event: len=%d dlen=%u\n",
+ len, dlen);
return -EINVAL;
}
ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq);
- cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC);
+ cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0, GFP_ATOMIC);
return 0;
}
@@ -620,7 +675,8 @@ static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size)
}
/* Send a "simple" wmi command -- one with no arguments */
-static int ath6kl_wmi_simple_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id)
+static int ath6kl_wmi_simple_cmd(struct wmi *wmi, u8 if_idx,
+ enum wmi_cmd_id cmd_id)
{
struct sk_buff *skb;
int ret;
@@ -629,7 +685,7 @@ static int ath6kl_wmi_simple_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id)
if (!skb)
return -ENOMEM;
- ret = ath6kl_wmi_cmd_send(wmi, skb, cmd_id, NO_SYNC_WMIFLAG);
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, cmd_id, NO_SYNC_WMIFLAG);
return ret;
}
@@ -641,10 +697,9 @@ static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len)
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));
+ le32_to_cpu(ev->abi_version), ev->phy_cap);
return 0;
}
@@ -673,48 +728,121 @@ int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi)
cmd->info.params.roam_rssi_floor = DEF_LRSSI_ROAM_FLOOR;
cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS;
- ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG);
+ ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID,
+ NO_SYNC_WMIFLAG);
return 0;
}
-static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
+int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid)
+{
+ struct sk_buff *skb;
+ struct roam_ctrl_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct roam_ctrl_cmd *) skb->data;
+
+ memcpy(cmd->info.bssid, bssid, ETH_ALEN);
+ cmd->roam_ctrl = WMI_FORCE_ROAM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "force roam to %pM\n", bssid);
+ return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_ap_set_beacon_intvl_cmd(struct wmi *wmi, u8 if_idx,
+ u32 beacon_intvl)
+{
+ struct sk_buff *skb;
+ struct set_beacon_int_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct set_beacon_int_cmd *) skb->data;
+
+ cmd->beacon_intvl = cpu_to_le32(beacon_intvl);
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_SET_BEACON_INT_CMDID, NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period)
+{
+ struct sk_buff *skb;
+ struct set_dtim_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct set_dtim_cmd *) skb->data;
+
+ cmd->dtim_period = cpu_to_le32(dtim_period);
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_AP_SET_DTIM_CMDID, NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode)
+{
+ struct sk_buff *skb;
+ struct roam_ctrl_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct roam_ctrl_cmd *) skb->data;
+
+ cmd->info.roam_mode = mode;
+ cmd->roam_ctrl = WMI_SET_ROAM_MODE;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "set roam mode %d\n", mode);
+ return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_connect_event *ev;
u8 *pie, *peie;
- struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(struct wmi_connect_event))
return -EINVAL;
ev = (struct wmi_connect_event *) datap;
- if (ar->nw_type == AP_NETWORK) {
+ if (vif->nw_type == AP_NETWORK) {
/* AP mode start/STA connected event */
- struct net_device *dev = ar->net_dev;
+ struct net_device *dev = vif->ndev;
if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) {
- ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM "
- "(AP started)\n",
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "%s: freq %d bssid %pM (AP started)\n",
__func__, le16_to_cpu(ev->u.ap_bss.ch),
ev->u.ap_bss.bssid);
ath6kl_connect_ap_mode_bss(
- ar, le16_to_cpu(ev->u.ap_bss.ch));
+ vif, le16_to_cpu(ev->u.ap_bss.ch));
} else {
- ath6kl_dbg(ATH6KL_DBG_WMI, "%s: aid %u mac_addr %pM "
- "auth=%u keymgmt=%u cipher=%u apsd_info=%u "
- "(STA connected)\n",
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "%s: aid %u mac_addr %pM auth=%u keymgmt=%u cipher=%u apsd_info=%u (STA connected)\n",
__func__, ev->u.ap_sta.aid,
ev->u.ap_sta.mac_addr,
ev->u.ap_sta.auth,
ev->u.ap_sta.keymgmt,
le16_to_cpu(ev->u.ap_sta.cipher),
ev->u.ap_sta.apsd_info);
+
ath6kl_connect_ap_mode_sta(
- ar, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr,
+ vif, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr,
ev->u.ap_sta.keymgmt,
le16_to_cpu(ev->u.ap_sta.cipher),
ev->u.ap_sta.auth, ev->assoc_req_len,
- ev->assoc_info + ev->beacon_ie_len);
+ ev->assoc_info + ev->beacon_ie_len,
+ ev->u.ap_sta.apsd_info);
}
return 0;
}
@@ -742,8 +870,8 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
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)
+ if (pie[1] > 5 &&
+ pie[6] == WMM_PARAM_OUI_SUBTYPE)
wmi->is_wmm_enabled = true;
}
break;
@@ -755,7 +883,7 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
pie += pie[1] + 2;
}
- ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->u.sta.ch),
+ ath6kl_connect_event(vif, le16_to_cpu(ev->u.sta.ch),
ev->u.sta.bssid,
le16_to_cpu(ev->u.sta.listen_intvl),
le16_to_cpu(ev->u.sta.beacon_intvl),
@@ -788,7 +916,7 @@ ath6kl_get_regpair(u16 regdmn)
return NULL;
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
- if (regDomainPairs[i].regDmnEnum == regdmn)
+ if (regDomainPairs[i].reg_domain == regdmn)
return &regDomainPairs[i];
}
@@ -810,7 +938,6 @@ ath6kl_regd_find_country_by_rd(u16 regdmn)
static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
{
-
struct ath6kl_wmi_regdomain *ev;
struct country_code_to_enum_rd *country = NULL;
struct reg_dmn_pair_mapping *regpair = NULL;
@@ -820,28 +947,32 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
ev = (struct ath6kl_wmi_regdomain *) datap;
reg_code = le32_to_cpu(ev->reg_code);
- if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG)
+ if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG) {
country = ath6kl_regd_find_country((u16) reg_code);
- else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
-
+ } else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
regpair = ath6kl_get_regpair((u16) reg_code);
country = ath6kl_regd_find_country_by_rd((u16) reg_code);
- ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n",
- regpair->regDmnEnum);
+ if (regpair)
+ ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n",
+ regpair->reg_domain);
+ else
+ ath6kl_warn("Regpair not found reg_code 0x%0x\n",
+ reg_code);
}
- if (country) {
+ if (country && wmi->parent_dev->wiphy_registered) {
alpha2[0] = country->isoName[0];
alpha2[1] = country->isoName[1];
- regulatory_hint(wmi->parent_dev->wdev->wiphy, alpha2);
+ regulatory_hint(wmi->parent_dev->wiphy, alpha2);
ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n",
- alpha2[0], alpha2[1]);
+ alpha2[0], alpha2[1]);
}
}
-static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_disconnect_event *ev;
wmi->traffic_class = 100;
@@ -857,10 +988,8 @@ static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len)
ev->disconn_reason, ev->assoc_resp_len);
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,
+ ath6kl_disconnect_event(vif, ev->disconn_reason,
ev->bssid, ev->assoc_resp_len, ev->assoc_info,
le16_to_cpu(ev->proto_reason_status));
@@ -886,7 +1015,8 @@ static int ath6kl_wmi_peer_node_event_rx(struct wmi *wmi, u8 *datap, int len)
return 0;
}
-static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_tkip_micerr_event *ev;
@@ -895,12 +1025,20 @@ static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len)
ev = (struct wmi_tkip_micerr_event *) datap;
- ath6kl_tkip_micerr_event(wmi->parent_dev, ev->key_id, ev->is_mcast);
+ ath6kl_tkip_micerr_event(vif, ev->key_id, ev->is_mcast);
return 0;
}
-static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
+void ath6kl_wmi_sscan_timer(unsigned long ptr)
+{
+ struct ath6kl_vif *vif = (struct ath6kl_vif *) ptr;
+
+ cfg80211_sched_scan_results(vif->ar->wiphy);
+}
+
+static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_bss_info_hdr2 *bih;
u8 *buf;
@@ -927,26 +1065,28 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
return 0; /* Only update BSS table for now */
if (bih->frame_type == BEACON_FTYPE &&
- test_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag)) {
- clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
- ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+ test_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags)) {
+ clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
+ ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+ NONE_BSS_FILTER, 0);
}
- channel = ieee80211_get_channel(ar->wdev->wiphy, le16_to_cpu(bih->ch));
+ channel = ieee80211_get_channel(ar->wiphy, le16_to_cpu(bih->ch));
if (channel == NULL)
return -EINVAL;
if (len < 8 + 2 + 2)
return -EINVAL;
- if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &ar->flag) &&
- memcmp(bih->bssid, ar->bssid, ETH_ALEN) == 0) {
+ if (bih->frame_type == BEACON_FTYPE &&
+ test_bit(CONNECTED, &vif->flags) &&
+ memcmp(bih->bssid, vif->bssid, ETH_ALEN) == 0) {
const u8 *tim;
tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2,
len - 8 - 2 - 2);
if (tim && tim[1] >= 2) {
- ar->assoc_bss_dtim_period = tim[3];
- set_bit(DTIM_PERIOD_AVAIL, &ar->flag);
+ vif->assoc_bss_dtim_period = tim[3];
+ set_bit(DTIM_PERIOD_AVAIL, &vif->flags);
}
}
@@ -966,7 +1106,7 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
IEEE80211_STYPE_BEACON);
memset(mgmt->da, 0xff, ETH_ALEN);
} else {
- struct net_device *dev = ar->net_dev;
+ struct net_device *dev = vif->ndev;
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_PROBE_RESP);
@@ -979,13 +1119,28 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
memcpy(&mgmt->u.beacon, buf, len);
- bss = cfg80211_inform_bss_frame(ar->wdev->wiphy, channel, mgmt,
+ bss = cfg80211_inform_bss_frame(ar->wiphy, channel, mgmt,
24 + len, (bih->snr - 95) * 100,
GFP_ATOMIC);
kfree(mgmt);
if (bss == NULL)
return -ENOMEM;
- cfg80211_put_bss(bss);
+ cfg80211_put_bss(ar->wiphy, bss);
+
+ /*
+ * Firmware doesn't return any event when scheduled scan has
+ * finished, so we need to use a timer to find out when there are
+ * no more results.
+ *
+ * The timer is started from the first bss info received, otherwise
+ * the timer would not ever fire if the scan interval is short
+ * enough.
+ */
+ if (test_bit(SCHED_SCANNING, &vif->flags) &&
+ !timer_pending(&vif->sched_scan_timer)) {
+ mod_timer(&vif->sched_scan_timer, jiffies +
+ msecs_to_jiffies(ATH6KL_SCHED_SCAN_RESULT_DELAY));
+ }
return 0;
}
@@ -1035,6 +1190,9 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len)
rate = RATE_AUTO;
} else {
index = reply->rate_index & 0x7f;
+ if (WARN_ON_ONCE(index > (RATE_MCS_7_40 + 1)))
+ return -EINVAL;
+
sgi = (reply->rate_index & 0x80) ? 1 : 0;
rate = wmi_rate_tbl[index][sgi];
}
@@ -1044,9 +1202,9 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len)
return 0;
}
-static int ath6kl_wmi_tcmd_test_report_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_test_rx(struct wmi *wmi, u8 *datap, int len)
{
- ath6kl_tm_rx_report_event(wmi->parent_dev, datap, len);
+ ath6kl_tm_rx_event(wmi->parent_dev, datap, len);
return 0;
}
@@ -1094,20 +1252,21 @@ static int ath6kl_wmi_keepalive_reply_rx(struct wmi *wmi, u8 *datap, int len)
return 0;
}
-static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_scan_complete_event *ev;
ev = (struct wmi_scan_complete_event *) datap;
- ath6kl_scan_complete_evt(wmi->parent_dev, a_sle32_to_cpu(ev->status));
+ ath6kl_scan_complete_evt(vif, a_sle32_to_cpu(ev->status));
wmi->is_probe_ssid = false;
return 0;
}
static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap,
- int len)
+ int len, struct ath6kl_vif *vif)
{
struct wmi_neighbor_report_event *ev;
u8 i;
@@ -1117,15 +1276,16 @@ static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap,
ev = (struct wmi_neighbor_report_event *) datap;
if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info)
> len) {
- ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event "
- "(num=%d len=%d)\n", ev->num_neighbors, len);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "truncated neighbor event (num=%d len=%d)\n",
+ ev->num_neighbors, len);
return -EINVAL;
}
for (i = 0; i < ev->num_neighbors; i++) {
ath6kl_dbg(ATH6KL_DBG_WMI, "neighbor %d/%d - %pM 0x%x\n",
i + 1, ev->num_neighbors, ev->neighbor[i].bssid,
ev->neighbor[i].bss_flags);
- cfg80211_pmksa_candidate_notify(wmi->parent_dev->net_dev, i,
+ cfg80211_pmksa_candidate_notify(vif->ndev, i,
ev->neighbor[i].bssid,
!!(ev->neighbor[i].bss_flags &
WMI_PREAUTH_CAPABLE_BSS),
@@ -1166,9 +1326,10 @@ static int ath6kl_wmi_error_event_rx(struct wmi *wmi, u8 *datap, int len)
return 0;
}
-static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
- ath6kl_tgt_stats_event(wmi->parent_dev, datap, len);
+ ath6kl_tgt_stats_event(vif, datap, len);
return 0;
}
@@ -1222,7 +1383,7 @@ static int ath6kl_wmi_send_rssi_threshold_params(struct wmi *wmi,
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,
+ return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_RSSI_THRESHOLD_PARAMS_CMDID,
NO_SYNC_WMIFLAG);
}
@@ -1255,8 +1416,8 @@ static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap,
/* Upper threshold breached */
if (rssi < sq_thresh->upper_threshold[0]) {
ath6kl_dbg(ATH6KL_DBG_WMI,
- "spurious upper rssi threshold event: %d\n",
- rssi);
+ "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;
@@ -1279,7 +1440,7 @@ static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap,
/* Lower threshold breached */
if (rssi > sq_thresh->lower_threshold[0]) {
ath6kl_dbg(ATH6KL_DBG_WMI,
- "spurious lower rssi threshold event: %d %d\n",
+ "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])) {
@@ -1322,7 +1483,8 @@ static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap,
return 0;
}
-static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_cac_event *reply;
struct ieee80211_tspec_ie *ts;
@@ -1337,13 +1499,13 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len)
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);
+ ath6kl_wmi_delete_pstream_cmd(wmi, vif->fw_vif_idx,
+ reply->ac, tsid);
} else if (reply->cac_indication == CAC_INDICATION_NO_RESP) {
/*
* Following assumes that there is only one outstanding
@@ -1358,7 +1520,8 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len)
break;
}
if (index < (sizeof(active_tsids) * 8))
- ath6kl_wmi_delete_pstream_cmd(wmi, reply->ac, index);
+ ath6kl_wmi_delete_pstream_cmd(wmi, vif->fw_vif_idx,
+ reply->ac, index);
}
/*
@@ -1366,7 +1529,6 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len)
* 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) &
@@ -1390,6 +1552,68 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len)
return 0;
}
+static int ath6kl_wmi_txe_notify_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
+{
+ struct wmi_txe_notify_event *ev;
+ u32 rate, pkts;
+
+ if (len < sizeof(*ev))
+ return -EINVAL;
+
+ if (vif->sme_state != SME_CONNECTED)
+ return -ENOTCONN;
+
+ ev = (struct wmi_txe_notify_event *) datap;
+ rate = le32_to_cpu(ev->rate);
+ pkts = le32_to_cpu(ev->pkts);
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "TXE notify event: peer %pM rate %d% pkts %d intvl %ds\n",
+ vif->bssid, rate, pkts, vif->txe_intvl);
+
+ cfg80211_cqm_txe_notify(vif->ndev, vif->bssid, pkts,
+ rate, vif->txe_intvl, GFP_KERNEL);
+
+ return 0;
+}
+
+int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx,
+ u32 rate, u32 pkts, u32 intvl)
+{
+ struct sk_buff *skb;
+ struct wmi_txe_notify_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_txe_notify_cmd *) skb->data;
+ cmd->rate = cpu_to_le32(rate);
+ cmd->pkts = cpu_to_le32(pkts);
+ cmd->intvl = cpu_to_le32(intvl);
+
+ return ath6kl_wmi_cmd_send(wmi, idx, skb, WMI_SET_TXE_NOTIFY_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi)
+{
+ struct sk_buff *skb;
+ struct wmi_set_rssi_filter_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_rssi_filter_cmd *) skb->data;
+ cmd->rssi = rssi;
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSSI_FILTER_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi,
struct wmi_snr_threshold_params_cmd *snr_cmd)
{
@@ -1403,7 +1627,7 @@ static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi,
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,
+ return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SNR_THRESHOLD_PARAMS_CMDID,
NO_SYNC_WMIFLAG);
}
@@ -1437,8 +1661,8 @@ static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap,
/* Upper threshold breached */
if (snr < sq_thresh->upper_threshold[0]) {
ath6kl_dbg(ATH6KL_DBG_WMI,
- "spurious upper snr threshold event: %d\n",
- snr);
+ "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;
@@ -1455,8 +1679,8 @@ static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap,
/* 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]);
+ "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;
@@ -1528,15 +1752,19 @@ static int ath6kl_wmi_aplist_event_rx(struct wmi *wmi, u8 *datap, int len)
return 0;
}
-int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
+int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, 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;
+ u16 info1;
- if (WARN_ON(skb == NULL))
+ if (WARN_ON(skb == NULL ||
+ (if_idx > (wmi->parent_dev->vif_max - 1)))) {
+ dev_kfree_skb(skb);
return -EINVAL;
+ }
ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n",
cmd_id, skb->len, sync_flag);
@@ -1554,19 +1782,20 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
* Make sure all data currently queued is transmitted before
* the cmd execution. Establish a new sync point.
*/
- ath6kl_wmi_sync_point(wmi);
+ ath6kl_wmi_sync_point(wmi, if_idx);
}
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 */
+ info1 = if_idx & WMI_CMD_HDR_IF_ID_MASK;
+ cmd_hdr->info1 = cpu_to_le16(info1);
/* 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);
+ false, false, 0, NULL, if_idx);
if (ret) {
dev_kfree_skb(skb);
return ret;
@@ -1582,20 +1811,22 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
* Make sure all new data queued waits for the command to
* execute. Establish a new sync point.
*/
- ath6kl_wmi_sync_point(wmi);
+ ath6kl_wmi_sync_point(wmi, if_idx);
}
return 0;
}
-int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type,
+int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx,
+ 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)
+ u8 *bssid, u16 channel, u32 ctrl_flags,
+ u8 nw_subtype)
{
struct sk_buff *skb;
struct wmi_connect_cmd *cc;
@@ -1635,19 +1866,19 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type,
cc->grp_crypto_len = group_crypto_len;
cc->ch = cpu_to_le16(channel);
cc->ctrl_flags = cpu_to_le32(ctrl_flags);
+ cc->nw_subtype = nw_subtype;
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);
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_CONNECT_CMDID,
+ NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel)
+int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 if_idx, u8 *bssid,
+ u16 channel)
{
struct sk_buff *skb;
struct wmi_reconnect_cmd *cc;
@@ -1668,13 +1899,13 @@ int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel)
if (bssid != NULL)
memcpy(cc->bssid, bssid, ETH_ALEN);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_RECONNECT_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_RECONNECT_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_disconnect_cmd(struct wmi *wmi)
+int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx)
{
int ret;
@@ -1683,15 +1914,21 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi)
wmi->traffic_class = 100;
/* Disconnect command does not need to do a SYNC before. */
- ret = ath6kl_wmi_simple_cmd(wmi, WMI_DISCONNECT_CMDID);
+ ret = ath6kl_wmi_simple_cmd(wmi, if_idx, 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)
+/* ath6kl_wmi_start_scan_cmd is to be deprecated. Use
+ * ath6kl_wmi_begin_scan_cmd instead. The new function supports P2P
+ * mgmt operations using station interface.
+ */
+static int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx,
+ 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;
@@ -1724,13 +1961,119 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
for (i = 0; i < num_chan; i++)
sc->ch_list[i] = cpu_to_le16(ch_list[i]);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_START_SCAN_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_START_SCAN_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+/*
+ * beginscan supports (compared to old startscan) P2P mgmt operations using
+ * station interface, send additional information like supported rates to
+ * advertise and xmit rates for probe requests
+ */
+int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx,
+ 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, u32 no_cck, u32 *rates)
+{
+ struct ieee80211_supported_band *sband;
+ struct sk_buff *skb;
+ struct wmi_begin_scan_cmd *sc;
+ s8 size, *supp_rates;
+ int i, band, ret;
+ struct ath6kl *ar = wmi->parent_dev;
+ int num_rates;
+ u32 ratemask;
+
+ if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+ ar->fw_capabilities)) {
+ return ath6kl_wmi_startscan_cmd(wmi, if_idx,
+ scan_type, force_fgscan,
+ is_legacy, home_dwell_time,
+ force_scan_interval,
+ num_chan, ch_list);
+ }
+
+ size = sizeof(struct wmi_begin_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_begin_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->no_cck = cpu_to_le32(no_cck);
+ sc->num_ch = num_chan;
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ sband = ar->wiphy->bands[band];
+
+ if (!sband)
+ continue;
+
+ if (WARN_ON(band >= ATH6KL_NUM_BANDS))
+ break;
+
+ ratemask = rates[band];
+ supp_rates = sc->supp_rates[band].rates;
+ num_rates = 0;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((BIT(i) & ratemask) == 0)
+ continue; /* skip rate */
+ supp_rates[num_rates++] =
+ (u8) (sband->bitrates[i].bitrate / 5);
+ }
+ sc->supp_rates[band].nrates = num_rates;
+ }
+
+ for (i = 0; i < num_chan; i++)
+ sc->ch_list[i] = cpu_to_le16(ch_list[i]);
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_BEGIN_SCAN_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u16 fg_start_sec,
+int ath6kl_wmi_enable_sched_scan_cmd(struct wmi *wmi, u8 if_idx, bool enable)
+{
+ struct sk_buff *skb;
+ struct wmi_enable_sched_scan_cmd *sc;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*sc));
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "%s scheduled scan on vif %d\n",
+ enable ? "enabling" : "disabling", if_idx);
+ sc = (struct wmi_enable_sched_scan_cmd *) skb->data;
+ sc->enable = enable ? 1 : 0;
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_ENABLE_SCHED_SCAN_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx,
+ 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,
@@ -1757,12 +2100,12 @@ int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u16 fg_start_sec,
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,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_SCAN_PARAMS_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 filter, u32 ie_mask)
+int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 if_idx, u8 filter, u32 ie_mask)
{
struct sk_buff *skb;
struct wmi_bss_filter_cmd *cmd;
@@ -1779,19 +2122,19 @@ int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 filter, u32 ie_mask)
cmd->bss_filter = filter;
cmd->ie_mask = cpu_to_le32(ie_mask);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_BSS_FILTER_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_BSS_FILTER_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 index, u8 flag,
+int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, 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)
+ if (index >= MAX_PROBED_SSIDS)
return -EINVAL;
if (ssid_len > sizeof(cmd->ssid))
@@ -1816,12 +2159,13 @@ int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 index, u8 flag,
cmd->ssid_len = ssid_len;
memcpy(cmd->ssid, ssid, ssid_len);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_PROBED_SSID_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_PROBED_SSID_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u16 listen_interval,
+int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u8 if_idx,
+ u16 listen_interval,
u16 listen_beacons)
{
struct sk_buff *skb;
@@ -1836,12 +2180,32 @@ int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u16 listen_interval,
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,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_LISTEN_INT_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_bmisstime_cmd(struct wmi *wmi, u8 if_idx,
+ u16 bmiss_time, u16 num_beacons)
+{
+ struct sk_buff *skb;
+ struct wmi_bmiss_time_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_bmiss_time_cmd *) skb->data;
+ cmd->bmiss_time = cpu_to_le16(bmiss_time);
+ cmd->num_beacons = cpu_to_le16(num_beacons);
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_BMISS_TIME_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 pwr_mode)
+int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode)
{
struct sk_buff *skb;
struct wmi_power_mode_cmd *cmd;
@@ -1855,12 +2219,12 @@ int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 pwr_mode)
cmd->pwr_mode = pwr_mode;
wmi->pwr_mode = pwr_mode;
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_POWER_MODE_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_POWER_MODE_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u16 idle_period,
+int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u8 if_idx, u16 idle_period,
u16 ps_poll_num, u16 dtim_policy,
u16 tx_wakeup_policy, u16 num_tx_to_wakeup,
u16 ps_fail_event_policy)
@@ -1881,12 +2245,12 @@ int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u16 idle_period,
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,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_POWER_PARAMS_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 timeout)
+int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 if_idx, u8 timeout)
{
struct sk_buff *skb;
struct wmi_disc_timeout_cmd *cmd;
@@ -1899,15 +2263,20 @@ int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 timeout)
cmd = (struct wmi_disc_timeout_cmd *) skb->data;
cmd->discon_timeout = timeout;
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_DISC_TIMEOUT_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_DISC_TIMEOUT_CMDID,
NO_SYNC_WMIFLAG);
+
+ if (ret == 0)
+ ath6kl_debug_set_disconnect_timeout(wmi->parent_dev, timeout);
+
return ret;
}
-int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
+int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
enum crypto_type key_type,
u8 key_usage, u8 key_len,
- u8 *key_rsc, u8 *key_material,
+ u8 *key_rsc, unsigned int key_rsc_len,
+ u8 *key_material,
u8 key_op_ctrl, u8 *mac_addr,
enum wmi_sync_flag sync_flag)
{
@@ -1915,12 +2284,12 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
struct wmi_add_cipher_key_cmd *cmd;
int ret;
- ath6kl_dbg(ATH6KL_DBG_WMI, "addkey cmd: key_index=%u key_type=%d "
- "key_usage=%d key_len=%d key_op_ctrl=%d\n",
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "addkey cmd: key_index=%u key_type=%d key_usage=%d key_len=%d key_op_ctrl=%d\n",
key_index, key_type, key_usage, key_len, key_op_ctrl);
if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) ||
- (key_material == NULL))
+ (key_material == NULL) || key_rsc_len > 8)
return -EINVAL;
if ((WEP_CRYPT != key_type) && (NULL == key_rsc))
@@ -1938,20 +2307,20 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
memcpy(cmd->key, key_material, key_len);
if (key_rsc != NULL)
- memcpy(cmd->key_rsc, key_rsc, sizeof(cmd->key_rsc));
+ memcpy(cmd->key_rsc, key_rsc, key_rsc_len);
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,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_CIPHER_KEY_CMDID,
sync_flag);
return ret;
}
-int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 *krk)
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk)
{
struct sk_buff *skb;
struct wmi_add_krk_cmd *cmd;
@@ -1964,12 +2333,13 @@ int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 *krk)
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);
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_KRK_CMDID,
+ NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 key_index)
+int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index)
{
struct sk_buff *skb;
struct wmi_delete_cipher_key_cmd *cmd;
@@ -1985,13 +2355,13 @@ int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 key_index)
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,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DELETE_CIPHER_KEY_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, const u8 *bssid,
+int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid,
const u8 *pmkid, bool set)
{
struct sk_buff *skb;
@@ -2018,33 +2388,35 @@ int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, const u8 *bssid,
cmd->enable = PMKID_DISABLE;
}
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_PMKID_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, 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)
+ enum htc_endpoint_id ep_id, u8 if_idx)
{
struct wmi_data_hdr *data_hdr;
int ret;
- if (WARN_ON(skb == NULL || ep_id == wmi->ep_id))
+ if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) {
+ dev_kfree_skb(skb);
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;
+ data_hdr->info3 = cpu_to_le16(if_idx & WMI_DATA_HDR_IF_IDX_MASK);
ret = ath6kl_control_tx(wmi->parent_dev, skb, ep_id);
return ret;
}
-static int ath6kl_wmi_sync_point(struct wmi *wmi)
+static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx)
{
struct sk_buff *skb;
struct wmi_sync_cmd *cmd;
@@ -2068,10 +2440,8 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi)
spin_unlock_bh(&wmi->lock);
skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb) {
- ret = -ENOMEM;
- goto free_skb;
- }
+ if (!skb)
+ return -ENOMEM;
cmd = (struct wmi_sync_cmd *) skb->data;
@@ -2094,55 +2464,49 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi)
* then do not send the Synchronize cmd on the control ep
*/
if (ret)
- goto free_skb;
+ goto free_cmd_skb;
/*
* Send sync cmd followed by sync data messages on all
* endpoints being used
*/
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SYNCHRONIZE_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SYNCHRONIZE_CMDID,
NO_SYNC_WMIFLAG);
if (ret)
- goto free_skb;
-
- /* cmd buffer sent, we no longer own it */
- skb = NULL;
+ goto free_data_skb;
for (index = 0; index < num_pri_streams; index++) {
-
if (WARN_ON(!data_sync_bufs[index].skb))
- break;
+ goto free_data_skb;
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;
+ ep_id, if_idx);
data_sync_bufs[index].skb = NULL;
+
+ if (ret)
+ goto free_data_skb;
}
-free_skb:
+ return 0;
+
+free_cmd_skb:
/* free up any resources left over (possibly due to an error) */
- if (skb)
- dev_kfree_skb(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);
- }
- }
+free_data_skb:
+ for (index = 0; index < num_pri_streams; index++)
+ dev_kfree_skb((struct sk_buff *)data_sync_bufs[index].skb);
return ret;
}
-int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi,
+int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi, u8 if_idx,
struct wmi_create_pstream_cmd *params)
{
struct sk_buff *skb;
@@ -2231,12 +2595,13 @@ int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi,
ath6kl_indicate_tx_activity(wmi->parent_dev,
params->traffic_class, true);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_CREATE_PSTREAM_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_CREATE_PSTREAM_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 traffic_class, u8 tsid)
+int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 if_idx, u8 traffic_class,
+ u8 tsid)
{
struct sk_buff *skb;
struct wmi_delete_pstream_cmd *cmd;
@@ -2272,7 +2637,7 @@ int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 traffic_class, u8 tsid)
"sending delete_pstream_cmd: traffic class: %d tsid=%d\n",
traffic_class, tsid);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_DELETE_PSTREAM_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DELETE_PSTREAM_CMDID,
SYNC_BEFORE_WMIFLAG);
spin_lock_bh(&wmi->lock);
@@ -2293,15 +2658,16 @@ int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 traffic_class, u8 tsid)
return ret;
}
-int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd)
+int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx,
+ __be32 ips0, __be32 ips1)
{
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))
+ if (ipv4_is_multicast(ips0) ||
+ ipv4_is_multicast(ips1))
return -EINVAL;
skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_ip_cmd));
@@ -2309,21 +2675,304 @@ int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd)
return -ENOMEM;
cmd = (struct wmi_set_ip_cmd *) skb->data;
- memcpy(cmd, ip_cmd, sizeof(struct wmi_set_ip_cmd));
+ cmd->ips[0] = ips0;
+ cmd->ips[1] = ips1;
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IP_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi)
+{
+ u16 active_tsids;
+ u8 stream_exist;
+ int i;
+
+ /*
+ * Relinquish credits from all implicitly created pstreams
+ * since when we go to sleep. If user created explicit
+ * thinstreams exists with in a fatpipe leave them intact
+ * for the user to delete.
+ */
+ spin_lock_bh(&wmi->lock);
+ stream_exist = wmi->fat_pipe_exist;
+ spin_unlock_bh(&wmi->lock);
+
+ for (i = 0; i < WMM_NUM_AC; i++) {
+ if (stream_exist & (1 << i)) {
+ /*
+ * FIXME: Is this lock & unlock inside
+ * for loop correct? may need rework.
+ */
+ spin_lock_bh(&wmi->lock);
+ active_tsids = wmi->stream_exist_for_ac[i];
+ spin_unlock_bh(&wmi->lock);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_IP_CMDID, NO_SYNC_WMIFLAG);
+ /*
+ * If there are no user created thin streams
+ * delete the fatpipe
+ */
+ if (!active_tsids) {
+ stream_exist &= ~(1 << i);
+ /*
+ * Indicate inactivity to driver layer for
+ * this fatpipe (pstream)
+ */
+ ath6kl_indicate_tx_activity(wmi->parent_dev,
+ i, false);
+ }
+ }
+ }
+
+ /* FIXME: Can we do this assignment without locking ? */
+ spin_lock_bh(&wmi->lock);
+ wmi->fat_pipe_exist = stream_exist;
+ spin_unlock_bh(&wmi->lock);
+}
+
+static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct sk_buff *skb;
+ int ret, mode, band;
+ u64 mcsrate, ratemask[ATH6KL_NUM_BANDS];
+ struct wmi_set_tx_select_rates64_cmd *cmd;
+
+ memset(&ratemask, 0, sizeof(ratemask));
+
+ /* only check 2.4 and 5 GHz bands, skip the rest */
+ for (band = 0; band <= IEEE80211_BAND_5GHZ; band++) {
+ /* copy legacy rate mask */
+ ratemask[band] = mask->control[band].legacy;
+ if (band == IEEE80211_BAND_5GHZ)
+ ratemask[band] =
+ mask->control[band].legacy << 4;
+
+ /* copy mcs rate mask */
+ mcsrate = mask->control[band].ht_mcs[1];
+ mcsrate <<= 8;
+ mcsrate |= mask->control[band].ht_mcs[0];
+ ratemask[band] |= mcsrate << 12;
+ ratemask[band] |= mcsrate << 28;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "Ratemask 64 bit: 2.4:%llx 5:%llx\n",
+ ratemask[0], ratemask[1]);
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_tx_select_rates64_cmd *) skb->data;
+ for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) {
+ /* A mode operate in 5GHZ band */
+ if (mode == WMI_RATES_MODE_11A ||
+ mode == WMI_RATES_MODE_11A_HT20 ||
+ mode == WMI_RATES_MODE_11A_HT40)
+ band = IEEE80211_BAND_5GHZ;
+ else
+ band = IEEE80211_BAND_2GHZ;
+ cmd->ratemask[mode] = cpu_to_le64(ratemask[band]);
+ }
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_SET_TX_SELECT_RATES_CMDID,
+ NO_SYNC_WMIFLAG);
return ret;
}
-static int ath6kl_wmi_get_wow_list_event_rx(struct wmi *wmi, u8 * datap,
- int len)
+static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx,
+ const struct cfg80211_bitrate_mask *mask)
{
- if (len < sizeof(struct wmi_get_wow_list_reply))
+ struct sk_buff *skb;
+ int ret, mode, band;
+ u32 mcsrate, ratemask[ATH6KL_NUM_BANDS];
+ struct wmi_set_tx_select_rates32_cmd *cmd;
+
+ memset(&ratemask, 0, sizeof(ratemask));
+
+ /* only check 2.4 and 5 GHz bands, skip the rest */
+ for (band = 0; band <= IEEE80211_BAND_5GHZ; band++) {
+ /* copy legacy rate mask */
+ ratemask[band] = mask->control[band].legacy;
+ if (band == IEEE80211_BAND_5GHZ)
+ ratemask[band] =
+ mask->control[band].legacy << 4;
+
+ /* copy mcs rate mask */
+ mcsrate = mask->control[band].ht_mcs[0];
+ ratemask[band] |= mcsrate << 12;
+ ratemask[band] |= mcsrate << 20;
+ }
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "Ratemask 32 bit: 2.4:%x 5:%x\n",
+ ratemask[0], ratemask[1]);
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_tx_select_rates32_cmd *) skb->data;
+ for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) {
+ /* A mode operate in 5GHZ band */
+ if (mode == WMI_RATES_MODE_11A ||
+ mode == WMI_RATES_MODE_11A_HT20 ||
+ mode == WMI_RATES_MODE_11A_HT40)
+ band = IEEE80211_BAND_5GHZ;
+ else
+ band = IEEE80211_BAND_2GHZ;
+ cmd->ratemask[mode] = cpu_to_le32(ratemask[band]);
+ }
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_SET_TX_SELECT_RATES_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct ath6kl *ar = wmi->parent_dev;
+
+ if (ar->hw.flags & ATH6KL_HW_64BIT_RATES)
+ return ath6kl_set_bitrate_mask64(wmi, if_idx, mask);
+ else
+ return ath6kl_set_bitrate_mask32(wmi, if_idx, mask);
+}
+
+int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx,
+ enum ath6kl_host_mode host_mode)
+{
+ struct sk_buff *skb;
+ struct wmi_set_host_sleep_mode_cmd *cmd;
+ int ret;
+
+ if ((host_mode != ATH6KL_HOST_MODE_ASLEEP) &&
+ (host_mode != ATH6KL_HOST_MODE_AWAKE)) {
+ ath6kl_err("invalid host sleep mode: %d\n", host_mode);
return -EINVAL;
+ }
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_host_sleep_mode_cmd *) skb->data;
+
+ if (host_mode == ATH6KL_HOST_MODE_ASLEEP) {
+ ath6kl_wmi_relinquish_implicit_pstream_credits(wmi);
+ cmd->asleep = cpu_to_le32(1);
+ } else {
+ cmd->awake = cpu_to_le32(1);
+ }
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_SET_HOST_SLEEP_MODE_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+/* This command has zero length payload */
+static int ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(struct wmi *wmi,
+ struct ath6kl_vif *vif)
+{
+ struct ath6kl *ar = wmi->parent_dev;
+
+ set_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+ wake_up(&ar->event_wq);
return 0;
}
+int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx,
+ enum ath6kl_wow_mode wow_mode,
+ u32 filter, u16 host_req_delay)
+{
+ struct sk_buff *skb;
+ struct wmi_set_wow_mode_cmd *cmd;
+ int ret;
+
+ if ((wow_mode != ATH6KL_WOW_MODE_ENABLE) &&
+ wow_mode != ATH6KL_WOW_MODE_DISABLE) {
+ ath6kl_err("invalid wow mode: %d\n", wow_mode);
+ return -EINVAL;
+ }
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_wow_mode_cmd *) skb->data;
+ cmd->enable_wow = cpu_to_le32(wow_mode);
+ cmd->filter = cpu_to_le32(filter);
+ cmd->host_req_delay = cpu_to_le16(host_req_delay);
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_WOW_MODE_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx,
+ u8 list_id, u8 filter_size,
+ u8 filter_offset, const u8 *filter,
+ const u8 *mask)
+{
+ struct sk_buff *skb;
+ struct wmi_add_wow_pattern_cmd *cmd;
+ u16 size;
+ u8 *filter_mask;
+ int ret;
+
+ /*
+ * Allocate additional memory in the buffer to hold
+ * filter and mask value, which is twice of filter_size.
+ */
+ size = sizeof(*cmd) + (2 * filter_size);
+
+ skb = ath6kl_wmi_get_new_buf(size);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_add_wow_pattern_cmd *) skb->data;
+ cmd->filter_list_id = list_id;
+ cmd->filter_size = filter_size;
+ cmd->filter_offset = filter_offset;
+
+ memcpy(cmd->filter, filter, filter_size);
+
+ filter_mask = (u8 *) (cmd->filter + filter_size);
+ memcpy(filter_mask, mask, filter_size);
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_WOW_PATTERN_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx,
+ u16 list_id, u16 filter_id)
+{
+ struct sk_buff *skb;
+ struct wmi_del_wow_pattern_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_del_wow_pattern_cmd *) skb->data;
+ cmd->filter_list_id = cpu_to_le16(list_id);
+ cmd->filter_id = cpu_to_le16(filter_id);
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DEL_WOW_PATTERN_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
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)
@@ -2336,7 +2985,7 @@ static int ath6kl_wmi_cmd_send_xtnd(struct wmi *wmi, struct sk_buff *skb,
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);
+ ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_EXTENSION_CMDID, sync_flag);
return ret;
}
@@ -2379,12 +3028,12 @@ int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config)
return ret;
}
-int ath6kl_wmi_get_stats_cmd(struct wmi *wmi)
+int ath6kl_wmi_get_stats_cmd(struct wmi *wmi, u8 if_idx)
{
- return ath6kl_wmi_simple_cmd(wmi, WMI_GET_STATISTICS_CMDID);
+ return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_STATISTICS_CMDID);
}
-int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 dbM)
+int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 if_idx, u8 dbM)
{
struct sk_buff *skb;
struct wmi_set_tx_pwr_cmd *cmd;
@@ -2397,18 +3046,24 @@ int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 dbM)
cmd = (struct wmi_set_tx_pwr_cmd *) skb->data;
cmd->dbM = dbM;
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_TX_PWR_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_TX_PWR_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi)
+int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx)
+{
+ return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_TX_PWR_CMDID);
+}
+
+int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi)
{
- return ath6kl_wmi_simple_cmd(wmi, WMI_GET_TX_PWR_CMDID);
+ return ath6kl_wmi_simple_cmd(wmi, 0, WMI_GET_ROAM_TBL_CMDID);
}
-int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status, u8 preamble_policy)
+int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 if_idx, u8 status,
+ u8 preamble_policy)
{
struct sk_buff *skb;
struct wmi_set_lpreamble_cmd *cmd;
@@ -2422,7 +3077,7 @@ int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status, u8 preamble_policy)
cmd->status = status;
cmd->preamble_policy = preamble_policy;
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_LPREAMBLE_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_LPREAMBLE_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
@@ -2440,11 +3095,12 @@ int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u16 threshold)
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);
+ ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_RTS_CMDID,
+ NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg)
+int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg)
{
struct sk_buff *skb;
struct wmi_set_wmm_txop_cmd *cmd;
@@ -2460,12 +3116,13 @@ int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg)
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,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_WMM_TXOP_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl)
+int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx,
+ u8 keep_alive_intvl)
{
struct sk_buff *skb;
struct wmi_set_keepalive_cmd *cmd;
@@ -2477,13 +3134,53 @@ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl)
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,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_KEEPALIVE_CMDID,
NO_SYNC_WMIFLAG);
+
+ if (ret == 0)
+ ath6kl_debug_set_keepalive(wmi->parent_dev, keep_alive_intvl);
+
return ret;
}
+int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx,
+ enum ieee80211_band band,
+ struct ath6kl_htcap *htcap)
+{
+ struct sk_buff *skb;
+ struct wmi_set_htcap_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_htcap_cmd *) skb->data;
+
+ /*
+ * NOTE: Band in firmware matches enum ieee80211_band, it is unlikely
+ * this will be changed in firmware. If at all there is any change in
+ * band value, the host needs to be fixed.
+ */
+ cmd->band = band;
+ cmd->ht_enable = !!htcap->ht_enable;
+ cmd->ht20_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_20);
+ cmd->ht40_supported =
+ !!(htcap->cap_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ cmd->ht40_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_40);
+ cmd->intolerant_40mhz =
+ !!(htcap->cap_info & IEEE80211_HT_CAP_40MHZ_INTOLERANT);
+ cmd->max_ampdu_len_exp = htcap->ampdu_factor;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "Set htcap: band:%d ht_enable:%d 40mhz:%d sgi_20mhz:%d sgi_40mhz:%d 40mhz_intolerant:%d ampdu_len_exp:%d\n",
+ cmd->band, cmd->ht_enable, cmd->ht40_supported,
+ cmd->ht20_sgi, cmd->ht40_sgi, cmd->intolerant_40mhz,
+ cmd->max_ampdu_len_exp);
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HT_CAP_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len)
{
struct sk_buff *skb;
@@ -2495,18 +3192,110 @@ int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len)
memcpy(skb->data, buf, len);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_TEST_CMDID, NO_SYNC_WMIFLAG);
+ ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_TEST_CMDID, NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+
+int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on)
+{
+ struct sk_buff *skb;
+ struct wmi_mcast_filter_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_mcast_filter_cmd *) skb->data;
+ cmd->mcast_all_enable = mc_all_on;
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_MCAST_FILTER_CMDID,
+ NO_SYNC_WMIFLAG);
+ return ret;
+}
+
+int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx,
+ u8 *filter, bool add_filter)
+{
+ struct sk_buff *skb;
+ struct wmi_mcast_filter_add_del_cmd *cmd;
+ int ret;
+
+ if ((filter[0] != 0x33 || filter[1] != 0x33) &&
+ (filter[0] != 0x01 || filter[1] != 0x00 ||
+ filter[2] != 0x5e || filter[3] > 0x7f)) {
+ ath6kl_warn("invalid multicast filter address\n");
+ return -EINVAL;
+ }
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_mcast_filter_add_del_cmd *) skb->data;
+ memcpy(cmd->mcast_mac, filter, ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE);
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ add_filter ? WMI_SET_MCAST_FILTER_CMDID :
+ WMI_DEL_MCAST_FILTER_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ return ret;
+}
+int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enhance)
+{
+ struct sk_buff *skb;
+ struct wmi_sta_bmiss_enhance_cmd *cmd;
+ int ret;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_sta_bmiss_enhance_cmd *) skb->data;
+ cmd->enable = enhance ? 1 : 0;
+
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_STA_BMISS_ENHANCE_CMDID,
+ NO_SYNC_WMIFLAG);
return ret;
}
+int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2)
+{
+ struct sk_buff *skb;
+ struct wmi_set_regdomain_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_regdomain_cmd *) skb->data;
+ memcpy(cmd->iso_name, alpha2, 2);
+
+ return ath6kl_wmi_cmd_send(wmi, 0, skb,
+ WMI_SET_REGDOMAIN_CMDID,
+ NO_SYNC_WMIFLAG);
+}
s32 ath6kl_wmi_get_rate(s8 rate_index)
{
+ u8 sgi = 0;
+
if (rate_index == RATE_AUTO)
return 0;
- return wmi_rate_tbl[(u32) rate_index][0];
+ /* SGI is stored as the MSB of the rate_index */
+ if (rate_index & RATE_INDEX_MSB) {
+ rate_index &= RATE_INDEX_WITHOUT_SGI_MASK;
+ sgi = 1;
+ }
+
+ if (WARN_ON(rate_index > RATE_MCS_7_40))
+ rate_index = RATE_MCS_7_40;
+
+ return wmi_rate_tbl[(u32) rate_index][sgi];
}
static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
@@ -2528,28 +3317,31 @@ static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
return 0;
}
-static int ath6kl_wmi_addba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_addba_req_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_addba_req_event *cmd = (struct wmi_addba_req_event *) datap;
- aggr_recv_addba_req_evt(wmi->parent_dev, cmd->tid,
+ aggr_recv_addba_req_evt(vif, 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)
+static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_delba_event *cmd = (struct wmi_delba_event *) datap;
- aggr_recv_delba_req_evt(wmi->parent_dev, cmd->tid);
+ aggr_recv_delba_req_evt(vif, cmd->tid);
return 0;
}
/* AP mode functions */
-int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p)
+int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx,
+ struct wmi_connect_cmd *p)
{
struct sk_buff *skb;
struct wmi_connect_cmd *cm;
@@ -2562,16 +3354,17 @@ int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p)
cm = (struct wmi_connect_cmd *) skb->data;
memcpy(cm, p, sizeof(*cm));
- res = ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_CONFIG_COMMIT_CMDID,
+ res = ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_CONFIG_COMMIT_CMDID,
NO_SYNC_WMIFLAG);
- ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u "
- "ctrl_flags=0x%x-> res=%d\n",
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "%s: nw_type=%u auth_mode=%u ch=%u ctrl_flags=0x%x-> res=%d\n",
__func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch),
le32_to_cpu(p->ctrl_flags), res);
return res;
}
-int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason)
+int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac,
+ u16 reason)
{
struct sk_buff *skb;
struct wmi_ap_set_mlme_cmd *cm;
@@ -2585,11 +3378,68 @@ int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason)
cm->reason = cpu_to_le16(reason);
cm->cmd = cmd;
- return ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_SET_MLME_CMDID,
+ ath6kl_dbg(ATH6KL_DBG_WMI, "ap_set_mlme: cmd=%d reason=%d\n", cm->cmd,
+ cm->reason);
+
+ return ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_SET_MLME_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_ap_hidden_ssid(struct wmi *wmi, u8 if_idx, bool enable)
+{
+ struct sk_buff *skb;
+ struct wmi_ap_hidden_ssid_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_ap_hidden_ssid_cmd *) skb->data;
+ cmd->hidden_ssid = enable ? 1 : 0;
+
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_HIDDEN_SSID_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+/* This command will be used to enable/disable AP uAPSD feature */
+int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable)
+{
+ struct wmi_ap_set_apsd_cmd *cmd;
+ struct sk_buff *skb;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_ap_set_apsd_cmd *)skb->data;
+ cmd->enable = enable;
+
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_APSD_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_set_apsd_bfrd_traf(struct wmi *wmi, u8 if_idx,
+ u16 aid, u16 bitmap, u32 flags)
+{
+ struct wmi_ap_apsd_buffered_traffic_cmd *cmd;
+ struct sk_buff *skb;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_ap_apsd_buffered_traffic_cmd *)skb->data;
+ cmd->aid = cpu_to_le16(aid);
+ cmd->bitmap = cpu_to_le16(bitmap);
+ cmd->flags = cpu_to_le32(flags);
+
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID,
NO_SYNC_WMIFLAG);
}
-static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
struct wmi_pspoll_event *ev;
@@ -2598,19 +3448,21 @@ static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len)
ev = (struct wmi_pspoll_event *) datap;
- ath6kl_pspoll_event(wmi->parent_dev, le16_to_cpu(ev->aid));
+ ath6kl_pspoll_event(vif, le16_to_cpu(ev->aid));
return 0;
}
-static int ath6kl_wmi_dtimexpiry_event_rx(struct wmi *wmi, u8 *datap, int len)
+static int ath6kl_wmi_dtimexpiry_event_rx(struct wmi *wmi, u8 *datap, int len,
+ struct ath6kl_vif *vif)
{
- ath6kl_dtimexpiry_event(wmi->parent_dev);
+ ath6kl_dtimexpiry_event(vif);
return 0;
}
-int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag)
+int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u8 if_idx, u16 aid,
+ bool flag)
{
struct sk_buff *skb;
struct wmi_ap_set_pvb_cmd *cmd;
@@ -2625,13 +3477,14 @@ int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag)
cmd->rsvd = cpu_to_le16(0);
cmd->flag = cpu_to_le32(flag);
- ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_AP_SET_PVB_CMDID,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, 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,
+int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx,
+ u8 rx_meta_ver,
bool rx_dot11_hdr, bool defrag_on_host)
{
struct sk_buff *skb;
@@ -2648,14 +3501,14 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver,
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,
+ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_RX_FRAME_FORMAT_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
-int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
- u8 ie_len)
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type,
+ const u8 *ie, u8 ie_len)
{
struct sk_buff *skb;
struct wmi_set_appie_cmd *p;
@@ -2664,13 +3517,40 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
if (!skb)
return -ENOMEM;
- ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u "
- "ie_len=%u\n", mgmt_frm_type, ie_len);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "set_appie_cmd: mgmt_frm_type=%u ie_len=%u\n",
+ mgmt_frm_type, ie_len);
p = (struct wmi_set_appie_cmd *) skb->data;
p->mgmt_frm_type = mgmt_frm_type;
p->ie_len = ie_len;
- memcpy(p->ie_info, ie, ie_len);
- return ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_APPIE_CMDID,
+
+ if (ie != NULL && ie_len > 0)
+ memcpy(p->ie_info, ie, ie_len);
+
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_APPIE_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field,
+ const u8 *ie_info, u8 ie_len)
+{
+ struct sk_buff *skb;
+ struct wmi_set_ie_cmd *p;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len);
+ if (!skb)
+ return -ENOMEM;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI, "set_ie_cmd: ie_id=%u ie_ie_field=%u ie_len=%u\n",
+ ie_id, ie_field, ie_len);
+ p = (struct wmi_set_ie_cmd *) skb->data;
+ p->ie_id = ie_id;
+ p->ie_field = ie_field;
+ p->ie_len = ie_len;
+ if (ie_info && ie_len > 0)
+ memcpy(p->ie_info, ie_info, ie_len);
+
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IE_CMDID,
NO_SYNC_WMIFLAG);
}
@@ -2688,11 +3568,11 @@ int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable)
cmd = (struct wmi_disable_11b_rates_cmd *) skb->data;
cmd->disable = disable ? 1 : 0;
- return ath6kl_wmi_cmd_send(wmi, skb, WMI_DISABLE_11B_RATES_CMDID,
+ return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_DISABLE_11B_RATES_CMDID,
NO_SYNC_WMIFLAG);
}
-int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur)
+int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur)
{
struct sk_buff *skb;
struct wmi_remain_on_chnl_cmd *p;
@@ -2706,12 +3586,17 @@ int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur)
p = (struct wmi_remain_on_chnl_cmd *) skb->data;
p->freq = cpu_to_le32(freq);
p->duration = cpu_to_le32(dur);
- return ath6kl_wmi_cmd_send(wmi, skb, WMI_REMAIN_ON_CHNL_CMDID,
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_REMAIN_ON_CHNL_CMDID,
NO_SYNC_WMIFLAG);
}
-int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
- const u8 *data, u16 data_len)
+/* ath6kl_wmi_send_action_cmd is to be deprecated. Use
+ * ath6kl_wmi_send_mgmt_cmd instead. The new function supports P2P
+ * mgmt operations using station interface.
+ */
+static int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id,
+ u32 freq, u32 wait, const u8 *data,
+ u16 data_len)
{
struct sk_buff *skb;
struct wmi_send_action_cmd *p;
@@ -2731,44 +3616,118 @@ int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
}
kfree(wmi->last_mgmt_tx_frame);
+ memcpy(buf, data, data_len);
wmi->last_mgmt_tx_frame = buf;
wmi->last_mgmt_tx_frame_len = data_len;
- ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u "
- "len=%u\n", id, freq, wait, data_len);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "send_action_cmd: id=%u freq=%u wait=%u len=%u\n",
+ id, freq, wait, data_len);
p = (struct wmi_send_action_cmd *) skb->data;
p->id = cpu_to_le32(id);
p->freq = cpu_to_le32(freq);
p->wait = cpu_to_le32(wait);
p->len = cpu_to_le16(data_len);
memcpy(p->data, data, data_len);
- return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_ACTION_CMDID,
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SEND_ACTION_CMDID,
NO_SYNC_WMIFLAG);
}
-int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq,
- const u8 *dst,
- const u8 *data, u16 data_len)
+static int __ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id,
+ u32 freq, u32 wait, const u8 *data,
+ u16 data_len, u32 no_cck)
{
struct sk_buff *skb;
- struct wmi_p2p_probe_response_cmd *p;
+ struct wmi_send_mgmt_cmd *p;
+ u8 *buf;
+
+ if (wait)
+ return -EINVAL; /* Offload for wait not supported */
+
+ buf = kmalloc(data_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
+ if (!skb) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ kfree(wmi->last_mgmt_tx_frame);
+ memcpy(buf, data, data_len);
+ wmi->last_mgmt_tx_frame = buf;
+ wmi->last_mgmt_tx_frame_len = data_len;
+
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "send_action_cmd: id=%u freq=%u wait=%u len=%u\n",
+ id, freq, wait, data_len);
+ p = (struct wmi_send_mgmt_cmd *) skb->data;
+ p->id = cpu_to_le32(id);
+ p->freq = cpu_to_le32(freq);
+ p->wait = cpu_to_le32(wait);
+ p->no_cck = cpu_to_le32(no_cck);
+ p->len = cpu_to_le16(data_len);
+ memcpy(p->data, data, data_len);
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SEND_MGMT_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
+ u32 wait, const u8 *data, u16 data_len,
+ u32 no_cck)
+{
+ int status;
+ struct ath6kl *ar = wmi->parent_dev;
+
+ if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+ ar->fw_capabilities)) {
+ /*
+ * If capable of doing P2P mgmt operations using
+ * station interface, send additional information like
+ * supported rates to advertise and xmit rates for
+ * probe requests
+ */
+ status = __ath6kl_wmi_send_mgmt_cmd(ar->wmi, if_idx, id, freq,
+ wait, data, data_len,
+ no_cck);
+ } else {
+ status = ath6kl_wmi_send_action_cmd(ar->wmi, if_idx, id, freq,
+ wait, data, data_len);
+ }
+
+ return status;
+}
+
+int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq,
+ const u8 *dst, const u8 *data,
+ u16 data_len)
+{
+ struct sk_buff *skb;
+ struct wmi_p2p_probe_response_cmd *p;
+ size_t cmd_len = sizeof(*p) + data_len;
+
+ if (data_len == 0)
+ cmd_len++; /* work around target minimum length requirement */
+
+ skb = ath6kl_wmi_get_new_buf(cmd_len);
if (!skb)
return -ENOMEM;
- ath6kl_dbg(ATH6KL_DBG_WMI, "send_probe_response_cmd: freq=%u dst=%pM "
- "len=%u\n", freq, dst, data_len);
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "send_probe_response_cmd: freq=%u dst=%pM len=%u\n",
+ freq, dst, data_len);
p = (struct wmi_p2p_probe_response_cmd *) skb->data;
p->freq = cpu_to_le32(freq);
memcpy(p->destination_addr, dst, ETH_ALEN);
p->len = cpu_to_le16(data_len);
memcpy(p->data, data, data_len);
- return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_PROBE_RESPONSE_CMDID,
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+ WMI_SEND_PROBE_RESPONSE_CMDID,
NO_SYNC_WMIFLAG);
}
-int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable)
+int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, u8 if_idx, bool enable)
{
struct sk_buff *skb;
struct wmi_probe_req_report_cmd *p;
@@ -2781,11 +3740,11 @@ int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable)
enable);
p = (struct wmi_probe_req_report_cmd *) skb->data;
p->enable = enable ? 1 : 0;
- return ath6kl_wmi_cmd_send(wmi, skb, WMI_PROBE_REQ_REPORT_CMDID,
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_PROBE_REQ_REPORT_CMDID,
NO_SYNC_WMIFLAG);
}
-int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags)
+int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u8 if_idx, u32 info_req_flags)
{
struct sk_buff *skb;
struct wmi_get_p2p_info *p;
@@ -2798,14 +3757,45 @@ int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags)
info_req_flags);
p = (struct wmi_get_p2p_info *) skb->data;
p->info_req_flags = cpu_to_le32(info_req_flags);
- return ath6kl_wmi_cmd_send(wmi, skb, WMI_GET_P2P_INFO_CMDID,
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_GET_P2P_INFO_CMDID,
NO_SYNC_WMIFLAG);
}
-int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi)
+int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx)
{
ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl_cmd\n");
- return ath6kl_wmi_simple_cmd(wmi, WMI_CANCEL_REMAIN_ON_CHNL_CMDID);
+ return ath6kl_wmi_simple_cmd(wmi, if_idx,
+ WMI_CANCEL_REMAIN_ON_CHNL_CMDID);
+}
+
+int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout)
+{
+ struct sk_buff *skb;
+ struct wmi_set_inact_period_cmd *cmd;
+
+ skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_set_inact_period_cmd *) skb->data;
+ cmd->inact_period = cpu_to_le32(inact_timeout);
+ cmd->num_null_func = 0;
+
+ return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_CONN_INACT_CMDID,
+ NO_SYNC_WMIFLAG);
+}
+
+static void ath6kl_wmi_hb_challenge_resp_event(struct wmi *wmi, u8 *datap,
+ int len)
+{
+ struct wmix_hb_challenge_resp_cmd *cmd;
+
+ if (len < sizeof(struct wmix_hb_challenge_resp_cmd))
+ return;
+
+ cmd = (struct wmix_hb_challenge_resp_cmd *) datap;
+ ath6kl_recovery_hb_event(wmi->parent_dev,
+ le32_to_cpu(cmd->cookie));
}
static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
@@ -2818,7 +3808,6 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
if (skb->len < sizeof(struct wmix_cmd_hdr)) {
ath6kl_err("bad packet 1\n");
- wmi->stat.cmd_len_err++;
return -EINVAL;
}
@@ -2833,6 +3822,7 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
switch (id) {
case WMIX_HB_CHALLENGE_RESP_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n");
+ ath6kl_wmi_hb_challenge_resp_event(wmi, datap, len);
break;
case WMIX_DBGLOG_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len);
@@ -2840,7 +3830,6 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
break;
default:
ath6kl_warn("unknown cmd id 0x%x\n", id);
- wmi->stat.cmd_id_err++;
ret = -EINVAL;
break;
}
@@ -2848,30 +3837,109 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
return ret;
}
-/* Control Path */
-int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
+static int ath6kl_wmi_roam_tbl_event_rx(struct wmi *wmi, u8 *datap, int len)
{
- struct wmi_cmd_hdr *cmd;
- u32 len;
- u16 id;
- u8 *datap;
- int ret = 0;
+ return ath6kl_debug_roam_tbl_event(wmi->parent_dev, datap, len);
+}
- if (WARN_ON(skb == NULL))
+/* Process interface specific wmi events, caller would free the datap */
+static int ath6kl_wmi_proc_events_vif(struct wmi *wmi, u16 if_idx, u16 cmd_id,
+ u8 *datap, u32 len)
+{
+ struct ath6kl_vif *vif;
+
+ vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx);
+ if (!vif) {
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "Wmi event for unavailable vif, vif_index:%d\n",
+ if_idx);
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++;
+ switch (cmd_id) {
+ case WMI_CONNECT_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n");
+ return ath6kl_wmi_connect_event_rx(wmi, datap, len, vif);
+ case WMI_DISCONNECT_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n");
+ return ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif);
+ case WMI_TKIP_MICERR_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n");
+ return ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif);
+ case WMI_BSSINFO_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n");
+ return ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif);
+ case WMI_NEIGHBOR_REPORT_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n");
+ return ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len,
+ vif);
+ case WMI_SCAN_COMPLETE_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
+ return ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif);
+ case WMI_REPORT_STATISTICS_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n");
+ return ath6kl_wmi_stats_event_rx(wmi, datap, len, vif);
+ case WMI_CAC_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n");
+ return ath6kl_wmi_cac_event_rx(wmi, datap, len, vif);
+ case WMI_PSPOLL_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n");
+ return ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif);
+ case WMI_DTIMEXPIRY_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n");
+ return ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif);
+ case WMI_ADDBA_REQ_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n");
+ return ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif);
+ case WMI_DELBA_REQ_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n");
+ return ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif);
+ case WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID");
+ return ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif);
+ case WMI_REMAIN_ON_CHNL_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n");
+ return ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif);
+ case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n");
+ return ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap,
+ len, vif);
+ case WMI_TX_STATUS_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n");
+ return ath6kl_wmi_tx_status_event_rx(wmi, datap, len, vif);
+ case WMI_RX_PROBE_REQ_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n");
+ return ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len, vif);
+ case WMI_RX_ACTION_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n");
+ return ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif);
+ case WMI_TXE_NOTIFY_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TXE_NOTIFY_EVENTID\n");
+ return ath6kl_wmi_txe_notify_event_rx(wmi, datap, len, vif);
+ default:
+ ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", cmd_id);
return -EINVAL;
}
+ return 0;
+}
+
+static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb)
+{
+ struct wmi_cmd_hdr *cmd;
+ int ret = 0;
+ u32 len;
+ u16 id;
+ u8 if_idx;
+ u8 *datap;
+
cmd = (struct wmi_cmd_hdr *) skb->data;
id = le16_to_cpu(cmd->cmd_id);
+ if_idx = le16_to_cpu(cmd->info1) & WMI_CMD_HDR_IF_ID_MASK;
skb_pull(skb, sizeof(struct wmi_cmd_hdr));
-
datap = skb->data;
len = skb->len;
@@ -2896,26 +3964,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
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");
- ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len);
- break;
case WMI_REGDOMAIN_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n");
ath6kl_wmi_regdomain_event(wmi, datap, len);
@@ -2924,22 +3976,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
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");
- ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len);
- 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);
@@ -2953,15 +3993,12 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
break;
case WMI_REPORT_ROAM_TBL_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n");
+ ret = ath6kl_wmi_roam_tbl_event_rx(wmi, datap, len);
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;
@@ -2970,7 +4007,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
break;
case WMI_TEST_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TEST_EVENTID\n");
- ret = ath6kl_wmi_tcmd_test_report_rx(wmi, datap, len);
+ ret = ath6kl_wmi_test_rx(wmi, datap, len);
break;
case WMI_GET_FIXRATES_CMDID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n");
@@ -2996,34 +4033,17 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
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");
@@ -3036,53 +4056,43 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n");
ret = ath6kl_wmi_tx_complete_event_rx(datap, len);
break;
- case WMI_REMAIN_ON_CHNL_EVENTID:
- ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n");
- ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len);
- break;
- case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID:
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n");
- ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap,
- len);
- break;
- case WMI_TX_STATUS_EVENTID:
- ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n");
- ret = ath6kl_wmi_tx_status_event_rx(wmi, datap, len);
- break;
- case WMI_RX_PROBE_REQ_EVENTID:
- ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n");
- ret = ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len);
- break;
case WMI_P2P_CAPABILITIES_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n");
ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len);
break;
- case WMI_RX_ACTION_EVENTID:
- ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n");
- ret = ath6kl_wmi_rx_action_event_rx(wmi, datap, len);
- break;
case WMI_P2P_INFO_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n");
ret = ath6kl_wmi_p2p_info_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;
+ /* may be the event is interface specific */
+ ret = ath6kl_wmi_proc_events_vif(wmi, if_idx, id, datap, len);
break;
}
dev_kfree_skb(skb);
-
return ret;
}
-static void ath6kl_wmi_qos_state_init(struct wmi *wmi)
+/* Control Path */
+int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
{
- if (!wmi)
- return;
+ 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);
+ return -EINVAL;
+ }
+
+ trace_ath6kl_wmi_event(skb->data, skb->len);
+ return ath6kl_wmi_proc_events(wmi, skb);
+}
+
+void ath6kl_wmi_reset(struct wmi *wmi)
+{
spin_lock_bh(&wmi->lock);
wmi->fat_pipe_exist = 0;
@@ -3103,16 +4113,9 @@ void *ath6kl_wmi_init(struct ath6kl *dev)
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;
+ ath6kl_wmi_reset(wmi);
return wmi;
}
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
index f8e644d54aa..bb23fc00111 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.h
+++ b/drivers/net/wireless/ath/ath6kl/wmi.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -47,7 +48,7 @@
#define A_BAND_24GHZ 0
#define A_BAND_5GHZ 1
-#define A_NUM_BANDS 2
+#define ATH6KL_NUM_BANDS 2
/* in ms */
#define WMI_IMPLICIT_PSTREAM_INACTIVITY_INT 5000
@@ -93,11 +94,6 @@ struct sq_threshold_params {
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;
@@ -110,33 +106,31 @@ struct wmi_data_sync_bufs {
#define WMM_AC_VI 2 /* video */
#define WMM_AC_VO 3 /* voice */
+#define WMI_VOICE_USER_PRIORITY 0x7
+
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;
+
+ /* protects fat_pipe_exist and stream_exist_for_ac */
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;
u8 *last_mgmt_tx_frame;
size_t last_mgmt_tx_frame_len;
+ u8 saved_pwr_mode;
};
struct host_app_area {
- u32 wmi_protocol_ver;
-};
+ __le32 wmi_protocol_ver;
+} __packed;
enum wmi_msg_type {
DATA_MSGTYPE = 0x0,
@@ -160,8 +154,7 @@ enum wmi_msg_type {
#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
+#define WMI_DATA_HDR_MORE 0x20
enum wmi_data_hdr_data_type {
WMI_DATA_HDR_DATA_TYPE_802_3 = 0,
@@ -171,6 +164,13 @@ enum wmi_data_hdr_data_type {
WMI_DATA_HDR_DATA_TYPE_ACL,
};
+/* Bitmap of data header flags */
+enum wmi_data_hdr_flags {
+ WMI_DATA_HDR_FLAGS_MORE = 0x1,
+ WMI_DATA_HDR_FLAGS_EOSP = 0x2,
+ WMI_DATA_HDR_FLAGS_UAPSD = 0x4,
+};
+
#define WMI_DATA_HDR_DATA_TYPE_MASK 0x3
#define WMI_DATA_HDR_DATA_TYPE_SHIFT 6
@@ -184,6 +184,15 @@ enum wmi_data_hdr_data_type {
#define WMI_DATA_HDR_META_MASK 0x7
#define WMI_DATA_HDR_META_SHIFT 13
+#define WMI_DATA_HDR_PAD_BEFORE_DATA_MASK 0xFF
+#define WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT 0x8
+
+/* Macros for operating on WMI_DATA_HDR (info3) field */
+#define WMI_DATA_HDR_IF_IDX_MASK 0xF
+
+#define WMI_DATA_HDR_TRIG 0x10
+#define WMI_DATA_HDR_EOSP 0x10
+
struct wmi_data_hdr {
s8 rssi;
@@ -208,6 +217,13 @@ struct wmi_data_hdr {
* b15:b13 - META_DATA_VERSION 0 - 7
*/
__le16 info2;
+
+ /*
+ * usage of info3, 16-bit:
+ * b3:b0 - Interface index
+ * b4 - uAPSD trigger in rx & EOSP in tx
+ * b15:b5 - Reserved
+ */
__le16 info3;
} __packed;
@@ -250,11 +266,19 @@ static inline u8 wmi_data_hdr_get_meta(struct wmi_data_hdr *dhdr)
WMI_DATA_HDR_META_MASK;
}
+static inline u8 wmi_data_hdr_get_if_idx(struct wmi_data_hdr *dhdr)
+{
+ return le16_to_cpu(dhdr->info3) & WMI_DATA_HDR_IF_IDX_MASK;
+}
+
/* Tx meta version definitions */
#define WMI_MAX_TX_META_SZ 12
#define WMI_META_VERSION_1 0x01
#define WMI_META_VERSION_2 0x02
+/* Flag to signal to FW to calculate TCP checksum */
+#define WMI_META_V2_FLAG_CSUM_OFFLOAD 0x01
+
struct wmi_tx_meta_v1 {
/* packet ID to identify the tx request */
u8 pkt_id;
@@ -299,6 +323,8 @@ struct wmi_rx_meta_v2 {
u8 csum_flags;
} __packed;
+#define WMI_CMD_HDR_IF_ID_MASK 0xF
+
/* Control Path */
struct wmi_cmd_hdr {
__le16 cmd_id;
@@ -312,6 +338,11 @@ struct wmi_cmd_hdr {
__le16 reserved;
} __packed;
+static inline u8 wmi_cmd_hdr_get_if_idx(struct wmi_cmd_hdr *chdr)
+{
+ return le16_to_cpu(chdr->info1) & WMI_CMD_HDR_IF_ID_MASK;
+}
+
/* List of WMI commands */
enum wmi_cmd_id {
WMI_CONNECT_CMDID = 0x0001,
@@ -320,6 +351,10 @@ enum wmi_cmd_id {
WMI_SYNCHRONIZE_CMDID,
WMI_CREATE_PSTREAM_CMDID,
WMI_DELETE_PSTREAM_CMDID,
+ /* WMI_START_SCAN_CMDID is to be deprecated. Use
+ * WMI_BEGIN_SCAN_CMDID instead. The new cmd supports P2P mgmt
+ * operations using station interface.
+ */
WMI_START_SCAN_CMDID,
WMI_SET_SCAN_PARAMS_CMDID,
WMI_SET_BSS_FILTER_CMDID,
@@ -393,6 +428,7 @@ enum wmi_cmd_id {
WMI_SET_FRAMERATES_CMDID,
WMI_SET_AP_PS_CMDID,
WMI_SET_QOS_SUPP_CMDID,
+ WMI_SET_IE_CMDID,
/* WMI_THIN_RESERVED_... mark the start and end
* values for WMI_THIN_RESERVED command IDs. These
@@ -533,12 +569,79 @@ enum wmi_cmd_id {
WMI_GTK_OFFLOAD_OP_CMDID,
WMI_REMAIN_ON_CHNL_CMDID,
WMI_CANCEL_REMAIN_ON_CHNL_CMDID,
+ /* WMI_SEND_ACTION_CMDID is to be deprecated. Use
+ * WMI_SEND_MGMT_CMDID instead. The new cmd supports P2P mgmt
+ * operations using station interface.
+ */
WMI_SEND_ACTION_CMDID,
WMI_PROBE_REQ_REPORT_CMDID,
WMI_DISABLE_11B_RATES_CMDID,
WMI_SEND_PROBE_RESPONSE_CMDID,
WMI_GET_P2P_INFO_CMDID,
WMI_AP_JOIN_BSS_CMDID,
+
+ WMI_SMPS_ENABLE_CMDID,
+ WMI_SMPS_CONFIG_CMDID,
+ WMI_SET_RATECTRL_PARM_CMDID,
+ /* LPL specific commands*/
+ WMI_LPL_FORCE_ENABLE_CMDID,
+ WMI_LPL_SET_POLICY_CMDID,
+ WMI_LPL_GET_POLICY_CMDID,
+ WMI_LPL_GET_HWSTATE_CMDID,
+ WMI_LPL_SET_PARAMS_CMDID,
+ WMI_LPL_GET_PARAMS_CMDID,
+
+ WMI_SET_BUNDLE_PARAM_CMDID,
+
+ /*GreenTx specific commands*/
+
+ WMI_GREENTX_PARAMS_CMDID,
+
+ WMI_RTT_MEASREQ_CMDID,
+ WMI_RTT_CAPREQ_CMDID,
+ WMI_RTT_STATUSREQ_CMDID,
+
+ /* WPS Commands */
+ WMI_WPS_START_CMDID,
+ WMI_GET_WPS_STATUS_CMDID,
+
+ /* More P2P commands */
+ WMI_SET_NOA_CMDID,
+ WMI_GET_NOA_CMDID,
+ WMI_SET_OPPPS_CMDID,
+ WMI_GET_OPPPS_CMDID,
+ WMI_ADD_PORT_CMDID,
+ WMI_DEL_PORT_CMDID,
+
+ /* 802.11w cmd */
+ WMI_SET_RSN_CAP_CMDID,
+ WMI_GET_RSN_CAP_CMDID,
+ WMI_SET_IGTK_CMDID,
+
+ WMI_RX_FILTER_COALESCE_FILTER_OP_CMDID,
+ WMI_RX_FILTER_SET_FRAME_TEST_LIST_CMDID,
+
+ WMI_SEND_MGMT_CMDID,
+ WMI_BEGIN_SCAN_CMDID,
+
+ WMI_SET_BLACK_LIST,
+ WMI_SET_MCASTRATE,
+
+ WMI_STA_BMISS_ENHANCE_CMDID,
+
+ WMI_SET_REGDOMAIN_CMDID,
+
+ WMI_SET_RSSI_FILTER_CMDID,
+
+ WMI_SET_KEEP_ALIVE_EXT,
+
+ WMI_VOICE_DETECTION_ENABLE_CMDID,
+
+ WMI_SET_TXE_NOTIFY_CMDID,
+
+ WMI_SET_RECOVERY_TEST_PARAMETER_CMDID, /*0xf094*/
+
+ WMI_ENABLE_SCHED_SCAN_CMDID,
};
enum wmi_mgmt_frame_type {
@@ -550,6 +653,11 @@ enum wmi_mgmt_frame_type {
WMI_NUM_MGMT_FRAME
};
+enum wmi_ie_field_type {
+ WMI_RSN_IE_CAPB = 0x1,
+ WMI_IE_FULL = 0xFF, /* indicats full IE */
+};
+
/* WMI_CONNECT_CMDID */
enum network_type {
INFRA_NETWORK = 0x01,
@@ -558,6 +666,14 @@ enum network_type {
AP_NETWORK = 0x10,
};
+enum network_subtype {
+ SUBTYPE_NONE,
+ SUBTYPE_BT,
+ SUBTYPE_P2PDEV,
+ SUBTYPE_P2PCLIENT,
+ SUBTYPE_P2PGO,
+};
+
enum dot11_auth_mode {
OPEN_AUTH = 0x01,
SHARED_AUTH = 0x02,
@@ -576,10 +692,6 @@ enum auth_mode {
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
@@ -617,6 +729,7 @@ enum wmi_connect_ctrl_flags_bits {
CONNECT_CSA_FOLLOW_BSS = 0x0020,
CONNECT_DO_WPA_OFFLOAD = 0x0040,
CONNECT_DO_NOT_DEAUTH = 0x0080,
+ CONNECT_WPS_FLAG = 0x0100,
};
struct wmi_connect_cmd {
@@ -632,6 +745,7 @@ struct wmi_connect_cmd {
__le16 ch;
u8 bssid[ETH_ALEN];
__le32 ctrl_flags;
+ u8 nw_subtype;
} __packed;
/* WMI_RECONNECT_CMDID */
@@ -719,7 +833,12 @@ enum wmi_scan_type {
WMI_SHORT_SCAN = 1,
};
-struct wmi_start_scan_cmd {
+struct wmi_supp_rates {
+ u8 nrates;
+ u8 rates[ATH6KL_RATE_MAXSIZE];
+};
+
+struct wmi_begin_scan_cmd {
__le32 force_fg_scan;
/* for legacy cisco AP compatibility */
@@ -731,9 +850,15 @@ struct wmi_start_scan_cmd {
/* time interval between scans (msec) */
__le32 force_scan_intvl;
+ /* no CCK rates */
+ __le32 no_cck;
+
/* enum wmi_scan_type */
u8 scan_type;
+ /* Supported rates to advertise in the probe request frames */
+ struct wmi_supp_rates supp_rates[ATH6KL_NUM_BANDS];
+
/* how many channels follow */
u8 num_ch;
@@ -741,8 +866,31 @@ struct wmi_start_scan_cmd {
__le16 ch_list[1];
} __packed;
-/* WMI_SET_SCAN_PARAMS_CMDID */
-#define WMI_SHORTSCANRATIO_DEFAULT 3
+/* wmi_start_scan_cmd is to be deprecated. Use
+ * wmi_begin_scan_cmd instead. The new structure supports P2P mgmt
+ * operations using station interface.
+ */
+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;
/*
* Warning: scan control flag value of 0xFF is used to disable
@@ -750,7 +898,6 @@ struct wmi_start_scan_cmd {
* flags here
*/
enum wmi_scan_ctrl_flags_bits {
-
/* set if can scan in the connect cmd */
CONNECT_SCAN_CTRL_FLAGS = 0x01,
@@ -776,13 +923,6 @@ enum wmi_scan_ctrl_flags_bits {
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;
@@ -814,6 +954,11 @@ struct wmi_scan_params_cmd {
__le32 max_dfsch_act_time;
} __packed;
+/* WMI_ENABLE_SCHED_SCAN_CMDID */
+struct wmi_enable_sched_scan_cmd {
+ u8 enable;
+} __packed;
+
/* WMI_SET_BSS_FILTER_CMDID */
enum wmi_bss_filter {
/* no beacons forwarded */
@@ -837,6 +982,9 @@ enum wmi_bss_filter {
/* beacons matching probed ssid */
PROBED_SSID_FILTER,
+ /* beacons matching matched ssid */
+ MATCHED_SSID_FILTER,
+
/* marker only */
LAST_BSS_FILTER,
};
@@ -855,7 +1003,7 @@ struct wmi_bss_filter_cmd {
} __packed;
/* WMI_SET_PROBED_SSID_CMDID */
-#define MAX_PROBED_SSID_INDEX 9
+#define MAX_PROBED_SSIDS 16
enum wmi_ssid_flag {
/* disables entry */
@@ -866,10 +1014,13 @@ enum wmi_ssid_flag {
/* probes for any ssid */
ANY_SSID_FLAG = 0x02,
+
+ /* match for ssid */
+ MATCH_SSID_FLAG = 0x08,
};
struct wmi_probed_ssid_cmd {
- /* 0 to MAX_PROBED_SSID_INDEX */
+ /* 0 to MAX_PROBED_SSIDS - 1 */
u8 entry_index;
/* see, enum wmi_ssid_flg */
@@ -888,6 +1039,22 @@ struct wmi_listen_int_cmd {
__le16 num_beacons;
} __packed;
+/* WMI_SET_BMISS_TIME_CMDID */
+struct wmi_bmiss_time_cmd {
+ __le16 bmiss_time;
+ __le16 num_beacons;
+};
+
+/* WMI_STA_ENHANCE_BMISS_CMDID */
+struct wmi_sta_bmiss_enhance_cmd {
+ u8 enable;
+} __packed;
+
+struct wmi_set_regdomain_cmd {
+ u8 length;
+ u8 iso_name[2];
+} __packed;
+
/* WMI_SET_POWER_MODE_CMDID */
enum wmi_power_mode {
REC_POWER = 0x01,
@@ -900,12 +1067,12 @@ struct wmi_power_mode_cmd {
} __packed;
/*
- * Policy to determnine whether power save failure event should be sent to
+ * Policy to determine 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,
+ IGNORE_PS_FAIL_DURING_SCAN = 2,
};
struct wmi_power_params_cmd {
@@ -919,6 +1086,36 @@ struct wmi_power_params_cmd {
__le16 ps_fail_event_policy;
} __packed;
+/*
+ * Ratemask for below modes should be passed
+ * to WMI_SET_TX_SELECT_RATES_CMDID.
+ * AR6003 has 32 bit mask for each modes.
+ * First 12 bits for legacy rates, 13 to 20
+ * bits for HT 20 rates and 21 to 28 bits for
+ * HT 40 rates
+ */
+enum wmi_mode_phy {
+ WMI_RATES_MODE_11A = 0,
+ WMI_RATES_MODE_11G,
+ WMI_RATES_MODE_11B,
+ WMI_RATES_MODE_11GONLY,
+ WMI_RATES_MODE_11A_HT20,
+ WMI_RATES_MODE_11G_HT20,
+ WMI_RATES_MODE_11A_HT40,
+ WMI_RATES_MODE_11G_HT40,
+ WMI_RATES_MODE_MAX
+};
+
+/* WMI_SET_TX_SELECT_RATES_CMDID */
+struct wmi_set_tx_select_rates32_cmd {
+ __le32 ratemask[WMI_RATES_MODE_MAX];
+} __packed;
+
+/* WMI_SET_TX_SELECT_RATES_CMDID */
+struct wmi_set_tx_select_rates64_cmd {
+ __le64 ratemask[WMI_RATES_MODE_MAX];
+} __packed;
+
/* WMI_SET_DISC_TIMEOUT_CMDID */
struct wmi_disc_timeout_cmd {
/* seconds */
@@ -1024,6 +1221,7 @@ enum wmi_phy_mode {
WMI_11AG_MODE = 0x3,
WMI_11B_MODE = 0x4,
WMI_11GONLY_MODE = 0x5,
+ WMI_11G_HT20 = 0x6,
};
#define WMI_MAX_CHANNELS 32
@@ -1101,9 +1299,14 @@ struct wmi_snr_threshold_params_cmd {
u8 reserved[3];
} __packed;
+/* Don't report BSSs with signal (RSSI) below this threshold */
+struct wmi_set_rssi_filter_cmd {
+ s8 rssi;
+} __packed;
+
enum wmi_preamble_policy {
WMI_IGNORE_BARKER_IN_ERP = 0,
- WMI_DONOT_IGNORE_BARKER_IN_ERP
+ WMI_FOLLOW_BARKER_IN_ERP,
};
struct wmi_set_lpreamble_cmd {
@@ -1141,6 +1344,25 @@ enum target_event_report_config {
NO_DISCONN_EVT_IN_RECONN
};
+struct wmi_mcast_filter_cmd {
+ u8 mcast_all_enable;
+} __packed;
+
+#define ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE 6
+struct wmi_mcast_filter_add_del_cmd {
+ u8 mcast_mac[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE];
+} __packed;
+
+struct wmi_set_htcap_cmd {
+ u8 band;
+ u8 ht_enable;
+ u8 ht40_supported;
+ u8 ht20_sgi;
+ u8 ht40_sgi;
+ u8 intolerant_40mhz;
+ u8 max_ampdu_len_exp;
+} __packed;
+
/* Command Replies */
/* WMI_GET_CHANNEL_LIST_CMDID reply */
@@ -1239,6 +1461,8 @@ enum wmi_event_id {
WMI_P2P_START_SDPD_EVENTID,
WMI_P2P_SDPD_RX_EVENTID,
+ WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID = 0x1047,
+
WMI_THIN_RESERVED_START_EVENTID = 0x8000,
/* Events in this range are reserved for thinmode */
WMI_THIN_RESERVED_END_EVENTID = 0x8fff,
@@ -1259,6 +1483,20 @@ enum wmi_event_id {
WMI_P2P_CAPABILITIES_EVENTID,
WMI_RX_ACTION_EVENTID,
WMI_P2P_INFO_EVENTID,
+
+ /* WPS Events */
+ WMI_WPS_GET_STATUS_EVENTID,
+ WMI_WPS_PROFILE_EVENTID,
+
+ /* more P2P events */
+ WMI_NOA_INFO_EVENTID,
+ WMI_OPPPS_INFO_EVENTID,
+ WMI_PORT_STATUS_EVENTID,
+
+ /* 802.11w */
+ WMI_GET_RSN_CAP_EVENTID,
+
+ WMI_TXE_NOTIFY_EVENTID,
};
struct wmi_ready_event_2 {
@@ -1268,6 +1506,16 @@ struct wmi_ready_event_2 {
u8 phy_cap;
} __packed;
+/* WMI_PHY_CAPABILITY */
+enum wmi_phy_cap {
+ WMI_11A_CAP = 0x01,
+ WMI_11G_CAP = 0x02,
+ WMI_11AG_CAP = 0x03,
+ WMI_11AN_CAP = 0x04,
+ WMI_11GN_CAP = 0x05,
+ WMI_11AGN_CAP = 0x06,
+};
+
/* Connect Event */
struct wmi_connect_event {
union {
@@ -1320,6 +1568,17 @@ enum wmi_disconnect_reason {
IBSS_MERGE = 0xe,
};
+/* AP mode disconnect proto_reasons */
+enum ap_disconnect_reason {
+ WMI_AP_REASON_STA_LEFT = 101,
+ WMI_AP_REASON_FROM_HOST = 102,
+ WMI_AP_REASON_COMM_TIMEOUT = 103,
+ WMI_AP_REASON_MAX_STA = 104,
+ WMI_AP_REASON_ACL = 105,
+ WMI_AP_REASON_STA_ROAM = 106,
+ WMI_AP_REASON_DFS_CHANNEL = 107,
+};
+
#define ATH6KL_COUNTRY_RD_SHIFT 16
struct ath6kl_wmi_regdomain {
@@ -1365,14 +1624,20 @@ enum wmi_roam_ctrl {
WMI_SET_LRSSI_SCAN_PARAMS,
};
+enum wmi_roam_mode {
+ WMI_DEFAULT_ROAM_MODE = 1, /* RSSI based roam */
+ WMI_HOST_BIAS_ROAM_MODE = 2, /* Host bias based roam */
+ WMI_LOCK_BSS_MODE = 3, /* Lock to the current BSS */
+};
+
struct bss_bias {
u8 bssid[ETH_ALEN];
- u8 bias;
+ s8 bias;
} __packed;
struct bss_bias_info {
u8 num_bss;
- struct bss_bias bss_bias[1];
+ struct bss_bias bss_bias[0];
} __packed;
struct low_rssi_scan_params {
@@ -1385,14 +1650,23 @@ struct low_rssi_scan_params {
struct roam_ctrl_cmd {
union {
- u8 bssid[ETH_ALEN];
- u8 roam_mode;
- struct bss_bias_info bss;
- struct low_rssi_scan_params params;
+ u8 bssid[ETH_ALEN]; /* WMI_FORCE_ROAM */
+ u8 roam_mode; /* WMI_SET_ROAM_MODE */
+ struct bss_bias_info bss; /* WMI_SET_HOST_BIAS */
+ struct low_rssi_scan_params params; /* WMI_SET_LRSSI_SCAN_PARAMS
+ */
} __packed info;
u8 roam_ctrl;
} __packed;
+struct set_beacon_int_cmd {
+ __le32 beacon_intvl;
+} __packed;
+
+struct set_dtim_cmd {
+ __le32 dtim_period;
+} __packed;
+
/* BSS INFO HDR version 2.0 */
struct wmi_bss_info_hdr2 {
__le16 ch; /* frequency in MHz */
@@ -1455,6 +1729,10 @@ struct wmi_tkip_micerr_event {
u8 is_mcast;
} __packed;
+enum wmi_scan_status {
+ WMI_SCAN_STATUS_SUCCESS = 0,
+};
+
/* WMI_SCAN_COMPLETE_EVENTID */
struct wmi_scan_complete_event {
a_sle32 status;
@@ -1517,6 +1795,9 @@ struct rx_stats {
a_sle32 ucast_rate;
} __packed;
+#define RATE_INDEX_WITHOUT_SGI_MASK 0x7f
+#define RATE_INDEX_MSB 0x80
+
struct tkip_ccmp_stats {
__le32 tkip_local_mic_fail;
__le32 tkip_cnter_measures_invoked;
@@ -1635,6 +1916,12 @@ struct wmi_bss_roam_info {
u8 reserved;
} __packed;
+struct wmi_target_roam_tbl {
+ __le16 roam_mode;
+ __le16 num_entries;
+ struct wmi_bss_roam_info info[];
+} __packed;
+
/* WMI_CAC_EVENTID */
enum cac_indication {
CAC_INDICATION_ADMISSION = 0x00,
@@ -1767,14 +2054,20 @@ struct wmi_set_appie_cmd {
u8 ie_info[0];
} __packed;
+struct wmi_set_ie_cmd {
+ u8 ie_id;
+ u8 ie_field; /* enum wmi_ie_field_type */
+ u8 ie_len;
+ u8 reserved;
+ u8 ie_info[0];
+} __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
@@ -1783,7 +2076,7 @@ struct wow_filter {
u8 wow_filter_id;
u8 wow_filter_size;
u8 wow_filter_offset;
- u8 wow_filter_mask[WOW_MASK_SIZE];
+ u8 wow_filter_mask[WOW_PATTERN_SIZE];
u8 wow_filter_pattern[WOW_PATTERN_SIZE];
} __packed;
@@ -1791,20 +2084,68 @@ struct wow_filter {
struct wmi_set_ip_cmd {
/* IP in network byte order */
- __le32 ips[MAX_IP_ADDRS];
+ __be32 ips[MAX_IP_ADDRS];
+} __packed;
+
+enum ath6kl_wow_filters {
+ WOW_FILTER_SSID = BIT(1),
+ WOW_FILTER_OPTION_MAGIC_PACKET = BIT(2),
+ WOW_FILTER_OPTION_EAP_REQ = BIT(3),
+ WOW_FILTER_OPTION_PATTERNS = BIT(4),
+ WOW_FILTER_OPTION_OFFLOAD_ARP = BIT(5),
+ WOW_FILTER_OPTION_OFFLOAD_NS = BIT(6),
+ WOW_FILTER_OPTION_OFFLOAD_GTK = BIT(7),
+ WOW_FILTER_OPTION_8021X_4WAYHS = BIT(8),
+ WOW_FILTER_OPTION_NLO_DISCVRY = BIT(9),
+ WOW_FILTER_OPTION_NWK_DISASSOC = BIT(10),
+ WOW_FILTER_OPTION_GTK_ERROR = BIT(11),
+ WOW_FILTER_OPTION_TEST_MODE = BIT(15),
+};
+
+enum ath6kl_host_mode {
+ ATH6KL_HOST_MODE_AWAKE,
+ ATH6KL_HOST_MODE_ASLEEP,
+};
+
+struct wmi_set_host_sleep_mode_cmd {
+ __le32 awake;
+ __le32 asleep;
+} __packed;
+
+enum ath6kl_wow_mode {
+ ATH6KL_WOW_MODE_DISABLE,
+ ATH6KL_WOW_MODE_ENABLE,
+};
+
+struct wmi_set_wow_mode_cmd {
+ __le32 enable_wow;
+ __le32 filter;
+ __le16 host_req_delay;
} __packed;
-/* WMI_GET_WOW_LIST_CMD reply */
-struct wmi_get_wow_list_reply {
- /* number of patterns in reply */
- u8 num_filters;
+struct wmi_add_wow_pattern_cmd {
+ u8 filter_list_id;
+ u8 filter_size;
+ u8 filter_offset;
+ u8 filter[0];
+} __packed;
+
+struct wmi_del_wow_pattern_cmd {
+ __le16 filter_list_id;
+ __le16 filter_id;
+} __packed;
- /* this is filter # x of total num_filters */
- u8 this_filter_num;
+/* WMI_SET_TXE_NOTIFY_CMDID */
+struct wmi_txe_notify_cmd {
+ __le32 rate;
+ __le32 pkts;
+ __le32 intvl;
+} __packed;
- u8 wow_mode;
- u8 host_mode;
- struct wow_filter wow_filters[1];
+/* WMI_TXE_NOTIFY_EVENTID */
+struct wmi_txe_notify_event {
+ __le32 rate;
+ __le32 pkts;
} __packed;
/* WMI_SET_AKMP_PARAMS_CMD */
@@ -1905,7 +2246,7 @@ struct wmi_tx_complete_event {
* !!! Warning !!!
* -Changing the following values needs compilation of both driver and firmware
*/
-#define AP_MAX_NUM_STA 8
+#define AP_MAX_NUM_STA 10
/* Spl. AID used to set DTIM flag in the beacons */
#define MCAST_AID 0xFF
@@ -1957,7 +2298,29 @@ struct wmi_rx_frame_format_cmd {
u8 reserved[1];
} __packed;
+struct wmi_ap_hidden_ssid_cmd {
+ u8 hidden_ssid;
+} __packed;
+
+struct wmi_set_inact_period_cmd {
+ __le32 inact_period;
+ u8 num_null_func;
+} __packed;
+
/* AP mode events */
+struct wmi_ap_set_apsd_cmd {
+ u8 enable;
+} __packed;
+
+enum wmi_ap_apsd_buffered_traffic_flags {
+ WMI_AP_APSD_NO_DELIVERY_FRAMES = 0x1,
+};
+
+struct wmi_ap_apsd_buffered_traffic_cmd {
+ __le16 aid;
+ __le16 bitmap;
+ __le32 flags;
+} __packed;
/* WMI_PS_POLL_EVENT */
struct wmi_pspoll_event {
@@ -1988,6 +2351,10 @@ struct wmi_remain_on_chnl_cmd {
__le32 duration;
} __packed;
+/* wmi_send_action_cmd is to be deprecated. Use
+ * wmi_send_mgmt_cmd instead. The new structure supports P2P mgmt
+ * operations using station interface.
+ */
struct wmi_send_action_cmd {
__le32 id;
__le32 freq;
@@ -1996,6 +2363,15 @@ struct wmi_send_action_cmd {
u8 data[0];
} __packed;
+struct wmi_send_mgmt_cmd {
+ __le32 id;
+ __le32 freq;
+ __le32 wait;
+ __le32 no_cck;
+ __le16 len;
+ u8 data[0];
+} __packed;
+
struct wmi_tx_status_event {
__le32 id;
u8 ack_status;
@@ -2161,122 +2537,195 @@ 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,
+ u8 msg_type, u32 flags,
enum wmi_data_hdr_data_type data_type,
- u8 meta_ver, void *tx_meta_info);
+ u8 meta_ver, void *tx_meta_info, u8 if_idx);
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_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
- u32 layer2_priority, bool wmm_enabled,
- u8 *ac);
+int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx,
+ struct sk_buff *skb, u32 layer2_priority,
+ bool wmm_enabled, u8 *ac);
int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb);
-int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
+int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, 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,
+int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx,
+ 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);
+ u8 *bssid, u16 channel, u32 ctrl_flags,
+ u8 nw_subtype);
+
+int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 if_idx, u8 *bssid,
+ u16 channel);
+int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx);
-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,
+int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx,
+ 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,
+ s8 num_chan, u16 *ch_list, u32 no_cck,
+ u32 *rates);
+int ath6kl_wmi_enable_sched_scan_cmd(struct wmi *wmi, u8 if_idx, bool enable);
+
+int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, 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,
+int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 if_idx, u8 filter,
+ u32 ie_mask);
+int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, u8 index, u8 flag,
u8 ssid_len, u8 *ssid);
-int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u16 listen_interval,
+int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u8 if_idx,
+ 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,
+int ath6kl_wmi_bmisstime_cmd(struct wmi *wmi, u8 if_idx,
+ u16 bmiss_time, u16 num_beacons);
+int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode);
+int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u8 if_idx, 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,
+int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi, u8 if_idx,
struct wmi_create_pstream_cmd *pstream);
-int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 traffic_class, u8 tsid);
+int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 if_idx, u8 traffic_class,
+ u8 tsid);
+int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 if_idx, u8 timeout);
int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u16 threshold);
-int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status,
+int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 if_idx, u8 status,
u8 preamble_policy);
int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source);
int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config);
-int ath6kl_wmi_get_stats_cmd(struct wmi *wmi);
-int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
+int ath6kl_wmi_get_stats_cmd(struct wmi *wmi, u8 if_idx);
+int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
enum crypto_type key_type,
u8 key_usage, u8 key_len,
- u8 *key_rsc, u8 *key_material,
+ u8 *key_rsc, unsigned int key_rsc_len,
+ 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,
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk);
+int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index);
+int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, 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);
+int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 if_idx, u8 dbM);
+int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx);
+int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi);
+
+int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg);
+int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx,
+ u8 keep_alive_intvl);
+int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx,
+ enum ieee80211_band band,
+ struct ath6kl_htcap *htcap);
int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len);
s32 ath6kl_wmi_get_rate(s8 rate_index);
-int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd);
+int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx,
+ __be32 ips0, __be32 ips1);
+int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx,
+ enum ath6kl_host_mode host_mode);
+int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx,
+ const struct cfg80211_bitrate_mask *mask);
+int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx,
+ enum ath6kl_wow_mode wow_mode,
+ u32 filter, u16 host_req_delay);
+int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx,
+ u8 list_id, u8 filter_size,
+ u8 filter_offset, const u8 *filter,
+ const u8 *mask);
+int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx,
+ u16 list_id, u16 filter_id);
+int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi);
int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi);
-
+int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period);
+int ath6kl_wmi_ap_set_beacon_intvl_cmd(struct wmi *wmi, u8 if_idx,
+ u32 beacon_interval);
+int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid);
+int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode);
+int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on);
+int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx,
+ u8 *filter, bool add_filter);
+int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable);
+int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx,
+ u32 rate, u32 pkts, u32 intvl);
+int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2);
+
+/* AP mode uAPSD */
+int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable);
+
+int ath6kl_wmi_set_apsd_bfrd_traf(struct wmi *wmi,
+ u8 if_idx, u16 aid,
+ u16 bitmap, u32 flags);
+
+u8 ath6kl_wmi_get_traffic_class(u8 user_priority);
+
+u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri);
/* AP mode */
-int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p);
+int ath6kl_wmi_ap_hidden_ssid(struct wmi *wmi, u8 if_idx, bool enable);
+int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx,
+ struct wmi_connect_cmd *p);
-int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason);
+int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd,
+ const u8 *mac, u16 reason);
-int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag);
+int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u8 if_idx, u16 aid, bool flag);
-int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version,
+int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx,
+ u8 rx_meta_version,
bool rx_dot11_hdr, bool defrag_on_host);
-int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
- u8 ie_len);
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type,
+ const u8 *ie, u8 ie_len);
+
+int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field,
+ const u8 *ie_info, u8 ie_len);
/* P2P */
int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable);
-int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur);
+int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq,
+ u32 dur);
+
+int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
+ u32 wait, const u8 *data, u16 data_len,
+ u32 no_cck);
+
+int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq,
+ const u8 *dst, const u8 *data,
+ u16 data_len);
-int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
- const u8 *data, u16 data_len);
+int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, u8 if_idx, bool enable);
-int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq,
- const u8 *dst,
- const u8 *data, u16 data_len);
+int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u8 if_idx, u32 info_req_flags);
-int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable);
+int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx);
-int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags);
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type,
+ const u8 *ie, u8 ie_len);
-int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi);
+int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout);
-int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
- u8 ie_len);
+void ath6kl_wmi_sscan_timer(unsigned long ptr);
+
+int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source);
+struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx);
void *ath6kl_wmi_init(struct ath6kl *devt);
void ath6kl_wmi_shutdown(struct wmi *wmi);
+void ath6kl_wmi_reset(struct wmi *wmi);
#endif /* WMI_H */