diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-12-19 23:25:17 +0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-12-19 23:25:17 +0400 |
commit | 640f5950a7d9fd0d279d843b261eb934793605fb (patch) | |
tree | 0f6f4309d8b02c220243757da1cd06f4d5f6a9e9 /drivers/net | |
parent | 7c4559c92d45949726d7cf687a010664730a07da (diff) | |
parent | 7e95e365d5399647a41e10059e4b09826b82d78b (diff) | |
download | linux-640f5950a7d9fd0d279d843b261eb934793605fb.tar.xz |
Merge branch 'for-linville' of git://github.com/kvalo/ath6kl
Diffstat (limited to 'drivers/net')
22 files changed, 1809 insertions, 653 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig index 3d5f8be20eac..d755a5e7ed20 100644 --- a/drivers/net/wireless/ath/ath6kl/Kconfig +++ b/drivers/net/wireless/ath/ath6kl/Kconfig @@ -1,12 +1,29 @@ config ATH6KL - tristate "Atheros ath6kl support" + tristate "Atheros mobile chipsets support" + +config ATH6KL_SDIO + tristate "Atheros ath6kl SDIO support" + depends on ATH6KL depends on MMC depends on CFG80211 ---help--- This module adds support for wireless adapters based on - Atheros AR6003 chipset running over SDIO. If you choose to - build it as a module, it will be called ath6kl. Pls note - that AR6002 and AR6001 are not supported by this driver. + Atheros AR6003 and AR6004 chipsets running over SDIO. If you + choose to build it as a module, it will be called ath6kl_sdio. + Please note that AR6002 and AR6001 are not supported by this + driver. + +config ATH6KL_USB + tristate "Atheros ath6kl USB support" + depends on ATH6KL + depends on USB + depends on CFG80211 + depends on EXPERIMENTAL + ---help--- + This module adds support for wireless adapters based on + Atheros AR6004 chipset running over USB. This is still under + implementation and it isn't functional. If you choose to + build it as a module, it will be called ath6kl_usb. config ATH6KL_DEBUG bool "Atheros ath6kl debugging" diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 707069303550..e14cef9c3c0e 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -21,17 +21,30 @@ # Author(s): ="Atheros" #------------------------------------------------------------------------------ -obj-$(CONFIG_ATH6KL) := ath6kl.o -ath6kl-y += debug.o -ath6kl-y += hif.o -ath6kl-y += htc.o -ath6kl-y += bmi.o -ath6kl-y += cfg80211.o -ath6kl-y += init.o -ath6kl-y += main.o -ath6kl-y += txrx.o -ath6kl-y += wmi.o -ath6kl-y += sdio.o -ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o +obj-$(CONFIG_ATH6KL_SDIO) := ath6kl_sdio.o +ath6kl_sdio-y += debug.o +ath6kl_sdio-y += hif.o +ath6kl_sdio-y += htc.o +ath6kl_sdio-y += bmi.o +ath6kl_sdio-y += cfg80211.o +ath6kl_sdio-y += init.o +ath6kl_sdio-y += main.o +ath6kl_sdio-y += txrx.o +ath6kl_sdio-y += wmi.o +ath6kl_sdio-y += sdio.o +ath6kl_sdio-$(CONFIG_NL80211_TESTMODE) += testmode.o + +obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o +ath6kl_usb-y += debug.o +ath6kl_usb-y += hif.o +ath6kl_usb-y += htc.o +ath6kl_usb-y += bmi.o +ath6kl_usb-y += cfg80211.o +ath6kl_usb-y += init.o +ath6kl_usb-y += main.o +ath6kl_usb-y += txrx.o +ath6kl_usb-y += wmi.o +ath6kl_usb-y += usb.o +ath6kl_usb-$(CONFIG_NL80211_TESTMODE) += testmode.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c index a962fe4c6b7e..aef00d5a1438 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.c +++ b/drivers/net/wireless/ath/ath6kl/bmi.c @@ -19,165 +19,6 @@ #include "target.h" #include "debug.h" -static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar) -{ - u32 addr; - unsigned long timeout; - int ret; - - ar->bmi.cmd_credits = 0; - - /* Read the counter register to get the command credits */ - addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4; - - timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); - while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) { - - /* - * Hit the credit counter with a 4-byte access, the first byte - * read will hit the counter and cause a decrement, while the - * remaining 3 bytes has no effect. The rationale behind this - * is to make all HIF accesses 4-byte aligned. - */ - ret = hif_read_write_sync(ar, addr, - (u8 *)&ar->bmi.cmd_credits, 4, - HIF_RD_SYNC_BYTE_INC); - if (ret) { - ath6kl_err("Unable to decrement the command credit count register: %d\n", - ret); - return ret; - } - - /* The counter is only 8 bits. - * Ignore anything in the upper 3 bytes - */ - ar->bmi.cmd_credits &= 0xFF; - } - - if (!ar->bmi.cmd_credits) { - ath6kl_err("bmi communication timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar) -{ - unsigned long timeout; - u32 rx_word = 0; - int ret = 0; - - timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); - while (time_before(jiffies, timeout) && !rx_word) { - ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS, - (u8 *)&rx_word, sizeof(rx_word), - HIF_RD_SYNC_BYTE_INC); - if (ret) { - ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n"); - return ret; - } - - /* all we really want is one bit */ - rx_word &= (1 << ENDPOINT1); - } - - if (!rx_word) { - ath6kl_err("bmi_recv_buf FIFO empty\n"); - return -EINVAL; - } - - return ret; -} - -static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len) -{ - int ret; - u32 addr; - - ret = ath6kl_get_bmi_cmd_credits(ar); - if (ret) - return ret; - - addr = ar->mbox_info.htc_addr; - - ret = hif_read_write_sync(ar, addr, buf, len, - HIF_WR_SYNC_BYTE_INC); - if (ret) - ath6kl_err("unable to send the bmi data to the device\n"); - - return ret; -} - -static int ath6kl_bmi_recv_buf(struct ath6kl *ar, u8 *buf, u32 len) -{ - int ret; - u32 addr; - - /* - * During normal bootup, small reads may be required. - * Rather than issue an HIF Read and then wait as the Target - * adds successive bytes to the FIFO, we wait here until - * we know that response data is available. - * - * This allows us to cleanly timeout on an unexpected - * Target failure rather than risk problems at the HIF level. - * In particular, this avoids SDIO timeouts and possibly garbage - * data on some host controllers. And on an interconnect - * such as Compact Flash (as well as some SDIO masters) which - * does not provide any indication on data timeout, it avoids - * a potential hang or garbage response. - * - * Synchronization is more difficult for reads larger than the - * size of the MBOX FIFO (128B), because the Target is unable - * to push the 129th byte of data until AFTER the Host posts an - * HIF Read and removes some FIFO data. So for large reads the - * Host proceeds to post an HIF Read BEFORE all the data is - * actually available to read. Fortunately, large BMI reads do - * not occur in practice -- they're supported for debug/development. - * - * So Host/Target BMI synchronization is divided into these cases: - * CASE 1: length < 4 - * Should not happen - * - * CASE 2: 4 <= length <= 128 - * Wait for first 4 bytes to be in FIFO - * If CONSERVATIVE_BMI_READ is enabled, also wait for - * a BMI command credit, which indicates that the ENTIRE - * response is available in the the FIFO - * - * CASE 3: length > 128 - * Wait for the first 4 bytes to be in FIFO - * - * For most uses, a small timeout should be sufficient and we will - * usually see a response quickly; but there may be some unusual - * (debug) cases of BMI_EXECUTE where we want an larger timeout. - * For now, we use an unbounded busy loop while waiting for - * BMI_EXECUTE. - * - * If BMI_EXECUTE ever needs to support longer-latency execution, - * especially in production, this code needs to be enhanced to sleep - * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently - * a function of Host processor speed. - */ - if (len >= 4) { /* NB: Currently, always true */ - ret = ath6kl_bmi_get_rx_lkahd(ar); - if (ret) - return ret; - } - - addr = ar->mbox_info.htc_addr; - ret = hif_read_write_sync(ar, addr, buf, len, - HIF_RD_SYNC_BYTE_INC); - if (ret) { - ath6kl_err("Unable to read the bmi data from the device: %d\n", - ret); - return ret; - } - - return 0; -} - int ath6kl_bmi_done(struct ath6kl *ar) { int ret; @@ -190,7 +31,7 @@ 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; @@ -210,14 +51,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; @@ -225,7 +72,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) { @@ -244,7 +91,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) - @@ -276,8 +123,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; } @@ -290,8 +137,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); @@ -300,13 +147,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); @@ -326,7 +173,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) { @@ -334,12 +181,15 @@ 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); @@ -348,7 +198,7 @@ int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 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 + @@ -358,7 +208,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; @@ -371,7 +221,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); @@ -396,7 +246,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; } @@ -413,13 +263,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; @@ -443,7 +293,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; } @@ -457,7 +307,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; @@ -479,7 +329,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; } @@ -493,13 +343,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; @@ -522,7 +372,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; } @@ -540,7 +390,7 @@ int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); offset += sizeof(param); - ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; @@ -563,8 +413,8 @@ 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; } @@ -575,8 +425,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)); @@ -587,7 +437,7 @@ int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 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); @@ -613,7 +463,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; } @@ -629,7 +479,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); @@ -677,8 +527,13 @@ void ath6kl_bmi_reset(struct ath6kl *ar) 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 009e8f650ab1..f1ca6812456d 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.h +++ b/drivers/net/wireless/ath/ath6kl/bmi.h @@ -44,12 +44,6 @@ * BMI handles all required Target-side cache flushing. */ -#define MAX_BMI_CMDBUF_SZ (BMI_DATASZ_MAX + \ - (sizeof(u32) * 3 /* cmd + addr + len */)) - -/* Maximum data size used for BMI transfers */ -#define BMI_DATASZ_MAX 256 - /* BMI Commands */ #define BMI_NO_COMMAND 0 diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 02526044acb9..6c59a217b1a1 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -23,10 +23,8 @@ #include "testmode.h" static unsigned int ath6kl_p2p; -static unsigned int multi_norm_if_support; module_param(ath6kl_p2p, uint, 0644); -module_param(multi_norm_if_support, uint, 0644); #define RATETAB_ENT(_rate, _rateid, _flags) { \ .bitrate = (_rate), \ @@ -127,6 +125,37 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = { #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 (ar->state != ATH6KL_STATE_SCHED_SCAN) + return false; + + del_timer_sync(&vif->sched_scan_timer); + + ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); + + ar->state = ATH6KL_STATE_ON; + + 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) { @@ -205,6 +234,10 @@ static int ath6kl_set_cipher(struct ath6kl_vif *vif, 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; @@ -355,7 +388,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type, if (type == NL80211_IFTYPE_STATION || type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) { - for (i = 0; i < MAX_NUM_VIF; i++) { + for (i = 0; i < ar->vif_max; i++) { if ((ar->avail_idx_map >> i) & BIT(0)) { *if_idx = i; return true; @@ -365,7 +398,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type, if (type == NL80211_IFTYPE_P2P_CLIENT || type == NL80211_IFTYPE_P2P_GO) { - for (i = ar->max_norm_iface; i < MAX_NUM_VIF; i++) { + for (i = ar->max_norm_iface; i < ar->vif_max; i++) { if ((ar->avail_idx_map >> i) & BIT(0)) { *if_idx = i; return true; @@ -382,6 +415,9 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); int status; + u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE; + + ath6kl_cfg80211_sscan_disable(vif); vif->sme_state = SME_CONNECTING; @@ -427,9 +463,12 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, if (sme->ie && (sme->ie_len > 0)) { status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len); - if (status) + if (status) { + up(&ar->sem); return status; - } + } + } else + ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG; if (test_bit(CONNECTED, &vif->flags) && vif->ssid_len == sme->ssid_len && @@ -519,6 +558,9 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, vif->nw_type = vif->next_mode; + 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" @@ -536,7 +578,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, vif->grp_crypto, vif->grp_crypto_len, vif->ssid_len, vif->ssid, vif->req_bssid, vif->ch_hint, - ar->connect_ctrl_flags); + ar->connect_ctrl_flags, nw_subtype); up(&ar->sem); @@ -563,17 +605,28 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, return 0; } -static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, const u8 *bssid, +static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, + enum network_type nw_type, + const u8 *bssid, struct ieee80211_channel *chan, const u8 *beacon_ie, size_t beacon_ie_len) { struct ath6kl *ar = vif->ar; struct cfg80211_bss *bss; + u16 cap_mask, cap_val; u8 *ie; + if (nw_type & ADHOC_NETWORK) { + cap_mask = WLAN_CAPABILITY_IBSS; + cap_val = WLAN_CAPABILITY_IBSS; + } else { + cap_mask = WLAN_CAPABILITY_ESS; + cap_val = WLAN_CAPABILITY_ESS; + } + bss = cfg80211_get_bss(ar->wiphy, chan, bssid, - vif->ssid, vif->ssid_len, WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); + vif->ssid, vif->ssid_len, + cap_mask, cap_val); if (bss == NULL) { /* * Since cfg80211 may not yet know about the BSS, @@ -591,13 +644,12 @@ static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, const u8 *bssid, memcpy(ie + 2, vif->ssid, vif->ssid_len); memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len); bss = cfg80211_inform_bss(ar->wiphy, chan, - bssid, 0, WLAN_CAPABILITY_ESS, 100, + bssid, 0, cap_val, 100, ie, 2 + vif->ssid_len + beacon_ie_len, 0, GFP_KERNEL); if (bss) - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for " - "%pM prior to indicating connect/roamed " - "event\n", bssid); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to " + "cfg80211\n", bssid); kfree(ie); } else ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss " @@ -660,16 +712,16 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, chan = ieee80211_get_channel(ar->wiphy, (int) channel); - - if (nw_type & ADHOC_NETWORK) { - cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); + if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info, + beacon_ie_len) < 0) { + ath6kl_err("could not add cfg80211 bss entry\n"); return; } - if (ath6kl_add_bss_if_needed(vif, bssid, chan, assoc_info, - beacon_ie_len) < 0) { - ath6kl_err("could not add cfg80211 bss entry for " - "connect/roamed notification\n"); + if (nw_type & ADHOC_NETWORK) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n", + nw_type & ADHOC_CREATOR ? "creator" : "joiner"); + cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); return; } @@ -691,12 +743,14 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, 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); + ath6kl_cfg80211_sscan_disable(vif); + if (!ath6kl_cfg80211_ready(vif)) return -EIO; @@ -789,7 +843,7 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request) { - struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); + struct ath6kl *ar = ath6kl_priv(ndev); struct ath6kl_vif *vif = netdev_priv(ndev); s8 n_channels = 0; u16 *channels = NULL; @@ -799,6 +853,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, if (!ath6kl_cfg80211_ready(vif)) return -EIO; + ath6kl_cfg80211_sscan_disable(vif); + if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); ret = ath6kl_wmi_bssfilter_cmd( @@ -824,6 +880,10 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, request->ssids[i].ssid); } + /* + * FIXME: we should clear the IE in fw if it's not set so just + * remove the check altogether + */ if (request->ie) { ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_REQ, @@ -860,9 +920,25 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, if (test_bit(CONNECTED, &vif->flags)) force_fg_scan = 1; - ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN, - force_fg_scan, false, 0, 0, n_channels, - channels); + 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 + */ + ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, + WMI_LONG_SCAN, force_fg_scan, + false, 0, 0, n_channels, + channels, request->no_cck, + request->rates); + } else { + ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, + WMI_LONG_SCAN, force_fg_scan, + false, 0, 0, n_channels, + channels); + } if (ret) ath6kl_err("wmi_startscan_cmd failed\n"); else @@ -905,7 +981,7 @@ 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; u8 key_usage; @@ -937,13 +1013,19 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, key_usage = GROUP_USAGE; if (params) { + int seq_len = params->seq_len; + if (params->cipher == WLAN_CIPHER_SUITE_SMS4 && + seq_len > ATH6KL_KEY_SEQ_LEN) { + /* Only first half of the WPI PN is configured */ + seq_len = ATH6KL_KEY_SEQ_LEN; + } if (params->key_len > WLAN_MAX_KEY_LEN || - params->seq_len > sizeof(key->seq)) + seq_len > sizeof(key->seq)) return -EINVAL; key->key_len = params->key_len; memcpy(key->key, params->key, key->key_len); - key->seq_len = params->seq_len; + key->seq_len = seq_len; memcpy(key->seq, params->seq, key->seq_len); key->cipher = params->cipher; } @@ -961,6 +1043,9 @@ 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; @@ -976,10 +1061,9 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, __func__, key_index, key->key_len, key_type, key_usage, key->seq_len); - vif->def_txkey_index = key_index; - if (vif->nw_type == AP_NETWORK && !pairwise && - (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) { + (key_type == TKIP_CRYPT || key_type == AES_CRYPT || + key_type == WAPI_CRYPT) && params) { ar->ap_mode_bkey.valid = true; ar->ap_mode_bkey.key_index = key_index; ar->ap_mode_bkey.key_type = key_type; @@ -1012,8 +1096,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, return 0; } - return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, - vif->def_txkey_index, + return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index, key_type, key_usage, key->key_len, key->seq, key->seq_len, key->key, KEY_OP_INIT_VAL, @@ -1024,7 +1107,7 @@ 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); @@ -1090,7 +1173,7 @@ 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; u8 key_usage; @@ -1181,11 +1264,12 @@ static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) */ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, enum nl80211_tx_power_setting type, - int dbm) + int mbm) { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); struct ath6kl_vif *vif; u8 ath6kl_dbm; + int dbm = MBM_TO_DBM(mbm); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__, type, dbm); @@ -1288,7 +1372,7 @@ static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, struct net_device *ndev; u8 if_idx, nw_type; - if (ar->num_vif == MAX_NUM_VIF) { + if (ar->num_vif == ar->vif_max) { ath6kl_err("Reached maximum number of supported vif\n"); return ERR_PTR(-EINVAL); } @@ -1333,9 +1417,6 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - switch (type) { case NL80211_IFTYPE_STATION: vif->next_mode = INFRA_NETWORK; @@ -1426,7 +1507,7 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, vif->grp_crypto, vif->grp_crypto_len, vif->ssid_len, vif->ssid, vif->req_bssid, vif->ch_hint, - ar->connect_ctrl_flags); + ar->connect_ctrl_flags, SUBTYPE_NONE); set_bit(CONNECT_PEND, &vif->flags); return 0; @@ -1453,6 +1534,7 @@ static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, CCKM_KRK_CIPHER_SUITE, + WLAN_CIPHER_SUITE_SMS4, }; static bool is_rate_legacy(s32 rate) @@ -1779,7 +1861,7 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, case ATH6KL_CFG_SUSPEND_DEEPSLEEP: - ath6kl_cfg80211_stop(ar); + ath6kl_cfg80211_stop_all(ar); /* save the current power mode before enabling power save */ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; @@ -1796,7 +1878,7 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, case ATH6KL_CFG_SUSPEND_CUTPOWER: - ath6kl_cfg80211_stop(ar); + ath6kl_cfg80211_stop_all(ar); if (ar->state == ATH6KL_STATE_OFF) { ath6kl_dbg(ATH6KL_DBG_SUSPEND, @@ -1816,6 +1898,13 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, break; + case ATH6KL_CFG_SUSPEND_SCHED_SCAN: + /* + * Nothing needed for schedule scan, firmware is already in + * wow mode and sleeping most of the time. + */ + break; + default: break; } @@ -1864,6 +1953,9 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) } break; + case ATH6KL_STATE_SCHED_SCAN: + break; + default: break; } @@ -1987,7 +2079,7 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, int ies_len; struct wmi_connect_cmd p; int res; - int i; + int i, ret; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); @@ -2045,7 +2137,9 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE) return -EOPNOTSUPP; /* TODO */ - vif->dot11_auth_mode = OPEN_AUTH; + ret = ath6kl_set_auth_type(vif, info->auth_type); + if (ret) + return ret; memset(&p, 0, sizeof(p)); @@ -2081,6 +2175,9 @@ 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) { @@ -2100,6 +2197,9 @@ 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; @@ -2114,6 +2214,16 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, p.dot11_auth_mode = vif->dot11_auth_mode; p.ch = cpu_to_le16(vif->next_chan); + 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; + } + res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); if (res < 0) return res; @@ -2279,9 +2389,23 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, } *cookie = id; - return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id, - chan->center_freq, wait, - buf, len); + + 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 + */ + return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, + chan->center_freq, wait, + buf, len, no_cck); + } else { + return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id, + chan->center_freq, wait, + buf, len); + } } static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, @@ -2302,6 +2426,90 @@ static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, } } +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; + u8 i; + + if (ar->state != ATH6KL_STATE_ON) + return -EIO; + + if (vif->sme_state != SME_DISCONNECTED) + return -EBUSY; + + for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) { + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, + i, DISABLE_SSID_FLAG, + 0, NULL); + } + + /* 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, + 10, 0, 0, 0, 3, 0, 0, 0); + + if (request->n_ssids && request->ssids[0].ssid_len) { + for (i = 0; i < request->n_ssids; i++) { + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, + i, SPECIFIC_SSID_FLAG, + request->ssids[i].ssid_len, + request->ssids[i].ssid); + } + } + + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_WOW_MODE_ENABLE, + WOW_FILTER_SSID, + WOW_HOST_REQ_DELAY); + if (ret) { + ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret); + 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_warn("Failed to set probe request IE for scheduled scan: %d", + ret); + return ret; + } + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_ASLEEP); + if (ret) { + ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n", + ret); + return ret; + } + + ar->state = ATH6KL_STATE_SCHED_SCAN; + + return ret; +} + +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 const struct ieee80211_txrx_stypes ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { @@ -2359,25 +2567,17 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .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, }; -void ath6kl_cfg80211_stop(struct ath6kl *ar) +void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) { - struct ath6kl_vif *vif; - - /* FIXME: for multi vif */ - vif = ath6kl_vif_first(ar); - if (!vif) { - /* 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; - } + ath6kl_cfg80211_sscan_disable(vif); switch (vif->sme_state) { + case SME_DISCONNECTED: + break; case SME_CONNECTING: cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0, NULL, 0, @@ -2385,33 +2585,50 @@ void ath6kl_cfg80211_stop(struct ath6kl *ar) GFP_KERNEL); break; case SME_CONNECTED: - default: - /* - * FIXME: oddly enough smeState is in DISCONNECTED during - * suspend, why? Need to send disconnected event in that - * state. - */ cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL); break; } if (test_bit(CONNECTED, &vif->flags) || test_bit(CONNECT_PEND, &vif->flags)) - ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); + 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); /* disable scanning */ - if (ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0, - 0, 0, 0, 0, 0, 0, 0) != 0) - printk(KERN_WARNING "ath6kl: failed to disable scan " - "during suspend\n"); + if (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) { + /* 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); +} + struct ath6kl *ath6kl_core_alloc(struct device *dev) { struct ath6kl *ar; @@ -2427,17 +2644,12 @@ struct ath6kl *ath6kl_core_alloc(struct device *dev) } ar = wiphy_priv(wiphy); - if (!multi_norm_if_support) - ar->p2p = !!ath6kl_p2p; + ar->p2p = !!ath6kl_p2p; ar->wiphy = wiphy; ar->dev = dev; - if (multi_norm_if_support) - ar->max_norm_iface = 2; - else - ar->max_norm_iface = 1; + ar->vif_max = 1; - /* FIXME: Remove this once the multivif support is enabled */ ar->max_norm_iface = 1; spin_lock_init(&ar->lock); @@ -2459,9 +2671,6 @@ struct ath6kl *ath6kl_core_alloc(struct device *dev) ar->tx_pwr = 0; 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; ar->state = ATH6KL_STATE_OFF; @@ -2522,6 +2731,8 @@ int ath6kl_register_ieee80211_hw(struct ath6kl *ar) wiphy->wowlan.pattern_min_len = 1; wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; + wiphy->max_sched_scan_ssids = 10; + ret = wiphy_register(wiphy); if (ret < 0) { ath6kl_err("couldn't register wiphy device\n"); @@ -2541,6 +2752,9 @@ static int ath6kl_init_if_data(struct ath6kl_vif *vif) 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); diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index 59fa9d859def..81f20a572315 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -20,7 +20,8 @@ enum ath6kl_cfg_suspend_mode { ATH6KL_CFG_SUSPEND_DEEPSLEEP, ATH6KL_CFG_SUSPEND_CUTPOWER, - ATH6KL_CFG_SUSPEND_WOW + ATH6KL_CFG_SUSPEND_WOW, + ATH6KL_CFG_SUSPEND_SCHED_SCAN, }; struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, @@ -52,6 +53,7 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, int ath6kl_cfg80211_resume(struct ath6kl *ar); -void ath6kl_cfg80211_stop(struct ath6kl *ar); +void ath6kl_cfg80211_stop(struct ath6kl_vif *vif); +void ath6kl_cfg80211_stop_all(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 41e465f29e63..bfd6597763da 100644 --- a/drivers/net/wireless/ath/ath6kl/common.h +++ b/drivers/net/wireless/ath/ath6kl/common.h @@ -71,6 +71,7 @@ enum crypto_type { WEP_CRYPT = 0x02, TKIP_CRYPT = 0x04, AES_CRYPT = 0x08, + WAPI_CRYPT = 0x10, }; struct htc_endpoint_credit_dist; diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index e7e095e536a7..c863a28f2e0c 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -70,10 +70,20 @@ 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, /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, @@ -88,37 +98,47 @@ struct ath6kl_fw_ie { }; /* AR6003 1.0 definitions */ -#define AR6003_REV1_VERSION 0x300002ba +#define AR6003_HW_1_0_VERSION 0x300002ba /* AR6003 2.0 definitions */ -#define AR6003_REV2_VERSION 0x30000384 -#define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS 0x57e910 -#define AR6003_REV2_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77" -#define AR6003_REV2_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77" -#define AR6003_REV2_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athtcmd_ram.bin" -#define AR6003_REV2_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin" -#define AR6003_REV2_FIRMWARE_2_FILE "ath6k/AR6003/hw2.0/fw-2.bin" -#define AR6003_REV2_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin" -#define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin" +#define AR6003_HW_2_0_VERSION 0x30000384 +#define AR6003_HW_2_0_PATCH_DOWNLOAD_ADDRESS 0x57e910 +#define AR6003_HW_2_0_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77" +#define AR6003_HW_2_0_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77" +#define AR6003_HW_2_0_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athtcmd_ram.bin" +#define AR6003_HW_2_0_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin" +#define AR6003_HW_2_0_FIRMWARE_2_FILE "ath6k/AR6003/hw2.0/fw-2.bin" +#define AR6003_HW_2_0_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin" +#define AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6003/hw2.0/bdata.SD31.bin" /* AR6003 3.0 definitions */ -#define AR6003_REV3_VERSION 0x30000582 -#define AR6003_REV3_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin" -#define AR6003_REV3_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin" -#define AR6003_REV3_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin" -#define AR6003_REV3_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin" -#define AR6003_REV3_FIRMWARE_2_FILE "ath6k/AR6003/hw2.1.1/fw-2.bin" -#define AR6003_REV3_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin" -#define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \ - "ath6k/AR6003/hw2.1.1/bdata.SD31.bin" +#define AR6003_HW_2_1_1_VERSION 0x30000582 +#define AR6003_HW_2_1_1_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin" +#define AR6003_HW_2_1_1_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin" +#define AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE \ + "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin" +#define AR6003_HW_2_1_1_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin" +#define AR6003_HW_2_1_1_FIRMWARE_2_FILE "ath6k/AR6003/hw2.1.1/fw-2.bin" +#define AR6003_HW_2_1_1_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin" +#define AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6003/hw2.1.1/bdata.SD31.bin" /* AR6004 1.0 definitions */ -#define AR6004_REV1_VERSION 0x30000623 -#define AR6004_REV1_FIRMWARE_FILE "ath6k/AR6004/hw6.1/fw.ram.bin" -#define AR6004_REV1_FIRMWARE_2_FILE "ath6k/AR6004/hw6.1/fw-2.bin" -#define AR6004_REV1_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.bin" -#define AR6004_REV1_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.DB132.bin" -#define AR6004_REV1_EPPING_FIRMWARE_FILE "ath6k/AR6004/hw6.1/endpointping.bin" +#define AR6004_HW_1_0_VERSION 0x30000623 +#define AR6004_HW_1_0_FIRMWARE_2_FILE "ath6k/AR6004/hw1.0/fw-2.bin" +#define AR6004_HW_1_0_FIRMWARE_FILE "ath6k/AR6004/hw1.0/fw.ram.bin" +#define AR6004_HW_1_0_BOARD_DATA_FILE "ath6k/AR6004/hw1.0/bdata.bin" +#define AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6004/hw1.0/bdata.DB132.bin" + +/* AR6004 1.1 definitions */ +#define AR6004_HW_1_1_VERSION 0x30000001 +#define AR6004_HW_1_1_FIRMWARE_2_FILE "ath6k/AR6004/hw1.1/fw-2.bin" +#define AR6004_HW_1_1_FIRMWARE_FILE "ath6k/AR6004/hw1.1/fw.ram.bin" +#define AR6004_HW_1_1_BOARD_DATA_FILE "ath6k/AR6004/hw1.1/bdata.bin" +#define AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6004/hw1.1/bdata.DB132.bin" /* Per STA data, used in AP mode */ #define STA_PS_AWAKE BIT(0) @@ -272,6 +292,8 @@ struct ath6kl_bmi { u32 cmd_credits; bool done_sent; u8 *cmd_buf; + u32 max_data_size; + u32 max_cmd_size; }; struct target_stats { @@ -381,7 +403,16 @@ struct ath6kl_req_key { u8 key_len; }; -#define MAX_NUM_VIF 1 +enum ath6kl_hif_type { + ATH6KL_HIF_TYPE_SDIO, + ATH6KL_HIF_TYPE_USB, +}; + +/* + * Driver's maximum limit, note that some firmwares support only one vif + * and the runtime (current) limit must be checked from ar->vif_max. + */ +#define ATH6KL_VIF_MAX 3 /* vif flags info */ enum ath6kl_vif_state { @@ -424,7 +455,10 @@ struct ath6kl_vif { 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 timer_list disconnect_timer; + struct timer_list sched_scan_timer; + struct cfg80211_scan_request *scan_req; enum sme_state sme_state; int reconnect_flag; @@ -442,6 +476,8 @@ struct ath6kl_vif { #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, @@ -460,6 +496,7 @@ enum ath6kl_state { ATH6KL_STATE_DEEPSLEEP, ATH6KL_STATE_CUTPOWER, ATH6KL_STATE_WOW, + ATH6KL_STATE_SCHED_SCAN, }; struct ath6kl { @@ -474,11 +511,13 @@ struct ath6kl { int tx_pending[ENDPOINT_MAX]; int total_tx_data_pend; struct htc_target *htc_target; + enum ath6kl_hif_type hif_type; void *hif_priv; struct list_head vif_list; /* Lock to avoid race in vif_list entries among add/del/traverse */ spinlock_t list_lock; u8 num_vif; + unsigned int vif_max; u8 max_norm_iface; u8 avail_idx_map; spinlock_t lock; @@ -517,7 +556,6 @@ struct ath6kl { struct list_head amsdu_rx_buffer_queue; u8 rx_meta_ver; 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 { @@ -525,12 +563,25 @@ struct ath6kl { 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; + + const char *fw_otp; + const char *fw; + const char *fw_tcmd; + const char *fw_patch; + const char *fw_api2; + const char *fw_board; + const char *fw_default_board; } hw; u16 conf_flags; @@ -583,7 +634,7 @@ struct ath6kl { #endif /* CONFIG_ATH6KL_DEBUG */ }; -static inline void *ath6kl_priv(struct net_device *dev) +static inline struct ath6kl *ath6kl_priv(struct net_device *dev) { return ((struct ath6kl_vif *) netdev_priv(dev))->ar; } diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 9eff0d010bb1..eb808b46f94c 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -1551,10 +1551,10 @@ static ssize_t ath6kl_listen_int_read(struct file *file, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; - char buf[16]; + char buf[32]; int len; - len = snprintf(buf, sizeof(buf), "%u %u\n", ar->listen_intvl_t, + len = scnprintf(buf, sizeof(buf), "%u %u\n", ar->listen_intvl_t, ar->listen_intvl_b); return simple_read_from_buffer(user_buf, count, ppos, buf, len); diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 9853c9c125c1..e569c652e35c 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -41,6 +41,7 @@ enum ATH6K_DEBUG_MASK { ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */ ATH6KL_DBG_WMI_DUMP = BIT(19), ATH6KL_DBG_SUSPEND = BIT(20), + ATH6KL_DBG_USB = BIT(21), ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h index eed22870448b..2fe1dadfc77a 100644 --- a/drivers/net/wireless/ath/ath6kl/hif-ops.h +++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h @@ -91,6 +91,36 @@ static inline int ath6kl_hif_suspend(struct ath6kl *ar, return ar->hif_ops->suspend(ar, wow); } +/* + * Read from the ATH6KL through its diagnostic window. No cooperation from + * the Target is required for this. + */ +static inline int ath6kl_hif_diag_read32(struct ath6kl *ar, u32 address, + u32 *value) +{ + return ar->hif_ops->diag_read32(ar, address, value); +} + +/* + * Write to the ATH6KL through its diagnostic window. No cooperation from + * the Target is required for this. + */ +static inline int ath6kl_hif_diag_write32(struct ath6kl *ar, u32 address, + __le32 value) +{ + return ar->hif_ops->diag_write32(ar, address, value); +} + +static inline int ath6kl_hif_bmi_read(struct ath6kl *ar, u8 *buf, u32 len) +{ + return ar->hif_ops->bmi_read(ar, buf, len); +} + +static inline int ath6kl_hif_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) +{ + return ar->hif_ops->bmi_write(ar, buf, len); +} + static inline int ath6kl_hif_resume(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif resume\n"); diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index e57da35e59fa..0772ef650174 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c @@ -689,6 +689,11 @@ int ath6kl_hif_setup(struct ath6kl_device *dev) ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n", dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr); + /* usb doesn't support enabling interrupts */ + /* FIXME: remove check once USB support is implemented */ + if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) + return 0; + status = ath6kl_hif_disable_intrs(dev); fail_setup: diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h index f2dc3bcdae4a..699a036f3a44 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.h +++ b/drivers/net/wireless/ath/ath6kl/hif.h @@ -35,6 +35,7 @@ #define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024) #define MANUFACTURER_ID_AR6003_BASE 0x300 +#define MANUFACTURER_ID_AR6004_BASE 0x400 /* SDIO manufacturer ID and Codes */ #define MANUFACTURER_ID_ATH6KL_BASE_MASK 0xFF00 #define MANUFACTURER_CODE 0x271 /* Atheros */ @@ -244,6 +245,10 @@ struct ath6kl_hif_ops { void (*cleanup_scatter)(struct ath6kl *ar); int (*suspend)(struct ath6kl *ar, struct cfg80211_wowlan *wow); int (*resume)(struct ath6kl *ar); + int (*diag_read32)(struct ath6kl *ar, u32 address, u32 *value); + int (*diag_write32)(struct ath6kl *ar, u32 address, __le32 value); + int (*bmi_read)(struct ath6kl *ar, u8 *buf, u32 len); + int (*bmi_write)(struct ath6kl *ar, u8 *buf, u32 len); int (*power_on)(struct ath6kl *ar); int (*power_off)(struct ath6kl *ar); void (*stop)(struct ath6kl *ar); diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index f3b63ca25c7e..b01702258faf 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -2543,6 +2543,12 @@ int ath6kl_htc_wait_target(struct htc_target *target) struct htc_service_connect_resp resp; int status; + /* FIXME: remove once USB support is implemented */ + if (target->dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) { + ath6kl_err("HTC doesn't support USB yet. Patience!\n"); + return -EOPNOTSUPP; + } + /* we should be getting 1 control message that the target is ready */ packet = htc_wait_for_ctrl_msg(target); @@ -2772,7 +2778,9 @@ void ath6kl_htc_cleanup(struct htc_target *target) { struct htc_packet *packet, *tmp_packet; - ath6kl_hif_cleanup_scatter(target->dev->ar); + /* FIXME: remove check once USB support is implemented */ + if (target->dev->ar->hif_type != ATH6KL_HIF_TYPE_USB) + ath6kl_hif_cleanup_scatter(target->dev->ar); list_for_each_entry_safe(packet, tmp_packet, &target->free_ctrl_txbuf, list) { diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 30050af9d4c6..368ecbd172a3 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -33,6 +33,80 @@ module_param(debug_mask, uint, 0644); module_param(testmode, uint, 0644); module_param(suspend_cutpower, bool, 0444); +static const struct ath6kl_hw hw_list[] = { + { + .id = AR6003_HW_2_0_VERSION, + .name = "ar6003 hw 2.0", + .dataset_patch_addr = 0x57e884, + .app_load_addr = 0x543180, + .board_ext_data_addr = 0x57e500, + .reserved_ram_size = 6912, + .refclk_hz = 26000000, + .uarttx_pin = 8, + + /* hw2.0 needs override address hardcoded */ + .app_start_override_addr = 0x944C00, + + .fw_otp = AR6003_HW_2_0_OTP_FILE, + .fw = AR6003_HW_2_0_FIRMWARE_FILE, + .fw_tcmd = AR6003_HW_2_0_TCMD_FIRMWARE_FILE, + .fw_patch = AR6003_HW_2_0_PATCH_FILE, + .fw_api2 = AR6003_HW_2_0_FIRMWARE_2_FILE, + .fw_board = AR6003_HW_2_0_BOARD_DATA_FILE, + .fw_default_board = AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE, + }, + { + .id = AR6003_HW_2_1_1_VERSION, + .name = "ar6003 hw 2.1.1", + .dataset_patch_addr = 0x57ff74, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x542330, + .reserved_ram_size = 512, + .refclk_hz = 26000000, + .uarttx_pin = 8, + + .fw_otp = AR6003_HW_2_1_1_OTP_FILE, + .fw = AR6003_HW_2_1_1_FIRMWARE_FILE, + .fw_tcmd = AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE, + .fw_patch = AR6003_HW_2_1_1_PATCH_FILE, + .fw_api2 = AR6003_HW_2_1_1_FIRMWARE_2_FILE, + .fw_board = AR6003_HW_2_1_1_BOARD_DATA_FILE, + .fw_default_board = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE, + }, + { + .id = AR6004_HW_1_0_VERSION, + .name = "ar6004 hw 1.0", + .dataset_patch_addr = 0x57e884, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 19456, + .board_addr = 0x433900, + .refclk_hz = 26000000, + .uarttx_pin = 11, + + .fw = AR6004_HW_1_0_FIRMWARE_FILE, + .fw_api2 = AR6004_HW_1_0_FIRMWARE_2_FILE, + .fw_board = AR6004_HW_1_0_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE, + }, + { + .id = AR6004_HW_1_1_VERSION, + .name = "ar6004 hw 1.1", + .dataset_patch_addr = 0x57e884, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 11264, + .board_addr = 0x43d400, + .refclk_hz = 40000000, + .uarttx_pin = 11, + + .fw = AR6004_HW_1_1_FIRMWARE_FILE, + .fw_api2 = AR6004_HW_1_1_FIRMWARE_2_FILE, + .fw_board = AR6004_HW_1_1_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE, + }, +}; + /* * Include definitions here that can be used to tune the WLAN module * behavior. Different customers can tune the behavior as per their needs, @@ -58,7 +132,6 @@ module_param(suspend_cutpower, bool, 0444); */ #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) @@ -348,11 +421,7 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) status = -EIO; } - /* - * FIXME: Make sure p2p configurations are not applied to - * non-p2p capable interfaces when multivif support is enabled. - */ - if (ar->p2p) { + 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 | @@ -365,11 +434,7 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) } } - /* - * FIXME: Make sure p2p configurations are not applied to - * non-p2p capable interfaces when multivif support is enabled. - */ - 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, idx, true); if (ret) { @@ -385,7 +450,7 @@ int ath6kl_configure_target(struct ath6kl *ar) { u32 param, ram_reserved_size; u8 fw_iftype, fw_mode = 0, fw_submode = 0; - int i; + int i, status; /* * Note: Even though the firmware interface type is @@ -397,7 +462,7 @@ int ath6kl_configure_target(struct ath6kl *ar) */ fw_iftype = HI_OPTION_FW_MODE_BSS_STA; - for (i = 0; i < MAX_NUM_VIF; i++) + for (i = 0; i < ar->vif_max; i++) fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); /* @@ -411,15 +476,11 @@ int ath6kl_configure_target(struct ath6kl *ar) fw_submode |= HI_OPTION_FW_SUBMODE_NONE << (i * HI_OPTION_FW_SUBMODE_BITS); - for (i = ar->max_norm_iface; i < MAX_NUM_VIF; i++) + for (i = ar->max_norm_iface; i < ar->vif_max; i++) fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << (i * HI_OPTION_FW_SUBMODE_BITS); - /* - * FIXME: This needs to be removed once the multivif - * support is enabled. - */ - if (ar->p2p) + if (ar->p2p && ar->vif_max == 1) fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; param = HTC_PROTOCOL_VERSION; @@ -442,7 +503,7 @@ int ath6kl_configure_target(struct ath6kl *ar) return -EIO; } - param |= (MAX_NUM_VIF << HI_OPTION_NUM_DEV_SHIFT); + param |= (ar->vif_max << HI_OPTION_NUM_DEV_SHIFT); param |= fw_mode << HI_OPTION_FW_MODE_SHIFT; param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT; @@ -491,6 +552,24 @@ int ath6kl_configure_target(struct ath6kl *ar) /* use default number of control buffers */ return -EIO; + /* Configure GPIO AR600x UART */ + param = ar->hw.uarttx_pin; + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_dbg_uart_txpin)), + (u8 *)¶m, 4); + if (status) + return status; + + /* Configure target refclk_hz */ + param = ar->hw.refclk_hz; + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_refclk_hz)), + (u8 *)¶m, 4); + if (status) + return status; + return 0; } @@ -550,11 +629,11 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, static const char *get_target_ver_dir(const struct ath6kl *ar) { switch (ar->version.target_ver) { - case AR6003_REV1_VERSION: + case AR6003_HW_1_0_VERSION: return "ath6k/AR6003/hw1.0"; - case AR6003_REV2_VERSION: + case AR6003_HW_2_0_VERSION: return "ath6k/AR6003/hw2.0"; - case AR6003_REV3_VERSION: + case AR6003_HW_2_1_1_VERSION: return "ath6k/AR6003/hw2.1.1"; } ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__, @@ -612,17 +691,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); @@ -640,17 +712,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); @@ -674,19 +736,14 @@ static int ath6kl_fetch_otp_file(struct ath6kl *ar) if (ar->fw_otp != NULL) return 0; - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_OTP_FILE; - break; - case AR6004_REV1_VERSION: - ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n"); + if (ar->hw.fw_otp == NULL) { + ath6kl_dbg(ATH6KL_DBG_BOOT, + "no OTP file configured for this hw\n"); return 0; - break; - default: - filename = AR6003_REV3_OTP_FILE; - break; } + filename = ar->hw.fw_otp; + ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, &ar->fw_otp_len); if (ret) { @@ -707,38 +764,22 @@ static int ath6kl_fetch_fw_file(struct ath6kl *ar) return 0; if (testmode) { - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_TCMD_FIRMWARE_FILE; - break; - case AR6003_REV3_VERSION: - filename = AR6003_REV3_TCMD_FIRMWARE_FILE; - break; - case AR6004_REV1_VERSION: - ath6kl_warn("testmode not supported with ar6004\n"); + if (ar->hw.fw_tcmd == NULL) { + ath6kl_warn("testmode not supported\n"); return -EOPNOTSUPP; - default: - ath6kl_warn("unknown target version: 0x%x\n", - ar->version.target_ver); - return -EINVAL; } + filename = ar->hw.fw_tcmd; + set_bit(TESTMODE, &ar->flag); goto get_fw; } - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_FIRMWARE_FILE; - break; - case AR6004_REV1_VERSION: - filename = AR6004_REV1_FIRMWARE_FILE; - break; - default: - filename = AR6003_REV3_FIRMWARE_FILE; - break; - } + if (WARN_ON(ar->hw.fw == NULL)) + return -EINVAL; + + filename = ar->hw.fw; get_fw: ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); @@ -756,27 +797,20 @@ static int ath6kl_fetch_patch_file(struct ath6kl *ar) const char *filename; int ret; - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_PATCH_FILE; - break; - case AR6004_REV1_VERSION: - /* FIXME: implement for AR6004 */ + if (ar->fw_patch != NULL) return 0; - break; - default: - filename = AR6003_REV3_PATCH_FILE; - break; - } - if (ar->fw_patch == NULL) { - ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, - &ar->fw_patch_len); - if (ret) { - ath6kl_err("Failed to get patch file %s: %d\n", - filename, ret); - return ret; - } + if (ar->hw.fw_patch == NULL) + return 0; + + filename = ar->hw.fw_patch; + + ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, + &ar->fw_patch_len); + if (ret) { + ath6kl_err("Failed to get patch file %s: %d\n", + filename, ret); + return ret; } return 0; @@ -811,19 +845,10 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) int ret, ie_id, i, index, bit; __le32 *val; - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_FIRMWARE_2_FILE; - break; - case AR6003_REV3_VERSION: - filename = AR6003_REV3_FIRMWARE_2_FILE; - break; - case AR6004_REV1_VERSION: - filename = AR6004_REV1_FIRMWARE_2_FILE; - break; - default: + if (ar->hw.fw_api2 == NULL) return -EOPNOTSUPP; - } + + filename = ar->hw.fw_api2; ret = request_firmware(&fw, filename, ar->dev); if (ret) @@ -913,12 +938,15 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) ar->hw.reserved_ram_size); break; case ATH6KL_FW_IE_CAPABILITIES: + if (ie_len < DIV_ROUND_UP(ATH6KL_FW_CAPABILITY_MAX, 8)) + break; + ath6kl_dbg(ATH6KL_DBG_BOOT, "found firmware capabilities ie (%zd B)\n", ie_len); for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { - index = ALIGN(i, 8) / 8; + index = i / 8; bit = i % 8; if (data[index] & (1 << bit)) @@ -937,9 +965,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)); @@ -994,8 +1047,8 @@ 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; + if (ar->hw.board_addr != 0) { + board_address = ar->hw.board_addr; ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_board_data)), @@ -1013,7 +1066,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) HI_ITEM(hi_board_ext_data)), (u8 *) &board_ext_address, 4); - if (board_ext_address == 0) { + if (ar->target_type == TARGET_TYPE_AR6003 && + board_ext_address == 0) { ath6kl_err("Failed to get board file target address.\n"); return -EINVAL; } @@ -1033,8 +1087,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, @@ -1092,8 +1146,8 @@ static int ath6kl_upload_otp(struct ath6kl *ar) bool from_hw = false; int ret; - if (WARN_ON(ar->fw_otp == NULL)) - return -ENOENT; + if (ar->fw_otp == NULL) + return 0; address = ar->hw.app_load_addr; @@ -1142,7 +1196,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; @@ -1172,8 +1226,8 @@ static int ath6kl_upload_patch(struct ath6kl *ar) u32 address, param; int ret; - if (WARN_ON(ar->fw_patch == NULL)) - return -ENOENT; + if (ar->fw_patch == NULL) + return 0; address = ar->hw.dataset_patch_addr; @@ -1258,7 +1312,7 @@ 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->version.target_ver == AR6003_HW_2_0_VERSION) { ath6kl_err("temporary war to avoid sdio crc error\n"); param = 0x20; @@ -1315,47 +1369,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 *)¶m, 4); - return status; } static int ath6kl_init_hw_params(struct ath6kl *ar) { - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; - ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS; - ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; - ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE; + const struct ath6kl_hw *hw; + int i; - /* hw2.0 needs override address hardcoded */ - ar->hw.app_start_override_addr = 0x944C00; + for (i = 0; i < ARRAY_SIZE(hw_list); i++) { + hw = &hw_list[i]; - break; - case AR6003_REV3_VERSION: - ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS; - ar->hw.app_load_addr = 0x1234; - ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; - ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE; - break; - case AR6004_REV1_VERSION: - ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; - ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS; - ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS; - ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE; - break; - default: + if (hw->id == ar->version.target_ver) + break; + } + + if (i == ARRAY_SIZE(hw_list)) { ath6kl_err("Unsupported hardware version: 0x%x\n", ar->version.target_ver); return -EINVAL; } + ar->hw = *hw; + ath6kl_dbg(ATH6KL_DBG_BOOT, "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n", ar->version.target_ver, ar->target_type, @@ -1364,10 +1400,25 @@ 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 const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type) +{ + switch (type) { + case ATH6KL_HIF_TYPE_SDIO: + return "sdio"; + case ATH6KL_HIF_TYPE_USB: + return "usb"; + } + + return NULL; +} + int ath6kl_init_hw_start(struct ath6kl *ar) { long timeleft; @@ -1428,6 +1479,15 @@ int ath6kl_init_hw_start(struct ath6kl *ar) ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); + + if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { + ath6kl_info("%s %s fw %s%s\n", + ar->hw.name, + ath6kl_init_get_hif_name(ar->hif_type), + ar->wiphy->fw_version, + test_bit(TESTMODE, &ar->flag) ? " testmode" : ""); + } + if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", ATH6KL_ABI_VERSION, ar->version.abi_ver); @@ -1448,7 +1508,7 @@ int ath6kl_init_hw_start(struct ath6kl *ar) if ((ath6kl_set_host_app_area(ar)) != 0) ath6kl_err("unable to set the host app area\n"); - for (i = 0; i < MAX_NUM_VIF; i++) { + for (i = 0; i < ar->vif_max; i++) { ret = ath6kl_target_config_wlan_params(ar, i); if (ret) goto err_htc_stop; @@ -1558,7 +1618,7 @@ int ath6kl_core_init(struct ath6kl *ar) goto err_node_cleanup; } - for (i = 0; i < MAX_NUM_VIF; i++) + for (i = 0; i < ar->vif_max; i++) ar->avail_idx_map |= BIT(i); rtnl_lock(); @@ -1603,7 +1663,17 @@ int ath6kl_core_init(struct ath6kl *ar) ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | WIPHY_FLAG_HAVE_AP_SME | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + + if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) + ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + + ar->wiphy->probe_resp_offload = + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U; set_bit(FIRST_BOOT, &ar->flag); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 5e5f4ca8f3f0..eea3c747653e 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -175,64 +175,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. @@ -241,14 +183,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); @@ -266,18 +201,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) @@ -465,7 +397,9 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) case NONE_AUTH: if (vif->prwise_crypto == WEP_CRYPT) ath6kl_install_static_wep_keys(vif); - break; + 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): @@ -534,6 +468,18 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, 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]; } @@ -581,20 +527,6 @@ void ath6kl_disconnect(struct ath6kl_vif *vif) /* WMI Event handlers */ -static const char *get_hw_id_string(u32 id) -{ - switch (id) { - case AR6003_REV1_VERSION: - return "1.0"; - case AR6003_REV2_VERSION: - return "2.0"; - case AR6003_REV3_VERSION: - return "2.1.1"; - default: - return "unknown"; - } -} - void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) { struct ath6kl *ar = devt; @@ -617,13 +549,6 @@ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) /* indicate to the waiting thread that the ready event was received */ set_bit(WMI_READY, &ar->flag); wake_up(&ar->event_wq); - - if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { - ath6kl_info("hw %s fw %s%s\n", - get_hw_id_string(ar->wiphy->hw_version), - ar->wiphy->fw_version, - test_bit(TESTMODE, &ar->flag) ? " testmode" : ""); - } } void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) @@ -1077,21 +1002,11 @@ static int ath6kl_open(struct net_device *dev) 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(vif); - - if (test_bit(WMI_READY, &ar->flag)) { - if (ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0xFFFF, - 0, 0, 0, 0, 0, 0, 0, 0, 0)) - return -EIO; - - } - - ath6kl_cfg80211_scan_complete_event(vif, true); + ath6kl_cfg80211_stop(vif); clear_bit(WLAN_ENABLED, &vif->flags); diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index e69ca5ee5bb1..15c3f56caf4f 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -40,8 +40,12 @@ 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; @@ -396,6 +400,7 @@ 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); bounced = true; @@ -406,6 +411,9 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, if ((request & HIF_READ) && bounced) memcpy(buf, tbuf, len); + if (bounced) + mutex_unlock(&ar_sdio->dma_buffer_mutex); + return ret; } @@ -799,7 +807,28 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) return ret; } - if ((flags & MMC_PM_WAKE_SDIO_IRQ) && wow) { + if (!(flags & MMC_PM_WAKE_SDIO_IRQ)) + goto deepsleep; + + /* sdio irq wakes up host */ + + if (ar->state == ATH6KL_STATE_SCHED_SCAN) { + ret = ath6kl_cfg80211_suspend(ar, + ATH6KL_CFG_SUSPEND_SCHED_SCAN, + NULL); + if (ret) { + ath6kl_warn("Schedule scan suspend failed: %d", ret); + return ret; + } + + ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); + if (ret) + ath6kl_warn("set sdio wake irq flag failed: %d\n", ret); + + return ret; + } + + if (wow) { /* * The host sdio controller is capable of keep power and * sdio irq wake up at this point. It's fine to continue @@ -816,6 +845,7 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) return ret; } +deepsleep: return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL); } @@ -839,6 +869,8 @@ static int ath6kl_sdio_resume(struct ath6kl *ar) case ATH6KL_STATE_WOW: break; + case ATH6KL_STATE_SCHED_SCAN: + break; } ath6kl_cfg80211_resume(ar); @@ -846,6 +878,264 @@ static int ath6kl_sdio_resume(struct ath6kl *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; +} + +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); @@ -890,6 +1180,10 @@ static const struct ath6kl_hif_ops ath6kl_sdio_ops = { .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, @@ -958,6 +1252,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); @@ -976,8 +1271,10 @@ 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); @@ -1027,13 +1324,15 @@ 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))}, {}, }; MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices); static struct sdio_driver ath6kl_sdio_driver = { - .name = "ath6kl", + .name = "ath6kl_sdio", .id_table = ath6kl_sdio_devices, .probe = ath6kl_sdio_probe, .remove = ath6kl_sdio_remove, @@ -1063,13 +1362,19 @@ 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_OTP_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_PATCH_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_OTP_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_PATCH_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index 687e2b350e8f..108a723a1085 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -20,7 +20,7 @@ #define AR6003_BOARD_DATA_SZ 1024 #define AR6003_BOARD_EXT_DATA_SZ 768 -#define AR6004_BOARD_DATA_SZ 7168 +#define AR6004_BOARD_DATA_SZ 6144 #define AR6004_BOARD_EXT_DATA_SZ 0 #define RESET_CONTROL_ADDRESS 0x00000000 @@ -334,20 +334,6 @@ 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/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index d9cff2b950b1..506a3031a885 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -453,11 +453,11 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, set_bit(WMI_CTRL_EP_FULL, &ar->flag); spin_unlock_bh(&ar->lock); ath6kl_err("wmi ctrl ep is full\n"); - goto stop_adhoc_netq; + return action; } if (packet->info.tx.tag == ATH6KL_CONTROL_PKT_TAG) - goto stop_adhoc_netq; + return action; /* * The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for @@ -465,20 +465,18 @@ 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 <= MAX_HI_COOKIE_NUM) /* * Give preference to the highest priority stream by * dropping the packets which overflowed. */ action = HTC_SEND_FULL_DROP; - goto stop_adhoc_netq; - } -stop_adhoc_netq: /* FIXME: Locking */ spin_lock_bh(&ar->list_lock); list_for_each_entry(vif, &ar->vif_list, list) { - if (vif->nw_type == ADHOC_NETWORK) { + if (vif->nw_type == ADHOC_NETWORK || + action != HTC_SEND_FULL_DROP) { spin_unlock_bh(&ar->list_lock); spin_lock_bh(&vif->if_lock); @@ -543,7 +541,7 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue) int status; enum htc_endpoint_id eid; bool wake_event = false; - bool flushing[MAX_NUM_VIF] = {false}; + bool flushing[ATH6KL_VIF_MAX] = {false}; u8 if_idx; struct ath6kl_vif *vif; @@ -571,8 +569,6 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue) if (!skb || !skb->data) goto fatal; - packet->buf = skb->data; - __skb_queue_tail(&skb_queue, skb); if (!status && (packet->act_len != skb->len)) @@ -593,10 +589,10 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue) if (eid == ar->ctrl_ep) { if_idx = wmi_cmd_hdr_get_if_idx( - (struct wmi_cmd_hdr *) skb->data); + (struct wmi_cmd_hdr *) packet->buf); } else { if_idx = wmi_data_hdr_get_if_idx( - (struct wmi_data_hdr *) skb->data); + (struct wmi_data_hdr *) packet->buf); } vif = ath6kl_get_vif_by_index(ar, if_idx); diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c new file mode 100644 index 000000000000..e3cf397fcafe --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2007-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/usb.h> + +#include "debug.h" +#include "core.h" + +/* usb device object */ +struct ath6kl_usb { + struct usb_device *udev; + struct usb_interface *interface; + u8 *diag_cmd_buffer; + u8 *diag_resp_buffer; + struct ath6kl *ar; +}; + +/* diagnostic command defnitions */ +#define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD 1 +#define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP 2 +#define ATH6KL_USB_CONTROL_REQ_DIAG_CMD 3 +#define ATH6KL_USB_CONTROL_REQ_DIAG_RESP 4 + +#define ATH6KL_USB_CTRL_DIAG_CC_READ 0 +#define ATH6KL_USB_CTRL_DIAG_CC_WRITE 1 + +struct ath6kl_usb_ctrl_diag_cmd_write { + __le32 cmd; + __le32 address; + __le32 value; + __le32 _pad[1]; +} __packed; + +struct ath6kl_usb_ctrl_diag_cmd_read { + __le32 cmd; + __le32 address; +} __packed; + +struct ath6kl_usb_ctrl_diag_resp_read { + __le32 value; +} __packed; + +#define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)) +#define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read)) + +static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb) +{ + usb_set_intfdata(ar_usb->interface, NULL); + + kfree(ar_usb->diag_cmd_buffer); + kfree(ar_usb->diag_resp_buffer); + + kfree(ar_usb); +} + +static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface) +{ + struct ath6kl_usb *ar_usb = NULL; + struct usb_device *dev = interface_to_usbdev(interface); + int status = 0; + + ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL); + if (ar_usb == NULL) + goto fail_ath6kl_usb_create; + + memset(ar_usb, 0, sizeof(struct ath6kl_usb)); + usb_set_intfdata(interface, ar_usb); + ar_usb->udev = dev; + ar_usb->interface = interface; + + ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL); + if (ar_usb->diag_cmd_buffer == NULL) { + status = -ENOMEM; + goto fail_ath6kl_usb_create; + } + + ar_usb->diag_resp_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_RESP, + GFP_KERNEL); + if (ar_usb->diag_resp_buffer == NULL) { + status = -ENOMEM; + goto fail_ath6kl_usb_create; + } + +fail_ath6kl_usb_create: + if (status != 0) { + ath6kl_usb_destroy(ar_usb); + ar_usb = NULL; + } + return ar_usb; +} + +static void ath6kl_usb_device_detached(struct usb_interface *interface) +{ + struct ath6kl_usb *ar_usb; + + ar_usb = usb_get_intfdata(interface); + if (ar_usb == NULL) + return; + + ath6kl_stop_txrx(ar_usb->ar); + + ath6kl_core_cleanup(ar_usb->ar); + + ath6kl_usb_destroy(ar_usb); +} + +static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb, + u8 req, u16 value, u16 index, void *data, + u32 size) +{ + u8 *buf = NULL; + int ret; + + if (size > 0) { + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + memcpy(buf, data, size); + } + + /* note: if successful returns number of bytes transfered */ + ret = usb_control_msg(ar_usb->udev, + usb_sndctrlpipe(ar_usb->udev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, buf, + size, 1000); + + if (ret < 0) { + ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n", + __func__, ret); + } + + kfree(buf); + + return 0; +} + +static int ath6kl_usb_submit_ctrl_in(struct ath6kl_usb *ar_usb, + u8 req, u16 value, u16 index, void *data, + u32 size) +{ + u8 *buf = NULL; + int ret; + + if (size > 0) { + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + } + + /* note: if successful returns number of bytes transfered */ + ret = usb_control_msg(ar_usb->udev, + usb_rcvctrlpipe(ar_usb->udev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, buf, + size, 2 * HZ); + + if (ret < 0) { + ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n", + __func__, ret); + } + + memcpy((u8 *) data, buf, size); + + kfree(buf); + + return 0; +} + +static int ath6kl_usb_ctrl_msg_exchange(struct ath6kl_usb *ar_usb, + u8 req_val, u8 *req_buf, u32 req_len, + u8 resp_val, u8 *resp_buf, u32 *resp_len) +{ + int ret; + + /* send command */ + ret = ath6kl_usb_submit_ctrl_out(ar_usb, req_val, 0, 0, + req_buf, req_len); + + if (ret != 0) + return ret; + + if (resp_buf == NULL) { + /* no expected response */ + return ret; + } + + /* get response */ + ret = ath6kl_usb_submit_ctrl_in(ar_usb, resp_val, 0, 0, + resp_buf, *resp_len); + + return ret; +} + +static int ath6kl_usb_diag_read32(struct ath6kl *ar, u32 address, u32 *data) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + struct ath6kl_usb_ctrl_diag_resp_read *resp; + struct ath6kl_usb_ctrl_diag_cmd_read *cmd; + u32 resp_len; + int ret; + + cmd = (struct ath6kl_usb_ctrl_diag_cmd_read *) ar_usb->diag_cmd_buffer; + + memset(cmd, 0, sizeof(*cmd)); + cmd->cmd = ATH6KL_USB_CTRL_DIAG_CC_READ; + cmd->address = cpu_to_le32(address); + resp_len = sizeof(*resp); + + ret = ath6kl_usb_ctrl_msg_exchange(ar_usb, + ATH6KL_USB_CONTROL_REQ_DIAG_CMD, + (u8 *) cmd, + sizeof(struct ath6kl_usb_ctrl_diag_cmd_write), + ATH6KL_USB_CONTROL_REQ_DIAG_RESP, + ar_usb->diag_resp_buffer, &resp_len); + + if (ret) + return ret; + + resp = (struct ath6kl_usb_ctrl_diag_resp_read *) + ar_usb->diag_resp_buffer; + + *data = le32_to_cpu(resp->value); + + return ret; +} + +static int ath6kl_usb_diag_write32(struct ath6kl *ar, u32 address, __le32 data) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + struct ath6kl_usb_ctrl_diag_cmd_write *cmd; + + cmd = (struct ath6kl_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer; + + memset(cmd, 0, sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)); + cmd->cmd = cpu_to_le32(ATH6KL_USB_CTRL_DIAG_CC_WRITE); + cmd->address = cpu_to_le32(address); + cmd->value = data; + + return ath6kl_usb_ctrl_msg_exchange(ar_usb, + ATH6KL_USB_CONTROL_REQ_DIAG_CMD, + (u8 *) cmd, + sizeof(*cmd), + 0, NULL, NULL); + +} + +static int ath6kl_usb_bmi_read(struct ath6kl *ar, u8 *buf, u32 len) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + int ret; + + /* get response */ + ret = ath6kl_usb_submit_ctrl_in(ar_usb, + ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP, + 0, 0, buf, len); + if (ret != 0) { + ath6kl_err("Unable to read the bmi data from the device: %d\n", + ret); + return ret; + } + + return 0; +} + +static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + int ret; + + /* send command */ + ret = ath6kl_usb_submit_ctrl_out(ar_usb, + ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD, + 0, 0, buf, len); + if (ret != 0) { + ath6kl_err("unable to send the bmi data to the device: %d\n", + ret); + return ret; + } + + return 0; +} + +static int ath6kl_usb_power_on(struct ath6kl *ar) +{ + return 0; +} + +static int ath6kl_usb_power_off(struct ath6kl *ar) +{ + return 0; +} + +static const struct ath6kl_hif_ops ath6kl_usb_ops = { + .diag_read32 = ath6kl_usb_diag_read32, + .diag_write32 = ath6kl_usb_diag_write32, + .bmi_read = ath6kl_usb_bmi_read, + .bmi_write = ath6kl_usb_bmi_write, + .power_on = ath6kl_usb_power_on, + .power_off = ath6kl_usb_power_off, +}; + +/* ath6kl usb driver registered functions */ +static int ath6kl_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(interface); + struct ath6kl *ar; + struct ath6kl_usb *ar_usb = NULL; + int vendor_id, product_id; + int ret = 0; + + usb_get_dev(dev); + + vendor_id = le16_to_cpu(dev->descriptor.idVendor); + product_id = le16_to_cpu(dev->descriptor.idProduct); + + ath6kl_dbg(ATH6KL_DBG_USB, "vendor_id = %04x\n", vendor_id); + ath6kl_dbg(ATH6KL_DBG_USB, "product_id = %04x\n", product_id); + + if (interface->cur_altsetting) + ath6kl_dbg(ATH6KL_DBG_USB, "USB Interface %d\n", + interface->cur_altsetting->desc.bInterfaceNumber); + + + if (dev->speed == USB_SPEED_HIGH) + ath6kl_dbg(ATH6KL_DBG_USB, "USB 2.0 Host\n"); + else + ath6kl_dbg(ATH6KL_DBG_USB, "USB 1.1 Host\n"); + + ar_usb = ath6kl_usb_create(interface); + + if (ar_usb == NULL) { + ret = -ENOMEM; + goto err_usb_put; + } + + ar = ath6kl_core_alloc(&ar_usb->udev->dev); + if (ar == NULL) { + ath6kl_err("Failed to alloc ath6kl core\n"); + ret = -ENOMEM; + goto err_usb_destroy; + } + + ar->hif_priv = ar_usb; + ar->hif_type = ATH6KL_HIF_TYPE_USB; + ar->hif_ops = &ath6kl_usb_ops; + ar->mbox_info.block_size = 16; + ar->bmi.max_data_size = 252; + + ar_usb->ar = ar; + + ret = ath6kl_core_init(ar); + if (ret) { + ath6kl_err("Failed to init ath6kl core: %d\n", ret); + goto err_core_free; + } + + return ret; + +err_core_free: + ath6kl_core_free(ar); +err_usb_destroy: + ath6kl_usb_destroy(ar_usb); +err_usb_put: + usb_put_dev(dev); + + return ret; +} + +static void ath6kl_usb_remove(struct usb_interface *interface) +{ + usb_put_dev(interface_to_usbdev(interface)); + ath6kl_usb_device_detached(interface); +} + +/* table of devices that work with this driver */ +static struct usb_device_id ath6kl_usb_ids[] = { + {USB_DEVICE(0x0cf3, 0x9374)}, + { /* Terminating entry */ }, +}; + +MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids); + +static struct usb_driver ath6kl_usb_driver = { + .name = "ath6kl_usb", + .probe = ath6kl_usb_probe, + .disconnect = ath6kl_usb_remove, + .id_table = ath6kl_usb_ids, +}; + +static int ath6kl_usb_init(void) +{ + usb_register(&ath6kl_usb_driver); + return 0; +} + +static void ath6kl_usb_exit(void) +{ + usb_deregister(&ath6kl_usb_driver); +} + +module_init(ath6kl_usb_init); +module_exit(ath6kl_usb_exit); + +MODULE_AUTHOR("Atheros Communications, Inc."); +MODULE_DESCRIPTION("Driver support for Atheros AR600x USB devices"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 922344d3b262..f6f2aa27fc20 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -85,7 +85,7 @@ 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 > (MAX_NUM_VIF - 1))) + if (WARN_ON(if_idx > (ar->vif_max - 1))) return NULL; /* FIXME: Locking */ @@ -187,7 +187,7 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, struct wmi_data_hdr *data_hdr; int ret; - if (WARN_ON(skb == NULL || (if_idx > MAX_NUM_VIF - 1))) + if (WARN_ON(skb == NULL || (if_idx > wmi->parent_dev->vif_max - 1))) return -EINVAL; if (tx_meta_info) { @@ -977,6 +977,13 @@ static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len, return 0; } +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) { @@ -1066,6 +1073,21 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, return -ENOMEM; cfg80211_put_bss(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 (ar->state == ATH6KL_STATE_SCHED_SCAN && + !timer_pending(&vif->sched_scan_timer)) { + mod_timer(&vif->sched_scan_timer, jiffies + + msecs_to_jiffies(ATH6KL_SCHED_SCAN_RESULT_DELAY)); + } + return 0; } @@ -1620,7 +1642,7 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, int ret; u16 info1; - if (WARN_ON(skb == NULL || (if_idx > (MAX_NUM_VIF - 1)))) + if (WARN_ON(skb == NULL || (if_idx > (wmi->parent_dev->vif_max - 1)))) return -EINVAL; ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n", @@ -1682,7 +1704,8 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, 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; @@ -1722,6 +1745,7 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, 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); @@ -1774,6 +1798,72 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx) return ret; } +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 sk_buff *skb; + struct wmi_begin_scan_cmd *sc; + s8 size; + int i, band, ret; + struct ath6kl *ar = wmi->parent_dev; + int num_rates; + + 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++) { + struct ieee80211_supported_band *sband = + ar->wiphy->bands[band]; + u32 ratemask = rates[band]; + u8 *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; +} + +/* 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. + */ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, @@ -2940,7 +3030,10 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, 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); + + 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); } @@ -2981,6 +3074,10 @@ int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur) NO_SYNC_WMIFLAG); } +/* 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. + */ int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, u32 wait, const u8 *data, u16 data_len) { @@ -3018,14 +3115,57 @@ int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, 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) +{ + struct sk_buff *skb; + 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_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; - skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len); + if (data_len == 0) + cmd_len++; /* work around target minimum length requirement */ + + skb = ath6kl_wmi_get_new_buf(cmd_len); if (!skb) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 76342d5a1906..42ac311eda4e 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -329,6 +329,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, @@ -542,12 +546,61 @@ 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, + }; enum wmi_mgmt_frame_type { @@ -567,6 +620,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, @@ -639,6 +700,7 @@ struct wmi_connect_cmd { __le16 ch; u8 bssid[ETH_ALEN]; __le32 ctrl_flags; + u8 nw_subtype; } __packed; /* WMI_RECONNECT_CMDID */ @@ -726,7 +788,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 */ @@ -738,9 +805,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[IEEE80211_NUM_BANDS]; + /* how many channels follow */ u8 num_ch; @@ -748,8 +821,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 @@ -783,13 +879,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; @@ -1818,7 +1907,7 @@ struct wmi_set_ip_cmd { } __packed; enum ath6kl_wow_filters { - WOW_FILTER_SSID = BIT(0), + 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), @@ -1963,7 +2052,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 @@ -2046,6 +2135,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; @@ -2054,6 +2147,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; @@ -2242,7 +2344,8 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, 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); @@ -2252,6 +2355,14 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, u32 force_fgscan, u32 is_legacy, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list); + +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); + 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, @@ -2346,6 +2457,10 @@ int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, u32 wait, const u8 *data, u16 data_len); +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); @@ -2359,6 +2474,8 @@ int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx); int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); +void ath6kl_wmi_sscan_timer(unsigned long ptr); + 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); |