diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-21 08:04:47 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-21 08:04:47 +0400 |
commit | 3b59bf081622b6446db77ad06c93fe23677bc533 (patch) | |
tree | 3f4bb5a27c90cc86994a1f6d3c53fbf9208003cb /drivers/net/wireless/ath/ath6kl | |
parent | e45836fafe157df137a837093037f741ad8f4c90 (diff) | |
parent | bbdb32cb5b73597386913d052165423b9d736145 (diff) | |
download | linux-3b59bf081622b6446db77ad06c93fe23677bc533.tar.xz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking merge from David Miller:
"1) Move ixgbe driver over to purely page based buffering on receive.
From Alexander Duyck.
2) Add receive packet steering support to e1000e, from Bruce Allan.
3) Convert TCP MD5 support over to RCU, from Eric Dumazet.
4) Reduce cpu usage in handling out-of-order TCP packets on modern
systems, also from Eric Dumazet.
5) Support the IP{,V6}_UNICAST_IF socket options, making the wine
folks happy, from Erich Hoover.
6) Support VLAN trunking from guests in hyperv driver, from Haiyang
Zhang.
7) Support byte-queue-limtis in r8169, from Igor Maravic.
8) Outline code intended for IP_RECVTOS in IP_PKTOPTIONS existed but
was never properly implemented, Jiri Benc fixed that.
9) 64-bit statistics support in r8169 and 8139too, from Junchang Wang.
10) Support kernel side dump filtering by ctmark in netfilter
ctnetlink, from Pablo Neira Ayuso.
11) Support byte-queue-limits in gianfar driver, from Paul Gortmaker.
12) Add new peek socket options to assist with socket migration, from
Pavel Emelyanov.
13) Add sch_plug packet scheduler whose queue is controlled by
userland daemons using explicit freeze and release commands. From
Shriram Rajagopalan.
14) Fix FCOE checksum offload handling on transmit, from Yi Zou."
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1846 commits)
Fix pppol2tp getsockname()
Remove printk from rds_sendmsg
ipv6: fix incorrent ipv6 ipsec packet fragment
cpsw: Hook up default ndo_change_mtu.
net: qmi_wwan: fix build error due to cdc-wdm dependecy
netdev: driver: ethernet: Add TI CPSW driver
netdev: driver: ethernet: add cpsw address lookup engine support
phy: add am79c874 PHY support
mlx4_core: fix race on comm channel
bonding: send igmp report for its master
fs_enet: Add MPC5125 FEC support and PHY interface selection
net: bpf_jit: fix BPF_S_LDX_B_MSH compilation
net: update the usage of CHECKSUM_UNNECESSARY
fcoe: use CHECKSUM_UNNECESSARY instead of CHECKSUM_PARTIAL on tx
net: do not do gso for CHECKSUM_UNNECESSARY in netif_needs_gso
ixgbe: Fix issues with SR-IOV loopback when flow control is disabled
net/hyperv: Fix the code handling tx busy
ixgbe: fix namespace issues when FCoE/DCB is not enabled
rtlwifi: Remove unused ETH_ADDR_LEN defines
igbvf: Use ETH_ALEN
...
Fix up fairly trivial conflicts in drivers/isdn/gigaset/interface.c and
drivers/net/usb/{Kconfig,qmi_wwan.c} as per David.
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl')
26 files changed, 3568 insertions, 1478 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..85746c3eb027 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -1,5 +1,6 @@ #------------------------------------------------------------------------------ -# Copyright (c) 2004-2010 Atheros Communications Inc. +# Copyright (c) 2004-2011 Atheros Communications Inc. +# Copyright (c) 2011-2012 Qualcomm Atheros, Inc. # All rights reserved. # # @@ -21,17 +22,21 @@ # 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) += ath6kl_core.o +ath6kl_core-y += debug.o +ath6kl_core-y += hif.o +ath6kl_core-y += htc.o +ath6kl_core-y += bmi.o +ath6kl_core-y += cfg80211.o +ath6kl_core-y += init.o +ath6kl_core-y += main.o +ath6kl_core-y += txrx.o +ath6kl_core-y += wmi.o +ath6kl_core-y += core.o +ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o -ccflags-y += -D__CHECK_ENDIAN__ +obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o +ath6kl_sdio-y += sdio.o + +obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o +ath6kl_usb-y += usb.o diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c index bce3575c310a..334dbd834b3a 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.c +++ b/drivers/net/wireless/ath/ath6kl/bmi.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -57,8 +58,14 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, return ret; } - ret = ath6kl_hif_bmi_read(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; @@ -99,7 +106,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, } ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", - targ_info->version, targ_info->type); + targ_info->version, targ_info->type); return 0; } @@ -186,7 +193,7 @@ int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header); ath6kl_dbg(ATH6KL_DBG_BMI, - "bmi write memory: addr: 0x%x, len: %d\n", addr, len); + "bmi write memory: addr: 0x%x, len: %d\n", addr, len); len_remain = len; while (len_remain) { @@ -428,7 +435,7 @@ int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); offset += sizeof(tx_len); memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], - tx_len); + tx_len); offset += tx_len; ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); diff --git a/drivers/net/wireless/ath/ath6kl/bmi.h b/drivers/net/wireless/ath/ath6kl/bmi.h index f1ca6812456d..18fdd69e1f71 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.h +++ b/drivers/net/wireless/ath/ath6kl/bmi.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -222,6 +223,29 @@ struct ath6kl_bmi_target_info { __le32 type; /* target type */ } __packed; +#define ath6kl_bmi_write_hi32(ar, item, val) \ + ({ \ + u32 addr; \ + __le32 v; \ + \ + addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \ + v = cpu_to_le32(val); \ + ath6kl_bmi_write(ar, addr, (u8 *) &v, sizeof(v)); \ + }) + +#define ath6kl_bmi_read_hi32(ar, item, val) \ + ({ \ + u32 addr, *check_type = val; \ + __le32 tmp; \ + int ret; \ + \ + (void) (check_type == val); \ + addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \ + ret = ath6kl_bmi_read(ar, addr, (u8 *) &tmp, 4); \ + *val = le32_to_cpu(tmp); \ + ret; \ + }) + int ath6kl_bmi_init(struct ath6kl *ar); void ath6kl_bmi_cleanup(struct ath6kl *ar); void ath6kl_bmi_reset(struct ath6kl *ar); diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 6c59a217b1a1..00d38952b5fb 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,6 +16,8 @@ */ #include <linux/moduleparam.h> +#include <linux/inetdevice.h> +#include <linux/export.h> #include "core.h" #include "cfg80211.h" @@ -22,10 +25,6 @@ #include "hif-ops.h" #include "testmode.h" -static unsigned int ath6kl_p2p; - -module_param(ath6kl_p2p, uint, 0644); - #define RATETAB_ENT(_rate, _rateid, _flags) { \ .bitrate = (_rate), \ .flags = (_flags), \ @@ -70,6 +69,10 @@ static struct ieee80211_rate ath6kl_rates[] = { #define ath6kl_g_rates (ath6kl_rates + 0) #define ath6kl_g_rates_size 12 +#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ + IEEE80211_HT_CAP_SGI_20 | \ + IEEE80211_HT_CAP_SGI_40) + static struct ieee80211_channel ath6kl_2ghz_channels[] = { CHAN2G(1, 2412, 0), CHAN2G(2, 2417, 0), @@ -114,6 +117,8 @@ static struct ieee80211_supported_band ath6kl_band_2ghz = { .channels = ath6kl_2ghz_channels, .n_bitrates = ath6kl_g_rates_size, .bitrates = ath6kl_g_rates, + .ht_cap.cap = ath6kl_g_htcap, + .ht_cap.ht_supported = true, }; static struct ieee80211_supported_band ath6kl_band_5ghz = { @@ -121,6 +126,8 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = { .channels = ath6kl_5ghz_a_channels, .n_bitrates = ath6kl_a_rates_size, .bitrates = ath6kl_a_rates, + .ht_cap.cap = ath6kl_g_htcap, + .ht_cap.ht_supported = true, }; #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */ @@ -196,7 +203,7 @@ static int ath6kl_set_auth_type(struct ath6kl_vif *vif, break; default: - ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type); + ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type); return -ENOTSUPP; } @@ -383,7 +390,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type, return false; if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) && - ar->num_vif)) + ar->num_vif)) return false; if (type == NL80211_IFTYPE_STATION || @@ -409,6 +416,12 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type, return false; } +static bool ath6kl_is_tx_pending(struct ath6kl *ar) +{ + return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0; +} + + static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { @@ -416,6 +429,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct ath6kl_vif *vif = netdev_priv(dev); int status; u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE; + u16 interval; ath6kl_cfg80211_sscan_disable(vif); @@ -452,8 +466,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, * sleep until the command queue drains */ wait_event_interruptible_timeout(ar->event_wq, - ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0, - WMI_TIMEOUT); + ath6kl_is_tx_pending(ar), + WMI_TIMEOUT); if (signal_pending(current)) { ath6kl_err("cmd queue drain timeout\n"); up(&ar->sem); @@ -461,13 +475,13 @@ 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) { - up(&ar->sem); - return status; - } - } else + status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len); + if (status) { + up(&ar->sem); + return status; + } + + if (sme->ie == NULL || sme->ie_len == 0) ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG; if (test_bit(CONNECTED, &vif->flags) && @@ -523,8 +537,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, (vif->prwise_crypto == WEP_CRYPT)) { struct ath6kl_key *key = NULL; - if (sme->key_idx < WMI_MIN_KEY_INDEX || - sme->key_idx > WMI_MAX_KEY_INDEX) { + if (sme->key_idx > WMI_MAX_KEY_INDEX) { ath6kl_err("key index %d out of bounds\n", sme->key_idx); up(&ar->sem); @@ -549,7 +562,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, - ALL_BSS_FILTER, 0) != 0) { + ALL_BSS_FILTER, 0) != 0) { ath6kl_err("couldn't set bss filtering\n"); up(&ar->sem); return -EIO; @@ -571,6 +584,20 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, vif->grp_crypto_len, vif->ch_hint); vif->reconnect_flag = 0; + + if (vif->nw_type == INFRA_NETWORK) { + interval = max_t(u16, vif->listen_intvl_t, + ATH6KL_MAX_WOW_LISTEN_INTL); + status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, + interval, + 0); + if (status) { + ath6kl_err("couldn't set listen intervel\n"); + up(&ar->sem); + return status; + } + } + status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type, vif->dot11_auth_mode, vif->auth_mode, vif->prwise_crypto, @@ -593,8 +620,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, } if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) && - ((vif->auth_mode == WPA_PSK_AUTH) - || (vif->auth_mode == WPA2_PSK_AUTH))) { + ((vif->auth_mode == WPA_PSK_AUTH) || + (vif->auth_mode == WPA2_PSK_AUTH))) { mod_timer(&vif->disconnect_timer, jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL)); } @@ -605,11 +632,13 @@ 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, - enum network_type nw_type, - const u8 *bssid, - struct ieee80211_channel *chan, - const u8 *beacon_ie, size_t beacon_ie_len) +static struct cfg80211_bss * +ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, + enum network_type nw_type, + const u8 *bssid, + struct ieee80211_channel *chan, + const u8 *beacon_ie, + size_t beacon_ie_len) { struct ath6kl *ar = vif->ar; struct cfg80211_bss *bss; @@ -638,7 +667,7 @@ static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, */ ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL); if (ie == NULL) - return -ENOMEM; + return NULL; ie[0] = WLAN_EID_SSID; ie[1] = vif->ssid_len; memcpy(ie + 2, vif->ssid, vif->ssid_len); @@ -652,15 +681,9 @@ static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, "cfg80211\n", bssid); kfree(ie); } else - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss " - "entry\n"); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n"); - if (bss == NULL) - return -ENOMEM; - - cfg80211_put_bss(bss); - - return 0; + return bss; } void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, @@ -672,6 +695,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, { struct ieee80211_channel *chan; struct ath6kl *ar = vif->ar; + struct cfg80211_bss *bss; /* capinfo + listen interval */ u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16); @@ -712,8 +736,9 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, chan = ieee80211_get_channel(ar->wiphy, (int) channel); - if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info, - beacon_ie_len) < 0) { + bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, + assoc_info, beacon_ie_len); + if (!bss) { ath6kl_err("could not add cfg80211 bss entry\n"); return; } @@ -722,6 +747,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, 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); + cfg80211_put_bss(bss); return; } @@ -732,11 +758,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, assoc_req_ie, assoc_req_len, assoc_resp_ie, assoc_resp_len, WLAN_STATUS_SUCCESS, GFP_KERNEL); + cfg80211_put_bss(bss); } else if (vif->sme_state == SME_CONNECTED) { /* inform roam event to cfg80211 */ - cfg80211_roamed(vif->ndev, chan, bssid, - assoc_req_ie, assoc_req_len, - assoc_resp_ie, assoc_resp_len, GFP_KERNEL); + cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len, + assoc_resp_ie, assoc_resp_len, GFP_KERNEL); } } @@ -828,13 +854,13 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, if (vif->sme_state == SME_CONNECTING) { cfg80211_connect_result(vif->ndev, - bssid, NULL, 0, - NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); } else if (vif->sme_state == SME_CONNECTED) { cfg80211_disconnected(vif->ndev, reason, - NULL, 0, GFP_KERNEL); + NULL, 0, GFP_KERNEL); } vif->sme_state = SME_DISCONNECTED; @@ -880,19 +906,14 @@ 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, - request->ie, request->ie_len); - if (ret) { - ath6kl_err("failed to set Probe Request appie for " - "scan"); - return ret; - } + /* this also clears IE in fw if it's not set */ + ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_PROBE_REQ, + request->ie, request->ie_len); + if (ret) { + ath6kl_err("failed to set Probe Request appie for " + "scan"); + return ret; } /* @@ -921,7 +942,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, force_fg_scan = 1; if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, - ar->fw_capabilities)) { + ar->fw_capabilities)) { /* * If capable of doing P2P mgmt operations using * station interface, send additional information like @@ -930,14 +951,17 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, */ 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, + false, 0, + ATH6KL_FG_SCAN_INTERVAL, + 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); + false, 0, + ATH6KL_FG_SCAN_INTERVAL, + n_channels, channels); } if (ret) ath6kl_err("wmi_startscan_cmd failed\n"); @@ -984,6 +1008,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, struct ath6kl *ar = ath6kl_priv(ndev); struct ath6kl_vif *vif = netdev_priv(ndev); struct ath6kl_key *key = NULL; + int seq_len; u8 key_usage; u8 key_type; @@ -997,7 +1022,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, params->key); } - if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { + if (key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); @@ -1012,23 +1037,21 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, else 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 || - seq_len > sizeof(key->seq)) - return -EINVAL; - - key->key_len = params->key_len; - memcpy(key->key, params->key, key->key_len); - key->seq_len = seq_len; - memcpy(key->seq, params->seq, key->seq_len); - key->cipher = params->cipher; + seq_len = params->seq_len; + if (params->cipher == WLAN_CIPHER_SUITE_SMS4 && + seq_len > ATH6KL_KEY_SEQ_LEN) { + /* Only first half of the WPI PN is configured */ + seq_len = ATH6KL_KEY_SEQ_LEN; } + if (params->key_len > WLAN_MAX_KEY_LEN || + seq_len > sizeof(key->seq)) + return -EINVAL; + + key->key_len = params->key_len; + memcpy(key->key, params->key, key->key_len); + key->seq_len = seq_len; + memcpy(key->seq, params->seq, key->seq_len); + key->cipher = params->cipher; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: @@ -1051,9 +1074,9 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, return -ENOTSUPP; } - if (((vif->auth_mode == WPA_PSK_AUTH) - || (vif->auth_mode == WPA2_PSK_AUTH)) - && (key_usage & GROUP_USAGE)) + if (((vif->auth_mode == WPA_PSK_AUTH) || + (vif->auth_mode == WPA2_PSK_AUTH)) && + (key_usage & GROUP_USAGE)) del_timer(&vif->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, @@ -1063,7 +1086,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (vif->nw_type == AP_NETWORK && !pairwise && (key_type == TKIP_CRYPT || key_type == AES_CRYPT || - key_type == WAPI_CRYPT) && params) { + key_type == WAPI_CRYPT)) { ar->ap_mode_bkey.valid = true; ar->ap_mode_bkey.key_index = key_index; ar->ap_mode_bkey.key_type = key_type; @@ -1115,7 +1138,7 @@ static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, if (!ath6kl_cfg80211_ready(vif)) return -EIO; - if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { + if (key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); @@ -1148,7 +1171,7 @@ static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, if (!ath6kl_cfg80211_ready(vif)) return -EIO; - if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { + if (key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); @@ -1184,7 +1207,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, if (!ath6kl_cfg80211_ready(vif)) return -EIO; - if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { + if (key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); @@ -1268,7 +1291,6 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, { 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__, @@ -1285,7 +1307,7 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, case NL80211_TX_POWER_AUTOMATIC: return 0; case NL80211_TX_POWER_LIMITED: - ar->tx_pwr = ath6kl_dbm = dbm; + ar->tx_pwr = dbm; break; default: ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n", @@ -1293,7 +1315,7 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, return -EOPNOTSUPP; } - ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm); + ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm); return 0; } @@ -1354,7 +1376,7 @@ static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy, } if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx, - mode.pwr_mode) != 0) { + mode.pwr_mode) != 0) { ath6kl_err("wmi_powermode_cmd failed\n"); return -EIO; } @@ -1403,7 +1425,7 @@ static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); - ath6kl_deinit_if_data(vif); + ath6kl_cfg80211_vif_cleanup(vif); return 0; } @@ -1728,29 +1750,14 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) return 0; } -static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif, + struct cfg80211_wowlan *wow, u32 *filter) { - struct ath6kl_vif *vif; - int ret, pos, left; - u32 filter = 0; - u16 i; + int ret, pos; u8 mask[WOW_MASK_SIZE]; + u16 i; - vif = ath6kl_vif_first(ar); - if (!vif) - return -EIO; - - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - - if (!test_bit(CONNECTED, &vif->flags)) - return -EINVAL; - - /* Clear existing WOW patterns */ - for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++) - ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, - WOW_LIST_ID, i); - /* Configure new WOW patterns */ + /* Configure the patterns that we received from the user. */ for (i = 0; i < wow->n_patterns; i++) { /* @@ -1773,29 +1780,249 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) * matched from the first byte of received pkt in the firmware. */ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, - vif->fw_vif_idx, WOW_LIST_ID, - wow->patterns[i].pattern_len, - 0 /* pattern offset */, - wow->patterns[i].pattern, mask); + vif->fw_vif_idx, WOW_LIST_ID, + wow->patterns[i].pattern_len, + 0 /* pattern offset */, + wow->patterns[i].pattern, mask); if (ret) return ret; } if (wow->disconnect) - filter |= WOW_FILTER_OPTION_NWK_DISASSOC; + *filter |= WOW_FILTER_OPTION_NWK_DISASSOC; if (wow->magic_pkt) - filter |= WOW_FILTER_OPTION_MAGIC_PACKET; + *filter |= WOW_FILTER_OPTION_MAGIC_PACKET; if (wow->gtk_rekey_failure) - filter |= WOW_FILTER_OPTION_GTK_ERROR; + *filter |= WOW_FILTER_OPTION_GTK_ERROR; if (wow->eap_identity_req) - filter |= WOW_FILTER_OPTION_EAP_REQ; + *filter |= WOW_FILTER_OPTION_EAP_REQ; if (wow->four_way_handshake) - filter |= WOW_FILTER_OPTION_8021X_4WAYHS; + *filter |= WOW_FILTER_OPTION_8021X_4WAYHS; + + return 0; +} + +static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif) +{ + static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08 }; + static const u8 unicst_mask[] = { 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f }; + u8 unicst_offset = 0; + static const u8 arp_pattern[] = { 0x08, 0x06 }; + static const u8 arp_mask[] = { 0xff, 0xff }; + u8 arp_offset = 20; + static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 }; + static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 }; + u8 discvr_offset = 38; + static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ }; + static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ }; + u8 dhcp_offset = 0; + int ret; + + /* Setup unicast IP, EAPOL-like and ARP pkt pattern */ + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + sizeof(unicst_pattern), unicst_offset, + unicst_pattern, unicst_mask); + if (ret) { + ath6kl_err("failed to add WOW unicast IP pattern\n"); + return ret; + } + + /* Setup all ARP pkt pattern */ + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + sizeof(arp_pattern), arp_offset, + arp_pattern, arp_mask); + if (ret) { + ath6kl_err("failed to add WOW ARP pattern\n"); + return ret; + } + + /* + * Setup multicast pattern for mDNS 224.0.0.251, + * SSDP 239.255.255.250 and LLMNR 224.0.0.252 + */ + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + sizeof(discvr_pattern), discvr_offset, + discvr_pattern, discvr_mask); + if (ret) { + ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n"); + return ret; + } + + /* Setup all DHCP broadcast pkt pattern */ + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + sizeof(dhcp_pattern), dhcp_offset, + dhcp_pattern, dhcp_mask); + if (ret) { + ath6kl_err("failed to add WOW DHCP broadcast pattern\n"); + return ret; + } + + return 0; +} + +static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif) +{ + struct net_device *ndev = vif->ndev; + static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 }; + static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 }; + u8 discvr_offset = 38; + u8 mac_mask[ETH_ALEN]; + int ret; + + /* Setup unicast pkt pattern */ + memset(mac_mask, 0xff, ETH_ALEN); + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + ETH_ALEN, 0, ndev->dev_addr, + mac_mask); + if (ret) { + ath6kl_err("failed to add WOW unicast pattern\n"); + return ret; + } + + /* + * Setup multicast pattern for mDNS 224.0.0.251, + * SSDP 239.255.255.250 and LLMNR 224.0.0.252 + */ + if ((ndev->flags & IFF_ALLMULTI) || + (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) { + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + sizeof(discvr_pattern), discvr_offset, + discvr_pattern, discvr_mask); + if (ret) { + ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR " + "pattern\n"); + return ret; + } + } + return 0; +} + +static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + struct in_device *in_dev; + struct in_ifaddr *ifa; + struct ath6kl_vif *vif; + int ret, left; + u32 filter = 0; + u16 i, bmiss_time; + u8 index = 0; + __be32 ips[MAX_IP_ADDRS]; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (!test_bit(CONNECTED, &vif->flags)) + return -ENOTCONN; + + if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) + return -EINVAL; + + /* Clear existing WOW patterns */ + for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++) + ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, + WOW_LIST_ID, i); + + /* + * Skip the default WOW pattern configuration + * if the driver receives any WOW patterns from + * the user. + */ + if (wow) + ret = ath6kl_wow_usr(ar, vif, wow, &filter); + else if (vif->nw_type == AP_NETWORK) + ret = ath6kl_wow_ap(ar, vif); + else + ret = ath6kl_wow_sta(ar, vif); + + if (ret) + return ret; + + netif_stop_queue(vif->ndev); + + if (vif->nw_type != AP_NETWORK) { + ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_MAX_WOW_LISTEN_INTL, + 0); + if (ret) + return ret; + + /* Set listen interval x 15 times as bmiss time */ + bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15; + if (bmiss_time > ATH6KL_MAX_BMISS_TIME) + bmiss_time = ATH6KL_MAX_BMISS_TIME; + + ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx, + bmiss_time, 0); + if (ret) + return ret; + + ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + 0xFFFF, 0, 0xFFFF, 0, 0, 0, + 0, 0, 0, 0); + if (ret) + return ret; + } + + ar->state = ATH6KL_STATE_SUSPENDING; + + /* Setup own IP addr for ARP agent. */ + in_dev = __in_dev_get_rtnl(vif->ndev); + if (!in_dev) + goto skip_arp; + + ifa = in_dev->ifa_list; + memset(&ips, 0, sizeof(ips)); + + /* Configure IP addr only if IP address count < MAX_IP_ADDRS */ + while (index < MAX_IP_ADDRS && ifa) { + ips[index] = ifa->ifa_local; + ifa = ifa->ifa_next; + index++; + } + + if (ifa) { + ath6kl_err("total IP addr count is exceeding fw limit\n"); + return -EINVAL; + } + + ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]); + if (ret) { + ath6kl_err("fail to setup ip for arp agent\n"); + return ret; + } + +skip_arp: ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_WOW_MODE_ENABLE, filter, @@ -1803,11 +2030,26 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) if (ret) return ret; + clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_ASLEEP); if (ret) return ret; + left = wait_event_interruptible_timeout(ar->event_wq, + test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags), + WMI_TIMEOUT); + if (left == 0) { + ath6kl_warn("timeout, didn't get host sleep cmd " + "processed event\n"); + ret = -ETIMEDOUT; + } else if (left < 0) { + ath6kl_warn("error while waiting for host sleep cmd " + "processed event %d\n", left); + ret = left; + } + if (ar->tx_pending[ar->ctrl_ep]) { left = wait_event_interruptible_timeout(ar->event_wq, ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT); @@ -1832,15 +2074,46 @@ static int ath6kl_wow_resume(struct ath6kl *ar) if (!vif) return -EIO; + ar->state = ATH6KL_STATE_RESUMING; + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_AWAKE); - return ret; + if (ret) { + ath6kl_warn("Failed to configure host sleep mode for " + "wow resume: %d\n", ret); + ar->state = ATH6KL_STATE_WOW; + return ret; + } + + if (vif->nw_type != AP_NETWORK) { + ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + 0, 0, 0, 0, 0, 0, 3, 0, 0, 0); + if (ret) + return ret; + + ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, + vif->listen_intvl_t, 0); + if (ret) + return ret; + + ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx, + vif->bmiss_time_t, 0); + if (ret) + return ret; + } + + ar->state = ATH6KL_STATE_ON; + + netif_wake_queue(vif->ndev); + + return 0; } int ath6kl_cfg80211_suspend(struct ath6kl *ar, enum ath6kl_cfg_suspend_mode mode, struct cfg80211_wowlan *wow) { + enum ath6kl_state prev_state; int ret; switch (mode) { @@ -1851,11 +2124,14 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, /* Flush all non control pkts in TX path */ ath6kl_tx_data_cleanup(ar); + prev_state = ar->state; + ret = ath6kl_wow_suspend(ar, wow); if (ret) { - ath6kl_err("wow suspend failed: %d\n", ret); + ar->state = prev_state; return ret; } + ar->state = ATH6KL_STATE_WOW; break; @@ -1911,6 +2187,7 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, return 0; } +EXPORT_SYMBOL(ath6kl_cfg80211_suspend); int ath6kl_cfg80211_resume(struct ath6kl *ar) { @@ -1926,7 +2203,6 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) return ret; } - ar->state = ATH6KL_STATE_ON; break; case ATH6KL_STATE_DEEPSLEEP: @@ -1962,6 +2238,7 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) return 0; } +EXPORT_SYMBOL(ath6kl_cfg80211_resume); #ifdef CONFIG_PM @@ -1999,6 +2276,9 @@ static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) */ void ath6kl_check_wow_status(struct ath6kl *ar) { + if (ar->state == ATH6KL_STATE_SUSPENDING) + return; + if (ar->state == ATH6KL_STATE_WOW) ath6kl_cfg80211_resume(ar); } @@ -2014,7 +2294,18 @@ static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { - struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl_vif *vif; + + /* + * 'dev' could be NULL if a channel change is required for the hardware + * device itself, instead of a particular VIF. + * + * FIXME: To be handled properly when monitor mode is supported. + */ + if (!dev) + return -EBUSY; + + vif = netdev_priv(dev); if (!ath6kl_cfg80211_ready(vif)) return -EIO; @@ -2069,19 +2360,51 @@ static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif, return ret; } -static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info, bool add) +static int ath6kl_set_ies(struct ath6kl_vif *vif, + struct cfg80211_beacon_data *info) +{ + struct ath6kl *ar = vif->ar; + int res; + + /* this also clears IE in fw if it's not set */ + res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_BEACON, + info->beacon_ies, + info->beacon_ies_len); + if (res) + return res; + + /* this also clears IE in fw if it's not set */ + res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies, + info->proberesp_ies_len); + if (res) + return res; + + /* this also clears IE in fw if it's not set */ + res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_ASSOC_RESP, + info->assocresp_ies, + info->assocresp_ies_len); + if (res) + return res; + + return 0; +} + +static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *info) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); struct ieee80211_mgmt *mgmt; + bool hidden = false; u8 *ies; int ies_len; struct wmi_connect_cmd p; int res; int i, ret; - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); if (!ath6kl_cfg80211_ready(vif)) return -EIO; @@ -2089,31 +2412,7 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, if (vif->next_mode != AP_NETWORK) return -EOPNOTSUPP; - if (info->beacon_ies) { - res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, - WMI_FRAME_BEACON, - info->beacon_ies, - info->beacon_ies_len); - if (res) - return res; - } - if (info->proberesp_ies) { - res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies, - info->proberesp_ies_len); - if (res) - return res; - } - if (info->assocresp_ies) { - res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, - WMI_FRAME_ASSOC_RESP, - info->assocresp_ies, - info->assocresp_ies_len); - if (res) - return res; - } - - if (!add) - return 0; + res = ath6kl_set_ies(vif, &info->beacon); ar->ap_mode_bkey.valid = false; @@ -2122,20 +2421,24 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, * info->dtim_period */ - if (info->head == NULL) + if (info->beacon.head == NULL) return -EINVAL; - mgmt = (struct ieee80211_mgmt *) info->head; + mgmt = (struct ieee80211_mgmt *) info->beacon.head; ies = mgmt->u.beacon.variable; - if (ies > info->head + info->head_len) + if (ies > info->beacon.head + info->beacon.head_len) return -EINVAL; - ies_len = info->head + info->head_len - ies; + ies_len = info->beacon.head + info->beacon.head_len - ies; if (info->ssid == NULL) return -EINVAL; memcpy(vif->ssid, info->ssid, info->ssid_len); vif->ssid_len = info->ssid_len; if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE) - return -EOPNOTSUPP; /* TODO */ + hidden = true; + + res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden); + if (res) + return res; ret = ath6kl_set_auth_type(vif, info->auth_type); if (ret) @@ -2214,6 +2517,11 @@ 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); + /* Enable uAPSD support by default */ + res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true); + if (res < 0) + return res; + if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) { p.nw_subtype = SUBTYPE_P2PGO; } else { @@ -2231,19 +2539,21 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, return 0; } -static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info) +static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *beacon) { - return ath6kl_ap_beacon(wiphy, dev, info, true); -} + struct ath6kl_vif *vif = netdev_priv(dev); -static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info) -{ - return ath6kl_ap_beacon(wiphy, dev, info, false); + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (vif->next_mode != AP_NETWORK) + return -EOPNOTSUPP; + + return ath6kl_set_ies(vif, beacon); } -static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) +static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2259,6 +2569,19 @@ static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) return 0; } +static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + const u8 *addr = mac ? mac : bcast_addr; + + return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH, + addr, WLAN_REASON_PREV_AUTH_NOT_VALID); +} + static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_parameters *params) { @@ -2354,6 +2677,76 @@ static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif, return ret; } +static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, + u32 id, + u32 freq, + u32 wait, + const u8 *buf, + size_t len, + bool *more_data, + bool no_cck) +{ + struct ieee80211_mgmt *mgmt; + struct ath6kl_sta *conn; + bool is_psq_empty = false; + struct ath6kl_mgmt_buff *mgmt_buf; + size_t mgmt_buf_size; + struct ath6kl *ar = vif->ar; + + mgmt = (struct ieee80211_mgmt *) buf; + if (is_multicast_ether_addr(mgmt->da)) + return false; + + conn = ath6kl_find_sta(vif, mgmt->da); + if (!conn) + return false; + + if (conn->sta_flags & STA_PS_SLEEP) { + if (!(conn->sta_flags & STA_PS_POLLED)) { + /* Queue the frames if the STA is sleeping */ + mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff); + mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL); + if (!mgmt_buf) + return false; + + INIT_LIST_HEAD(&mgmt_buf->list); + mgmt_buf->id = id; + mgmt_buf->freq = freq; + mgmt_buf->wait = wait; + mgmt_buf->len = len; + mgmt_buf->no_cck = no_cck; + memcpy(mgmt_buf->buf, buf, len); + spin_lock_bh(&conn->psq_lock); + is_psq_empty = skb_queue_empty(&conn->psq) && + (conn->mgmt_psq_len == 0); + list_add_tail(&mgmt_buf->list, &conn->mgmt_psq); + conn->mgmt_psq_len++; + spin_unlock_bh(&conn->psq_lock); + + /* + * If this is the first pkt getting queued + * for this STA, update the PVB for this + * STA. + */ + if (is_psq_empty) + ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, + conn->aid, 1); + return true; + } + + /* + * This tx is because of a PsPoll. + * Determine if MoreData bit has to be set. + */ + spin_lock_bh(&conn->psq_lock); + if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0)) + *more_data = true; + spin_unlock_bh(&conn->psq_lock); + } + + return false; +} + static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, @@ -2365,6 +2758,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ath6kl_vif *vif = netdev_priv(dev); u32 id; const struct ieee80211_mgmt *mgmt; + bool more_data, queued; mgmt = (const struct ieee80211_mgmt *) buf; if (buf + len >= mgmt->u.probe_resp.variable && @@ -2390,22 +2784,19 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, *cookie = id; - 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); + /* AP mode Power saving processing */ + if (vif->nw_type == AP_NETWORK) { + queued = ath6kl_mgmt_powersave_ap(vif, + id, chan->center_freq, + wait, buf, + len, &more_data, no_cck); + if (queued) + return 0; } + + return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, + chan->center_freq, wait, + buf, len, no_cck); } static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, @@ -2518,6 +2909,12 @@ ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, [NL80211_IFTYPE_P2P_CLIENT] = { .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), @@ -2559,9 +2956,10 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .resume = __ath6kl_cfg80211_resume, #endif .set_channel = ath6kl_set_channel, - .add_beacon = ath6kl_add_beacon, - .set_beacon = ath6kl_set_beacon, - .del_beacon = ath6kl_del_beacon, + .start_ap = ath6kl_start_ap, + .change_beacon = ath6kl_change_beacon, + .stop_ap = ath6kl_stop_ap, + .del_station = ath6kl_del_station, .change_station = ath6kl_change_station, .remain_on_channel = ath6kl_remain_on_channel, .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, @@ -2629,122 +3027,9 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) ath6kl_cfg80211_stop(vif); } -struct ath6kl *ath6kl_core_alloc(struct device *dev) -{ - struct ath6kl *ar; - struct wiphy *wiphy; - u8 ctr; - - /* create a new wiphy for use with cfg80211 */ - wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl)); - - if (!wiphy) { - ath6kl_err("couldn't allocate wiphy device\n"); - return NULL; - } - - ar = wiphy_priv(wiphy); - ar->p2p = !!ath6kl_p2p; - ar->wiphy = wiphy; - ar->dev = dev; - - ar->vif_max = 1; - - ar->max_norm_iface = 1; - - spin_lock_init(&ar->lock); - spin_lock_init(&ar->mcastpsq_lock); - spin_lock_init(&ar->list_lock); - - init_waitqueue_head(&ar->event_wq); - sema_init(&ar->sem, 1); - - INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue); - INIT_LIST_HEAD(&ar->vif_list); - - clear_bit(WMI_ENABLED, &ar->flag); - clear_bit(SKIP_SCAN, &ar->flag); - clear_bit(DESTROY_IN_PROGRESS, &ar->flag); - - ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL; - ar->listen_intvl_b = 0; - ar->tx_pwr = 0; - - ar->intra_bss = 1; - ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; - - ar->state = ATH6KL_STATE_OFF; - - memset((u8 *)ar->sta_list, 0, - AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); - - /* Init the PS queues */ - for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { - spin_lock_init(&ar->sta_list[ctr].psq_lock); - skb_queue_head_init(&ar->sta_list[ctr].psq); - } - - skb_queue_head_init(&ar->mcastpsq); - - memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3); - - return ar; -} - -int ath6kl_register_ieee80211_hw(struct ath6kl *ar) -{ - struct wiphy *wiphy = ar->wiphy; - int ret; - - wiphy->mgmt_stypes = ath6kl_mgmt_stypes; - - wiphy->max_remain_on_channel_duration = 5000; - - /* set device pointer for wiphy */ - set_wiphy_dev(wiphy, ar->dev); - - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP); - if (ar->p2p) { - wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT); - } - - /* max num of ssids that can be probed during scanning */ - wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; - wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ - wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; - wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - - wiphy->cipher_suites = cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); - - wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_GTK_REKEY_FAILURE | - WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_4WAY_HANDSHAKE; - wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST; - 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"); - return ret; - } - - return 0; -} - -static int ath6kl_init_if_data(struct ath6kl_vif *vif) +static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) { - vif->aggr_cntxt = aggr_init(vif->ndev); + vif->aggr_cntxt = aggr_init(vif); if (!vif->aggr_cntxt) { ath6kl_err("failed to initialize aggr\n"); return -ENOMEM; @@ -2758,12 +3043,15 @@ static int ath6kl_init_if_data(struct ath6kl_vif *vif) set_bit(WMM_ENABLED, &vif->flags); spin_lock_init(&vif->if_lock); + INIT_LIST_HEAD(&vif->mc_filter); + return 0; } -void ath6kl_deinit_if_data(struct ath6kl_vif *vif) +void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; + struct ath6kl_mc_filter *mc_filter, *tmp; aggr_module_destroy(vif->aggr_cntxt); @@ -2772,6 +3060,11 @@ void ath6kl_deinit_if_data(struct ath6kl_vif *vif) if (vif->nw_type == ADHOC_NETWORK) ar->ibss_if_active = false; + list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) { + list_del(&mc_filter->list); + kfree(mc_filter); + } + unregister_netdevice(vif->ndev); ar->num_vif--; @@ -2797,7 +3090,10 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, vif->wdev.netdev = ndev; vif->wdev.iftype = type; vif->fw_vif_idx = fw_vif_idx; - vif->nw_type = vif->next_mode = nw_type; + vif->nw_type = nw_type; + vif->next_mode = nw_type; + vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL; + vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME; memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); if (fw_vif_idx != 0) @@ -2808,8 +3104,7 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, ath6kl_init_control_info(vif); - /* TODO: Pass interface specific pointer instead of ar */ - if (ath6kl_init_if_data(vif)) + if (ath6kl_cfg80211_vif_init(vif)) goto err; if (register_netdevice(ndev)) @@ -2836,8 +3131,107 @@ err: return NULL; } -void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar) +int ath6kl_cfg80211_init(struct ath6kl *ar) +{ + struct wiphy *wiphy = ar->wiphy; + int ret; + + wiphy->mgmt_stypes = ath6kl_mgmt_stypes; + + wiphy->max_remain_on_channel_duration = 5000; + + /* set device pointer for wiphy */ + set_wiphy_dev(wiphy, ar->dev); + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + if (ar->p2p) { + wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT); + } + + /* max num of ssids that can be probed during scanning */ + wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; + wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ + wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; + wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->cipher_suites = cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_4WAY_HANDSHAKE; + wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST; + wiphy->wowlan.pattern_min_len = 1; + wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; + + wiphy->max_sched_scan_ssids = 10; + + ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | + WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + + if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, 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; + + ret = wiphy_register(wiphy); + if (ret < 0) { + ath6kl_err("couldn't register wiphy device\n"); + return ret; + } + + ar->wiphy_registered = true; + + return 0; +} + +void ath6kl_cfg80211_cleanup(struct ath6kl *ar) { wiphy_unregister(ar->wiphy); + + ar->wiphy_registered = false; +} + +struct ath6kl *ath6kl_cfg80211_create(void) +{ + struct ath6kl *ar; + struct wiphy *wiphy; + + /* create a new wiphy for use with cfg80211 */ + wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl)); + + if (!wiphy) { + ath6kl_err("couldn't allocate wiphy device\n"); + return NULL; + } + + ar = wiphy_priv(wiphy); + ar->wiphy = wiphy; + + return ar; +} + +/* Note: ar variable must not be accessed after calling this! */ +void ath6kl_cfg80211_destroy(struct ath6kl *ar) +{ + int i; + + for (i = 0; i < AP_MAX_NUM_STA; i++) + kfree(ar->sta_list[i].aggr_conn); + wiphy_free(ar->wiphy); } + diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index 81f20a572315..c5def436417f 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -27,10 +28,6 @@ enum ath6kl_cfg_suspend_mode { struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type); -int ath6kl_register_ieee80211_hw(struct ath6kl *ar); -struct ath6kl *ath6kl_core_alloc(struct device *dev); -void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar); - void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, @@ -53,7 +50,15 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, int ath6kl_cfg80211_resume(struct ath6kl *ar); +void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif); + void ath6kl_cfg80211_stop(struct ath6kl_vif *vif); void ath6kl_cfg80211_stop_all(struct ath6kl *ar); +int ath6kl_cfg80211_init(struct ath6kl *ar); +void ath6kl_cfg80211_cleanup(struct ath6kl *ar); + +struct ath6kl *ath6kl_cfg80211_create(void); +void ath6kl_cfg80211_destroy(struct ath6kl *ar); + #endif /* ATH6KL_CFG80211_H */ diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h index bfd6597763da..a60e78c0472f 100644 --- a/drivers/net/wireless/ath/ath6kl/common.h +++ b/drivers/net/wireless/ath/ath6kl/common.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -79,8 +80,5 @@ struct ath6kl; enum htc_credit_dist_reason; struct ath6kl_htc_credit_info; -struct ath6kl *ath6kl_core_alloc(struct device *sdev); -int ath6kl_core_init(struct ath6kl *ar); -void ath6kl_core_cleanup(struct ath6kl *ar); struct sk_buff *ath6kl_buf_alloc(int size); #endif /* COMMON_H */ diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c new file mode 100644 index 000000000000..45e641f3a41b --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/export.h> + +#include "debug.h" +#include "hif-ops.h" +#include "cfg80211.h" + +unsigned int debug_mask; +static unsigned int suspend_mode; +static unsigned int wow_mode; +static unsigned int uart_debug; +static unsigned int ath6kl_p2p; +static unsigned int testmode; + +module_param(debug_mask, uint, 0644); +module_param(suspend_mode, uint, 0644); +module_param(wow_mode, uint, 0644); +module_param(uart_debug, uint, 0644); +module_param(ath6kl_p2p, uint, 0644); +module_param(testmode, uint, 0644); + +int ath6kl_core_init(struct ath6kl *ar) +{ + struct ath6kl_bmi_target_info targ_info; + struct net_device *ndev; + int ret = 0, i; + + ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); + if (!ar->ath6kl_wq) + return -ENOMEM; + + ret = ath6kl_bmi_init(ar); + if (ret) + goto err_wq; + + /* + * Turn on power to get hardware (target) version and leave power + * on delibrately as we will boot the hardware anyway within few + * seconds. + */ + ret = ath6kl_hif_power_on(ar); + if (ret) + goto err_bmi_cleanup; + + ret = ath6kl_bmi_get_target_info(ar, &targ_info); + if (ret) + goto err_power_off; + + ar->version.target_ver = le32_to_cpu(targ_info.version); + ar->target_type = le32_to_cpu(targ_info.type); + ar->wiphy->hw_version = le32_to_cpu(targ_info.version); + + ret = ath6kl_init_hw_params(ar); + if (ret) + goto err_power_off; + + ar->htc_target = ath6kl_htc_create(ar); + + if (!ar->htc_target) { + ret = -ENOMEM; + goto err_power_off; + } + + ar->testmode = testmode; + + ret = ath6kl_init_fetch_firmwares(ar); + if (ret) + goto err_htc_cleanup; + + /* FIXME: we should free all firmwares in the error cases below */ + + /* Indicate that WMI is enabled (although not ready yet) */ + set_bit(WMI_ENABLED, &ar->flag); + ar->wmi = ath6kl_wmi_init(ar); + if (!ar->wmi) { + ath6kl_err("failed to initialize wmi\n"); + ret = -EIO; + goto err_htc_cleanup; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); + + /* setup access class priority mappings */ + ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ + ar->ac_stream_pri_map[WMM_AC_BE] = 1; + ar->ac_stream_pri_map[WMM_AC_VI] = 2; + ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ + + /* allocate some buffers that handle larger AMSDU frames */ + ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); + + ath6kl_cookie_init(ar); + + ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | + ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; + + if (suspend_mode && + suspend_mode >= WLAN_POWER_STATE_CUT_PWR && + suspend_mode <= WLAN_POWER_STATE_WOW) + ar->suspend_mode = suspend_mode; + else + ar->suspend_mode = 0; + + if (suspend_mode == WLAN_POWER_STATE_WOW && + (wow_mode == WLAN_POWER_STATE_CUT_PWR || + wow_mode == WLAN_POWER_STATE_DEEP_SLEEP)) + ar->wow_suspend_mode = wow_mode; + else + ar->wow_suspend_mode = 0; + + if (uart_debug) + ar->conf_flags |= ATH6KL_CONF_UART_DEBUG; + + set_bit(FIRST_BOOT, &ar->flag); + + ath6kl_debug_init(ar); + + ret = ath6kl_init_hw_start(ar); + if (ret) { + ath6kl_err("Failed to start hardware: %d\n", ret); + goto err_rxbuf_cleanup; + } + + /* give our connected endpoints some buffers */ + ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); + ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); + + ret = ath6kl_cfg80211_init(ar); + if (ret) + goto err_rxbuf_cleanup; + + ret = ath6kl_debug_init_fs(ar); + if (ret) { + wiphy_unregister(ar->wiphy); + goto err_rxbuf_cleanup; + } + + for (i = 0; i < ar->vif_max; i++) + ar->avail_idx_map |= BIT(i); + + rtnl_lock(); + + /* Add an initial station interface */ + ndev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0, + INFRA_NETWORK); + + rtnl_unlock(); + + if (!ndev) { + ath6kl_err("Failed to instantiate a network device\n"); + ret = -ENOMEM; + wiphy_unregister(ar->wiphy); + goto err_rxbuf_cleanup; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", + __func__, ndev->name, ndev, ar); + + return ret; + +err_rxbuf_cleanup: + ath6kl_debug_cleanup(ar); + ath6kl_htc_flush_rx_buf(ar->htc_target); + ath6kl_cleanup_amsdu_rxbufs(ar); + ath6kl_wmi_shutdown(ar->wmi); + clear_bit(WMI_ENABLED, &ar->flag); + ar->wmi = NULL; +err_htc_cleanup: + ath6kl_htc_cleanup(ar->htc_target); +err_power_off: + ath6kl_hif_power_off(ar); +err_bmi_cleanup: + ath6kl_bmi_cleanup(ar); +err_wq: + destroy_workqueue(ar->ath6kl_wq); + + return ret; +} +EXPORT_SYMBOL(ath6kl_core_init); + +struct ath6kl *ath6kl_core_create(struct device *dev) +{ + struct ath6kl *ar; + u8 ctr; + + ar = ath6kl_cfg80211_create(); + if (!ar) + return NULL; + + ar->p2p = !!ath6kl_p2p; + ar->dev = dev; + + ar->vif_max = 1; + + ar->max_norm_iface = 1; + + spin_lock_init(&ar->lock); + spin_lock_init(&ar->mcastpsq_lock); + spin_lock_init(&ar->list_lock); + + init_waitqueue_head(&ar->event_wq); + sema_init(&ar->sem, 1); + + INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue); + INIT_LIST_HEAD(&ar->vif_list); + + clear_bit(WMI_ENABLED, &ar->flag); + clear_bit(SKIP_SCAN, &ar->flag); + clear_bit(DESTROY_IN_PROGRESS, &ar->flag); + + ar->tx_pwr = 0; + ar->intra_bss = 1; + ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; + + ar->state = ATH6KL_STATE_OFF; + + memset((u8 *)ar->sta_list, 0, + AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); + + /* Init the PS queues */ + for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { + spin_lock_init(&ar->sta_list[ctr].psq_lock); + skb_queue_head_init(&ar->sta_list[ctr].psq); + skb_queue_head_init(&ar->sta_list[ctr].apsdq); + ar->sta_list[ctr].mgmt_psq_len = 0; + INIT_LIST_HEAD(&ar->sta_list[ctr].mgmt_psq); + ar->sta_list[ctr].aggr_conn = + kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL); + if (!ar->sta_list[ctr].aggr_conn) { + ath6kl_err("Failed to allocate memory for sta aggregation information\n"); + ath6kl_core_destroy(ar); + return NULL; + } + } + + skb_queue_head_init(&ar->mcastpsq); + + memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3); + + return ar; +} +EXPORT_SYMBOL(ath6kl_core_create); + +void ath6kl_core_cleanup(struct ath6kl *ar) +{ + ath6kl_hif_power_off(ar); + + destroy_workqueue(ar->ath6kl_wq); + + if (ar->htc_target) + ath6kl_htc_cleanup(ar->htc_target); + + ath6kl_cookie_cleanup(ar); + + ath6kl_cleanup_amsdu_rxbufs(ar); + + ath6kl_bmi_cleanup(ar); + + ath6kl_debug_cleanup(ar); + + kfree(ar->fw_board); + kfree(ar->fw_otp); + kfree(ar->fw); + kfree(ar->fw_patch); + kfree(ar->fw_testscript); + + ath6kl_cfg80211_cleanup(ar); +} +EXPORT_SYMBOL(ath6kl_core_cleanup); + +void ath6kl_core_destroy(struct ath6kl *ar) +{ + ath6kl_cfg80211_destroy(ar); +} +EXPORT_SYMBOL(ath6kl_core_destroy); + +MODULE_AUTHOR("Qualcomm Atheros"); +MODULE_DESCRIPTION("Core module for AR600x SDIO and USB devices."); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index c863a28f2e0c..f1dd8906be45 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -44,6 +45,10 @@ #define ATH6KL_MAX_ENDPOINTS 4 #define MAX_NODE_NUM 15 +#define ATH6KL_APSD_ALL_FRAME 0xFFFF +#define ATH6KL_APSD_NUM_OF_AC 0x4 +#define ATH6KL_APSD_FRAME_MASK 0xF + /* Extra bytes for htc header alignment */ #define ATH6KL_HTC_ALIGN_BYTES 3 @@ -55,8 +60,9 @@ #define MAX_DEFAULT_SEND_QUEUE_DEPTH (MAX_DEF_COOKIE_NUM / WMM_NUM_AC) #define DISCON_TIMER_INTVAL 10000 /* in msec */ -#define A_DEFAULT_LISTEN_INTERVAL 100 -#define A_MAX_WOW_LISTEN_INTERVAL 1000 + +/* Channel dwell time in fg scan */ +#define ATH6KL_FG_SCAN_INTERVAL 50 /* in ms */ /* includes also the null byte */ #define ATH6KL_FIRMWARE_MAGIC "QCA-ATH6KL" @@ -97,45 +103,49 @@ struct ath6kl_fw_ie { u8 data[0]; }; +#define ATH6KL_FW_API2_FILE "fw-2.bin" +#define ATH6KL_FW_API3_FILE "fw-3.bin" + /* AR6003 1.0 definitions */ #define AR6003_HW_1_0_VERSION 0x300002ba /* AR6003 2.0 definitions */ #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_FW_DIR "ath6k/AR6003/hw2.0" +#define AR6003_HW_2_0_OTP_FILE "otp.bin.z77" +#define AR6003_HW_2_0_FIRMWARE_FILE "athwlan.bin.z77" +#define AR6003_HW_2_0_TCMD_FIRMWARE_FILE "athtcmd_ram.bin" +#define AR6003_HW_2_0_PATCH_FILE "data.patch.bin" #define AR6003_HW_2_0_BOARD_DATA_FILE "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_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_FW_DIR "ath6k/AR6003/hw2.1.1" +#define AR6003_HW_2_1_1_OTP_FILE "otp.bin" +#define AR6003_HW_2_1_1_FIRMWARE_FILE "athwlan.bin" +#define AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE "athtcmd_ram.bin" +#define AR6003_HW_2_1_1_UTF_FIRMWARE_FILE "utf.bin" +#define AR6003_HW_2_1_1_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6003_HW_2_1_1_PATCH_FILE "data.patch.bin" #define AR6003_HW_2_1_1_BOARD_DATA_FILE "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_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_FW_DIR "ath6k/AR6004/hw1.0" +#define AR6004_HW_1_0_FIRMWARE_FILE "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_FW_DIR "ath6k/AR6004/hw1.1" +#define AR6004_HW_1_1_FIRMWARE_FILE "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" @@ -144,6 +154,8 @@ struct ath6kl_fw_ie { #define STA_PS_AWAKE BIT(0) #define STA_PS_SLEEP BIT(1) #define STA_PS_POLLED BIT(2) +#define STA_PS_APSD_TRIGGER BIT(3) +#define STA_PS_APSD_EOSP BIT(4) /* HTC TX packet tagging definitions */ #define ATH6KL_CONTROL_PKT_TAG HTC_TX_PACKET_TAG_USER_DEFINED @@ -173,6 +185,11 @@ struct ath6kl_fw_ie { #define MBOX_YIELD_LIMIT 99 +#define ATH6KL_DEFAULT_LISTEN_INTVAL 100 /* in TUs */ +#define ATH6KL_DEFAULT_BMISS_TIME 1500 +#define ATH6KL_MAX_WOW_LISTEN_INTL 300 /* in TUs */ +#define ATH6KL_MAX_BMISS_TIME 5000 + /* configuration lags */ /* * ATH6KL_CONF_IGNORE_ERP_BARKER: Ignore the barker premable in @@ -186,7 +203,7 @@ struct ath6kl_fw_ie { #define ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN BIT(1) #define ATH6KL_CONF_ENABLE_11N BIT(2) #define ATH6KL_CONF_ENABLE_TX_BURST BIT(3) -#define ATH6KL_CONF_SUSPEND_CUTPOWER BIT(4) +#define ATH6KL_CONF_UART_DEBUG BIT(4) enum wlan_low_pwr_state { WLAN_POWER_STATE_ON, @@ -216,6 +233,12 @@ struct rxtid { u32 hold_q_sz; struct skb_hold_q *hold_q; struct sk_buff_head q; + + /* + * FIXME: No clue what this should protect. Apparently it should + * protect some of the fields above but they are also accessed + * without taking the lock. + */ spinlock_t lock; }; @@ -231,14 +254,19 @@ struct rxtid_stats { u32 num_bar; }; -struct aggr_info { +struct aggr_info_conn { u8 aggr_sz; u8 timer_scheduled; struct timer_list timer; struct net_device *dev; struct rxtid rx_tid[NUM_OF_TIDS]; - struct sk_buff_head free_q; struct rxtid_stats stat[NUM_OF_TIDS]; + struct aggr_info *aggr_info; +}; + +struct aggr_info { + struct aggr_info_conn *aggr_conn; + struct sk_buff_head rx_amsdu_freeq; }; struct ath6kl_wep_key { @@ -270,6 +298,16 @@ struct ath6kl_cookie { struct ath6kl_cookie *arc_list_next; }; +struct ath6kl_mgmt_buff { + struct list_head list; + u32 freq; + u32 wait; + u32 id; + bool no_cck; + size_t len; + u8 buf[0]; +}; + struct ath6kl_sta { u16 sta_flags; u8 mac[ETH_ALEN]; @@ -279,7 +317,15 @@ struct ath6kl_sta { u8 auth; u8 wpa_ie[ATH6KL_MAX_IE]; struct sk_buff_head psq; + + /* protects psq, mgmt_psq, apsdq, and mgmt_psq_len fields */ spinlock_t psq_lock; + + struct list_head mgmt_psq; + size_t mgmt_psq_len; + u8 apsd_info; + struct sk_buff_head apsdq; + struct aggr_info_conn *aggr_conn; }; struct ath6kl_version { @@ -408,6 +454,13 @@ enum ath6kl_hif_type { ATH6KL_HIF_TYPE_USB, }; +/* Max number of filters that hw supports */ +#define ATH6K_MAX_MC_FILTERS_PER_LIST 7 +struct ath6kl_mc_filter { + struct list_head list; + char hw_addr[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; +}; + /* * Driver's maximum limit, note that some firmwares support only one vif * and the runtime (current) limit must be checked from ar->vif_max. @@ -426,6 +479,7 @@ enum ath6kl_vif_state { DTIM_PERIOD_AVAIL, WLAN_ENABLED, STATS_UPDATE_PEND, + HOST_SLEEP_MODE_CMD_PROCESSED, }; struct ath6kl_vif { @@ -468,9 +522,13 @@ struct ath6kl_vif { bool probe_req_report; u16 next_chan; u16 assoc_bss_beacon_int; + u16 listen_intvl_t; + u16 bmiss_time_t; u8 assoc_bss_dtim_period; struct net_device_stats net_stats; struct target_stats target_stats; + + struct list_head mc_filter; }; #define WOW_LIST_ID 0 @@ -493,6 +551,8 @@ enum ath6kl_dev_state { enum ath6kl_state { ATH6KL_STATE_OFF, ATH6KL_STATE_ON, + ATH6KL_STATE_SUSPENDING, + ATH6KL_STATE_RESUMING, ATH6KL_STATE_DEEPSLEEP, ATH6KL_STATE_CUTPOWER, ATH6KL_STATE_WOW, @@ -504,6 +564,7 @@ struct ath6kl { struct wiphy *wiphy; enum ath6kl_state state; + unsigned int testmode; struct ath6kl_bmi bmi; const struct ath6kl_hif_ops *hif_ops; @@ -520,10 +581,14 @@ struct ath6kl { unsigned int vif_max; u8 max_norm_iface; u8 avail_idx_map; + + /* + * Protects at least amsdu_rx_buffer_queue, ath6kl_alloc_cookie() + * calls, tx_pending and total_tx_data_pend. + */ spinlock_t lock; + struct semaphore sem; - u16 listen_intvl_b; - u16 listen_intvl_t; u8 lrssi_roam_threshold; struct ath6kl_version version; u32 target_type; @@ -549,7 +614,13 @@ struct ath6kl { u8 sta_list_index; struct ath6kl_req_key ap_mode_bkey; struct sk_buff_head mcastpsq; + + /* + * FIXME: protects access to mcastpsq but is actually useless as + * all skbe_queue_*() functions provide serialisation themselves + */ spinlock_t mcastpsq_lock; + u8 intra_bss; struct wmi_ap_mode_stat ap_stats; u8 ap_country_code[3]; @@ -574,17 +645,25 @@ struct ath6kl { u32 board_addr; u32 refclk_hz; u32 uarttx_pin; + u32 testscript_addr; + + struct ath6kl_hw_fw { + const char *dir; + const char *otp; + const char *fw; + const char *tcmd; + const char *patch; + const char *utf; + const char *testscript; + } fw; - const char *fw_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; + u16 suspend_mode; + u16 wow_suspend_mode; wait_queue_head_t event_wq; struct ath6kl_mbox_info mbox_info; @@ -603,6 +682,10 @@ struct ath6kl { u8 *fw_patch; size_t fw_patch_len; + u8 *fw_testscript; + size_t fw_testscript_len; + + unsigned int fw_api; unsigned long fw_capabilities[ATH6KL_CAPABILITY_LEN]; struct workqueue_struct *ath6kl_wq; @@ -611,12 +694,16 @@ struct ath6kl { bool p2p; + bool wiphy_registered; + #ifdef CONFIG_ATH6KL_DEBUG struct { - struct circ_buf fwlog_buf; - spinlock_t fwlog_lock; - void *fwlog_tmp; + struct sk_buff_head fwlog_queue; + struct completion fwlog_completion; + bool fwlog_open; + u32 fwlog_mask; + unsigned int dbgfs_diag_reg; u32 diag_reg_addr_wr; u32 diag_reg_val_wr; @@ -676,7 +763,9 @@ struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar); void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie); int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev); -struct aggr_info *aggr_init(struct net_device *dev); +struct aggr_info *aggr_init(struct ath6kl_vif *vif); +void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info, + struct aggr_info_conn *aggr_conn); void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint); void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count); @@ -684,12 +773,12 @@ struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target, enum htc_endpoint_id endpoint, int len); void aggr_module_destroy(struct aggr_info *aggr_info); -void aggr_reset_state(struct aggr_info *aggr_info); +void aggr_reset_state(struct aggr_info_conn *aggr_conn); -struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 * node_addr); +struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr); struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid); -void ath6kl_ready_event(void *devt, u8 * datap, u32 sw_ver, u32 abi_ver); +void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver); int ath6kl_control_tx(void *devt, struct sk_buff *skb, enum htc_endpoint_id eid); void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, @@ -700,7 +789,7 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel); void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, u8 keymgmt, u8 ucipher, u8 auth, - u8 assoc_req_len, u8 *assoc_info); + u8 assoc_req_len, u8 *assoc_info, u8 apsd_info); void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 prot_reason_status); @@ -723,12 +812,18 @@ void ath6kl_wakeup_event(void *dev); void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, bool wait_fot_compltn, bool cold_reset); void ath6kl_init_control_info(struct ath6kl_vif *vif); -void ath6kl_deinit_if_data(struct ath6kl_vif *vif); -void ath6kl_core_free(struct ath6kl *ar); struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar); void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready); int ath6kl_init_hw_start(struct ath6kl *ar); int ath6kl_init_hw_stop(struct ath6kl *ar); +int ath6kl_init_fetch_firmwares(struct ath6kl *ar); +int ath6kl_init_hw_params(struct ath6kl *ar); + void ath6kl_check_wow_status(struct ath6kl *ar); +struct ath6kl *ath6kl_core_create(struct device *dev); +int ath6kl_core_init(struct ath6kl *ar); +void ath6kl_core_cleanup(struct ath6kl *ar); +void ath6kl_core_destroy(struct ath6kl *ar); + #endif /* CORE_H */ diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index eb808b46f94c..552adb3f80d0 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,7 +17,7 @@ #include "core.h" -#include <linux/circ_buf.h> +#include <linux/skbuff.h> #include <linux/fs.h> #include <linux/vmalloc.h> #include <linux/export.h> @@ -32,9 +33,8 @@ struct ath6kl_fwlog_slot { u8 payload[0]; }; -#define ATH6KL_FWLOG_SIZE 32768 -#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \ - ATH6KL_FWLOG_PAYLOAD_SIZE) +#define ATH6KL_FWLOG_MAX_ENTRIES 20 + #define ATH6KL_FWLOG_VALID_MASK 0x1ffff int ath6kl_printk(const char *level, const char *fmt, ...) @@ -54,9 +54,42 @@ int ath6kl_printk(const char *level, const char *fmt, ...) return rtn; } +EXPORT_SYMBOL(ath6kl_printk); #ifdef CONFIG_ATH6KL_DEBUG +void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!(debug_mask & mask)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + ath6kl_printk(KERN_DEBUG, "%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL(ath6kl_dbg); + +void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ + if (debug_mask & mask) { + if (msg) + ath6kl_dbg(mask, "%s\n", msg); + + print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); + } +} +EXPORT_SYMBOL(ath6kl_dbg_dump); + #define REG_OUTPUT_LEN_PER_LINE 25 #define REGTYPE_STR_LEN 100 @@ -82,63 +115,63 @@ void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_enable_reg *irq_enable_reg) { - ath6kl_dbg(ATH6KL_DBG_ANY, ("<------- Register Table -------->\n")); + ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n")); if (irq_proc_reg != NULL) { - ath6kl_dbg(ATH6KL_DBG_ANY, - "Host Int status: 0x%x\n", - irq_proc_reg->host_int_status); - ath6kl_dbg(ATH6KL_DBG_ANY, + ath6kl_dbg(ATH6KL_DBG_IRQ, + "Host Int status: 0x%x\n", + irq_proc_reg->host_int_status); + ath6kl_dbg(ATH6KL_DBG_IRQ, "CPU Int status: 0x%x\n", - irq_proc_reg->cpu_int_status); - ath6kl_dbg(ATH6KL_DBG_ANY, + irq_proc_reg->cpu_int_status); + ath6kl_dbg(ATH6KL_DBG_IRQ, "Error Int status: 0x%x\n", - irq_proc_reg->error_int_status); - ath6kl_dbg(ATH6KL_DBG_ANY, + irq_proc_reg->error_int_status); + ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status: 0x%x\n", - irq_proc_reg->counter_int_status); - ath6kl_dbg(ATH6KL_DBG_ANY, + irq_proc_reg->counter_int_status); + ath6kl_dbg(ATH6KL_DBG_IRQ, "Mbox Frame: 0x%x\n", - irq_proc_reg->mbox_frame); - ath6kl_dbg(ATH6KL_DBG_ANY, + irq_proc_reg->mbox_frame); + ath6kl_dbg(ATH6KL_DBG_IRQ, "Rx Lookahead Valid: 0x%x\n", - irq_proc_reg->rx_lkahd_valid); - ath6kl_dbg(ATH6KL_DBG_ANY, + irq_proc_reg->rx_lkahd_valid); + ath6kl_dbg(ATH6KL_DBG_IRQ, "Rx Lookahead 0: 0x%x\n", - irq_proc_reg->rx_lkahd[0]); - ath6kl_dbg(ATH6KL_DBG_ANY, + irq_proc_reg->rx_lkahd[0]); + ath6kl_dbg(ATH6KL_DBG_IRQ, "Rx Lookahead 1: 0x%x\n", - irq_proc_reg->rx_lkahd[1]); + irq_proc_reg->rx_lkahd[1]); if (dev->ar->mbox_info.gmbox_addr != 0) { /* * If the target supports GMBOX hardware, dump some * additional state. */ - ath6kl_dbg(ATH6KL_DBG_ANY, - "GMBOX Host Int status 2: 0x%x\n", - irq_proc_reg->host_int_status2); - ath6kl_dbg(ATH6KL_DBG_ANY, - "GMBOX RX Avail: 0x%x\n", - irq_proc_reg->gmbox_rx_avail); - ath6kl_dbg(ATH6KL_DBG_ANY, - "GMBOX lookahead alias 0: 0x%x\n", - irq_proc_reg->rx_gmbox_lkahd_alias[0]); - ath6kl_dbg(ATH6KL_DBG_ANY, - "GMBOX lookahead alias 1: 0x%x\n", - irq_proc_reg->rx_gmbox_lkahd_alias[1]); + ath6kl_dbg(ATH6KL_DBG_IRQ, + "GMBOX Host Int status 2: 0x%x\n", + irq_proc_reg->host_int_status2); + ath6kl_dbg(ATH6KL_DBG_IRQ, + "GMBOX RX Avail: 0x%x\n", + irq_proc_reg->gmbox_rx_avail); + ath6kl_dbg(ATH6KL_DBG_IRQ, + "GMBOX lookahead alias 0: 0x%x\n", + irq_proc_reg->rx_gmbox_lkahd_alias[0]); + ath6kl_dbg(ATH6KL_DBG_IRQ, + "GMBOX lookahead alias 1: 0x%x\n", + irq_proc_reg->rx_gmbox_lkahd_alias[1]); } } if (irq_enable_reg != NULL) { - ath6kl_dbg(ATH6KL_DBG_ANY, - "Int status Enable: 0x%x\n", - irq_enable_reg->int_status_en); - ath6kl_dbg(ATH6KL_DBG_ANY, "Counter Int status Enable: 0x%x\n", - irq_enable_reg->cntr_int_status_en); + ath6kl_dbg(ATH6KL_DBG_IRQ, + "Int status Enable: 0x%x\n", + irq_enable_reg->int_status_en); + ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status Enable: 0x%x\n", + irq_enable_reg->cntr_int_status_en); } - ath6kl_dbg(ATH6KL_DBG_ANY, "<------------------------------->\n"); + ath6kl_dbg(ATH6KL_DBG_IRQ, "<------------------------------->\n"); } static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist) @@ -175,9 +208,6 @@ void dump_cred_dist_stats(struct htc_target *target) { struct htc_endpoint_credit_dist *ep_list; - if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_CREDIT)) - return; - list_for_each_entry(ep_list, &target->cred_dist_list, list) dump_cred_dist(ep_list); @@ -238,105 +268,103 @@ static const struct file_operations fops_war_stats = { .llseek = default_llseek, }; -static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf, - size_t buf_len) +void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) { - struct circ_buf *fwlog = &ar->debug.fwlog_buf; - size_t space; - int i; + struct ath6kl_fwlog_slot *slot; + struct sk_buff *skb; + size_t slot_len; - /* entries must all be equal size */ - if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE)) + if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE)) return; - space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE); - if (space < buf_len) - /* discard oldest slot */ - fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) & - (ATH6KL_FWLOG_SIZE - 1); + slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE; - for (i = 0; i < buf_len; i += space) { - space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail, - ATH6KL_FWLOG_SIZE); + skb = alloc_skb(slot_len, GFP_KERNEL); + if (!skb) + return; - if ((size_t) space > buf_len - i) - space = buf_len - i; + slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len); + slot->timestamp = cpu_to_le32(jiffies); + slot->length = cpu_to_le32(len); + memcpy(slot->payload, buf, len); - memcpy(&fwlog->buf[fwlog->head], buf, space); - fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1); - } + /* Need to pad each record to fixed length ATH6KL_FWLOG_PAYLOAD_SIZE */ + memset(slot->payload + len, 0, ATH6KL_FWLOG_PAYLOAD_SIZE - len); -} + spin_lock(&ar->debug.fwlog_queue.lock); -void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) -{ - struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp; - size_t slot_len; + __skb_queue_tail(&ar->debug.fwlog_queue, skb); + complete(&ar->debug.fwlog_completion); - if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE)) - return; + /* drop oldest entries */ + while (skb_queue_len(&ar->debug.fwlog_queue) > + ATH6KL_FWLOG_MAX_ENTRIES) { + skb = __skb_dequeue(&ar->debug.fwlog_queue); + kfree_skb(skb); + } - spin_lock_bh(&ar->debug.fwlog_lock); + spin_unlock(&ar->debug.fwlog_queue.lock); - slot->timestamp = cpu_to_le32(jiffies); - slot->length = cpu_to_le32(len); - memcpy(slot->payload, buf, len); + return; +} - slot_len = sizeof(*slot) + len; +static int ath6kl_fwlog_open(struct inode *inode, struct file *file) +{ + struct ath6kl *ar = inode->i_private; - if (slot_len < ATH6KL_FWLOG_SLOT_SIZE) - memset(slot->payload + len, 0, - ATH6KL_FWLOG_SLOT_SIZE - slot_len); + if (ar->debug.fwlog_open) + return -EBUSY; - ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE); + ar->debug.fwlog_open = true; - spin_unlock_bh(&ar->debug.fwlog_lock); + file->private_data = inode->i_private; + return 0; } -static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar) +static int ath6kl_fwlog_release(struct inode *inode, struct file *file) { - return CIRC_CNT(ar->debug.fwlog_buf.head, - ar->debug.fwlog_buf.tail, - ATH6KL_FWLOG_SLOT_SIZE) == 0; + struct ath6kl *ar = inode->i_private; + + ar->debug.fwlog_open = false; + + return 0; } static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; - struct circ_buf *fwlog = &ar->debug.fwlog_buf; - size_t len = 0, buf_len = count; + struct sk_buff *skb; ssize_t ret_cnt; + size_t len = 0; char *buf; - int ccnt; - buf = vmalloc(buf_len); + buf = vmalloc(count); if (!buf) return -ENOMEM; /* read undelivered logs from firmware */ ath6kl_read_fwlogs(ar); - spin_lock_bh(&ar->debug.fwlog_lock); + spin_lock(&ar->debug.fwlog_queue.lock); - while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) { - ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail, - ATH6KL_FWLOG_SIZE); + while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { + if (skb->len > count - len) { + /* not enough space, put skb back and leave */ + __skb_queue_head(&ar->debug.fwlog_queue, skb); + break; + } - if ((size_t) ccnt > buf_len - len) - ccnt = buf_len - len; - memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt); - len += ccnt; + memcpy(buf + len, skb->data, skb->len); + len += skb->len; - fwlog->tail = (fwlog->tail + ccnt) & - (ATH6KL_FWLOG_SIZE - 1); + kfree_skb(skb); } - spin_unlock_bh(&ar->debug.fwlog_lock); + spin_unlock(&ar->debug.fwlog_queue.lock); - if (WARN_ON(len > buf_len)) - len = buf_len; + /* FIXME: what to do if len == 0? */ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); @@ -346,12 +374,87 @@ static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, } static const struct file_operations fops_fwlog = { - .open = ath6kl_debugfs_open, + .open = ath6kl_fwlog_open, + .release = ath6kl_fwlog_release, .read = ath6kl_fwlog_read, .owner = THIS_MODULE, .llseek = default_llseek, }; +static ssize_t ath6kl_fwlog_block_read(struct file *file, + char __user *user_buf, + size_t count, + loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct sk_buff *skb; + ssize_t ret_cnt; + size_t len = 0, not_copied; + char *buf; + int ret; + + buf = vmalloc(count); + if (!buf) + return -ENOMEM; + + spin_lock(&ar->debug.fwlog_queue.lock); + + if (skb_queue_len(&ar->debug.fwlog_queue) == 0) { + /* we must init under queue lock */ + init_completion(&ar->debug.fwlog_completion); + + spin_unlock(&ar->debug.fwlog_queue.lock); + + ret = wait_for_completion_interruptible( + &ar->debug.fwlog_completion); + if (ret == -ERESTARTSYS) + return ret; + + spin_lock(&ar->debug.fwlog_queue.lock); + } + + while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { + if (skb->len > count - len) { + /* not enough space, put skb back and leave */ + __skb_queue_head(&ar->debug.fwlog_queue, skb); + break; + } + + + memcpy(buf + len, skb->data, skb->len); + len += skb->len; + + kfree_skb(skb); + } + + spin_unlock(&ar->debug.fwlog_queue.lock); + + /* FIXME: what to do if len == 0? */ + + not_copied = copy_to_user(user_buf, buf, len); + if (not_copied != 0) { + ret_cnt = -EFAULT; + goto out; + } + + *ppos = *ppos + len; + + ret_cnt = len; + +out: + vfree(buf); + + return ret_cnt; +} + +static const struct file_operations fops_fwlog_block = { + .open = ath6kl_fwlog_open, + .release = ath6kl_fwlog_release, + .read = ath6kl_fwlog_block_read, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -637,9 +740,13 @@ static ssize_t ath6kl_endpoint_stats_read(struct file *file, return -ENOMEM; #define EPSTAT(name) \ - len = print_endpoint_stat(target, buf, buf_len, len, \ - offsetof(struct htc_endpoint_stats, name), \ - #name) + do { \ + len = print_endpoint_stat(target, buf, buf_len, len, \ + offsetof(struct htc_endpoint_stats, \ + name), \ + #name); \ + } while (0) + EPSTAT(cred_low_indicate); EPSTAT(tx_issued); EPSTAT(tx_pkt_bundled); @@ -749,17 +856,9 @@ static ssize_t ath6kl_regread_write(struct file *file, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; - u8 buf[50]; - unsigned int len; unsigned long reg_addr; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - - if (strict_strtoul(buf, 0, ®_addr)) + if (kstrtoul_from_user(user_buf, count, 0, ®_addr)) return -EINVAL; if ((reg_addr % 4) != 0) @@ -873,15 +972,8 @@ static ssize_t ath6kl_lrssi_roam_write(struct file *file, { struct ath6kl *ar = file->private_data; unsigned long lrssi_roam_threshold; - char buf[32]; - ssize_t len; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (strict_strtoul(buf, 0, &lrssi_roam_threshold)) + if (kstrtoul_from_user(user_buf, count, 0, &lrssi_roam_threshold)) return -EINVAL; ar->lrssi_roam_threshold = lrssi_roam_threshold; @@ -1411,6 +1503,8 @@ static ssize_t ath6kl_create_qos_write(struct file *file, return -EINVAL; pstream.medium_time = cpu_to_le32(val32); + pstream.nominal_phy = le32_to_cpu(pstream.min_phy_rate) / 1000000; + ath6kl_wmi_create_pstream_cmd(ar->wmi, vif->fw_vif_idx, &pstream); return count; @@ -1505,57 +1599,51 @@ static const struct file_operations fops_bgscan_int = { }; static ssize_t ath6kl_listen_int_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) + const char __user *user_buf, + size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; - u16 listen_int_t, listen_int_b; + struct ath6kl_vif *vif; + u16 listen_interval; char buf[32]; - char *sptr, *token; ssize_t len; + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; - sptr = buf; - - token = strsep(&sptr, " "); - if (!token) + if (kstrtou16(buf, 0, &listen_interval)) return -EINVAL; - if (kstrtou16(token, 0, &listen_int_t)) + if ((listen_interval < 15) || (listen_interval > 3000)) return -EINVAL; - if (kstrtou16(sptr, 0, &listen_int_b)) - return -EINVAL; - - if ((listen_int_t < 15) || (listen_int_t > 5000)) - return -EINVAL; - - if ((listen_int_b < 1) || (listen_int_b > 50)) - return -EINVAL; - - ar->listen_intvl_t = listen_int_t; - ar->listen_intvl_b = listen_int_b; - - ath6kl_wmi_listeninterval_cmd(ar->wmi, 0, ar->listen_intvl_t, - ar->listen_intvl_b); + vif->listen_intvl_t = listen_interval; + ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, + vif->listen_intvl_t, 0); return count; } static ssize_t ath6kl_listen_int_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) + char __user *user_buf, + size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; char buf[32]; int len; - len = scnprintf(buf, sizeof(buf), "%u %u\n", ar->listen_intvl_t, - ar->listen_intvl_b); + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + len = scnprintf(buf, sizeof(buf), "%u\n", vif->listen_intvl_t); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } @@ -1628,33 +1716,29 @@ static const struct file_operations fops_power_params = { .llseek = default_llseek, }; -int ath6kl_debug_init(struct ath6kl *ar) +void ath6kl_debug_init(struct ath6kl *ar) { - ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE); - if (ar->debug.fwlog_buf.buf == NULL) - return -ENOMEM; - - ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL); - if (ar->debug.fwlog_tmp == NULL) { - vfree(ar->debug.fwlog_buf.buf); - return -ENOMEM; - } - - spin_lock_init(&ar->debug.fwlog_lock); + skb_queue_head_init(&ar->debug.fwlog_queue); + init_completion(&ar->debug.fwlog_completion); /* * Actually we are lying here but don't know how to read the mask * value from the firmware. */ ar->debug.fwlog_mask = 0; +} +/* + * Initialisation needs to happen in two stages as fwlog events can come + * before cfg80211 is initialised, and debugfs depends on cfg80211 + * initialisation. + */ +int ath6kl_debug_init_fs(struct ath6kl *ar) +{ ar->debugfs_phy = debugfs_create_dir("ath6kl", ar->wiphy->debugfsdir); - if (!ar->debugfs_phy) { - vfree(ar->debug.fwlog_buf.buf); - kfree(ar->debug.fwlog_tmp); + if (!ar->debugfs_phy) return -ENOMEM; - } debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, &fops_tgt_stats); @@ -1668,6 +1752,9 @@ int ath6kl_debug_init(struct ath6kl *ar) debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar, &fops_fwlog); + debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar, + &fops_fwlog_block); + debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_fwlog_mask); @@ -1702,24 +1789,26 @@ int ath6kl_debug_init(struct ath6kl *ar) ar->debugfs_phy, ar, &fops_disconnect_timeout); debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar, - &fops_create_qos); + &fops_create_qos); debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar, - &fops_delete_qos); + &fops_delete_qos); debugfs_create_file("bgscan_interval", S_IWUSR, - ar->debugfs_phy, ar, &fops_bgscan_int); + ar->debugfs_phy, ar, &fops_bgscan_int); + + debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_listen_int); debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar, - &fops_power_params); + &fops_power_params); return 0; } void ath6kl_debug_cleanup(struct ath6kl *ar) { - vfree(ar->debug.fwlog_buf.buf); - kfree(ar->debug.fwlog_tmp); + skb_queue_purge(&ar->debug.fwlog_queue); kfree(ar->debug.roam_tbl); } diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 9853c9c125c1..1803a0baae82 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -41,6 +42,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 */ }; @@ -55,35 +57,16 @@ int ath6kl_printk(const char *level, const char *fmt, ...); #define ath6kl_warn(fmt, ...) \ ath6kl_printk(KERN_WARNING, fmt, ##__VA_ARGS__) -#define AR_DBG_LVL_CHECK(mask) (debug_mask & mask) - enum ath6kl_war { ATH6KL_WAR_INVALID_RATE, }; #ifdef CONFIG_ATH6KL_DEBUG -#define ath6kl_dbg(mask, fmt, ...) \ - ({ \ - int rtn; \ - if (debug_mask & mask) \ - rtn = ath6kl_printk(KERN_DEBUG, fmt, ##__VA_ARGS__); \ - else \ - rtn = 0; \ - \ - rtn; \ - }) - -static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, - const char *msg, const char *prefix, - const void *buf, size_t len) -{ - if (debug_mask & mask) { - if (msg) - ath6kl_dbg(mask, "%s\n", msg); - print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); - } -} +void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...); +void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, + const char *msg, const char *prefix, + const void *buf, size_t len); void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, @@ -95,7 +78,8 @@ int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, size_t len); void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive); void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout); -int ath6kl_debug_init(struct ath6kl *ar); +void ath6kl_debug_init(struct ath6kl *ar); +int ath6kl_debug_init_fs(struct ath6kl *ar); void ath6kl_debug_cleanup(struct ath6kl *ar); #else @@ -145,7 +129,11 @@ static inline void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, { } -static inline int ath6kl_debug_init(struct ath6kl *ar) +static inline void ath6kl_debug_init(struct ath6kl *ar) +{ +} + +static inline int ath6kl_debug_init_fs(struct ath6kl *ar) { return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h index 2fe1dadfc77a..fd84086638e3 100644 --- a/drivers/net/wireless/ath/ath6kl/hif-ops.h +++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index e57da35e59fa..68ed6c2665b7 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2007-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,6 +16,8 @@ */ #include "hif.h" +#include <linux/export.h> + #include "core.h" #include "target.h" #include "hif-ops.h" @@ -59,6 +62,8 @@ int ath6kl_hif_rw_comp_handler(void *context, int status) return 0; } +EXPORT_SYMBOL(ath6kl_hif_rw_comp_handler); + #define REG_DUMP_COUNT_AR6003 60 #define REGISTER_DUMP_LEN_MAX 60 @@ -85,7 +90,7 @@ static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) } ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n", - regdump_addr); + regdump_addr); regdump_addr = TARG_VTOP(ar->target_type, regdump_addr); /* fetch register dump data */ @@ -102,9 +107,9 @@ static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) BUILD_BUG_ON(REG_DUMP_COUNT_AR6003 % 4); - for (i = 0; i < REG_DUMP_COUNT_AR6003 / 4; i++) { + for (i = 0; i < REG_DUMP_COUNT_AR6003; i += 4) { ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", - 4 * i, + i, le32_to_cpu(regdump_val[i]), le32_to_cpu(regdump_val[i + 1]), le32_to_cpu(regdump_val[i + 2]), @@ -130,6 +135,7 @@ static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) ath6kl_warn("Failed to clear debug interrupt: %d\n", ret); ath6kl_hif_dump_fw_crash(dev->ar); + ath6kl_read_fwlogs(dev->ar); return ret; } @@ -279,7 +285,7 @@ static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev) dev->irq_en_reg.cntr_int_status_en; ath6kl_dbg(ATH6KL_DBG_IRQ, - "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", + "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", counter_int_status); /* @@ -354,7 +360,7 @@ static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev) } ath6kl_dbg(ATH6KL_DBG_IRQ, - "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", + "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", cpu_int_status); /* Clear the interrupt */ @@ -429,9 +435,8 @@ static int proc_pending_irqs(struct ath6kl_device *dev, bool *done) if (status) goto out; - if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ)) - ath6kl_dump_registers(dev, &dev->irq_proc_reg, - &dev->irq_en_reg); + ath6kl_dump_registers(dev, &dev->irq_proc_reg, + &dev->irq_en_reg); /* Update only those registers that are enabled */ host_int_status = dev->irq_proc_reg.host_int_status & @@ -561,6 +566,7 @@ int ath6kl_hif_intr_bh_handler(struct ath6kl *ar) return status; } +EXPORT_SYMBOL(ath6kl_hif_intr_bh_handler); static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev) { @@ -689,6 +695,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 699a036f3a44..20ed6b73517b 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.h +++ b/drivers/net/wireless/ath/ath6kl/hif.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -197,6 +198,8 @@ struct hif_scatter_req { u8 *virt_dma_buf; struct hif_scatter_item scat_list[1]; + + u32 scat_q_depth; }; struct ath6kl_irq_proc_registers { @@ -220,6 +223,7 @@ struct ath6kl_irq_enable_reg { } __packed; struct ath6kl_device { + /* protects irq_proc_reg and irq_en_reg below */ spinlock_t lock; struct ath6kl_irq_proc_registers irq_proc_reg; struct ath6kl_irq_enable_reg irq_en_reg; diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index f3b63ca25c7e..4849d99cce77 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2007-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,6 +23,9 @@ #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) +/* threshold to re-enable Tx bundling for an AC*/ +#define TX_RESUME_BUNDLE_THRESHOLD 1500 + /* Functions for Tx credit handling */ static void ath6kl_credit_deposit(struct ath6kl_htc_credit_info *cred_info, struct htc_endpoint_credit_dist *ep_dist, @@ -168,31 +172,29 @@ static void ath6kl_credit_reduce(struct ath6kl_htc_credit_info *cred_info, static void ath6kl_credit_update(struct ath6kl_htc_credit_info *cred_info, struct list_head *epdist_list) { - struct htc_endpoint_credit_dist *cur_dist_list; + struct htc_endpoint_credit_dist *cur_list; - list_for_each_entry(cur_dist_list, epdist_list, list) { - if (cur_dist_list->endpoint == ENDPOINT_0) + list_for_each_entry(cur_list, epdist_list, list) { + if (cur_list->endpoint == ENDPOINT_0) continue; - if (cur_dist_list->cred_to_dist > 0) { - cur_dist_list->credits += - cur_dist_list->cred_to_dist; - cur_dist_list->cred_to_dist = 0; - if (cur_dist_list->credits > - cur_dist_list->cred_assngd) + if (cur_list->cred_to_dist > 0) { + cur_list->credits += cur_list->cred_to_dist; + cur_list->cred_to_dist = 0; + + if (cur_list->credits > cur_list->cred_assngd) ath6kl_credit_reduce(cred_info, - cur_dist_list, - cur_dist_list->cred_assngd); + cur_list, + cur_list->cred_assngd); - if (cur_dist_list->credits > - cur_dist_list->cred_norm) - ath6kl_credit_reduce(cred_info, cur_dist_list, - cur_dist_list->cred_norm); + if (cur_list->credits > cur_list->cred_norm) + ath6kl_credit_reduce(cred_info, cur_list, + cur_list->cred_norm); - if (!(cur_dist_list->dist_flags & HTC_EP_ACTIVE)) { - if (cur_dist_list->txq_depth == 0) + if (!(cur_list->dist_flags & HTC_EP_ACTIVE)) { + if (cur_list->txq_depth == 0) ath6kl_credit_reduce(cred_info, - cur_dist_list, 0); + cur_list, 0); } } } @@ -460,8 +462,8 @@ static void htc_async_tx_scat_complete(struct htc_target *target, INIT_LIST_HEAD(&tx_compq); ath6kl_dbg(ATH6KL_DBG_HTC, - "htc tx scat complete len %d entries %d\n", - scat_req->len, scat_req->scat_entries); + "htc tx scat complete len %d entries %d\n", + scat_req->len, scat_req->scat_entries); if (scat_req->status) ath6kl_err("send scatter req failed: %d\n", scat_req->status); @@ -599,8 +601,8 @@ static void ath6kl_htc_tx_pkts_get(struct htc_target *target, list); ath6kl_dbg(ATH6KL_DBG_HTC, - "htc tx got packet 0x%p queue depth %d\n", - packet, get_queue_depth(&endpoint->txq)); + "htc tx got packet 0x%p queue depth %d\n", + packet, get_queue_depth(&endpoint->txq)); len = CALC_TXRX_PADDED_LEN(target, packet->act_len + HTC_HDR_LENGTH); @@ -670,6 +672,7 @@ static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, struct htc_packet *packet; int i, len, rem_scat, cred_pad; int status = 0; + u8 flags; rem_scat = target->max_tx_bndl_sz; @@ -696,9 +699,9 @@ static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, scat_req->scat_list[i].packet = packet; /* prepare packet and flag message as part of a send bundle */ - ath6kl_htc_tx_prep_pkt(packet, - packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE, - cred_pad, packet->info.tx.seqno); + flags = packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE; + ath6kl_htc_tx_prep_pkt(packet, flags, + cred_pad, packet->info.tx.seqno); /* Make sure the buffer is 4-byte aligned */ ath6kl_htc_tx_buf_align(&packet->buf, packet->act_len + HTC_HDR_LENGTH); @@ -744,6 +747,12 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, struct hif_scatter_req *scat_req = NULL; int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0; int status; + u32 txb_mask; + u8 ac = WMM_NUM_AC; + + if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) || + (WMI_CONTROL_SVC != endpoint->svc_id)) + ac = target->dev->ar->ep2ac_map[endpoint->eid]; while (true) { status = 0; @@ -759,10 +768,35 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, if (!scat_req) { /* no scatter resources */ ath6kl_dbg(ATH6KL_DBG_HTC, - "htc tx no more scatter resources\n"); + "htc tx no more scatter resources\n"); break; } + if ((ac < WMM_NUM_AC) && (ac != WMM_AC_BK)) { + if (WMM_AC_BE == ac) + /* + * BE, BK have priorities and bit + * positions reversed + */ + txb_mask = (1 << WMM_AC_BK); + else + /* + * any AC with priority lower than + * itself + */ + txb_mask = ((1 << ac) - 1); + /* + * when the scatter request resources drop below a + * certain threshold, disable Tx bundling for all + * AC's with priority lower than the current requesting + * AC. Otherwise re-enable Tx bundling for them + */ + if (scat_req->scat_q_depth < ATH6KL_SCATTER_REQS) + target->tx_bndl_mask &= ~txb_mask; + else + target->tx_bndl_mask |= txb_mask; + } + ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx pkts to scatter: %d\n", n_scat); @@ -806,6 +840,7 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, struct htc_packet *packet; int bundle_sent; int n_pkts_bundle; + u8 ac = WMM_NUM_AC; spin_lock_bh(&target->tx_lock); @@ -823,6 +858,10 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, */ INIT_LIST_HEAD(&txq); + if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) || + (WMI_CONTROL_SVC != endpoint->svc_id)) + ac = target->dev->ar->ep2ac_map[endpoint->eid]; + while (true) { if (list_empty(&endpoint->txq)) @@ -840,15 +879,18 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, while (true) { /* try to send a bundle on each pass */ - if ((target->tx_bndl_enable) && + if ((target->tx_bndl_mask) && (get_queue_depth(&txq) >= HTC_MIN_HTC_MSGS_TO_BUNDLE)) { int temp1 = 0, temp2 = 0; - ath6kl_htc_tx_bundle(endpoint, &txq, - &temp1, &temp2); - bundle_sent += temp1; - n_pkts_bundle += temp2; + /* check if bundling is enabled for an AC */ + if (target->tx_bndl_mask & (1 << ac)) { + ath6kl_htc_tx_bundle(endpoint, &txq, + &temp1, &temp2); + bundle_sent += temp1; + n_pkts_bundle += temp2; + } } if (list_empty(&txq)) @@ -867,6 +909,26 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, endpoint->ep_st.tx_bundles += bundle_sent; endpoint->ep_st.tx_pkt_bundled += n_pkts_bundle; + + /* + * if an AC has bundling disabled and no tx bundling + * has occured continously for a certain number of TX, + * enable tx bundling for this AC + */ + if (!bundle_sent) { + if (!(target->tx_bndl_mask & (1 << ac)) && + (ac < WMM_NUM_AC)) { + if (++target->ac_tx_count[ac] >= + TX_RESUME_BUNDLE_THRESHOLD) { + target->ac_tx_count[ac] = 0; + target->tx_bndl_mask |= (1 << ac); + } + } + } else { + /* tx bundling will reset the counter */ + if (ac < WMM_NUM_AC) + target->ac_tx_count[ac] = 0; + } } endpoint->tx_proc_cnt = 0; @@ -979,8 +1041,8 @@ static int htc_setup_tx_complete(struct htc_target *target) memcpy(&setup_comp_ext->flags, &flags, sizeof(setup_comp_ext->flags)); set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp_ext, - sizeof(struct htc_setup_comp_ext_msg), - ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + sizeof(struct htc_setup_comp_ext_msg), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); } else { struct htc_setup_comp_msg *setup_comp; @@ -988,8 +1050,8 @@ static int htc_setup_tx_complete(struct htc_target *target) memset(setup_comp, 0, sizeof(struct htc_setup_comp_msg)); setup_comp->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_ID); set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp, - sizeof(struct htc_setup_comp_msg), - ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + sizeof(struct htc_setup_comp_msg), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); } /* we want synchronous operation */ @@ -1088,9 +1150,9 @@ void ath6kl_htc_flush_txep(struct htc_target *target, packet->status = -ECANCELED; list_del(&packet->list); ath6kl_dbg(ATH6KL_DBG_HTC, - "htc tx flushing pkt 0x%p len %d ep %d tag 0x%x\n", - packet, packet->act_len, - packet->endpoint, packet->info.tx.tag); + "htc tx flushing pkt 0x%p len %d ep %d tag 0x%x\n", + packet, packet->act_len, + packet->endpoint, packet->info.tx.tag); INIT_LIST_HEAD(&container); list_add_tail(&packet->list, &container); @@ -1490,7 +1552,7 @@ static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets) if (packets->act_len > 0) { ath6kl_err("htc_ctrl_rx, got message with len:%zu\n", - packets->act_len + HTC_HDR_LENGTH); + packets->act_len + HTC_HDR_LENGTH); ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx unexpected endpoint 0 message", "", @@ -1609,8 +1671,8 @@ static int htc_parse_trailer(struct htc_target *target, } lk_ahd = (struct htc_lookahead_report *) record_buf; - if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) - && next_lk_ahds) { + if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) && + next_lk_ahds) { ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx lk_ahd found pre_valid 0x%x post_valid 0x%x\n", @@ -2038,13 +2100,13 @@ fail_rx: list_for_each_entry_safe(packet, tmp_pkt, rx_pktq, list) { list_del(&packet->list); htc_reclaim_rxbuf(target, packet, - &target->endpoint[packet->endpoint]); + &target->endpoint[packet->endpoint]); } list_for_each_entry_safe(packet, tmp_pkt, &tmp_rxq, list) { list_del(&packet->list); htc_reclaim_rxbuf(target, packet, - &target->endpoint[packet->endpoint]); + &target->endpoint[packet->endpoint]); } return status; @@ -2062,6 +2124,7 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, enum htc_endpoint_id id; int n_fetched = 0; + INIT_LIST_HEAD(&comp_pktq); *num_pkts = 0; /* @@ -2175,11 +2238,11 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target) u32 look_ahead; if (ath6kl_hif_poll_mboxmsg_rx(target->dev, &look_ahead, - HTC_TARGET_RESPONSE_TIMEOUT)) + HTC_TARGET_RESPONSE_TIMEOUT)) return NULL; ath6kl_dbg(ATH6KL_DBG_HTC, - "htc rx wait ctrl look_ahead 0x%X\n", look_ahead); + "htc rx wait ctrl look_ahead 0x%X\n", look_ahead); htc_hdr = (struct htc_frame_hdr *)&look_ahead; @@ -2244,7 +2307,7 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, depth = get_queue_depth(pkt_queue); ath6kl_dbg(ATH6KL_DBG_HTC, - "htc rx add multiple ep id %d cnt %d len %d\n", + "htc rx add multiple ep id %d cnt %d len %d\n", first_pkt->endpoint, depth, first_pkt->buf_len); endpoint = &target->endpoint[first_pkt->endpoint]; @@ -2270,8 +2333,8 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) { if (target->ep_waiting == first_pkt->endpoint) { ath6kl_dbg(ATH6KL_DBG_HTC, - "htc rx blocked on ep %d, unblocking\n", - target->ep_waiting); + "htc rx blocked on ep %d, unblocking\n", + target->ep_waiting); target->rx_st_flags &= ~HTC_RECV_WAIT_BUFFERS; target->ep_waiting = ENDPOINT_MAX; rx_unblock = true; @@ -2308,7 +2371,21 @@ void ath6kl_htc_flush_rx_buf(struct htc_target *target) "htc rx flush pkt 0x%p len %d ep %d\n", packet, packet->buf_len, packet->endpoint); - dev_kfree_skb(packet->pkt_cntxt); + /* + * packets in rx_bufq of endpoint 0 have originally + * been queued from target->free_ctrl_rxbuf where + * packet and packet->buf_start are allocated + * separately using kmalloc(). For other endpoint + * rx_bufq, it is allocated as skb where packet is + * skb->head. Take care of this difference while freeing + * the memory. + */ + if (packet->endpoint == ENDPOINT_0) { + kfree(packet->buf_start); + kfree(packet); + } else { + dev_kfree_skb(packet->pkt_cntxt); + } spin_lock_bh(&target->rx_lock); } spin_unlock_bh(&target->rx_lock); @@ -2327,6 +2404,7 @@ int ath6kl_htc_conn_service(struct htc_target *target, enum htc_endpoint_id assigned_ep = ENDPOINT_MAX; unsigned int max_msg_sz = 0; int status = 0; + u16 msg_id; ath6kl_dbg(ATH6KL_DBG_HTC, "htc connect service target 0x%p service id 0x%x\n", @@ -2370,9 +2448,10 @@ int ath6kl_htc_conn_service(struct htc_target *target, } resp_msg = (struct htc_conn_service_resp *)rx_pkt->buf; + msg_id = le16_to_cpu(resp_msg->msg_id); - if ((le16_to_cpu(resp_msg->msg_id) != HTC_MSG_CONN_SVC_RESP_ID) - || (rx_pkt->act_len < sizeof(*resp_msg))) { + if ((msg_id != HTC_MSG_CONN_SVC_RESP_ID) || + (rx_pkt->act_len < sizeof(*resp_msg))) { status = -ENOMEM; goto fail_tx; } @@ -2419,6 +2498,15 @@ int ath6kl_htc_conn_service(struct htc_target *target, endpoint->cred_dist.endpoint = assigned_ep; endpoint->cred_dist.cred_sz = target->tgt_cred_sz; + switch (endpoint->svc_id) { + case WMI_DATA_BK_SVC: + endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 3; + break; + default: + endpoint->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM; + break; + } + if (conn_req->max_rxmsg_sz) { /* * Override cred_per_msg calculation, this optimizes @@ -2516,7 +2604,8 @@ static void htc_setup_msg_bndl(struct htc_target *target) target->max_rx_bndl_sz, target->max_tx_bndl_sz); if (target->max_tx_bndl_sz) - target->tx_bndl_enable = true; + /* tx_bndl_mask is enabled per AC, each has 1 bit */ + target->tx_bndl_mask = (1 << WMM_NUM_AC) - 1; if (target->max_rx_bndl_sz) target->rx_bndl_enable = true; @@ -2531,7 +2620,7 @@ static void htc_setup_msg_bndl(struct htc_target *target) * padding will spill into the next credit buffer * which is fatal. */ - target->tx_bndl_enable = false; + target->tx_bndl_mask = 0; } } @@ -2543,6 +2632,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); @@ -2582,8 +2677,8 @@ int ath6kl_htc_wait_target(struct htc_target *target) } ath6kl_dbg(ATH6KL_DBG_BOOT, "htc using protocol %s (%d)\n", - (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1", - target->htc_tgt_ver); + (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1", + target->htc_tgt_ver); if (target->msg_per_bndl_max > 0) htc_setup_msg_bndl(target); @@ -2772,17 +2867,19 @@ 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) { + &target->free_ctrl_txbuf, list) { list_del(&packet->list); kfree(packet->buf_start); kfree(packet); } list_for_each_entry_safe(packet, tmp_packet, - &target->free_ctrl_rxbuf, list) { + &target->free_ctrl_rxbuf, list) { list_del(&packet->list); kfree(packet->buf_start); kfree(packet); diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h index 57672e1ed1a6..5027ccc36b62 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.h +++ b/drivers/net/wireless/ath/ath6kl/htc.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -87,6 +88,8 @@ #define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4) #define WMI_MAX_SERVICES 5 +#define WMM_NUM_AC 4 + /* reserved and used to flush ALL packets */ #define HTC_TX_PACKET_TAG_ALL 0 #define HTC_SERVICE_TX_PACKET_TAG 1 @@ -498,6 +501,7 @@ struct htc_endpoint { u8 seqno; u32 conn_flags; struct htc_endpoint_stats ep_st; + u16 tx_drop_packet_threshold; }; struct htc_control_buffer { @@ -519,9 +523,16 @@ struct htc_target { struct ath6kl_htc_credit_info *credit_info; int tgt_creds; unsigned int tgt_cred_sz; + + /* protects free_ctrl_txbuf and free_ctrl_rxbuf */ spinlock_t htc_lock; + + /* FIXME: does this protext rx_bufq and endpoint structures or what? */ spinlock_t rx_lock; + + /* protects endpoint->txq */ spinlock_t tx_lock; + struct ath6kl_device *dev; u32 htc_flags; u32 rx_st_flags; @@ -531,7 +542,7 @@ struct htc_target { /* max messages per bundle for HTC */ int msg_per_bndl_max; - bool tx_bndl_enable; + u32 tx_bndl_mask; int rx_bndl_enable; int max_rx_bndl_sz; int max_tx_bndl_sz; @@ -543,6 +554,9 @@ struct htc_target { int max_xfer_szper_scatreq; int chk_irq_status_cnt; + + /* counts the number of Tx without bundling continously per AC */ + u32 ac_tx_count[WMM_NUM_AC]; }; void *ath6kl_htc_create(struct ath6kl *ar); diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 7f55be3092d1..03cae142f178 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,22 +18,16 @@ #include <linux/moduleparam.h> #include <linux/errno.h> +#include <linux/export.h> #include <linux/of.h> #include <linux/mmc/sdio_func.h> + #include "core.h" #include "cfg80211.h" #include "target.h" #include "debug.h" #include "hif-ops.h" -unsigned int debug_mask; -static unsigned int testmode; -static bool suspend_cutpower; - -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, @@ -47,11 +42,14 @@ static const struct ath6kl_hw hw_list[] = { /* 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 = { + .dir = AR6003_HW_2_0_FW_DIR, + .otp = AR6003_HW_2_0_OTP_FILE, + .fw = AR6003_HW_2_0_FIRMWARE_FILE, + .tcmd = AR6003_HW_2_0_TCMD_FIRMWARE_FILE, + .patch = AR6003_HW_2_0_PATCH_FILE, + }, + .fw_board = AR6003_HW_2_0_BOARD_DATA_FILE, .fw_default_board = AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE, }, @@ -64,14 +62,20 @@ static const struct ath6kl_hw hw_list[] = { .reserved_ram_size = 512, .refclk_hz = 26000000, .uarttx_pin = 8, + .testscript_addr = 0x57ef74, + + .fw = { + .dir = AR6003_HW_2_1_1_FW_DIR, + .otp = AR6003_HW_2_1_1_OTP_FILE, + .fw = AR6003_HW_2_1_1_FIRMWARE_FILE, + .tcmd = AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE, + .patch = AR6003_HW_2_1_1_PATCH_FILE, + .utf = AR6003_HW_2_1_1_UTF_FIRMWARE_FILE, + .testscript = AR6003_HW_2_1_1_TESTSCRIPT_FILE, + }, - .fw_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, + .fw_default_board = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE, }, { .id = AR6004_HW_1_0_VERSION, @@ -84,8 +88,11 @@ static const struct ath6kl_hw hw_list[] = { .refclk_hz = 26000000, .uarttx_pin = 11, - .fw = AR6004_HW_1_0_FIRMWARE_FILE, - .fw_api2 = AR6004_HW_1_0_FIRMWARE_2_FILE, + .fw = { + .dir = AR6004_HW_1_0_FW_DIR, + .fw = AR6004_HW_1_0_FIRMWARE_FILE, + }, + .fw_board = AR6004_HW_1_0_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE, }, @@ -100,8 +107,11 @@ static const struct ath6kl_hw hw_list[] = { .refclk_hz = 40000000, .uarttx_pin = 11, - .fw = AR6004_HW_1_1_FIRMWARE_FILE, - .fw_api2 = AR6004_HW_1_1_FIRMWARE_2_FILE, + .fw = { + .dir = AR6004_HW_1_1_FW_DIR, + .fw = AR6004_HW_1_1_FIRMWARE_FILE, + }, + .fw_board = AR6004_HW_1_1_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE, }, @@ -342,11 +352,7 @@ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, blk_size |= ((u32)htc_ctrl_buf) << 16; /* set the host interest area for the block size */ - status = ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_mbox_io_block_sz)), - (u8 *)&blk_size, - 4); + status = ath6kl_bmi_write_hi32(ar, hi_mbox_io_block_sz, blk_size); if (status) { ath6kl_err("bmi_write_memory for IO block size failed\n"); goto out; @@ -358,11 +364,8 @@ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, if (mbox_isr_yield_val) { /* set the host interest area for the mbox ISR yield limit */ - status = ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_mbox_isr_yield_limit)), - (u8 *)&mbox_isr_yield_val, - 4); + status = ath6kl_bmi_write_hi32(ar, hi_mbox_isr_yield_limit, + mbox_isr_yield_val); if (status) { ath6kl_err("bmi_write_memory for yield limit failed\n"); goto out; @@ -375,7 +378,6 @@ out: static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) { - int status = 0; int ret; /* @@ -383,43 +385,54 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) * default values. Required if checksum offload is needed. Set * RxMetaVersion to 2. */ - if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx, - ar->rx_meta_ver, 0, 0)) { - ath6kl_err("unable to set the rx frame format\n"); - status = -EIO; + ret = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx, + ar->rx_meta_ver, 0, 0); + if (ret) { + ath6kl_err("unable to set the rx frame format: %d\n", ret); + return ret; } - if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) - if ((ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1, - IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN)) != 0) { - ath6kl_err("unable to set power save fail event policy\n"); - status = -EIO; + if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) { + ret = ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1, + IGNORE_PS_FAIL_DURING_SCAN); + if (ret) { + ath6kl_err("unable to set power save fail event policy: %d\n", + ret); + return ret; } + } - if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) - if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0, - WMI_DONOT_IGNORE_BARKER_IN_ERP)) != 0) { - ath6kl_err("unable to set barker preamble policy\n"); - status = -EIO; + if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) { + ret = ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0, + WMI_FOLLOW_BARKER_IN_ERP); + if (ret) { + ath6kl_err("unable to set barker preamble policy: %d\n", + ret); + return ret; } + } - if (ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx, - WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) { - ath6kl_err("unable to set keep alive interval\n"); - status = -EIO; + ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx, + WLAN_CONFIG_KEEP_ALIVE_INTERVAL); + if (ret) { + ath6kl_err("unable to set keep alive interval: %d\n", ret); + return ret; } - if (ath6kl_wmi_disctimeout_cmd(ar->wmi, idx, - WLAN_CONFIG_DISCONNECT_TIMEOUT)) { - ath6kl_err("unable to set disconnect timeout\n"); - status = -EIO; + ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, idx, + WLAN_CONFIG_DISCONNECT_TIMEOUT); + if (ret) { + ath6kl_err("unable to set disconnect timeout: %d\n", ret); + return ret; } - if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) - if (ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED)) { - ath6kl_err("unable to set txop bursting\n"); - status = -EIO; + if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) { + ret = ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED); + if (ret) { + ath6kl_err("unable to set txop bursting: %d\n", ret); + return ret; } + } if (ar->p2p && (ar->vif_max == 1 || idx)) { ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx, @@ -443,7 +456,7 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) } } - return status; + return ret; } int ath6kl_configure_target(struct ath6kl *ar) @@ -452,6 +465,12 @@ int ath6kl_configure_target(struct ath6kl *ar) u8 fw_iftype, fw_mode = 0, fw_submode = 0; int i, status; + param = !!(ar->conf_flags & ATH6KL_CONF_UART_DEBUG); + if (ath6kl_bmi_write_hi32(ar, hi_serial_enable, param)) { + ath6kl_err("bmi_write_memory for uart debug failed\n"); + return -EIO; + } + /* * Note: Even though the firmware interface type is * chosen as BSS_STA for all three interfaces, can @@ -483,11 +502,8 @@ int ath6kl_configure_target(struct ath6kl *ar) if (ar->p2p && ar->vif_max == 1) fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; - param = HTC_PROTOCOL_VERSION; - if (ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_app_host_interest)), - (u8 *)¶m, 4) != 0) { + if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest, + HTC_PROTOCOL_VERSION) != 0) { ath6kl_err("bmi_write_memory for htc version failed\n"); return -EIO; } @@ -495,10 +511,7 @@ int ath6kl_configure_target(struct ath6kl *ar) /* set the firmware mode to STA/IBSS/AP */ param = 0; - if (ath6kl_bmi_read(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_option_flag)), - (u8 *)¶m, 4) != 0) { + if (ath6kl_bmi_read_hi32(ar, hi_option_flag, ¶m) != 0) { ath6kl_err("bmi_read_memory for setting fwmode failed\n"); return -EIO; } @@ -510,11 +523,7 @@ int ath6kl_configure_target(struct ath6kl *ar) param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); - if (ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_option_flag)), - (u8 *)¶m, - 4) != 0) { + if (ath6kl_bmi_write_hi32(ar, hi_option_flag, param) != 0) { ath6kl_err("bmi_write_memory for setting fwmode failed\n"); return -EIO; } @@ -533,16 +542,13 @@ int ath6kl_configure_target(struct ath6kl *ar) param = ar->hw.board_ext_data_addr; ram_reserved_size = ar->hw.reserved_ram_size; - if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_board_ext_data)), - (u8 *)¶m, 4) != 0) { + if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) { ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); return -EIO; } - if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_end_ram_reserve_sz)), - (u8 *)&ram_reserved_size, 4) != 0) { + if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, + ram_reserved_size) != 0) { ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); return -EIO; } @@ -553,56 +559,19 @@ int ath6kl_configure_target(struct ath6kl *ar) 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); + status = ath6kl_bmi_write_hi32(ar, hi_dbg_uart_txpin, + ar->hw.uarttx_pin); 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); + status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, ar->hw.refclk_hz); if (status) return status; return 0; } -void ath6kl_core_free(struct ath6kl *ar) -{ - wiphy_free(ar->wiphy); -} - -void ath6kl_core_cleanup(struct ath6kl *ar) -{ - ath6kl_hif_power_off(ar); - - destroy_workqueue(ar->ath6kl_wq); - - if (ar->htc_target) - ath6kl_htc_cleanup(ar->htc_target); - - ath6kl_cookie_cleanup(ar); - - ath6kl_cleanup_amsdu_rxbufs(ar); - - ath6kl_bmi_cleanup(ar); - - ath6kl_debug_cleanup(ar); - - kfree(ar->fw_board); - kfree(ar->fw_otp); - kfree(ar->fw); - kfree(ar->fw_patch); - - ath6kl_deinit_ieee80211_hw(ar); -} - /* firmware upload */ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, u8 **fw, size_t *fw_len) @@ -626,21 +595,6 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, } #ifdef CONFIG_OF -static const char *get_target_ver_dir(const struct ath6kl *ar) -{ - switch (ar->version.target_ver) { - case AR6003_HW_1_0_VERSION: - return "ath6k/AR6003/hw1.0"; - case AR6003_HW_2_0_VERSION: - return "ath6k/AR6003/hw2.0"; - case AR6003_HW_2_1_1_VERSION: - return "ath6k/AR6003/hw2.1.1"; - } - ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__, - ar->version.target_ver); - return NULL; -} - /* * Check the device tree for a board-id and use it to construct * the pathname to the firmware file. Used (for now) to find a @@ -663,7 +617,7 @@ static bool check_device_tree(struct ath6kl *ar) continue; } snprintf(board_filename, sizeof(board_filename), - "%s/bdata.%s.bin", get_target_ver_dir(ar), board_id); + "%s/bdata.%s.bin", ar->hw.fw.dir, board_id); ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board, &ar->fw_board_len); @@ -730,19 +684,20 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) static int ath6kl_fetch_otp_file(struct ath6kl *ar) { - const char *filename; + char filename[100]; int ret; if (ar->fw_otp != NULL) return 0; - if (ar->hw.fw_otp == NULL) { + if (ar->hw.fw.otp == NULL) { ath6kl_dbg(ATH6KL_DBG_BOOT, "no OTP file configured for this hw\n"); return 0; } - filename = ar->hw.fw_otp; + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.otp); ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, &ar->fw_otp_len); @@ -755,33 +710,61 @@ static int ath6kl_fetch_otp_file(struct ath6kl *ar) return 0; } -static int ath6kl_fetch_fw_file(struct ath6kl *ar) +static int ath6kl_fetch_testmode_file(struct ath6kl *ar) { - const char *filename; + char filename[100]; int ret; - if (ar->fw != NULL) + if (ar->testmode == 0) return 0; - if (testmode) { - if (ar->hw.fw_tcmd == NULL) { - ath6kl_warn("testmode not supported\n"); + ath6kl_dbg(ATH6KL_DBG_BOOT, "testmode %d\n", ar->testmode); + + if (ar->testmode == 2) { + if (ar->hw.fw.utf == NULL) { + ath6kl_warn("testmode 2 not supported\n"); return -EOPNOTSUPP; } - filename = ar->hw.fw_tcmd; + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.utf); + } else { + if (ar->hw.fw.tcmd == NULL) { + ath6kl_warn("testmode 1 not supported\n"); + return -EOPNOTSUPP; + } - set_bit(TESTMODE, &ar->flag); + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.tcmd); + } + + set_bit(TESTMODE, &ar->flag); - goto get_fw; + ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); + if (ret) { + ath6kl_err("Failed to get testmode %d firmware file %s: %d\n", + ar->testmode, filename, ret); + return ret; } - if (WARN_ON(ar->hw.fw == NULL)) + return 0; +} + +static int ath6kl_fetch_fw_file(struct ath6kl *ar) +{ + char filename[100]; + int ret; + + if (ar->fw != NULL) + return 0; + + /* FIXME: remove WARN_ON() as we won't support FW API 1 for long */ + if (WARN_ON(ar->hw.fw.fw == NULL)) return -EINVAL; - filename = ar->hw.fw; + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.fw); -get_fw: ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); if (ret) { ath6kl_err("Failed to get firmware file %s: %d\n", @@ -794,16 +777,17 @@ get_fw: static int ath6kl_fetch_patch_file(struct ath6kl *ar) { - const char *filename; + char filename[100]; int ret; if (ar->fw_patch != NULL) return 0; - if (ar->hw.fw_patch == NULL) + if (ar->hw.fw.patch == NULL) return 0; - filename = ar->hw.fw_patch; + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.patch); ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, &ar->fw_patch_len); @@ -816,6 +800,34 @@ static int ath6kl_fetch_patch_file(struct ath6kl *ar) return 0; } +static int ath6kl_fetch_testscript_file(struct ath6kl *ar) +{ + char filename[100]; + int ret; + + if (ar->testmode != 2) + return 0; + + if (ar->fw_testscript != NULL) + return 0; + + if (ar->hw.fw.testscript == NULL) + return 0; + + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.testscript); + + ret = ath6kl_get_fw(ar, filename, &ar->fw_testscript, + &ar->fw_testscript_len); + if (ret) { + ath6kl_err("Failed to get testscript file %s: %d\n", + filename, ret); + return ret; + } + + return 0; +} + static int ath6kl_fetch_fw_api1(struct ath6kl *ar) { int ret; @@ -832,23 +844,24 @@ static int ath6kl_fetch_fw_api1(struct ath6kl *ar) if (ret) return ret; + ret = ath6kl_fetch_testscript_file(ar); + if (ret) + return ret; + return 0; } -static int ath6kl_fetch_fw_api2(struct ath6kl *ar) +static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) { size_t magic_len, len, ie_len; const struct firmware *fw; struct ath6kl_fw_ie *hdr; - const char *filename; + char filename[100]; const u8 *data; int ret, ie_id, i, index, bit; __le32 *val; - if (ar->hw.fw_api2 == NULL) - return -EOPNOTSUPP; - - filename = ar->hw.fw_api2; + snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, name); ret = request_firmware(&fw, filename, ar->dev); if (ret) @@ -892,7 +905,7 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) switch (ie_id) { case ATH6KL_FW_IE_OTP_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n", - ie_len); + ie_len); ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); @@ -905,7 +918,11 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) break; case ATH6KL_FW_IE_FW_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n", - ie_len); + ie_len); + + /* in testmode we already might have a fw file */ + if (ar->fw != NULL) + break; ar->fw = kmemdup(data, ie_len, GFP_KERNEL); @@ -918,7 +935,7 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) break; case ATH6KL_FW_IE_PATCH_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n", - ie_len); + ie_len); ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); @@ -1010,7 +1027,7 @@ out: return ret; } -static int ath6kl_fetch_firmwares(struct ath6kl *ar) +int ath6kl_init_fetch_firmwares(struct ath6kl *ar) { int ret; @@ -1018,17 +1035,30 @@ static int ath6kl_fetch_firmwares(struct ath6kl *ar) if (ret) return ret; - ret = ath6kl_fetch_fw_api2(ar); + ret = ath6kl_fetch_testmode_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API3_FILE); if (ret == 0) { - ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n"); - return 0; + ar->fw_api = 3; + goto out; + } + + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API2_FILE); + if (ret == 0) { + ar->fw_api = 2; + goto out; } ret = ath6kl_fetch_fw_api1(ar); if (ret) return ret; - ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n"); + ar->fw_api = 1; + +out: + ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api %d\n", ar->fw_api); return 0; } @@ -1049,22 +1079,14 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) */ 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)), - (u8 *) &board_address, 4); + ath6kl_bmi_write_hi32(ar, hi_board_data, + board_address); } else { - ath6kl_bmi_read(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_board_data)), - (u8 *) &board_address, 4); + ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address); } /* determine where in target ram to write extended board data */ - ath6kl_bmi_read(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_board_ext_data)), - (u8 *) &board_ext_address, 4); + ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address); if (ar->target_type == TARGET_TYPE_AR6003 && board_ext_address == 0) { @@ -1076,6 +1098,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) case TARGET_TYPE_AR6003: board_data_size = AR6003_BOARD_DATA_SZ; board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ; + if (ar->fw_board_len > (board_data_size + board_ext_data_size)) + board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ_V2; break; case TARGET_TYPE_AR6004: board_data_size = AR6004_BOARD_DATA_SZ; @@ -1107,10 +1131,7 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) /* record that extended board data is initialized */ param = (board_ext_data_size << 16) | 1; - ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_board_ext_data_config)), - (unsigned char *) ¶m, 4); + ath6kl_bmi_write_hi32(ar, hi_board_ext_data_config, param); } if (ar->fw_board_len < board_data_size) { @@ -1131,11 +1152,7 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) } /* record the fact that Board Data IS initialized */ - param = 1; - ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_board_data_initialized)), - (u8 *)¶m, 4); + ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, 1); return ret; } @@ -1162,10 +1179,7 @@ static int ath6kl_upload_otp(struct ath6kl *ar) } /* read firmware start address */ - ret = ath6kl_bmi_read(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_app_start)), - (u8 *) &address, sizeof(address)); + ret = ath6kl_bmi_read_hi32(ar, hi_app_start, &address); if (ret) { ath6kl_err("Failed to read hi_app_start: %d\n", ret); @@ -1223,7 +1237,7 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) static int ath6kl_upload_patch(struct ath6kl *ar) { - u32 address, param; + u32 address; int ret; if (ar->fw_patch == NULL) @@ -1240,11 +1254,37 @@ static int ath6kl_upload_patch(struct ath6kl *ar) return ret; } - param = address; - ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_dset_list_head)), - (unsigned char *) ¶m, 4); + ath6kl_bmi_write_hi32(ar, hi_dset_list_head, address); + + return 0; +} + +static int ath6kl_upload_testscript(struct ath6kl *ar) +{ + u32 address; + int ret; + + if (ar->testmode != 2) + return 0; + + if (ar->fw_testscript == NULL) + return 0; + + address = ar->hw.testscript_addr; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing testscript to 0x%x (%zd B)\n", + address, ar->fw_testscript_len); + + ret = ath6kl_bmi_write(ar, address, ar->fw_testscript, + ar->fw_testscript_len); + if (ret) { + ath6kl_err("Failed to write testscript file: %d\n", ret); + return ret; + } + + ath6kl_bmi_write_hi32(ar, hi_ota_testscript, address); + ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096); + ath6kl_bmi_write_hi32(ar, hi_test_apps_related, 1); return 0; } @@ -1255,7 +1295,7 @@ static int ath6kl_init_upload(struct ath6kl *ar) int status = 0; if (ar->target_type != TARGET_TYPE_AR6003 && - ar->target_type != TARGET_TYPE_AR6004) + ar->target_type != TARGET_TYPE_AR6004) return -EINVAL; /* temporarily disable system sleep */ @@ -1312,7 +1352,8 @@ static int ath6kl_init_upload(struct ath6kl *ar) return status; /* WAR to avoid SDIO CRC err */ - if (ar->version.target_ver == AR6003_HW_2_0_VERSION) { + if (ar->version.target_ver == AR6003_HW_2_0_VERSION || + ar->version.target_ver == AR6003_HW_2_1_1_VERSION) { ath6kl_err("temporary war to avoid sdio crc error\n"); param = 0x20; @@ -1357,6 +1398,11 @@ static int ath6kl_init_upload(struct ath6kl *ar) if (status) return status; + /* Download the test script */ + status = ath6kl_upload_testscript(ar); + if (status) + return status; + /* Restore system sleep */ address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, sleep); @@ -1372,9 +1418,9 @@ static int ath6kl_init_upload(struct ath6kl *ar) return status; } -static int ath6kl_init_hw_params(struct ath6kl *ar) +int ath6kl_init_hw_params(struct ath6kl *ar) { - const struct ath6kl_hw *hw; + const struct ath6kl_hw *uninitialized_var(hw); int i; for (i = 0; i < ARRAY_SIZE(hw_list); i++) { @@ -1481,10 +1527,11 @@ int ath6kl_init_hw_start(struct ath6kl *ar) if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { - ath6kl_info("%s %s fw %s%s\n", + ath6kl_info("%s %s fw %s api %d%s\n", ar->hw.name, ath6kl_init_get_hif_name(ar->hif_type), ar->wiphy->fw_version, + ar->fw_api, test_bit(TESTMODE, &ar->flag) ? " testmode" : ""); } @@ -1549,173 +1596,7 @@ int ath6kl_init_hw_stop(struct ath6kl *ar) return 0; } -int ath6kl_core_init(struct ath6kl *ar) -{ - struct ath6kl_bmi_target_info targ_info; - struct net_device *ndev; - int ret = 0, i; - - ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); - if (!ar->ath6kl_wq) - return -ENOMEM; - - ret = ath6kl_bmi_init(ar); - if (ret) - goto err_wq; - - /* - * Turn on power to get hardware (target) version and leave power - * on delibrately as we will boot the hardware anyway within few - * seconds. - */ - ret = ath6kl_hif_power_on(ar); - if (ret) - goto err_bmi_cleanup; - - ret = ath6kl_bmi_get_target_info(ar, &targ_info); - if (ret) - goto err_power_off; - - ar->version.target_ver = le32_to_cpu(targ_info.version); - ar->target_type = le32_to_cpu(targ_info.type); - ar->wiphy->hw_version = le32_to_cpu(targ_info.version); - - ret = ath6kl_init_hw_params(ar); - if (ret) - goto err_power_off; - - ar->htc_target = ath6kl_htc_create(ar); - - if (!ar->htc_target) { - ret = -ENOMEM; - goto err_power_off; - } - - ret = ath6kl_fetch_firmwares(ar); - if (ret) - goto err_htc_cleanup; - - /* FIXME: we should free all firmwares in the error cases below */ - - /* Indicate that WMI is enabled (although not ready yet) */ - set_bit(WMI_ENABLED, &ar->flag); - ar->wmi = ath6kl_wmi_init(ar); - if (!ar->wmi) { - ath6kl_err("failed to initialize wmi\n"); - ret = -EIO; - goto err_htc_cleanup; - } - - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); - - ret = ath6kl_register_ieee80211_hw(ar); - if (ret) - goto err_node_cleanup; - - ret = ath6kl_debug_init(ar); - if (ret) { - wiphy_unregister(ar->wiphy); - goto err_node_cleanup; - } - - for (i = 0; i < ar->vif_max; i++) - ar->avail_idx_map |= BIT(i); - - rtnl_lock(); - - /* Add an initial station interface */ - ndev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0, - INFRA_NETWORK); - - rtnl_unlock(); - - if (!ndev) { - ath6kl_err("Failed to instantiate a network device\n"); - ret = -ENOMEM; - wiphy_unregister(ar->wiphy); - goto err_debug_init; - } - - - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", - __func__, ndev->name, ndev, ar); - - /* setup access class priority mappings */ - ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ - ar->ac_stream_pri_map[WMM_AC_BE] = 1; - ar->ac_stream_pri_map[WMM_AC_VI] = 2; - ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ - - /* give our connected endpoints some buffers */ - ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); - ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); - - /* allocate some buffers that handle larger AMSDU frames */ - ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); - - ath6kl_cookie_init(ar); - - ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | - ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; - - if (suspend_cutpower) - ar->conf_flags |= ATH6KL_CONF_SUSPEND_CUTPOWER; - - ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | - WIPHY_FLAG_HAVE_AP_SME | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; - - if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, 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); - - ret = ath6kl_init_hw_start(ar); - if (ret) { - ath6kl_err("Failed to start hardware: %d\n", ret); - goto err_rxbuf_cleanup; - } - - /* - * Set mac address which is received in ready event - * FIXME: Move to ath6kl_interface_add() - */ - memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); - - return ret; - -err_rxbuf_cleanup: - ath6kl_htc_flush_rx_buf(ar->htc_target); - ath6kl_cleanup_amsdu_rxbufs(ar); - rtnl_lock(); - ath6kl_deinit_if_data(netdev_priv(ndev)); - rtnl_unlock(); - wiphy_unregister(ar->wiphy); -err_debug_init: - ath6kl_debug_cleanup(ar); -err_node_cleanup: - ath6kl_wmi_shutdown(ar->wmi); - clear_bit(WMI_ENABLED, &ar->flag); - ar->wmi = NULL; -err_htc_cleanup: - ath6kl_htc_cleanup(ar->htc_target); -err_power_off: - ath6kl_hif_power_off(ar); -err_bmi_cleanup: - ath6kl_bmi_cleanup(ar); -err_wq: - destroy_workqueue(ar->ath6kl_wq); - - return ret; -} - +/* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) { static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; @@ -1747,6 +1628,7 @@ void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) void ath6kl_stop_txrx(struct ath6kl *ar) { struct ath6kl_vif *vif, *tmp_vif; + int i; set_bit(DESTROY_IN_PROGRESS, &ar->flag); @@ -1755,13 +1637,16 @@ void ath6kl_stop_txrx(struct ath6kl *ar) return; } + for (i = 0; i < AP_MAX_NUM_STA; i++) + aggr_reset_state(ar->sta_list[i].aggr_conn); + spin_lock_bh(&ar->list_lock); list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) { list_del(&vif->list); spin_unlock_bh(&ar->list_lock); ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); rtnl_lock(); - ath6kl_deinit_if_data(vif); + ath6kl_cfg80211_vif_cleanup(vif); rtnl_unlock(); spin_lock_bh(&ar->list_lock); } @@ -1791,8 +1676,11 @@ void ath6kl_stop_txrx(struct ath6kl *ar) * configure NOT to reset the target during a debug session. */ ath6kl_dbg(ATH6KL_DBG_TRC, - "attempting to reset target on instance destroy\n"); + "attempting to reset target on instance destroy\n"); ath6kl_reset_device(ar, ar->target_type, true, true); clear_bit(WLAN_ENABLED, &ar->flag); + + up(&ar->sem); } +EXPORT_SYMBOL(ath6kl_stop_txrx); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index eea3c747653e..229e1922ebe4 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -52,9 +53,11 @@ struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid) return conn; } -static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie, - u8 ielen, u8 keymgmt, u8 ucipher, u8 auth) +static void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u16 aid, + u8 *wpaie, size_t ielen, u8 keymgmt, + u8 ucipher, u8 auth, u8 apsd_info) { + struct ath6kl *ar = vif->ar; struct ath6kl_sta *sta; u8 free_slot; @@ -68,18 +71,31 @@ static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie, sta->keymgmt = keymgmt; sta->ucipher = ucipher; sta->auth = auth; + sta->apsd_info = apsd_info; ar->sta_list_index = ar->sta_list_index | (1 << free_slot); ar->ap_stats.sta[free_slot].aid = cpu_to_le32(aid); + aggr_conn_init(vif, vif->aggr_cntxt, sta->aggr_conn); } static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i) { struct ath6kl_sta *sta = &ar->sta_list[i]; + struct ath6kl_mgmt_buff *entry, *tmp; /* empty the queued pkts in the PS queue if any */ spin_lock_bh(&sta->psq_lock); skb_queue_purge(&sta->psq); + skb_queue_purge(&sta->apsdq); + + if (sta->mgmt_psq_len != 0) { + list_for_each_entry_safe(entry, tmp, &sta->mgmt_psq, list) { + kfree(entry); + } + INIT_LIST_HEAD(&sta->mgmt_psq); + sta->mgmt_psq_len = 0; + } + spin_unlock_bh(&sta->psq_lock); memset(&ar->ap_stats.sta[sta->aid - 1], 0, @@ -90,7 +106,7 @@ static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i) sta->sta_flags = 0; ar->sta_list_index = ar->sta_list_index & ~(1 << i); - + aggr_reset_state(sta->aggr_conn); } static u8 ath6kl_remove_sta(struct ath6kl *ar, u8 *mac, u16 reason) @@ -252,7 +268,7 @@ int ath6kl_read_fwlogs(struct ath6kl *ar) struct ath6kl_dbglog_hdr debug_hdr; struct ath6kl_dbglog_buf debug_buf; u32 address, length, dropped, firstbuf, debug_hdr_addr; - int ret = 0, loop; + int ret, loop; u8 *buf; buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL); @@ -334,7 +350,7 @@ void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, __le32 data; if (target_type != TARGET_TYPE_AR6003 && - target_type != TARGET_TYPE_AR6004) + target_type != TARGET_TYPE_AR6004) return; data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) : @@ -347,9 +363,6 @@ void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, case TARGET_TYPE_AR6004: address = AR6004_RESET_CONTROL_ADDRESS; break; - default: - address = AR6003_RESET_CONTROL_ADDRESS; - break; } status = ath6kl_diag_write32(ar, address, data); @@ -363,7 +376,7 @@ static void ath6kl_install_static_wep_keys(struct ath6kl_vif *vif) u8 index; u8 keyusage; - for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) { + for (index = 0; index <= WMI_MAX_KEY_INDEX; index++) { if (vif->wep_key_list[index].key_len) { keyusage = GROUP_USAGE; if (index == vif->def_txkey_index) @@ -428,9 +441,8 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, u8 keymgmt, u8 ucipher, u8 auth, - u8 assoc_req_len, u8 *assoc_info) + u8 assoc_req_len, u8 *assoc_info, u8 apsd_info) { - struct ath6kl *ar = vif->ar; u8 *ies = NULL, *wpa_ie = NULL, *pos; size_t ies_len = 0; struct station_info sinfo; @@ -484,9 +496,9 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, pos += 2 + pos[1]; } - ath6kl_add_new_sta(ar, mac_addr, aid, wpa_ie, + ath6kl_add_new_sta(vif, mac_addr, aid, wpa_ie, wpa_ie ? 2 + wpa_ie[1] : 0, - keymgmt, ucipher, auth); + keymgmt, ucipher, auth, apsd_info); /* send event to application */ memset(&sinfo, 0, sizeof(sinfo)); @@ -589,8 +601,7 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, if ((vif->nw_type == INFRA_NETWORK)) ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, - ar->listen_intvl_t, - ar->listen_intvl_b); + vif->listen_intvl_t, 0); netif_wake_queue(vif->ndev); @@ -601,7 +612,7 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, netif_carrier_on(vif->ndev); spin_unlock_bh(&vif->if_lock); - aggr_reset_state(vif->aggr_cntxt); + aggr_reset_state(vif->aggr_cntxt->aggr_conn); vif->reconnect_flag = 0; if ((vif->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable) { @@ -808,6 +819,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid) struct sk_buff *skb; bool psq_empty = false; struct ath6kl *ar = vif->ar; + struct ath6kl_mgmt_buff *mgmt_buf; conn = ath6kl_find_sta_by_aid(ar, aid); @@ -818,7 +830,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid) * becomes empty update the PVB for this station. */ spin_lock_bh(&conn->psq_lock); - psq_empty = skb_queue_empty(&conn->psq); + psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0); spin_unlock_bh(&conn->psq_lock); if (psq_empty) @@ -826,15 +838,31 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid) return; spin_lock_bh(&conn->psq_lock); - skb = skb_dequeue(&conn->psq); - spin_unlock_bh(&conn->psq_lock); + if (conn->mgmt_psq_len > 0) { + mgmt_buf = list_first_entry(&conn->mgmt_psq, + struct ath6kl_mgmt_buff, list); + list_del(&mgmt_buf->list); + conn->mgmt_psq_len--; + spin_unlock_bh(&conn->psq_lock); + + conn->sta_flags |= STA_PS_POLLED; + ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, + mgmt_buf->id, mgmt_buf->freq, + mgmt_buf->wait, mgmt_buf->buf, + mgmt_buf->len, mgmt_buf->no_cck); + conn->sta_flags &= ~STA_PS_POLLED; + kfree(mgmt_buf); + } else { + skb = skb_dequeue(&conn->psq); + spin_unlock_bh(&conn->psq_lock); - conn->sta_flags |= STA_PS_POLLED; - ath6kl_data_tx(skb, vif->ndev); - conn->sta_flags &= ~STA_PS_POLLED; + conn->sta_flags |= STA_PS_POLLED; + ath6kl_data_tx(skb, vif->ndev); + conn->sta_flags &= ~STA_PS_POLLED; + } spin_lock_bh(&conn->psq_lock); - psq_empty = skb_queue_empty(&conn->psq); + psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0); spin_unlock_bh(&conn->psq_lock); if (psq_empty) @@ -920,10 +948,10 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, } ath6kl_cfg80211_disconnect_event(vif, reason, bssid, - assoc_resp_len, assoc_info, - prot_reason_status); + assoc_resp_len, assoc_info, + prot_reason_status); - aggr_reset_state(vif->aggr_cntxt); + aggr_reset_state(vif->aggr_cntxt->aggr_conn); del_timer(&vif->disconnect_timer); @@ -941,9 +969,9 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, } else { set_bit(CONNECT_PEND, &vif->flags); if (((reason == ASSOC_FAILED) && - (prot_reason_status == 0x11)) || - ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) - && (vif->reconnect_flag == 1))) { + (prot_reason_status == 0x11)) || + ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) && + (vif->reconnect_flag == 1))) { set_bit(CONNECTED, &vif->flags); return; } @@ -1020,11 +1048,155 @@ static struct net_device_stats *ath6kl_get_stats(struct net_device *dev) return &vif->net_stats; } -static struct net_device_ops ath6kl_netdev_ops = { +static int ath6kl_set_features(struct net_device *dev, + netdev_features_t features) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl *ar = vif->ar; + int err = 0; + + if ((features & NETIF_F_RXCSUM) && + (ar->rx_meta_ver != WMI_META_VERSION_2)) { + ar->rx_meta_ver = WMI_META_VERSION_2; + err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, + vif->fw_vif_idx, + ar->rx_meta_ver, 0, 0); + if (err) { + dev->features = features & ~NETIF_F_RXCSUM; + return err; + } + } else if (!(features & NETIF_F_RXCSUM) && + (ar->rx_meta_ver == WMI_META_VERSION_2)) { + ar->rx_meta_ver = 0; + err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, + vif->fw_vif_idx, + ar->rx_meta_ver, 0, 0); + if (err) { + dev->features = features | NETIF_F_RXCSUM; + return err; + } + + } + + return err; +} + +static void ath6kl_set_multicast_list(struct net_device *ndev) +{ + struct ath6kl_vif *vif = netdev_priv(ndev); + bool mc_all_on = false, mc_all_off = false; + int mc_count = netdev_mc_count(ndev); + struct netdev_hw_addr *ha; + bool found; + struct ath6kl_mc_filter *mc_filter, *tmp; + struct list_head mc_filter_new; + int ret; + + if (!test_bit(WMI_READY, &vif->ar->flag) || + !test_bit(WLAN_ENABLED, &vif->flags)) + return; + + mc_all_on = !!(ndev->flags & IFF_PROMISC) || + !!(ndev->flags & IFF_ALLMULTI) || + !!(mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST); + + mc_all_off = !(ndev->flags & IFF_MULTICAST) || mc_count == 0; + + if (mc_all_on || mc_all_off) { + /* Enable/disable all multicast */ + ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast filter\n", + mc_all_on ? "enabling" : "disabling"); + ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, + mc_all_on); + if (ret) + ath6kl_warn("Failed to %s multicast receive\n", + mc_all_on ? "enable" : "disable"); + return; + } + + list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) { + found = false; + netdev_for_each_mc_addr(ha, ndev) { + if (memcmp(ha->addr, mc_filter->hw_addr, + ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { + found = true; + break; + } + } + + if (!found) { + /* + * Delete the filter which was previously set + * but not in the new request. + */ + ath6kl_dbg(ATH6KL_DBG_TRC, + "Removing %pM from multicast filter\n", + mc_filter->hw_addr); + ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi, + vif->fw_vif_idx, mc_filter->hw_addr, + false); + if (ret) { + ath6kl_warn("Failed to remove multicast filter:%pM\n", + mc_filter->hw_addr); + return; + } + + list_del(&mc_filter->list); + kfree(mc_filter); + } + } + + INIT_LIST_HEAD(&mc_filter_new); + + netdev_for_each_mc_addr(ha, ndev) { + found = false; + list_for_each_entry(mc_filter, &vif->mc_filter, list) { + if (memcmp(ha->addr, mc_filter->hw_addr, + ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { + found = true; + break; + } + } + + if (!found) { + mc_filter = kzalloc(sizeof(struct ath6kl_mc_filter), + GFP_ATOMIC); + if (!mc_filter) { + WARN_ON(1); + goto out; + } + + memcpy(mc_filter->hw_addr, ha->addr, + ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE); + /* Set the multicast filter */ + ath6kl_dbg(ATH6KL_DBG_TRC, + "Adding %pM to multicast filter list\n", + mc_filter->hw_addr); + ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi, + vif->fw_vif_idx, mc_filter->hw_addr, + true); + if (ret) { + ath6kl_warn("Failed to add multicast filter :%pM\n", + mc_filter->hw_addr); + kfree(mc_filter); + goto out; + } + + list_add_tail(&mc_filter->list, &mc_filter_new); + } + } + +out: + list_splice_tail(&mc_filter_new, &vif->mc_filter); +} + +static const struct net_device_ops ath6kl_netdev_ops = { .ndo_open = ath6kl_open, .ndo_stop = ath6kl_close, .ndo_start_xmit = ath6kl_data_tx, .ndo_get_stats = ath6kl_get_stats, + .ndo_set_features = ath6kl_set_features, + .ndo_set_rx_mode = ath6kl_set_multicast_list, }; void init_netdev(struct net_device *dev) @@ -1038,5 +1210,7 @@ void init_netdev(struct net_device *dev) sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES; + dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; + return; } diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 9475e2d0d0b7..53528648b425 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -31,6 +32,7 @@ struct ath6kl_sdio { struct sdio_func *func; + /* protects access to bus_req_freeq */ spinlock_t lock; /* free list */ @@ -49,14 +51,20 @@ struct ath6kl_sdio { /* scatter request list head */ struct list_head scat_req; + atomic_t irq_handling; + wait_queue_head_t irq_wq; + + /* protects access to scat_req */ spinlock_t scat_lock; + bool scatter_enabled; bool is_disabled; - atomic_t irq_handling; const struct sdio_device_id *id; struct work_struct wr_async_work; struct list_head wr_asyncq; + + /* protects access to wr_asyncq */ spinlock_t wr_async_lock; }; @@ -402,7 +410,10 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, return -ENOMEM; mutex_lock(&ar_sdio->dma_buffer_mutex); tbuf = ar_sdio->dma_buffer; - memcpy(tbuf, buf, len); + + if (request & HIF_WRITE) + memcpy(tbuf, buf, len); + bounced = true; } else tbuf = buf; @@ -461,7 +472,6 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func) ar_sdio = sdio_get_drvdata(func); atomic_set(&ar_sdio->irq_handling, 1); - /* * Release the host during interrups so we can pick it back up when * we process commands. @@ -470,7 +480,10 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func) status = ath6kl_hif_intr_bh_handler(ar_sdio->ar); sdio_claim_host(ar_sdio->func); + atomic_set(&ar_sdio->irq_handling, 0); + wake_up(&ar_sdio->irq_wq); + WARN_ON(status && status != -ECANCELED); } @@ -571,6 +584,13 @@ static void ath6kl_sdio_irq_enable(struct ath6kl *ar) sdio_release_host(ar_sdio->func); } +static bool ath6kl_sdio_is_on_irq(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + + return !atomic_read(&ar_sdio->irq_handling); +} + static void ath6kl_sdio_irq_disable(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); @@ -578,10 +598,14 @@ static void ath6kl_sdio_irq_disable(struct ath6kl *ar) sdio_claim_host(ar_sdio->func); - /* Mask our function IRQ */ - while (atomic_read(&ar_sdio->irq_handling)) { + if (atomic_read(&ar_sdio->irq_handling)) { sdio_release_host(ar_sdio->func); - schedule_timeout(HZ / 10); + + ret = wait_event_interruptible(ar_sdio->irq_wq, + ath6kl_sdio_is_on_irq(ar)); + if (ret) + return; + sdio_claim_host(ar_sdio->func); } @@ -603,6 +627,8 @@ static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar) node = list_first_entry(&ar_sdio->scat_req, struct hif_scatter_req, list); list_del(&node->list); + + node->scat_q_depth = get_queue_depth(&ar_sdio->scat_req); } spin_unlock_bh(&ar_sdio->scat_lock); @@ -635,8 +661,8 @@ static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar, return -EINVAL; ath6kl_dbg(ATH6KL_DBG_SCATTER, - "hif-scatter: total len: %d scatter entries: %d\n", - scat_req->len, scat_req->scat_entries); + "hif-scatter: total len: %d scatter entries: %d\n", + scat_req->len, scat_req->scat_entries); if (request & HIF_SYNCHRONOUS) status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest); @@ -772,7 +798,6 @@ static int ath6kl_sdio_config(struct ath6kl *ar) if (ret) { ath6kl_err("Set sdio block size %d failed: %d)\n", HIF_MBOX_BLOCK_SIZE, ret); - sdio_release_host(func); goto out; } @@ -782,7 +807,7 @@ out: return ret; } -static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct sdio_func *func = ar_sdio->func; @@ -793,60 +818,104 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags); - if (!(flags & MMC_PM_KEEP_POWER) || - (ar->conf_flags & ATH6KL_CONF_SUSPEND_CUTPOWER)) { - /* as host doesn't support keep power we need to cut power */ - return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, - NULL); - } + if (!(flags & MMC_PM_WAKE_SDIO_IRQ) || + !(flags & MMC_PM_KEEP_POWER)) + return -EINVAL; ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) { - printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n", - ret); + ath6kl_err("set sdio keep pwr flag failed: %d\n", ret); return ret; } - if (!(flags & MMC_PM_WAKE_SDIO_IRQ)) - goto deepsleep; - /* sdio irq wakes up host */ + ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); + if (ret) + ath6kl_err("set sdio wake irq flag failed: %d\n", ret); + + return ret; +} + +static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct sdio_func *func = ar_sdio->func; + mmc_pm_flag_t flags; + bool try_deepsleep = false; + int ret; if (ar->state == ATH6KL_STATE_SCHED_SCAN) { + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan is in progress\n"); + + ret = ath6kl_set_sdio_pm_caps(ar); + if (ret) + goto cut_pwr; + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_SCHED_SCAN, NULL); - if (ret) { - ath6kl_warn("Schedule scan suspend failed: %d", ret); - return ret; - } + if (ret) + goto cut_pwr; + + return 0; + } + + if (ar->suspend_mode == WLAN_POWER_STATE_WOW || + (!ar->suspend_mode && wow)) { - ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); + ret = ath6kl_set_sdio_pm_caps(ar); if (ret) - ath6kl_warn("set sdio wake irq flag failed: %d\n", ret); + goto cut_pwr; - return ret; + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow); + if (ret && ret != -ENOTCONN) + ath6kl_err("wow suspend failed: %d\n", ret); + + if (ret && + (!ar->wow_suspend_mode || + ar->wow_suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP)) + try_deepsleep = true; + else if (ret && + ar->wow_suspend_mode == WLAN_POWER_STATE_CUT_PWR) + goto cut_pwr; + if (!ret) + return 0; } - if (wow) { + if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP || + !ar->suspend_mode || try_deepsleep) { + + flags = sdio_get_host_pm_caps(func); + if (!(flags & MMC_PM_KEEP_POWER)) + goto cut_pwr; + + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + goto cut_pwr; + /* - * The host sdio controller is capable of keep power and - * sdio irq wake up at this point. It's fine to continue - * wow suspend operation. + * Workaround to support Deep Sleep with MSM, set the host pm + * flag as MMC_PM_WAKE_SDIO_IRQ to allow SDCC deiver to disable + * the sdc2_clock and internally allows MSM to enter + * TCXO shutdown properly. */ - ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow); - if (ret) - return ret; + if ((flags & MMC_PM_WAKE_SDIO_IRQ)) { + ret = sdio_set_host_pm_flags(func, + MMC_PM_WAKE_SDIO_IRQ); + if (ret) + goto cut_pwr; + } - ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, + NULL); if (ret) - ath6kl_err("set sdio wake irq flag failed: %d\n", ret); + goto cut_pwr; - return ret; + return 0; } -deepsleep: - return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL); +cut_pwr: + return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL); } static int ath6kl_sdio_resume(struct ath6kl *ar) @@ -869,8 +938,15 @@ static int ath6kl_sdio_resume(struct ath6kl *ar) case ATH6KL_STATE_WOW: break; + case ATH6KL_STATE_SCHED_SCAN: break; + + case ATH6KL_STATE_SUSPENDING: + break; + + case ATH6KL_STATE_RESUMING: + break; } ath6kl_cfg80211_resume(ar); @@ -949,7 +1025,7 @@ static int ath6kl_sdio_diag_read32(struct ath6kl *ar, u32 address, u32 *data) (u8 *)data, sizeof(u32), HIF_RD_SYNC_BYTE_INC); if (status) { ath6kl_err("%s: failed to read from window data addr\n", - __func__); + __func__); return status; } @@ -1260,10 +1336,12 @@ static int ath6kl_sdio_probe(struct sdio_func *func, INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work); + init_waitqueue_head(&ar_sdio->irq_wq); + for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]); - ar = ath6kl_core_alloc(&ar_sdio->func->dev); + ar = ath6kl_core_create(&ar_sdio->func->dev); if (!ar) { ath6kl_err("Failed to alloc ath6kl core\n"); ret = -ENOMEM; @@ -1293,7 +1371,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, return ret; err_core_alloc: - ath6kl_core_free(ar_sdio->ar); + ath6kl_core_destroy(ar_sdio->ar); err_dma: kfree(ar_sdio->dma_buffer); err_hif: @@ -1316,6 +1394,7 @@ static void ath6kl_sdio_remove(struct sdio_func *func) cancel_work_sync(&ar_sdio->wr_async_work); ath6kl_core_cleanup(ar_sdio->ar); + ath6kl_core_destroy(ar_sdio->ar); kfree(ar_sdio->dma_buffer); kfree(ar_sdio); @@ -1332,7 +1411,7 @@ static const struct sdio_device_id ath6kl_sdio_devices[] = { 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, @@ -1362,19 +1441,19 @@ MODULE_AUTHOR("Atheros Communications, Inc."); MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices"); MODULE_LICENSE("Dual BSD/GPL"); -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_FW_DIR "/" AR6003_HW_2_0_OTP_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_PATCH_FILE); MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE); -MODULE_FIRMWARE(AR6003_HW_2_1_1_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_FW_DIR "/" AR6003_HW_2_1_1_OTP_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_PATCH_FILE); MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE); -MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); -MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index 108a723a1085..78e0ef4567a5 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2010 Atheros Communications Inc. + * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,6 +20,7 @@ #define AR6003_BOARD_DATA_SZ 1024 #define AR6003_BOARD_EXT_DATA_SZ 768 +#define AR6003_BOARD_EXT_DATA_SZ_V2 1024 #define AR6004_BOARD_DATA_SZ 6144 #define AR6004_BOARD_EXT_DATA_SZ 0 diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c index 381eb66a605f..6675c92b542b 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.c +++ b/drivers/net/wireless/ath/ath6kl/testmode.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,6 +16,7 @@ */ #include "testmode.h" +#include "debug.h" #include <net/netlink.h> @@ -30,7 +32,7 @@ enum ath6kl_tm_attr { enum ath6kl_tm_cmd { ATH6KL_TM_CMD_TCMD = 0, - ATH6KL_TM_CMD_RX_REPORT = 1, + ATH6KL_TM_CMD_RX_REPORT = 1, /* not used anymore */ }; #define ATH6KL_TM_DATA_MAX_LEN 5000 @@ -41,84 +43,33 @@ static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = { .len = ATH6KL_TM_DATA_MAX_LEN }, }; -void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len) +void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len) { - if (down_interruptible(&ar->sem)) - return; - - kfree(ar->tm.rx_report); - - ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL); - ar->tm.rx_report_len = buf_len; - - up(&ar->sem); - - wake_up(&ar->event_wq); -} - -static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len, - struct sk_buff *skb) -{ - int ret = 0; - long left; - - if (down_interruptible(&ar->sem)) - return -ERESTARTSYS; - - if (!test_bit(WMI_READY, &ar->flag)) { - ret = -EIO; - goto out; - } - - if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { - ret = -EBUSY; - goto out; - } - - if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) { - up(&ar->sem); - return -EIO; - } - - left = wait_event_interruptible_timeout(ar->event_wq, - ar->tm.rx_report != NULL, - WMI_TIMEOUT); + struct sk_buff *skb; - if (left == 0) { - ret = -ETIMEDOUT; - goto out; - } else if (left < 0) { - ret = left; - goto out; - } + if (!buf || buf_len == 0) + return; - if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) { - ret = -EINVAL; - goto out; + skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_KERNEL); + if (!skb) { + ath6kl_warn("failed to allocate testmode rx skb!\n"); + return; } - - NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len, - ar->tm.rx_report); - - kfree(ar->tm.rx_report); - ar->tm.rx_report = NULL; - -out: - up(&ar->sem); - - return ret; + NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD); + NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf); + cfg80211_testmode_event(skb, GFP_KERNEL); + return; nla_put_failure: - ret = -ENOBUFS; - goto out; + kfree_skb(skb); + ath6kl_warn("nla_put failed on testmode rx skb!\n"); } int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) { struct ath6kl *ar = wiphy_priv(wiphy); struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; - int err, buf_len, reply_len; - struct sk_buff *skb; + int err, buf_len; void *buf; err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, @@ -143,24 +94,6 @@ int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) break; case ATH6KL_TM_CMD_RX_REPORT: - if (!tb[ATH6KL_TM_ATTR_DATA]) - return -EINVAL; - - buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); - buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); - - reply_len = nla_total_size(ATH6KL_TM_DATA_MAX_LEN); - skb = cfg80211_testmode_alloc_reply_skb(wiphy, reply_len); - if (!skb) - return -ENOMEM; - - err = ath6kl_tm_rx_report(ar, buf, buf_len, skb); - if (err < 0) { - kfree_skb(skb); - return err; - } - - return cfg80211_testmode_reply(skb); default: return -EOPNOTSUPP; } diff --git a/drivers/net/wireless/ath/ath6kl/testmode.h b/drivers/net/wireless/ath/ath6kl/testmode.h index 43dffcc11fb1..fe651d6707df 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.h +++ b/drivers/net/wireless/ath/ath6kl/testmode.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,13 +19,13 @@ #ifdef CONFIG_NL80211_TESTMODE -void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len); +void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len); int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len); #else -static inline void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, - size_t buf_len) +static inline void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, + size_t buf_len) { } diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 506a3031a885..f85353fd1792 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,6 +18,23 @@ #include "core.h" #include "debug.h" +/* + * tid - tid_mux0..tid_mux3 + * aid - tid_mux4..tid_mux7 + */ +#define ATH6KL_TID_MASK 0xf +#define ATH6KL_AID_SHIFT 4 + +static inline u8 ath6kl_get_tid(u8 tid_mux) +{ + return tid_mux & ATH6KL_TID_MASK; +} + +static inline u8 ath6kl_get_aid(u8 tid_mux) +{ + return tid_mux >> ATH6KL_AID_SHIFT; +} + static u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev, u32 *map_no) { @@ -77,12 +95,118 @@ static u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev, return ar->node_map[ep_map].ep_id; } +static bool ath6kl_process_uapsdq(struct ath6kl_sta *conn, + struct ath6kl_vif *vif, + struct sk_buff *skb, + u32 *flags) +{ + struct ath6kl *ar = vif->ar; + bool is_apsdq_empty = false; + struct ethhdr *datap = (struct ethhdr *) skb->data; + u8 up = 0, traffic_class, *ip_hdr; + u16 ether_type; + struct ath6kl_llc_snap_hdr *llc_hdr; + + if (conn->sta_flags & STA_PS_APSD_TRIGGER) { + /* + * This tx is because of a uAPSD trigger, determine + * more and EOSP bit. Set EOSP if queue is empty + * or sufficient frames are delivered for this trigger. + */ + spin_lock_bh(&conn->psq_lock); + if (!skb_queue_empty(&conn->apsdq)) + *flags |= WMI_DATA_HDR_FLAGS_MORE; + else if (conn->sta_flags & STA_PS_APSD_EOSP) + *flags |= WMI_DATA_HDR_FLAGS_EOSP; + *flags |= WMI_DATA_HDR_FLAGS_UAPSD; + spin_unlock_bh(&conn->psq_lock); + return false; + } else if (!conn->apsd_info) + return false; + + if (test_bit(WMM_ENABLED, &vif->flags)) { + ether_type = be16_to_cpu(datap->h_proto); + if (is_ethertype(ether_type)) { + /* packet is in DIX format */ + ip_hdr = (u8 *)(datap + 1); + } else { + /* packet is in 802.3 format */ + llc_hdr = (struct ath6kl_llc_snap_hdr *) + (datap + 1); + ether_type = be16_to_cpu(llc_hdr->eth_type); + ip_hdr = (u8 *)(llc_hdr + 1); + } + + if (ether_type == IP_ETHERTYPE) + up = ath6kl_wmi_determine_user_priority( + ip_hdr, 0); + } + + traffic_class = ath6kl_wmi_get_traffic_class(up); + + if ((conn->apsd_info & (1 << traffic_class)) == 0) + return false; + + /* Queue the frames if the STA is sleeping */ + spin_lock_bh(&conn->psq_lock); + is_apsdq_empty = skb_queue_empty(&conn->apsdq); + skb_queue_tail(&conn->apsdq, skb); + spin_unlock_bh(&conn->psq_lock); + + /* + * If this is the first pkt getting queued + * for this STA, update the PVB for this STA + */ + if (is_apsdq_empty) { + ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi, + vif->fw_vif_idx, + conn->aid, 1, 0); + } + *flags |= WMI_DATA_HDR_FLAGS_UAPSD; + + return true; +} + +static bool ath6kl_process_psq(struct ath6kl_sta *conn, + struct ath6kl_vif *vif, + struct sk_buff *skb, + u32 *flags) +{ + bool is_psq_empty = false; + struct ath6kl *ar = vif->ar; + + if (conn->sta_flags & STA_PS_POLLED) { + spin_lock_bh(&conn->psq_lock); + if (!skb_queue_empty(&conn->psq)) + *flags |= WMI_DATA_HDR_FLAGS_MORE; + spin_unlock_bh(&conn->psq_lock); + return false; + } + + /* Queue the frames if the STA is sleeping */ + spin_lock_bh(&conn->psq_lock); + is_psq_empty = skb_queue_empty(&conn->psq); + skb_queue_tail(&conn->psq, skb); + spin_unlock_bh(&conn->psq_lock); + + /* + * If this is the first pkt getting queued + * for this STA, update the PVB for this + * STA. + */ + if (is_psq_empty) + ath6kl_wmi_set_pvb_cmd(ar->wmi, + vif->fw_vif_idx, + conn->aid, 1); + return true; +} + static bool ath6kl_powersave_ap(struct ath6kl_vif *vif, struct sk_buff *skb, - bool *more_data) + u32 *flags) { struct ethhdr *datap = (struct ethhdr *) skb->data; struct ath6kl_sta *conn = NULL; - bool ps_queued = false, is_psq_empty = false; + bool ps_queued = false; struct ath6kl *ar = vif->ar; if (is_multicast_ether_addr(datap->h_dest)) { @@ -128,7 +252,7 @@ static bool ath6kl_powersave_ap(struct ath6kl_vif *vif, struct sk_buff *skb, */ spin_lock_bh(&ar->mcastpsq_lock); if (!skb_queue_empty(&ar->mcastpsq)) - *more_data = true; + *flags |= WMI_DATA_HDR_FLAGS_MORE; spin_unlock_bh(&ar->mcastpsq_lock); } } @@ -142,37 +266,13 @@ static bool ath6kl_powersave_ap(struct ath6kl_vif *vif, struct sk_buff *skb, } if (conn->sta_flags & STA_PS_SLEEP) { - if (!(conn->sta_flags & STA_PS_POLLED)) { - /* Queue the frames if the STA is sleeping */ - spin_lock_bh(&conn->psq_lock); - is_psq_empty = skb_queue_empty(&conn->psq); - skb_queue_tail(&conn->psq, skb); - spin_unlock_bh(&conn->psq_lock); - - /* - * If this is the first pkt getting queued - * for this STA, update the PVB for this - * STA. - */ - if (is_psq_empty) - ath6kl_wmi_set_pvb_cmd(ar->wmi, - vif->fw_vif_idx, - conn->aid, 1); - - ps_queued = true; - } else { - /* - * This tx is because of a PsPoll. - * Determine if MoreData bit has to be set. - */ - spin_lock_bh(&conn->psq_lock); - if (!skb_queue_empty(&conn->psq)) - *more_data = true; - spin_unlock_bh(&conn->psq_lock); - } + ps_queued = ath6kl_process_uapsdq(conn, + vif, skb, flags); + if (!(*flags & WMI_DATA_HDR_FLAGS_UAPSD)) + ps_queued = ath6kl_process_psq(conn, + vif, skb, flags); } } - return ps_queued; } @@ -185,6 +285,9 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, int status = 0; struct ath6kl_cookie *cookie = NULL; + if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) + return -EACCES; + spin_lock_bh(&ar->lock); ath6kl_dbg(ATH6KL_DBG_WLAN_TX, @@ -242,8 +345,13 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) u32 map_no = 0; u16 htc_tag = ATH6KL_DATA_PKT_TAG; u8 ac = 99 ; /* initialize to unmapped ac */ - bool chk_adhoc_ps_mapping = false, more_data = false; + bool chk_adhoc_ps_mapping = false; int ret; + struct wmi_tx_meta_v2 meta_v2; + void *meta; + u8 csum_start = 0, csum_dest = 0, csum = skb->ip_summed; + u8 meta_ver = 0; + u32 flags = 0; ath6kl_dbg(ATH6KL_DBG_WLAN_TX, "%s: skb=0x%p, data=0x%p, len=0x%x\n", __func__, @@ -255,16 +363,29 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) return 0; } + if (WARN_ON_ONCE(ar->state != ATH6KL_STATE_ON)) { + dev_kfree_skb(skb); + return 0; + } + if (!test_bit(WMI_READY, &ar->flag)) goto fail_tx; /* AP mode Power saving processing */ if (vif->nw_type == AP_NETWORK) { - if (ath6kl_powersave_ap(vif, skb, &more_data)) + if (ath6kl_powersave_ap(vif, skb, &flags)) return 0; } if (test_bit(WMI_ENABLED, &ar->flag)) { + if ((dev->features & NETIF_F_IP_CSUM) && + (csum == CHECKSUM_PARTIAL)) { + csum_start = skb->csum_start - + (skb_network_header(skb) - skb->head) + + sizeof(struct ath6kl_llc_snap_hdr); + csum_dest = skb->csum_offset + csum_start; + } + if (skb_headroom(skb) < dev->needed_headroom) { struct sk_buff *tmp_skb = skb; @@ -281,15 +402,33 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) goto fail_tx; } - if (ath6kl_wmi_data_hdr_add(ar->wmi, skb, DATA_MSGTYPE, - more_data, 0, 0, NULL, - vif->fw_vif_idx)) { - ath6kl_err("wmi_data_hdr_add failed\n"); + if ((dev->features & NETIF_F_IP_CSUM) && + (csum == CHECKSUM_PARTIAL)) { + meta_v2.csum_start = csum_start; + meta_v2.csum_dest = csum_dest; + + /* instruct target to calculate checksum */ + meta_v2.csum_flags = WMI_META_V2_FLAG_CSUM_OFFLOAD; + meta_ver = WMI_META_VERSION_2; + meta = &meta_v2; + } else { + meta_ver = 0; + meta = NULL; + } + + ret = ath6kl_wmi_data_hdr_add(ar->wmi, skb, + DATA_MSGTYPE, flags, 0, + meta_ver, + meta, vif->fw_vif_idx); + + if (ret) { + ath6kl_warn("failed to add wmi data header:%d\n" + , ret); goto fail_tx; } if ((vif->nw_type == ADHOC_NETWORK) && - ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags)) + ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags)) chk_adhoc_ps_mapping = true; else { /* get the stream mapping */ @@ -449,9 +588,7 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, * WMI queue with too many commands the only exception to * this is during testing using endpointping. */ - spin_lock_bh(&ar->lock); set_bit(WMI_CTRL_EP_FULL, &ar->flag); - spin_unlock_bh(&ar->lock); ath6kl_err("wmi ctrl ep is full\n"); return action; } @@ -465,7 +602,8 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, */ if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] < ar->hiac_stream_active_pri && - ar->cookie_count <= MAX_HI_COOKIE_NUM) + ar->cookie_count <= + target->endpoint[endpoint].tx_drop_packet_threshold) /* * Give preference to the highest priority stream by * dropping the packets which overflowed. @@ -479,9 +617,7 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, action != HTC_SEND_FULL_DROP) { spin_unlock_bh(&ar->list_lock); - spin_lock_bh(&vif->if_lock); set_bit(NETQ_STOPPED, &vif->flags); - spin_unlock_bh(&vif->if_lock); netif_stop_queue(vif->ndev); return action; @@ -710,10 +846,12 @@ static struct sk_buff *aggr_get_free_skb(struct aggr_info *p_aggr) { struct sk_buff *skb = NULL; - if (skb_queue_len(&p_aggr->free_q) < (AGGR_NUM_OF_FREE_NETBUFS >> 2)) - ath6kl_alloc_netbufs(&p_aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS); + if (skb_queue_len(&p_aggr->rx_amsdu_freeq) < + (AGGR_NUM_OF_FREE_NETBUFS >> 2)) + ath6kl_alloc_netbufs(&p_aggr->rx_amsdu_freeq, + AGGR_NUM_OF_FREE_NETBUFS); - skb = skb_dequeue(&p_aggr->free_q); + skb = skb_dequeue(&p_aggr->rx_amsdu_freeq); return skb; } @@ -748,7 +886,7 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) if (!IS_ALIGNED((unsigned long) skb->data, 4)) skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, - ATH6KL_BUFFER_SIZE, endpoint); + ATH6KL_BUFFER_SIZE, endpoint); list_add_tail(&packet->list, &queue); } @@ -881,7 +1019,7 @@ static void aggr_slice_amsdu(struct aggr_info *p_aggr, dev_kfree_skb(skb); } -static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid, +static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid, u16 seq_no, u8 order) { struct sk_buff *skb; @@ -890,11 +1028,8 @@ static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid, u16 idx, idx_end, seq_end; struct rxtid_stats *stats; - if (!p_aggr) - return; - - rxtid = &p_aggr->rx_tid[tid]; - stats = &p_aggr->stat[tid]; + rxtid = &agg_conn->rx_tid[tid]; + stats = &agg_conn->stat[tid]; idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz); @@ -923,7 +1058,8 @@ static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid, if (node->skb) { if (node->is_amsdu) - aggr_slice_amsdu(p_aggr, rxtid, node->skb); + aggr_slice_amsdu(agg_conn->aggr_info, rxtid, + node->skb); else skb_queue_tail(&rxtid->q, node->skb); node->skb = NULL; @@ -939,10 +1075,10 @@ static void aggr_deque_frms(struct aggr_info *p_aggr, u8 tid, stats->num_delivered += skb_queue_len(&rxtid->q); while ((skb = skb_dequeue(&rxtid->q))) - ath6kl_deliver_frames_to_nw_stack(p_aggr->dev, skb); + ath6kl_deliver_frames_to_nw_stack(agg_conn->dev, skb); } -static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid, +static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, u16 seq_no, bool is_amsdu, struct sk_buff *frame) { @@ -954,18 +1090,18 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid, bool is_queued = false; u16 extended_end; - rxtid = &agg_info->rx_tid[tid]; - stats = &agg_info->stat[tid]; + rxtid = &agg_conn->rx_tid[tid]; + stats = &agg_conn->stat[tid]; stats->num_into_aggr++; if (!rxtid->aggr) { if (is_amsdu) { - aggr_slice_amsdu(agg_info, rxtid, frame); + aggr_slice_amsdu(agg_conn->aggr_info, rxtid, frame); is_queued = true; stats->num_amsdu++; while ((skb = skb_dequeue(&rxtid->q))) - ath6kl_deliver_frames_to_nw_stack(agg_info->dev, + ath6kl_deliver_frames_to_nw_stack(agg_conn->dev, skb); } return is_queued; @@ -985,7 +1121,7 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid, (cur < end || cur > extended_end)) || ((end > extended_end) && (cur > extended_end) && (cur < end))) { - aggr_deque_frms(agg_info, tid, 0, 0); + aggr_deque_frms(agg_conn, tid, 0, 0); if (cur >= rxtid->hold_q_sz - 1) rxtid->seq_next = cur - (rxtid->hold_q_sz - 1); else @@ -1002,7 +1138,7 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid, st = ATH6KL_MAX_SEQ_NO - (rxtid->hold_q_sz - 2 - cur); - aggr_deque_frms(agg_info, tid, st, 0); + aggr_deque_frms(agg_conn, tid, st, 0); } stats->num_oow++; @@ -1041,9 +1177,9 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid, spin_unlock_bh(&rxtid->lock); - aggr_deque_frms(agg_info, tid, 0, 1); + aggr_deque_frms(agg_conn, tid, 0, 1); - if (agg_info->timer_scheduled) + if (agg_conn->timer_scheduled) rxtid->progress = true; else for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { @@ -1054,8 +1190,8 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid, * the frame doesn't remain stuck * forever. */ - agg_info->timer_scheduled = true; - mod_timer(&agg_info->timer, + agg_conn->timer_scheduled = true; + mod_timer(&agg_conn->timer, (jiffies + HZ * (AGGR_RX_TIMEOUT) / 1000)); rxtid->progress = false; @@ -1067,6 +1203,76 @@ static bool aggr_process_recv_frm(struct aggr_info *agg_info, u8 tid, return is_queued; } +static void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif, + struct ath6kl_sta *conn) +{ + struct ath6kl *ar = vif->ar; + bool is_apsdq_empty, is_apsdq_empty_at_start; + u32 num_frames_to_deliver, flags; + struct sk_buff *skb = NULL; + + /* + * If the APSD q for this STA is not empty, dequeue and + * send a pkt from the head of the q. Also update the + * More data bit in the WMI_DATA_HDR if there are + * more pkts for this STA in the APSD q. + * If there are no more pkts for this STA, + * update the APSD bitmap for this STA. + */ + + num_frames_to_deliver = (conn->apsd_info >> ATH6KL_APSD_NUM_OF_AC) & + ATH6KL_APSD_FRAME_MASK; + /* + * Number of frames to send in a service period is + * indicated by the station + * in the QOS_INFO of the association request + * If it is zero, send all frames + */ + if (!num_frames_to_deliver) + num_frames_to_deliver = ATH6KL_APSD_ALL_FRAME; + + spin_lock_bh(&conn->psq_lock); + is_apsdq_empty = skb_queue_empty(&conn->apsdq); + spin_unlock_bh(&conn->psq_lock); + is_apsdq_empty_at_start = is_apsdq_empty; + + while ((!is_apsdq_empty) && (num_frames_to_deliver)) { + + spin_lock_bh(&conn->psq_lock); + skb = skb_dequeue(&conn->apsdq); + is_apsdq_empty = skb_queue_empty(&conn->apsdq); + spin_unlock_bh(&conn->psq_lock); + + /* + * Set the STA flag to Trigger delivery, + * so that the frame will go out + */ + conn->sta_flags |= STA_PS_APSD_TRIGGER; + num_frames_to_deliver--; + + /* Last frame in the service period, set EOSP or queue empty */ + if ((is_apsdq_empty) || (!num_frames_to_deliver)) + conn->sta_flags |= STA_PS_APSD_EOSP; + + ath6kl_data_tx(skb, vif->ndev); + conn->sta_flags &= ~(STA_PS_APSD_TRIGGER); + conn->sta_flags &= ~(STA_PS_APSD_EOSP); + } + + if (is_apsdq_empty) { + if (is_apsdq_empty_at_start) + flags = WMI_AP_APSD_NO_DELIVERY_FRAMES; + else + flags = 0; + + ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi, + vif->fw_vif_idx, + conn->aid, 0, flags); + } + + return; +} + void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) { struct ath6kl *ar = target->dev->ar; @@ -1078,10 +1284,12 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) int status = packet->status; enum htc_endpoint_id ept = packet->endpoint; bool is_amsdu, prev_ps, ps_state = false; + bool trig_state = false; struct ath6kl_sta *conn = NULL; struct sk_buff *skb1 = NULL; struct ethhdr *datap = NULL; struct ath6kl_vif *vif; + struct aggr_info_conn *aggr_conn; u16 seq_no, offset; u8 tid, if_idx; @@ -1098,7 +1306,15 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) skb_put(skb, packet->act_len + HTC_HDR_LENGTH); skb_pull(skb, HTC_HDR_LENGTH); + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ", + skb->data, skb->len); + if (ept == ar->ctrl_ep) { + if (test_bit(WMI_ENABLED, &ar->flag)) { + ath6kl_check_wow_status(ar); + ath6kl_wmi_control_rx(ar->wmi, skb); + return; + } if_idx = wmi_cmd_hdr_get_if_idx((struct wmi_cmd_hdr *) skb->data); } else { @@ -1123,10 +1339,6 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) spin_unlock_bh(&vif->if_lock); - - ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ", - skb->data, skb->len); - skb->dev = vif->ndev; if (!test_bit(WMI_ENABLED, &ar->flag)) { @@ -1138,11 +1350,6 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) ath6kl_check_wow_status(ar); - if (ept == ar->ctrl_ep) { - ath6kl_wmi_control_rx(ar->wmi, skb); - return; - } - min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) + sizeof(struct ath6kl_llc_snap_hdr); @@ -1171,6 +1378,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) WMI_DATA_HDR_PS_MASK); offset = sizeof(struct wmi_data_hdr); + trig_state = !!(le16_to_cpu(dhdr->info3) & WMI_DATA_HDR_TRIG); switch (meta_type) { case 0: @@ -1209,18 +1417,61 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) else conn->sta_flags &= ~STA_PS_SLEEP; + /* Accept trigger only when the station is in sleep */ + if ((conn->sta_flags & STA_PS_SLEEP) && trig_state) + ath6kl_uapsd_trigger_frame_rx(vif, conn); + if (prev_ps ^ !!(conn->sta_flags & STA_PS_SLEEP)) { if (!(conn->sta_flags & STA_PS_SLEEP)) { struct sk_buff *skbuff = NULL; + bool is_apsdq_empty; + struct ath6kl_mgmt_buff *mgmt; + u8 idx; spin_lock_bh(&conn->psq_lock); - while ((skbuff = skb_dequeue(&conn->psq)) - != NULL) { + while (conn->mgmt_psq_len > 0) { + mgmt = list_first_entry( + &conn->mgmt_psq, + struct ath6kl_mgmt_buff, + list); + list_del(&mgmt->list); + conn->mgmt_psq_len--; + spin_unlock_bh(&conn->psq_lock); + idx = vif->fw_vif_idx; + + ath6kl_wmi_send_mgmt_cmd(ar->wmi, + idx, + mgmt->id, + mgmt->freq, + mgmt->wait, + mgmt->buf, + mgmt->len, + mgmt->no_cck); + + kfree(mgmt); + spin_lock_bh(&conn->psq_lock); + } + conn->mgmt_psq_len = 0; + while ((skbuff = skb_dequeue(&conn->psq))) { + spin_unlock_bh(&conn->psq_lock); + ath6kl_data_tx(skbuff, vif->ndev); + spin_lock_bh(&conn->psq_lock); + } + + is_apsdq_empty = skb_queue_empty(&conn->apsdq); + while ((skbuff = skb_dequeue(&conn->apsdq))) { spin_unlock_bh(&conn->psq_lock); ath6kl_data_tx(skbuff, vif->ndev); spin_lock_bh(&conn->psq_lock); } spin_unlock_bh(&conn->psq_lock); + + if (!is_apsdq_empty) + ath6kl_wmi_set_apsd_bfrd_traf( + ar->wmi, + vif->fw_vif_idx, + conn->aid, 0, 0); + /* Clear the PVB for this STA */ ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, conn->aid, 0); @@ -1314,11 +1565,21 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) datap = (struct ethhdr *) skb->data; - if (is_unicast_ether_addr(datap->h_dest) && - aggr_process_recv_frm(vif->aggr_cntxt, tid, seq_no, - is_amsdu, skb)) - /* aggregation code will handle the skb */ - return; + if (is_unicast_ether_addr(datap->h_dest)) { + if (vif->nw_type == AP_NETWORK) { + conn = ath6kl_find_sta(vif, datap->h_source); + if (!conn) + return; + aggr_conn = conn->aggr_conn; + } else + aggr_conn = vif->aggr_cntxt->aggr_conn; + + if (aggr_process_recv_frm(aggr_conn, tid, seq_no, + is_amsdu, skb)) { + /* aggregation code will handle the skb */ + return; + } + } ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); } @@ -1326,13 +1587,13 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) static void aggr_timeout(unsigned long arg) { u8 i, j; - struct aggr_info *p_aggr = (struct aggr_info *) arg; + struct aggr_info_conn *aggr_conn = (struct aggr_info_conn *) arg; struct rxtid *rxtid; struct rxtid_stats *stats; for (i = 0; i < NUM_OF_TIDS; i++) { - rxtid = &p_aggr->rx_tid[i]; - stats = &p_aggr->stat[i]; + rxtid = &aggr_conn->rx_tid[i]; + stats = &aggr_conn->stat[i]; if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress) continue; @@ -1343,18 +1604,18 @@ static void aggr_timeout(unsigned long arg) rxtid->seq_next, ((rxtid->seq_next + rxtid->hold_q_sz-1) & ATH6KL_MAX_SEQ_NO)); - aggr_deque_frms(p_aggr, i, 0, 0); + aggr_deque_frms(aggr_conn, i, 0, 0); } - p_aggr->timer_scheduled = false; + aggr_conn->timer_scheduled = false; for (i = 0; i < NUM_OF_TIDS; i++) { - rxtid = &p_aggr->rx_tid[i]; + rxtid = &aggr_conn->rx_tid[i]; if (rxtid->aggr && rxtid->hold_q) { for (j = 0; j < rxtid->hold_q_sz; j++) { if (rxtid->hold_q[j].skb) { - p_aggr->timer_scheduled = true; + aggr_conn->timer_scheduled = true; rxtid->timer_mon = true; rxtid->progress = false; break; @@ -1366,24 +1627,24 @@ static void aggr_timeout(unsigned long arg) } } - if (p_aggr->timer_scheduled) - mod_timer(&p_aggr->timer, + if (aggr_conn->timer_scheduled) + mod_timer(&aggr_conn->timer, jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT)); } -static void aggr_delete_tid_state(struct aggr_info *p_aggr, u8 tid) +static void aggr_delete_tid_state(struct aggr_info_conn *aggr_conn, u8 tid) { struct rxtid *rxtid; struct rxtid_stats *stats; - if (!p_aggr || tid >= NUM_OF_TIDS) + if (!aggr_conn || tid >= NUM_OF_TIDS) return; - rxtid = &p_aggr->rx_tid[tid]; - stats = &p_aggr->stat[tid]; + rxtid = &aggr_conn->rx_tid[tid]; + stats = &aggr_conn->stat[tid]; if (rxtid->aggr) - aggr_deque_frms(p_aggr, tid, 0, 0); + aggr_deque_frms(aggr_conn, tid, 0, 0); rxtid->aggr = false; rxtid->progress = false; @@ -1398,26 +1659,40 @@ static void aggr_delete_tid_state(struct aggr_info *p_aggr, u8 tid) memset(stats, 0, sizeof(struct rxtid_stats)); } -void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid, u16 seq_no, +void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no, u8 win_sz) { - struct aggr_info *p_aggr = vif->aggr_cntxt; + struct ath6kl_sta *sta; + struct aggr_info_conn *aggr_conn = NULL; struct rxtid *rxtid; struct rxtid_stats *stats; u16 hold_q_size; + u8 tid, aid; - if (!p_aggr) + if (vif->nw_type == AP_NETWORK) { + aid = ath6kl_get_aid(tid_mux); + sta = ath6kl_find_sta_by_aid(vif->ar, aid); + if (sta) + aggr_conn = sta->aggr_conn; + } else + aggr_conn = vif->aggr_cntxt->aggr_conn; + + if (!aggr_conn) return; - rxtid = &p_aggr->rx_tid[tid]; - stats = &p_aggr->stat[tid]; + tid = ath6kl_get_tid(tid_mux); + if (tid >= NUM_OF_TIDS) + return; + + rxtid = &aggr_conn->rx_tid[tid]; + stats = &aggr_conn->stat[tid]; if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n", __func__, win_sz, tid); if (rxtid->aggr) - aggr_delete_tid_state(p_aggr, tid); + aggr_delete_tid_state(aggr_conn, tid); rxtid->seq_next = seq_no; hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q); @@ -1433,31 +1708,23 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid, u16 seq_no, rxtid->aggr = true; } -struct aggr_info *aggr_init(struct net_device *dev) +void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info, + struct aggr_info_conn *aggr_conn) { - struct aggr_info *p_aggr = NULL; struct rxtid *rxtid; u8 i; - p_aggr = kzalloc(sizeof(struct aggr_info), GFP_KERNEL); - if (!p_aggr) { - ath6kl_err("failed to alloc memory for aggr_node\n"); - return NULL; - } - - p_aggr->aggr_sz = AGGR_SZ_DEFAULT; - p_aggr->dev = dev; - init_timer(&p_aggr->timer); - p_aggr->timer.function = aggr_timeout; - p_aggr->timer.data = (unsigned long) p_aggr; - - p_aggr->timer_scheduled = false; - skb_queue_head_init(&p_aggr->free_q); + aggr_conn->aggr_sz = AGGR_SZ_DEFAULT; + aggr_conn->dev = vif->ndev; + init_timer(&aggr_conn->timer); + aggr_conn->timer.function = aggr_timeout; + aggr_conn->timer.data = (unsigned long) aggr_conn; + aggr_conn->aggr_info = aggr_info; - ath6kl_alloc_netbufs(&p_aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS); + aggr_conn->timer_scheduled = false; for (i = 0; i < NUM_OF_TIDS; i++) { - rxtid = &p_aggr->rx_tid[i]; + rxtid = &aggr_conn->rx_tid[i]; rxtid->aggr = false; rxtid->progress = false; rxtid->timer_mon = false; @@ -1465,29 +1732,75 @@ struct aggr_info *aggr_init(struct net_device *dev) spin_lock_init(&rxtid->lock); } +} + +struct aggr_info *aggr_init(struct ath6kl_vif *vif) +{ + struct aggr_info *p_aggr = NULL; + + p_aggr = kzalloc(sizeof(struct aggr_info), GFP_KERNEL); + if (!p_aggr) { + ath6kl_err("failed to alloc memory for aggr_node\n"); + return NULL; + } + + p_aggr->aggr_conn = kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL); + if (!p_aggr->aggr_conn) { + ath6kl_err("failed to alloc memory for connection specific aggr info\n"); + kfree(p_aggr); + return NULL; + } + + aggr_conn_init(vif, p_aggr, p_aggr->aggr_conn); + + skb_queue_head_init(&p_aggr->rx_amsdu_freeq); + ath6kl_alloc_netbufs(&p_aggr->rx_amsdu_freeq, AGGR_NUM_OF_FREE_NETBUFS); + return p_aggr; } -void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid) +void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid_mux) { - struct aggr_info *p_aggr = vif->aggr_cntxt; + struct ath6kl_sta *sta; struct rxtid *rxtid; + struct aggr_info_conn *aggr_conn = NULL; + u8 tid, aid; + + if (vif->nw_type == AP_NETWORK) { + aid = ath6kl_get_aid(tid_mux); + sta = ath6kl_find_sta_by_aid(vif->ar, aid); + if (sta) + aggr_conn = sta->aggr_conn; + } else + aggr_conn = vif->aggr_cntxt->aggr_conn; - if (!p_aggr) + if (!aggr_conn) return; - rxtid = &p_aggr->rx_tid[tid]; + tid = ath6kl_get_tid(tid_mux); + if (tid >= NUM_OF_TIDS) + return; + + rxtid = &aggr_conn->rx_tid[tid]; if (rxtid->aggr) - aggr_delete_tid_state(p_aggr, tid); + aggr_delete_tid_state(aggr_conn, tid); } -void aggr_reset_state(struct aggr_info *aggr_info) +void aggr_reset_state(struct aggr_info_conn *aggr_conn) { u8 tid; + if (!aggr_conn) + return; + + if (aggr_conn->timer_scheduled) { + del_timer(&aggr_conn->timer); + aggr_conn->timer_scheduled = false; + } + for (tid = 0; tid < NUM_OF_TIDS; tid++) - aggr_delete_tid_state(aggr_info, tid); + aggr_delete_tid_state(aggr_conn, tid); } /* clean up our amsdu buffer list */ @@ -1514,28 +1827,11 @@ void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar) void aggr_module_destroy(struct aggr_info *aggr_info) { - struct rxtid *rxtid; - u8 i, k; - if (!aggr_info) return; - if (aggr_info->timer_scheduled) { - del_timer(&aggr_info->timer); - aggr_info->timer_scheduled = false; - } - - for (i = 0; i < NUM_OF_TIDS; i++) { - rxtid = &aggr_info->rx_tid[i]; - if (rxtid->hold_q) { - for (k = 0; k < rxtid->hold_q_sz; k++) - dev_kfree_skb(rxtid->hold_q[k].skb); - kfree(rxtid->hold_q); - } - - skb_queue_purge(&rxtid->q); - } - - skb_queue_purge(&aggr_info->free_q); + aggr_reset_state(aggr_info->aggr_conn); + skb_queue_purge(&aggr_info->rx_amsdu_freeq); + kfree(aggr_info->aggr_conn); kfree(aggr_info); } diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c new file mode 100644 index 000000000000..325b1224c2b1 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2007-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/usb.h> + +#include "debug.h" +#include "core.h" + +/* 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_create(&ar_usb->udev->dev); + if (ar == NULL) { + ath6kl_err("Failed to alloc ath6kl core\n"); + ret = -ENOMEM; + goto err_usb_destroy; + } + + ar->hif_priv = ar_usb; + ar->hif_type = ATH6KL_HIF_TYPE_USB; + ar->hif_ops = &ath6kl_usb_ops; + ar->mbox_info.block_size = 16; + ar->bmi.max_data_size = 252; + + ar_usb->ar = ar; + + ret = ath6kl_core_init(ar); + if (ret) { + ath6kl_err("Failed to init ath6kl core: %d\n", ret); + goto err_core_free; + } + + return ret; + +err_core_free: + ath6kl_core_destroy(ar); +err_usb_destroy: + ath6kl_usb_destroy(ar_usb); +err_usb_put: + usb_put_dev(dev); + + return ret; +} + +static void ath6kl_usb_remove(struct usb_interface *interface) +{ + usb_put_dev(interface_to_usbdev(interface)); + ath6kl_usb_device_detached(interface); +} + +/* 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 f6f2aa27fc20..2b442332cd0f 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -126,7 +127,7 @@ int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb) if (!is_ethertype(be16_to_cpu(type))) { ath6kl_dbg(ATH6KL_DBG_WMI, - "%s: pkt is already in 802.3 format\n", __func__); + "%s: pkt is already in 802.3 format\n", __func__); return 0; } @@ -180,7 +181,7 @@ static int ath6kl_wmi_meta_add(struct wmi *wmi, struct sk_buff *skb, } int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, - u8 msg_type, bool more_data, + u8 msg_type, u32 flags, enum wmi_data_hdr_data_type data_type, u8 meta_ver, void *tx_meta_info, u8 if_idx) { @@ -204,17 +205,19 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, data_hdr->info = msg_type << WMI_DATA_HDR_MSG_TYPE_SHIFT; data_hdr->info |= data_type << WMI_DATA_HDR_DATA_TYPE_SHIFT; - if (more_data) - data_hdr->info |= - WMI_DATA_HDR_MORE_MASK << WMI_DATA_HDR_MORE_SHIFT; + if (flags & WMI_DATA_HDR_FLAGS_MORE) + data_hdr->info |= WMI_DATA_HDR_MORE; - data_hdr->info2 = cpu_to_le16(meta_ver << WMI_DATA_HDR_META_SHIFT); - data_hdr->info3 = cpu_to_le16(if_idx & WMI_DATA_HDR_IF_IDX_MASK); + if (flags & WMI_DATA_HDR_FLAGS_EOSP) + data_hdr->info3 |= cpu_to_le16(WMI_DATA_HDR_EOSP); + + data_hdr->info2 |= cpu_to_le16(meta_ver << WMI_DATA_HDR_META_SHIFT); + data_hdr->info3 |= cpu_to_le16(if_idx & WMI_DATA_HDR_IF_IDX_MASK); return 0; } -static u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri) +u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri) { struct iphdr *ip_hdr = (struct iphdr *) pkt; u8 ip_pri; @@ -236,6 +239,11 @@ static u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri) return ip_pri; } +u8 ath6kl_wmi_get_traffic_class(u8 user_priority) +{ + return up_to_ac[user_priority & 0x7]; +} + int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, u32 layer2_priority, bool wmm_enabled, @@ -419,9 +427,6 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n", evt->num_msg, evt->msg_len, evt->msg_type); - if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_WMI)) - return 0; - for (index = 0; index < evt->num_msg; index++) { size = sizeof(struct wmi_tx_complete_event) + (index * sizeof(struct tx_complete_msg_v1)); @@ -552,7 +557,8 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) - cfg80211_rx_mgmt(vif->ndev, freq, ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(vif->ndev, freq, 0, + ev->data, dlen, GFP_ATOMIC); return 0; } @@ -591,7 +597,8 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); - cfg80211_rx_mgmt(vif->ndev, freq, ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(vif->ndev, freq, 0, + ev->data, dlen, GFP_ATOMIC); return 0; } @@ -786,12 +793,14 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, ev->u.ap_sta.keymgmt, le16_to_cpu(ev->u.ap_sta.cipher), ev->u.ap_sta.apsd_info); + ath6kl_connect_ap_mode_sta( vif, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr, ev->u.ap_sta.keymgmt, le16_to_cpu(ev->u.ap_sta.cipher), ev->u.ap_sta.auth, ev->assoc_req_len, - ev->assoc_info + ev->beacon_ie_len); + ev->assoc_info + ev->beacon_ie_len, + ev->u.ap_sta.apsd_info); } return 0; } @@ -819,8 +828,8 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, if (pie[1] > 3 && pie[2] == 0x00 && pie[3] == 0x50 && pie[4] == 0xf2 && pie[5] == WMM_OUI_TYPE) { /* WMM OUT (00:50:F2) */ - if (pie[1] > 5 - && pie[6] == WMM_PARAM_OUI_SUBTYPE) + if (pie[1] > 5 && + pie[6] == WMM_PARAM_OUI_SUBTYPE) wmi->is_wmm_enabled = true; } break; @@ -904,17 +913,17 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) regpair = ath6kl_get_regpair((u16) reg_code); country = ath6kl_regd_find_country_by_rd((u16) reg_code); ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", - regpair->regDmnEnum); + regpair->regDmnEnum); } - if (country) { + if (country && wmi->parent_dev->wiphy_registered) { alpha2[0] = country->isoName[0]; alpha2[1] = country->isoName[1]; regulatory_hint(wmi->parent_dev->wiphy, alpha2); ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n", - alpha2[0], alpha2[1]); + alpha2[0], alpha2[1]); } } @@ -1025,8 +1034,9 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, if (len < 8 + 2 + 2) return -EINVAL; - if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &vif->flags) - && memcmp(bih->bssid, vif->bssid, ETH_ALEN) == 0) { + if (bih->frame_type == BEACON_FTYPE && + test_bit(CONNECTED, &vif->flags) && + memcmp(bih->bssid, vif->bssid, ETH_ALEN) == 0) { const u8 *tim; tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2, len - 8 - 2 - 2); @@ -1145,9 +1155,9 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len) return 0; } -static int ath6kl_wmi_tcmd_test_report_rx(struct wmi *wmi, u8 *datap, int len) +static int ath6kl_wmi_test_rx(struct wmi *wmi, u8 *datap, int len) { - ath6kl_tm_rx_report_event(wmi->parent_dev, datap, len); + ath6kl_tm_rx_event(wmi->parent_dev, datap, len); return 0; } @@ -1358,8 +1368,8 @@ static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap, /* Upper threshold breached */ if (rssi < sq_thresh->upper_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, - "spurious upper rssi threshold event: %d\n", - rssi); + "spurious upper rssi threshold event: %d\n", + rssi); } else if ((rssi < sq_thresh->upper_threshold[1]) && (rssi >= sq_thresh->upper_threshold[0])) { new_threshold = WMI_RSSI_THRESHOLD1_ABOVE; @@ -1382,7 +1392,7 @@ static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap, /* Lower threshold breached */ if (rssi > sq_thresh->lower_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, - "spurious lower rssi threshold event: %d %d\n", + "spurious lower rssi threshold event: %d %d\n", rssi, sq_thresh->lower_threshold[0]); } else if ((rssi > sq_thresh->lower_threshold[1]) && (rssi <= sq_thresh->lower_threshold[0])) { @@ -1543,8 +1553,8 @@ static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap, /* Upper threshold breached */ if (snr < sq_thresh->upper_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, - "spurious upper snr threshold event: %d\n", - snr); + "spurious upper snr threshold event: %d\n", + snr); } else if ((snr < sq_thresh->upper_threshold[1]) && (snr >= sq_thresh->upper_threshold[0])) { new_threshold = WMI_SNR_THRESHOLD1_ABOVE; @@ -1561,8 +1571,8 @@ static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap, /* Lower threshold breached */ if (snr > sq_thresh->lower_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, - "spurious lower snr threshold event: %d\n", - sq_thresh->lower_threshold[0]); + "spurious lower snr threshold event: %d\n", + sq_thresh->lower_threshold[0]); } else if ((snr > sq_thresh->lower_threshold[1]) && (snr <= sq_thresh->lower_threshold[0])) { new_threshold = WMI_SNR_THRESHOLD4_BELOW; @@ -2020,6 +2030,26 @@ int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u8 if_idx, return ret; } +int ath6kl_wmi_bmisstime_cmd(struct wmi *wmi, u8 if_idx, + u16 bmiss_time, u16 num_beacons) +{ + struct sk_buff *skb; + struct wmi_bmiss_time_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_bmiss_time_cmd *) skb->data; + cmd->bmiss_time = cpu_to_le16(bmiss_time); + cmd->num_beacons = cpu_to_le16(num_beacons); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_BMISS_TIME_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode) { struct sk_buff *skb; @@ -2479,15 +2509,16 @@ int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 if_idx, u8 traffic_class, return ret; } -int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd) +int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx, + __be32 ips0, __be32 ips1) { struct sk_buff *skb; struct wmi_set_ip_cmd *cmd; int ret; /* Multicast address are not valid */ - if ((*((u8 *) &ip_cmd->ips[0]) >= 0xE0) || - (*((u8 *) &ip_cmd->ips[1]) >= 0xE0)) + if (ipv4_is_multicast(ips0) || + ipv4_is_multicast(ips1)) return -EINVAL; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_ip_cmd)); @@ -2495,9 +2526,10 @@ int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd) return -ENOMEM; cmd = (struct wmi_set_ip_cmd *) skb->data; - memcpy(cmd, ip_cmd, sizeof(struct wmi_set_ip_cmd)); + cmd->ips[0] = ips0; + cmd->ips[1] = ips1; - ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_IP_CMDID, + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IP_CMDID, NO_SYNC_WMIFLAG); return ret; } @@ -2582,6 +2614,18 @@ int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, return ret; } +/* This command has zero length payload */ +static int ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(struct wmi *wmi, + struct ath6kl_vif *vif) +{ + struct ath6kl *ar = wmi->parent_dev; + + set_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); + wake_up(&ar->event_wq); + + return 0; +} + int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_wow_mode wow_mode, u32 filter, u16 host_req_delay) @@ -2591,7 +2635,7 @@ int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, int ret; if ((wow_mode != ATH6KL_WOW_MODE_ENABLE) && - wow_mode != ATH6KL_WOW_MODE_DISABLE) { + wow_mode != ATH6KL_WOW_MODE_DISABLE) { ath6kl_err("invalid wow mode: %d\n", wow_mode); return -EINVAL; } @@ -2612,7 +2656,8 @@ int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u8 list_id, u8 filter_size, - u8 filter_offset, u8 *filter, u8 *mask) + u8 filter_offset, const u8 *filter, + const u8 *mask) { struct sk_buff *skb; struct wmi_add_wow_pattern_cmd *cmd; @@ -2853,6 +2898,51 @@ int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len) return ret; } +int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on) +{ + struct sk_buff *skb; + struct wmi_mcast_filter_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_mcast_filter_cmd *) skb->data; + cmd->mcast_all_enable = mc_all_on; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_MCAST_FILTER_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, + u8 *filter, bool add_filter) +{ + struct sk_buff *skb; + struct wmi_mcast_filter_add_del_cmd *cmd; + int ret; + + if ((filter[0] != 0x33 || filter[1] != 0x33) && + (filter[0] != 0x01 || filter[1] != 0x00 || + filter[2] != 0x5e || filter[3] > 0x7f)) { + ath6kl_warn("invalid multicast filter address\n"); + return -EINVAL; + } + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_mcast_filter_add_del_cmd *) skb->data; + memcpy(cmd->mcast_mac, filter, ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE); + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + add_filter ? WMI_SET_MCAST_FILTER_CMDID : + WMI_DEL_MCAST_FILTER_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} s32 ath6kl_wmi_get_rate(s8 rate_index) { @@ -2946,6 +3036,59 @@ int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac, NO_SYNC_WMIFLAG); } +int ath6kl_wmi_ap_hidden_ssid(struct wmi *wmi, u8 if_idx, bool enable) +{ + struct sk_buff *skb; + struct wmi_ap_hidden_ssid_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ap_hidden_ssid_cmd *) skb->data; + cmd->hidden_ssid = enable ? 1 : 0; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_HIDDEN_SSID_CMDID, + NO_SYNC_WMIFLAG); +} + +/* This command will be used to enable/disable AP uAPSD feature */ +int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable) +{ + struct wmi_ap_set_apsd_cmd *cmd; + struct sk_buff *skb; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ap_set_apsd_cmd *)skb->data; + cmd->enable = enable; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_APSD_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_apsd_bfrd_traf(struct wmi *wmi, u8 if_idx, + u16 aid, u16 bitmap, u32 flags) +{ + struct wmi_ap_apsd_buffered_traffic_cmd *cmd; + struct sk_buff *skb; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ap_apsd_buffered_traffic_cmd *)skb->data; + cmd->aid = cpu_to_le16(aid); + cmd->bitmap = cpu_to_le16(bitmap); + cmd->flags = cpu_to_le32(flags); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID, + NO_SYNC_WMIFLAG); +} + static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { @@ -3078,8 +3221,9 @@ int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur) * 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) +static int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, + u32 freq, u32 wait, const u8 *data, + u16 data_len) { struct sk_buff *skb; struct wmi_send_action_cmd *p; @@ -3115,9 +3259,9 @@ 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) +static int __ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, + u32 freq, u32 wait, const u8 *data, + u16 data_len, u32 no_cck) { struct sk_buff *skb; struct wmi_send_mgmt_cmd *p; @@ -3154,6 +3298,32 @@ int ath6kl_wmi_send_mgmt_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) +{ + int status; + struct ath6kl *ar = wmi->parent_dev; + + if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + ar->fw_capabilities)) { + /* + * If capable of doing P2P mgmt operations using + * station interface, send additional information like + * supported rates to advertise and xmit rates for + * probe requests + */ + status = __ath6kl_wmi_send_mgmt_cmd(ar->wmi, if_idx, id, freq, + wait, data, data_len, + no_cck); + } else { + status = ath6kl_wmi_send_action_cmd(ar->wmi, if_idx, id, freq, + wait, data, data_len); + } + + return status; +} + int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq, const u8 *dst, const u8 *data, u16 data_len) @@ -3265,32 +3435,101 @@ static int ath6kl_wmi_roam_tbl_event_rx(struct wmi *wmi, u8 *datap, int len) return ath6kl_debug_roam_tbl_event(wmi->parent_dev, datap, len); } -/* Control Path */ -int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) +/* Process interface specific wmi events, caller would free the datap */ +static int ath6kl_wmi_proc_events_vif(struct wmi *wmi, u16 if_idx, u16 cmd_id, + u8 *datap, u32 len) { - struct wmi_cmd_hdr *cmd; struct ath6kl_vif *vif; - u32 len; - u16 id; - u8 if_idx; - u8 *datap; - int ret = 0; - if (WARN_ON(skb == NULL)) + vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); + if (!vif) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "Wmi event for unavailable vif, vif_index:%d\n", + if_idx); return -EINVAL; + } - if (skb->len < sizeof(struct wmi_cmd_hdr)) { - ath6kl_err("bad packet 1\n"); - dev_kfree_skb(skb); + switch (cmd_id) { + case WMI_CONNECT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n"); + return ath6kl_wmi_connect_event_rx(wmi, datap, len, vif); + case WMI_DISCONNECT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n"); + return ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif); + case WMI_TKIP_MICERR_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n"); + return ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif); + case WMI_BSSINFO_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n"); + return ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif); + case WMI_NEIGHBOR_REPORT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n"); + return ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len, + vif); + case WMI_SCAN_COMPLETE_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n"); + return ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif); + case WMI_REPORT_STATISTICS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n"); + return ath6kl_wmi_stats_event_rx(wmi, datap, len, vif); + case WMI_CAC_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n"); + return ath6kl_wmi_cac_event_rx(wmi, datap, len, vif); + case WMI_PSPOLL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n"); + return ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif); + case WMI_DTIMEXPIRY_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n"); + return ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif); + case WMI_ADDBA_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n"); + return ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif); + case WMI_DELBA_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n"); + return ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif); + case WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID"); + return ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif); + case WMI_REMAIN_ON_CHNL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n"); + return ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif); + case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n"); + return ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap, + len, vif); + case WMI_TX_STATUS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); + return ath6kl_wmi_tx_status_event_rx(wmi, datap, len, vif); + case WMI_RX_PROBE_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); + return ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len, vif); + case WMI_RX_ACTION_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); + return ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif); + default: + ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", cmd_id); return -EINVAL; } + return 0; +} + +static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd; + int ret = 0; + u32 len; + u16 id; + u8 if_idx; + u8 *datap; + cmd = (struct wmi_cmd_hdr *) skb->data; id = le16_to_cpu(cmd->cmd_id); if_idx = le16_to_cpu(cmd->info1) & WMI_CMD_HDR_IF_ID_MASK; skb_pull(skb, sizeof(struct wmi_cmd_hdr)); - datap = skb->data; len = skb->len; @@ -3298,15 +3537,6 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ", datap, len); - vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); - if (!vif) { - ath6kl_dbg(ATH6KL_DBG_WMI, - "Wmi event for unavailable vif, vif_index:%d\n", - if_idx); - dev_kfree_skb(skb); - return -EINVAL; - } - switch (id) { case WMI_GET_BITRATE_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_BITRATE_CMDID\n"); @@ -3324,26 +3554,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_READY_EVENTID\n"); ret = ath6kl_wmi_ready_event_rx(wmi, datap, len); break; - case WMI_CONNECT_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n"); - ret = ath6kl_wmi_connect_event_rx(wmi, datap, len, vif); - break; - case WMI_DISCONNECT_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n"); - ret = ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif); - break; case WMI_PEER_NODE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PEER_NODE_EVENTID\n"); ret = ath6kl_wmi_peer_node_event_rx(wmi, datap, len); break; - case WMI_TKIP_MICERR_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n"); - ret = ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif); - break; - case WMI_BSSINFO_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n"); - ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif); - break; case WMI_REGDOMAIN_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n"); ath6kl_wmi_regdomain_event(wmi, datap, len); @@ -3352,23 +3566,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n"); ret = ath6kl_wmi_pstream_timeout_event_rx(wmi, datap, len); break; - case WMI_NEIGHBOR_REPORT_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n"); - ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len, - vif); - break; - case WMI_SCAN_COMPLETE_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n"); - ret = ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif); - break; case WMI_CMDERROR_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CMDERROR_EVENTID\n"); ret = ath6kl_wmi_error_event_rx(wmi, datap, len); break; - case WMI_REPORT_STATISTICS_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n"); - ret = ath6kl_wmi_stats_event_rx(wmi, datap, len, vif); - break; case WMI_RSSI_THRESHOLD_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RSSI_THRESHOLD_EVENTID\n"); ret = ath6kl_wmi_rssi_threshold_event_rx(wmi, datap, len); @@ -3388,10 +3589,6 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_EXTENSION_EVENTID\n"); ret = ath6kl_wmi_control_rx_xtnd(wmi, skb); break; - case WMI_CAC_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n"); - ret = ath6kl_wmi_cac_event_rx(wmi, datap, len, vif); - break; case WMI_CHANNEL_CHANGE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CHANNEL_CHANGE_EVENTID\n"); break; @@ -3400,7 +3597,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_TEST_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TEST_EVENTID\n"); - ret = ath6kl_wmi_tcmd_test_report_rx(wmi, datap, len); + ret = ath6kl_wmi_test_rx(wmi, datap, len); break; case WMI_GET_FIXRATES_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n"); @@ -3431,28 +3628,12 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_PMKID_LIST_EVENTID\n"); ret = ath6kl_wmi_get_pmkid_list_event_rx(wmi, datap, len); break; - case WMI_PSPOLL_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n"); - ret = ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif); - break; - case WMI_DTIMEXPIRY_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n"); - ret = ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif); - break; case WMI_SET_PARAMS_REPLY_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SET_PARAMS_REPLY_EVENTID\n"); break; - case WMI_ADDBA_REQ_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n"); - ret = ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif); - break; case WMI_ADDBA_RESP_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_RESP_EVENTID\n"); break; - case WMI_DELBA_REQ_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n"); - ret = ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif); - break; case WMI_REPORT_BTCOEX_CONFIG_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_BTCOEX_CONFIG_EVENTID\n"); @@ -3465,47 +3646,39 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n"); ret = ath6kl_wmi_tx_complete_event_rx(datap, len); break; - case WMI_REMAIN_ON_CHNL_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n"); - ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif); - break; - case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, - "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n"); - ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap, - len, vif); - break; - case WMI_TX_STATUS_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); - ret = ath6kl_wmi_tx_status_event_rx(wmi, datap, len, vif); - break; - case WMI_RX_PROBE_REQ_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); - ret = ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len, vif); - break; case WMI_P2P_CAPABILITIES_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n"); ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len); break; - case WMI_RX_ACTION_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); - ret = ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif); - break; case WMI_P2P_INFO_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n"); ret = ath6kl_wmi_p2p_info_event_rx(datap, len); break; default: - ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id); - ret = -EINVAL; + /* may be the event is interface specific */ + ret = ath6kl_wmi_proc_events_vif(wmi, if_idx, id, datap, len); break; } dev_kfree_skb(skb); - return ret; } +/* Control Path */ +int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) +{ + if (WARN_ON(skb == NULL)) + return -EINVAL; + + if (skb->len < sizeof(struct wmi_cmd_hdr)) { + ath6kl_err("bad packet 1\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + return ath6kl_wmi_proc_events(wmi, skb); +} + void ath6kl_wmi_reset(struct wmi *wmi) { spin_lock_bh(&wmi->lock); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 42ac311eda4e..4092e3e80790 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -110,6 +111,8 @@ struct wmi { u8 fat_pipe_exist; struct ath6kl *parent_dev; u8 pwr_mode; + + /* protects fat_pipe_exist and stream_exist_for_ac */ spinlock_t lock; enum htc_endpoint_id ep_id; struct sq_threshold_params @@ -149,8 +152,7 @@ enum wmi_msg_type { #define WMI_DATA_HDR_PS_MASK 0x1 #define WMI_DATA_HDR_PS_SHIFT 5 -#define WMI_DATA_HDR_MORE_MASK 0x1 -#define WMI_DATA_HDR_MORE_SHIFT 5 +#define WMI_DATA_HDR_MORE 0x20 enum wmi_data_hdr_data_type { WMI_DATA_HDR_DATA_TYPE_802_3 = 0, @@ -160,6 +162,13 @@ enum wmi_data_hdr_data_type { WMI_DATA_HDR_DATA_TYPE_ACL, }; +/* Bitmap of data header flags */ +enum wmi_data_hdr_flags { + WMI_DATA_HDR_FLAGS_MORE = 0x1, + WMI_DATA_HDR_FLAGS_EOSP = 0x2, + WMI_DATA_HDR_FLAGS_UAPSD = 0x4, +}; + #define WMI_DATA_HDR_DATA_TYPE_MASK 0x3 #define WMI_DATA_HDR_DATA_TYPE_SHIFT 6 @@ -173,8 +182,12 @@ enum wmi_data_hdr_data_type { #define WMI_DATA_HDR_META_MASK 0x7 #define WMI_DATA_HDR_META_SHIFT 13 +/* Macros for operating on WMI_DATA_HDR (info3) field */ #define WMI_DATA_HDR_IF_IDX_MASK 0xF +#define WMI_DATA_HDR_TRIG 0x10 +#define WMI_DATA_HDR_EOSP 0x10 + struct wmi_data_hdr { s8 rssi; @@ -203,7 +216,8 @@ struct wmi_data_hdr { /* * usage of info3, 16-bit: * b3:b0 - Interface index - * b15:b4 - Reserved + * b4 - uAPSD trigger in rx & EOSP in tx + * b15:b5 - Reserved */ __le16 info3; } __packed; @@ -257,6 +271,9 @@ static inline u8 wmi_data_hdr_get_if_idx(struct wmi_data_hdr *dhdr) #define WMI_META_VERSION_1 0x01 #define WMI_META_VERSION_2 0x02 +/* Flag to signal to FW to calculate TCP checksum */ +#define WMI_META_V2_FLAG_CSUM_OFFLOAD 0x01 + struct wmi_tx_meta_v1 { /* packet ID to identify the tx request */ u8 pkt_id; @@ -646,7 +663,6 @@ enum auth_mode { WPA2_AUTH_CCKM = 0x40, }; -#define WMI_MIN_KEY_INDEX 0 #define WMI_MAX_KEY_INDEX 3 #define WMI_MAX_KEY_LEN 32 @@ -984,6 +1000,12 @@ struct wmi_listen_int_cmd { __le16 num_beacons; } __packed; +/* WMI_SET_BMISS_TIME_CMDID */ +struct wmi_bmiss_time_cmd { + __le16 bmiss_time; + __le16 num_beacons; +}; + /* WMI_SET_POWER_MODE_CMDID */ enum wmi_power_mode { REC_POWER = 0x01, @@ -1001,7 +1023,7 @@ struct wmi_power_mode_cmd { */ enum power_save_fail_event_policy { SEND_POWER_SAVE_FAIL_EVENT_ALWAYS = 1, - IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN = 2, + IGNORE_PS_FAIL_DURING_SCAN = 2, }; struct wmi_power_params_cmd { @@ -1199,7 +1221,7 @@ struct wmi_snr_threshold_params_cmd { enum wmi_preamble_policy { WMI_IGNORE_BARKER_IN_ERP = 0, - WMI_DONOT_IGNORE_BARKER_IN_ERP + WMI_FOLLOW_BARKER_IN_ERP, }; struct wmi_set_lpreamble_cmd { @@ -1237,6 +1259,15 @@ enum target_event_report_config { NO_DISCONN_EVT_IN_RECONN }; +struct wmi_mcast_filter_cmd { + u8 mcast_all_enable; +} __packed; + +#define ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE 6 +struct wmi_mcast_filter_add_del_cmd { + u8 mcast_mac[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; +} __packed; + /* Command Replies */ /* WMI_GET_CHANNEL_LIST_CMDID reply */ @@ -1335,6 +1366,8 @@ enum wmi_event_id { WMI_P2P_START_SDPD_EVENTID, WMI_P2P_SDPD_RX_EVENTID, + WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID = 0x1047, + WMI_THIN_RESERVED_START_EVENTID = 0x8000, /* Events in this range are reserved for thinmode */ WMI_THIN_RESERVED_END_EVENTID = 0x8fff, @@ -1903,7 +1936,7 @@ struct wow_filter { struct wmi_set_ip_cmd { /* IP in network byte order */ - __le32 ips[MAX_IP_ADDRS]; + __be32 ips[MAX_IP_ADDRS]; } __packed; enum ath6kl_wow_filters { @@ -2104,7 +2137,24 @@ struct wmi_rx_frame_format_cmd { u8 reserved[1]; } __packed; +struct wmi_ap_hidden_ssid_cmd { + u8 hidden_ssid; +} __packed; + /* AP mode events */ +struct wmi_ap_set_apsd_cmd { + u8 enable; +} __packed; + +enum wmi_ap_apsd_buffered_traffic_flags { + WMI_AP_APSD_NO_DELIVERY_FRAMES = 0x1, +}; + +struct wmi_ap_apsd_buffered_traffic_cmd { + __le16 aid; + __le16 bitmap; + __le32 flags; +} __packed; /* WMI_PS_POLL_EVENT */ struct wmi_pspoll_event { @@ -2321,7 +2371,7 @@ enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi); void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id); int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb); int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, - u8 msg_type, bool more_data, + u8 msg_type, u32 flags, enum wmi_data_hdr_data_type data_type, u8 meta_ver, void *tx_meta_info, u8 if_idx); @@ -2376,6 +2426,8 @@ int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, u8 index, u8 flag, int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u8 if_idx, u16 listen_interval, u16 listen_beacons); +int ath6kl_wmi_bmisstime_cmd(struct wmi *wmi, u8 if_idx, + u16 bmiss_time, u16 num_beacons); int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode); int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u8 if_idx, u16 idle_period, u16 ps_poll_num, u16 dtim_policy, @@ -2417,7 +2469,8 @@ int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); s32 ath6kl_wmi_get_rate(s8 rate_index); -int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd); +int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx, + __be32 ips0, __be32 ips1); int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_host_mode host_mode); int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, @@ -2425,14 +2478,28 @@ int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, u32 filter, u16 host_req_delay); int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u8 list_id, u8 filter_size, - u8 filter_offset, u8 *filter, u8 *mask); + u8 filter_offset, const u8 *filter, + const u8 *mask); int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u16 list_id, u16 filter_id); int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi); int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid); int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode); +int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on); +int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, + u8 *filter, bool add_filter); +/* AP mode uAPSD */ +int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); +int ath6kl_wmi_set_apsd_bfrd_traf(struct wmi *wmi, + u8 if_idx, u16 aid, + u16 bitmap, u32 flags); + +u8 ath6kl_wmi_get_traffic_class(u8 user_priority); + +u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri); /* AP mode */ +int ath6kl_wmi_ap_hidden_ssid(struct wmi *wmi, u8 if_idx, bool enable); int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx, struct wmi_connect_cmd *p); @@ -2454,9 +2521,6 @@ int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable); int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur); -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); |