Merge branch 'for-linville' of git://github.com/kvalo/ath6kl
This commit is contained in:
commit
b9d9057847
@ -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"
|
||||
|
@ -21,17 +21,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
|
||||
|
@ -57,8 +57,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;
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "cfg80211.h"
|
||||
@ -22,10 +24,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), \
|
||||
@ -196,7 +194,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;
|
||||
}
|
||||
|
||||
@ -461,13 +459,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 +521,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);
|
||||
@ -605,11 +602,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 +637,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 +651,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 +665,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 +706,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 +717,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 +728,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -984,6 +980,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 +994,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 +1009,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:
|
||||
@ -1115,7 +1110,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 +1143,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 +1179,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);
|
||||
@ -1403,7 +1398,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 +1723,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 +1753,221 @@ 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;
|
||||
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;
|
||||
|
||||
/* 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 +1975,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);
|
||||
@ -1911,6 +2098,7 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
|
||||
|
||||
int ath6kl_cfg80211_resume(struct ath6kl *ar)
|
||||
{
|
||||
@ -1962,6 +2150,7 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ath6kl_cfg80211_resume);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
@ -2014,7 +2203,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;
|
||||
@ -2214,6 +2414,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 {
|
||||
@ -2259,6 +2464,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)
|
||||
{
|
||||
@ -2518,6 +2736,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),
|
||||
@ -2562,6 +2786,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
|
||||
.add_beacon = ath6kl_add_beacon,
|
||||
.set_beacon = ath6kl_set_beacon,
|
||||
.del_beacon = ath6kl_del_beacon,
|
||||
.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,69 +2854,108 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
|
||||
ath6kl_cfg80211_stop(vif);
|
||||
}
|
||||
|
||||
struct ath6kl *ath6kl_core_alloc(struct device *dev)
|
||||
static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
|
||||
{
|
||||
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;
|
||||
vif->aggr_cntxt = aggr_init(vif);
|
||||
if (!vif->aggr_cntxt) {
|
||||
ath6kl_err("failed to initialize aggr\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ar = wiphy_priv(wiphy);
|
||||
ar->p2p = !!ath6kl_p2p;
|
||||
ar->wiphy = wiphy;
|
||||
ar->dev = dev;
|
||||
setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
|
||||
(unsigned long) vif->ndev);
|
||||
setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
|
||||
(unsigned long) vif);
|
||||
|
||||
ar->vif_max = 1;
|
||||
set_bit(WMM_ENABLED, &vif->flags);
|
||||
spin_lock_init(&vif->if_lock);
|
||||
|
||||
ar->max_norm_iface = 1;
|
||||
INIT_LIST_HEAD(&vif->mc_filter);
|
||||
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
|
||||
void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
|
||||
{
|
||||
struct ath6kl *ar = vif->ar;
|
||||
struct ath6kl_mc_filter *mc_filter, *tmp;
|
||||
|
||||
aggr_module_destroy(vif->aggr_cntxt);
|
||||
|
||||
ar->avail_idx_map |= BIT(vif->fw_vif_idx);
|
||||
|
||||
if (vif->nw_type == ADHOC_NETWORK)
|
||||
ar->ibss_if_active = false;
|
||||
|
||||
list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
|
||||
list_del(&mc_filter->list);
|
||||
kfree(mc_filter);
|
||||
}
|
||||
|
||||
unregister_netdevice(vif->ndev);
|
||||
|
||||
ar->num_vif--;
|
||||
}
|
||||
|
||||
struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
|
||||
enum nl80211_iftype type, u8 fw_vif_idx,
|
||||
u8 nw_type)
|
||||
{
|
||||
struct net_device *ndev;
|
||||
struct ath6kl_vif *vif;
|
||||
|
||||
ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
|
||||
if (!ndev)
|
||||
return NULL;
|
||||
|
||||
vif = netdev_priv(ndev);
|
||||
ndev->ieee80211_ptr = &vif->wdev;
|
||||
vif->wdev.wiphy = ar->wiphy;
|
||||
vif->ar = ar;
|
||||
vif->ndev = ndev;
|
||||
SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
|
||||
vif->wdev.netdev = ndev;
|
||||
vif->wdev.iftype = type;
|
||||
vif->fw_vif_idx = fw_vif_idx;
|
||||
vif->nw_type = vif->next_mode = nw_type;
|
||||
|
||||
memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
|
||||
if (fw_vif_idx != 0)
|
||||
ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
|
||||
0x2;
|
||||
|
||||
init_netdev(ndev);
|
||||
|
||||
ath6kl_init_control_info(vif);
|
||||
|
||||
if (ath6kl_cfg80211_vif_init(vif))
|
||||
goto err;
|
||||
|
||||
if (register_netdevice(ndev))
|
||||
goto err;
|
||||
|
||||
ar->avail_idx_map &= ~BIT(fw_vif_idx);
|
||||
vif->sme_state = SME_DISCONNECTED;
|
||||
set_bit(WLAN_ENABLED, &vif->flags);
|
||||
ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
|
||||
set_bit(NETDEV_REGISTERED, &vif->flags);
|
||||
|
||||
if (type == NL80211_IFTYPE_ADHOC)
|
||||
ar->ibss_if_active = true;
|
||||
|
||||
spin_lock_bh(&ar->list_lock);
|
||||
list_add_tail(&vif->list, &ar->vif_list);
|
||||
spin_unlock_bh(&ar->list_lock);
|
||||
|
||||
return ndev;
|
||||
|
||||
err:
|
||||
aggr_module_destroy(vif->aggr_cntxt);
|
||||
free_netdev(ndev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ath6kl_cfg80211_init(struct ath6kl *ar)
|
||||
{
|
||||
struct wiphy *wiphy = ar->wiphy;
|
||||
int ret;
|
||||
@ -2742,102 +3006,38 @@ int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_init_if_data(struct ath6kl_vif *vif)
|
||||
{
|
||||
vif->aggr_cntxt = aggr_init(vif->ndev);
|
||||
if (!vif->aggr_cntxt) {
|
||||
ath6kl_err("failed to initialize aggr\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
|
||||
(unsigned long) vif->ndev);
|
||||
setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
|
||||
(unsigned long) vif);
|
||||
|
||||
set_bit(WMM_ENABLED, &vif->flags);
|
||||
spin_lock_init(&vif->if_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
|
||||
{
|
||||
struct ath6kl *ar = vif->ar;
|
||||
|
||||
aggr_module_destroy(vif->aggr_cntxt);
|
||||
|
||||
ar->avail_idx_map |= BIT(vif->fw_vif_idx);
|
||||
|
||||
if (vif->nw_type == ADHOC_NETWORK)
|
||||
ar->ibss_if_active = false;
|
||||
|
||||
unregister_netdevice(vif->ndev);
|
||||
|
||||
ar->num_vif--;
|
||||
}
|
||||
|
||||
struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
|
||||
enum nl80211_iftype type, u8 fw_vif_idx,
|
||||
u8 nw_type)
|
||||
{
|
||||
struct net_device *ndev;
|
||||
struct ath6kl_vif *vif;
|
||||
|
||||
ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
|
||||
if (!ndev)
|
||||
return NULL;
|
||||
|
||||
vif = netdev_priv(ndev);
|
||||
ndev->ieee80211_ptr = &vif->wdev;
|
||||
vif->wdev.wiphy = ar->wiphy;
|
||||
vif->ar = ar;
|
||||
vif->ndev = ndev;
|
||||
SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
|
||||
vif->wdev.netdev = ndev;
|
||||
vif->wdev.iftype = type;
|
||||
vif->fw_vif_idx = fw_vif_idx;
|
||||
vif->nw_type = vif->next_mode = nw_type;
|
||||
|
||||
memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
|
||||
if (fw_vif_idx != 0)
|
||||
ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
|
||||
0x2;
|
||||
|
||||
init_netdev(ndev);
|
||||
|
||||
ath6kl_init_control_info(vif);
|
||||
|
||||
/* TODO: Pass interface specific pointer instead of ar */
|
||||
if (ath6kl_init_if_data(vif))
|
||||
goto err;
|
||||
|
||||
if (register_netdevice(ndev))
|
||||
goto err;
|
||||
|
||||
ar->avail_idx_map &= ~BIT(fw_vif_idx);
|
||||
vif->sme_state = SME_DISCONNECTED;
|
||||
set_bit(WLAN_ENABLED, &vif->flags);
|
||||
ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
|
||||
set_bit(NETDEV_REGISTERED, &vif->flags);
|
||||
|
||||
if (type == NL80211_IFTYPE_ADHOC)
|
||||
ar->ibss_if_active = true;
|
||||
|
||||
spin_lock_bh(&ar->list_lock);
|
||||
list_add_tail(&vif->list, &ar->vif_list);
|
||||
spin_unlock_bh(&ar->list_lock);
|
||||
|
||||
return ndev;
|
||||
|
||||
err:
|
||||
aggr_module_destroy(vif->aggr_cntxt);
|
||||
free_netdev(ndev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar)
|
||||
void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
|
||||
{
|
||||
wiphy_unregister(ar->wiphy);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,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 +49,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 */
|
||||
|
@ -79,8 +79,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 */
|
||||
|
316
drivers/net/wireless/ath/ath6kl/core.c
Normal file
316
drivers/net/wireless/ath/ath6kl/core.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright (c) 2004-2011 Atheros Communications Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#include <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 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(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);
|
||||
|
||||
ret = ath6kl_cfg80211_init(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_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 (uart_debug)
|
||||
ar->conf_flags |= ATH6KL_CONF_UART_DEBUG;
|
||||
|
||||
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);
|
||||
|
||||
ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
|
||||
|
||||
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_cfg80211_vif_cleanup(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;
|
||||
}
|
||||
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->listen_intvl_b = A_DEFAULT_LISTEN_INTERVAL;
|
||||
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].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");
|
@ -44,6 +44,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,7 +59,7 @@
|
||||
#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_DEFAULT_LISTEN_INTERVAL 1 /* beacon intervals */
|
||||
#define A_MAX_WOW_LISTEN_INTERVAL 1000
|
||||
|
||||
/* includes also the null byte */
|
||||
@ -97,45 +101,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 +152,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
|
||||
@ -186,7 +196,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,
|
||||
@ -231,14 +241,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 {
|
||||
@ -280,6 +295,9 @@ struct ath6kl_sta {
|
||||
u8 wpa_ie[ATH6KL_MAX_IE];
|
||||
struct sk_buff_head psq;
|
||||
spinlock_t psq_lock;
|
||||
u8 apsd_info;
|
||||
struct sk_buff_head apsdq;
|
||||
struct aggr_info_conn *aggr_conn;
|
||||
};
|
||||
|
||||
struct ath6kl_version {
|
||||
@ -408,6 +426,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 +451,7 @@ enum ath6kl_vif_state {
|
||||
DTIM_PERIOD_AVAIL,
|
||||
WLAN_ENABLED,
|
||||
STATS_UPDATE_PEND,
|
||||
HOST_SLEEP_MODE_CMD_PROCESSED,
|
||||
};
|
||||
|
||||
struct ath6kl_vif {
|
||||
@ -471,6 +497,8 @@ struct ath6kl_vif {
|
||||
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
|
||||
@ -504,6 +532,7 @@ struct ath6kl {
|
||||
struct wiphy *wiphy;
|
||||
|
||||
enum ath6kl_state state;
|
||||
unsigned int testmode;
|
||||
|
||||
struct ath6kl_bmi bmi;
|
||||
const struct ath6kl_hif_ops *hif_ops;
|
||||
@ -523,7 +552,6 @@ struct ath6kl {
|
||||
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;
|
||||
@ -574,17 +602,24 @@ 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;
|
||||
wait_queue_head_t event_wq;
|
||||
struct ath6kl_mbox_info mbox_info;
|
||||
|
||||
@ -603,6 +638,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;
|
||||
@ -676,7 +715,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,7 +725,7 @@ 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_by_aid(struct ath6kl *ar, u8 aid);
|
||||
@ -700,7 +741,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 +764,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 */
|
||||
|
@ -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,31 +115,31 @@ 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,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"Host Int status: 0x%x\n",
|
||||
irq_proc_reg->host_int_status);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"CPU Int status: 0x%x\n",
|
||||
irq_proc_reg->cpu_int_status);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"Error Int status: 0x%x\n",
|
||||
irq_proc_reg->error_int_status);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"Counter Int status: 0x%x\n",
|
||||
irq_proc_reg->counter_int_status);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"Mbox Frame: 0x%x\n",
|
||||
irq_proc_reg->mbox_frame);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"Rx Lookahead Valid: 0x%x\n",
|
||||
irq_proc_reg->rx_lkahd_valid);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"Rx Lookahead 0: 0x%x\n",
|
||||
irq_proc_reg->rx_lkahd[0]);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"Rx Lookahead 1: 0x%x\n",
|
||||
irq_proc_reg->rx_lkahd[1]);
|
||||
|
||||
@ -115,16 +148,16 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
|
||||
* If the target supports GMBOX hardware, dump some
|
||||
* additional state.
|
||||
*/
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"GMBOX Host Int status 2: 0x%x\n",
|
||||
irq_proc_reg->host_int_status2);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"GMBOX RX Avail: 0x%x\n",
|
||||
irq_proc_reg->gmbox_rx_avail);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"GMBOX lookahead alias 0: 0x%x\n",
|
||||
irq_proc_reg->rx_gmbox_lkahd_alias[0]);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"GMBOX lookahead alias 1: 0x%x\n",
|
||||
irq_proc_reg->rx_gmbox_lkahd_alias[1]);
|
||||
}
|
||||
@ -132,13 +165,13 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
|
||||
}
|
||||
|
||||
if (irq_enable_reg != NULL) {
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY,
|
||||
ath6kl_dbg(ATH6KL_DBG_IRQ,
|
||||
"Int status Enable: 0x%x\n",
|
||||
irq_enable_reg->int_status_en);
|
||||
ath6kl_dbg(ATH6KL_DBG_ANY, "Counter Int status Enable: 0x%x\n",
|
||||
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);
|
||||
|
||||
@ -1411,6 +1441,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 +1537,46 @@ 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 < 1) || (listen_interval > 50))
|
||||
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 = listen_interval;
|
||||
ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, 0,
|
||||
ar->listen_intvl_b);
|
||||
|
||||
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;
|
||||
char buf[32];
|
||||
int len;
|
||||
|
||||
len = scnprintf(buf, sizeof(buf), "%u %u\n", ar->listen_intvl_t,
|
||||
ar->listen_intvl_b);
|
||||
len = scnprintf(buf, sizeof(buf), "%u\n", ar->listen_intvl_b);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
}
|
||||
@ -1710,6 +1731,9 @@ int ath6kl_debug_init(struct ath6kl *ar)
|
||||
debugfs_create_file("bgscan_interval", S_IWUSR,
|
||||
ar->debugfs_phy, ar, &fops_bgscan_int);
|
||||
|
||||
debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR,
|
||||
ar->debugfs_phy, ar, &fops_listen_int);
|
||||
|
||||
debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar,
|
||||
&fops_power_params);
|
||||
|
||||
|
@ -41,6 +41,7 @@ enum ATH6K_DEBUG_MASK {
|
||||
ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */
|
||||
ATH6KL_DBG_WMI_DUMP = BIT(19),
|
||||
ATH6KL_DBG_SUSPEND = BIT(20),
|
||||
ATH6KL_DBG_USB = BIT(21),
|
||||
ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */
|
||||
};
|
||||
|
||||
@ -55,35 +56,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,
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
#include "hif.h"
|
||||
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "target.h"
|
||||
#include "hif-ops.h"
|
||||
@ -59,6 +61,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
|
||||
|
||||
@ -429,9 +433,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 +564,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 +693,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:
|
||||
|
@ -2062,6 +2062,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;
|
||||
|
||||
/*
|
||||
@ -2543,6 +2544,12 @@ int ath6kl_htc_wait_target(struct htc_target *target)
|
||||
struct htc_service_connect_resp resp;
|
||||
int status;
|
||||
|
||||
/* FIXME: remove once USB support is implemented */
|
||||
if (target->dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) {
|
||||
ath6kl_err("HTC doesn't support USB yet. Patience!\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* we should be getting 1 control message that the target is ready */
|
||||
packet = htc_wait_for_ctrl_msg(target);
|
||||
|
||||
@ -2772,7 +2779,9 @@ void ath6kl_htc_cleanup(struct htc_target *target)
|
||||
{
|
||||
struct htc_packet *packet, *tmp_packet;
|
||||
|
||||
ath6kl_hif_cleanup_scatter(target->dev->ar);
|
||||
/* FIXME: remove check once USB support is implemented */
|
||||
if (target->dev->ar->hif_type != ATH6KL_HIF_TYPE_USB)
|
||||
ath6kl_hif_cleanup_scatter(target->dev->ar);
|
||||
|
||||
list_for_each_entry_safe(packet, tmp_packet,
|
||||
&target->free_ctrl_txbuf, list) {
|
||||
|
@ -17,22 +17,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 +41,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,12 +61,18 @@ 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,
|
||||
},
|
||||
@ -84,8 +87,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 +106,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,
|
||||
},
|
||||
@ -452,6 +461,13 @@ 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(ar, ath6kl_get_hi_item_addr(ar,
|
||||
HI_ITEM(hi_serial_enable)), (u8 *)¶m, 4)) {
|
||||
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
|
||||
@ -573,36 +589,6 @@ int ath6kl_configure_target(struct ath6kl *ar)
|
||||
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 +612,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 +634,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 +701,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 +727,61 @@ static int ath6kl_fetch_otp_file(struct ath6kl *ar)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_fetch_testmode_file(struct ath6kl *ar)
|
||||
{
|
||||
char filename[100];
|
||||
int ret;
|
||||
|
||||
if (ar->testmode == 0)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s",
|
||||
ar->hw.fw.dir, ar->hw.fw.tcmd);
|
||||
}
|
||||
|
||||
set_bit(TESTMODE, &ar->flag);
|
||||
|
||||
ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
|
||||
if (ret) {
|
||||
ath6kl_err("Failed to get testmode %d firmware file %s: %d\n",
|
||||
ar->testmode, filename, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_fetch_fw_file(struct ath6kl *ar)
|
||||
{
|
||||
const char *filename;
|
||||
char filename[100];
|
||||
int ret;
|
||||
|
||||
if (ar->fw != NULL)
|
||||
return 0;
|
||||
|
||||
if (testmode) {
|
||||
if (ar->hw.fw_tcmd == NULL) {
|
||||
ath6kl_warn("testmode not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
filename = ar->hw.fw_tcmd;
|
||||
|
||||
set_bit(TESTMODE, &ar->flag);
|
||||
|
||||
goto get_fw;
|
||||
}
|
||||
|
||||
if (WARN_ON(ar->hw.fw == NULL))
|
||||
/* 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 +794,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 +817,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 +861,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)
|
||||
@ -907,6 +937,10 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
|
||||
ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n",
|
||||
ie_len);
|
||||
|
||||
/* in testmode we already might have a fw file */
|
||||
if (ar->fw != NULL)
|
||||
break;
|
||||
|
||||
ar->fw = kmemdup(data, ie_len, GFP_KERNEL);
|
||||
|
||||
if (ar->fw == NULL) {
|
||||
@ -1010,7 +1044,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath6kl_fetch_firmwares(struct ath6kl *ar)
|
||||
int ath6kl_init_fetch_firmwares(struct ath6kl *ar)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -1018,17 +1052,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;
|
||||
}
|
||||
@ -1249,6 +1296,50 @@ static int ath6kl_upload_patch(struct ath6kl *ar)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_upload_testscript(struct ath6kl *ar)
|
||||
{
|
||||
u32 address, param;
|
||||
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;
|
||||
}
|
||||
|
||||
param = address;
|
||||
ath6kl_bmi_write(ar,
|
||||
ath6kl_get_hi_item_addr(ar,
|
||||
HI_ITEM(hi_ota_testscript)),
|
||||
(unsigned char *) ¶m, 4);
|
||||
|
||||
param = 4096;
|
||||
ath6kl_bmi_write(ar,
|
||||
ath6kl_get_hi_item_addr(ar,
|
||||
HI_ITEM(hi_end_ram_reserve_sz)),
|
||||
(unsigned char *) ¶m, 4);
|
||||
|
||||
param = 1;
|
||||
ath6kl_bmi_write(ar,
|
||||
ath6kl_get_hi_item_addr(ar,
|
||||
HI_ITEM(hi_test_apps_related)),
|
||||
(unsigned char *) ¶m, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_init_upload(struct ath6kl *ar)
|
||||
{
|
||||
u32 param, options, sleep, address;
|
||||
@ -1357,6 +1448,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 +1468,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 +1577,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 +1646,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 +1678,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 +1687,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);
|
||||
}
|
||||
@ -1796,3 +1731,4 @@ void ath6kl_stop_txrx(struct ath6kl *ar)
|
||||
|
||||
clear_bit(WLAN_ENABLED, &ar->flag);
|
||||
}
|
||||
EXPORT_SYMBOL(ath6kl_stop_txrx);
|
||||
|
@ -52,9 +52,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,9 +70,11 @@ 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)
|
||||
@ -80,6 +84,7 @@ static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
|
||||
/* 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);
|
||||
spin_unlock_bh(&sta->psq_lock);
|
||||
|
||||
memset(&ar->ap_stats.sta[sta->aid - 1], 0,
|
||||
@ -90,7 +95,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 +257,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);
|
||||
@ -347,9 +352,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 +365,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 +430,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 +485,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));
|
||||
@ -587,10 +588,11 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid,
|
||||
memcpy(vif->bssid, bssid, sizeof(vif->bssid));
|
||||
vif->bss_ch = channel;
|
||||
|
||||
if ((vif->nw_type == INFRA_NETWORK))
|
||||
if ((vif->nw_type == INFRA_NETWORK)) {
|
||||
ar->listen_intvl_b = listen_int;
|
||||
ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
|
||||
ar->listen_intvl_t,
|
||||
ar->listen_intvl_b);
|
||||
0, ar->listen_intvl_b);
|
||||
}
|
||||
|
||||
netif_wake_queue(vif->ndev);
|
||||
|
||||
@ -601,7 +603,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) {
|
||||
@ -923,7 +925,7 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid,
|
||||
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);
|
||||
|
||||
@ -1020,11 +1022,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)
|
||||
|
@ -49,11 +49,13 @@ struct ath6kl_sdio {
|
||||
/* scatter request list head */
|
||||
struct list_head scat_req;
|
||||
|
||||
/* Avoids disabling irq while the interrupts being handled */
|
||||
struct mutex mtx_irq;
|
||||
|
||||
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;
|
||||
@ -460,8 +462,7 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func)
|
||||
ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n");
|
||||
|
||||
ar_sdio = sdio_get_drvdata(func);
|
||||
atomic_set(&ar_sdio->irq_handling, 1);
|
||||
|
||||
mutex_lock(&ar_sdio->mtx_irq);
|
||||
/*
|
||||
* Release the host during interrups so we can pick it back up when
|
||||
* we process commands.
|
||||
@ -470,7 +471,7 @@ 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);
|
||||
mutex_unlock(&ar_sdio->mtx_irq);
|
||||
WARN_ON(status && status != -ECANCELED);
|
||||
}
|
||||
|
||||
@ -578,17 +579,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)) {
|
||||
sdio_release_host(ar_sdio->func);
|
||||
schedule_timeout(HZ / 10);
|
||||
sdio_claim_host(ar_sdio->func);
|
||||
}
|
||||
mutex_lock(&ar_sdio->mtx_irq);
|
||||
|
||||
ret = sdio_release_irq(ar_sdio->func);
|
||||
if (ret)
|
||||
ath6kl_err("Failed to release sdio irq: %d\n", ret);
|
||||
|
||||
mutex_unlock(&ar_sdio->mtx_irq);
|
||||
|
||||
sdio_release_host(ar_sdio->func);
|
||||
}
|
||||
|
||||
@ -772,7 +770,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,6 +779,35 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar)
|
||||
{
|
||||
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
|
||||
struct sdio_func *func = ar_sdio->func;
|
||||
mmc_pm_flag_t flags;
|
||||
int ret;
|
||||
|
||||
flags = sdio_get_host_pm_caps(func);
|
||||
|
||||
ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags);
|
||||
|
||||
if (!(flags & MMC_PM_WAKE_SDIO_IRQ) ||
|
||||
!(flags & MMC_PM_KEEP_POWER))
|
||||
return -EINVAL;
|
||||
|
||||
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
|
||||
if (ret) {
|
||||
ath6kl_err("set sdio keep pwr flag failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sdio irq wakes up host */
|
||||
ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
|
||||
if (ret)
|
||||
ath6kl_err("set sdio wake irq flag failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
|
||||
{
|
||||
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
|
||||
@ -789,64 +815,70 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
|
||||
mmc_pm_flag_t flags;
|
||||
int ret;
|
||||
|
||||
flags = sdio_get_host_pm_caps(func);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!(flags & MMC_PM_WAKE_SDIO_IRQ))
|
||||
goto deepsleep;
|
||||
|
||||
/* sdio irq wakes up host */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
|
||||
if (ret)
|
||||
ath6kl_warn("set sdio wake irq flag failed: %d\n", ret);
|
||||
goto cut_pwr;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wow) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (ar->suspend_mode == WLAN_POWER_STATE_WOW ||
|
||||
(!ar->suspend_mode && wow)) {
|
||||
|
||||
ret = ath6kl_set_sdio_pm_caps(ar);
|
||||
if (ret)
|
||||
goto cut_pwr;
|
||||
|
||||
ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto cut_pwr;
|
||||
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
deepsleep:
|
||||
return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL);
|
||||
if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP ||
|
||||
!ar->suspend_mode) {
|
||||
|
||||
flags = sdio_get_host_pm_caps(func);
|
||||
if (!(flags & MMC_PM_KEEP_POWER))
|
||||
goto cut_pwr;
|
||||
|
||||
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
|
||||
if (ret)
|
||||
goto cut_pwr;
|
||||
|
||||
/*
|
||||
* Workaround to support Deep Sleep with MSM, set the host pm
|
||||
* flag as MMC_PM_WAKE_SDIO_IRQ to allow SDCC deiver to disable
|
||||
* the sdc2_clock and internally allows MSM to enter
|
||||
* TCXO shutdown properly.
|
||||
*/
|
||||
if ((flags & MMC_PM_WAKE_SDIO_IRQ)) {
|
||||
ret = sdio_set_host_pm_flags(func,
|
||||
MMC_PM_WAKE_SDIO_IRQ);
|
||||
if (ret)
|
||||
goto cut_pwr;
|
||||
}
|
||||
|
||||
ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto cut_pwr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cut_pwr:
|
||||
return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL);
|
||||
}
|
||||
|
||||
static int ath6kl_sdio_resume(struct ath6kl *ar)
|
||||
@ -1253,6 +1285,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
|
||||
spin_lock_init(&ar_sdio->scat_lock);
|
||||
spin_lock_init(&ar_sdio->wr_async_lock);
|
||||
mutex_init(&ar_sdio->dma_buffer_mutex);
|
||||
mutex_init(&ar_sdio->mtx_irq);
|
||||
|
||||
INIT_LIST_HEAD(&ar_sdio->scat_req);
|
||||
INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
|
||||
@ -1263,7 +1296,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
|
||||
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 +1326,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 +1349,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 +1366,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 +1396,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);
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "testmode.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <net/netlink.h>
|
||||
|
||||
@ -30,7 +31,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 +42,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))
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!buf || buf_len == 0)
|
||||
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;
|
||||
skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
ath6kl_warn("failed to allocate testmode rx skb!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (left == 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
} else if (left < 0) {
|
||||
ret = left;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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 +93,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;
|
||||
}
|
||||
|
@ -18,13 +18,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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,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 +94,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 +251,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 +265,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;
|
||||
}
|
||||
|
||||
@ -242,8 +341,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__,
|
||||
@ -260,11 +364,19 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
/* 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,10 +393,28 @@ 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;
|
||||
}
|
||||
|
||||
@ -449,9 +579,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;
|
||||
}
|
||||
@ -479,9 +607,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 +836,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;
|
||||
}
|
||||
@ -881,7 +1009,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 +1018,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 +1048,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 +1065,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 +1080,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 +1111,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 +1128,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 +1167,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 +1180,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 +1193,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 +1274,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;
|
||||
|
||||
@ -1171,6 +1369,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 +1408,36 @@ 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;
|
||||
|
||||
spin_lock_bh(&conn->psq_lock);
|
||||
while ((skbuff = skb_dequeue(&conn->psq))
|
||||
!= NULL) {
|
||||
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 +1531,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 +1553,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 +1570,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 +1593,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 +1625,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 +1674,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;
|
||||
}
|
||||
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;
|
||||
|
||||
p_aggr->aggr_sz = AGGR_SZ_DEFAULT;
|
||||
p_aggr->dev = dev;
|
||||
init_timer(&p_aggr->timer);
|
||||
p_aggr->timer.function = aggr_timeout;
|
||||
p_aggr->timer.data = (unsigned long) p_aggr;
|
||||
|
||||
p_aggr->timer_scheduled = false;
|
||||
skb_queue_head_init(&p_aggr->free_q);
|
||||
|
||||
ath6kl_alloc_netbufs(&p_aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS);
|
||||
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 +1698,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 (!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];
|
||||
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 +1793,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);
|
||||
}
|
||||
|
431
drivers/net/wireless/ath/ath6kl/usb.c
Normal file
431
drivers/net/wireless/ath/ath6kl/usb.c
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright (c) 2007-2011 Atheros Communications Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "core.h"
|
||||
|
||||
/* usb device object */
|
||||
struct ath6kl_usb {
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *interface;
|
||||
u8 *diag_cmd_buffer;
|
||||
u8 *diag_resp_buffer;
|
||||
struct ath6kl *ar;
|
||||
};
|
||||
|
||||
/* diagnostic command defnitions */
|
||||
#define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD 1
|
||||
#define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP 2
|
||||
#define ATH6KL_USB_CONTROL_REQ_DIAG_CMD 3
|
||||
#define ATH6KL_USB_CONTROL_REQ_DIAG_RESP 4
|
||||
|
||||
#define ATH6KL_USB_CTRL_DIAG_CC_READ 0
|
||||
#define ATH6KL_USB_CTRL_DIAG_CC_WRITE 1
|
||||
|
||||
struct ath6kl_usb_ctrl_diag_cmd_write {
|
||||
__le32 cmd;
|
||||
__le32 address;
|
||||
__le32 value;
|
||||
__le32 _pad[1];
|
||||
} __packed;
|
||||
|
||||
struct ath6kl_usb_ctrl_diag_cmd_read {
|
||||
__le32 cmd;
|
||||
__le32 address;
|
||||
} __packed;
|
||||
|
||||
struct ath6kl_usb_ctrl_diag_resp_read {
|
||||
__le32 value;
|
||||
} __packed;
|
||||
|
||||
#define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write))
|
||||
#define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read))
|
||||
|
||||
static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb)
|
||||
{
|
||||
usb_set_intfdata(ar_usb->interface, NULL);
|
||||
|
||||
kfree(ar_usb->diag_cmd_buffer);
|
||||
kfree(ar_usb->diag_resp_buffer);
|
||||
|
||||
kfree(ar_usb);
|
||||
}
|
||||
|
||||
static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface)
|
||||
{
|
||||
struct ath6kl_usb *ar_usb = NULL;
|
||||
struct usb_device *dev = interface_to_usbdev(interface);
|
||||
int status = 0;
|
||||
|
||||
ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL);
|
||||
if (ar_usb == NULL)
|
||||
goto fail_ath6kl_usb_create;
|
||||
|
||||
memset(ar_usb, 0, sizeof(struct ath6kl_usb));
|
||||
usb_set_intfdata(interface, ar_usb);
|
||||
ar_usb->udev = dev;
|
||||
ar_usb->interface = interface;
|
||||
|
||||
ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL);
|
||||
if (ar_usb->diag_cmd_buffer == NULL) {
|
||||
status = -ENOMEM;
|
||||
goto fail_ath6kl_usb_create;
|
||||
}
|
||||
|
||||
ar_usb->diag_resp_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_RESP,
|
||||
GFP_KERNEL);
|
||||
if (ar_usb->diag_resp_buffer == NULL) {
|
||||
status = -ENOMEM;
|
||||
goto fail_ath6kl_usb_create;
|
||||
}
|
||||
|
||||
fail_ath6kl_usb_create:
|
||||
if (status != 0) {
|
||||
ath6kl_usb_destroy(ar_usb);
|
||||
ar_usb = NULL;
|
||||
}
|
||||
return ar_usb;
|
||||
}
|
||||
|
||||
static void ath6kl_usb_device_detached(struct usb_interface *interface)
|
||||
{
|
||||
struct ath6kl_usb *ar_usb;
|
||||
|
||||
ar_usb = usb_get_intfdata(interface);
|
||||
if (ar_usb == NULL)
|
||||
return;
|
||||
|
||||
ath6kl_stop_txrx(ar_usb->ar);
|
||||
|
||||
ath6kl_core_cleanup(ar_usb->ar);
|
||||
|
||||
ath6kl_usb_destroy(ar_usb);
|
||||
}
|
||||
|
||||
static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb,
|
||||
u8 req, u16 value, u16 index, void *data,
|
||||
u32 size)
|
||||
{
|
||||
u8 *buf = NULL;
|
||||
int ret;
|
||||
|
||||
if (size > 0) {
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf, data, size);
|
||||
}
|
||||
|
||||
/* note: if successful returns number of bytes transfered */
|
||||
ret = usb_control_msg(ar_usb->udev,
|
||||
usb_sndctrlpipe(ar_usb->udev, 0),
|
||||
req,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR |
|
||||
USB_RECIP_DEVICE, value, index, buf,
|
||||
size, 1000);
|
||||
|
||||
if (ret < 0) {
|
||||
ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_usb_submit_ctrl_in(struct ath6kl_usb *ar_usb,
|
||||
u8 req, u16 value, u16 index, void *data,
|
||||
u32 size)
|
||||
{
|
||||
u8 *buf = NULL;
|
||||
int ret;
|
||||
|
||||
if (size > 0) {
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* note: if successful returns number of bytes transfered */
|
||||
ret = usb_control_msg(ar_usb->udev,
|
||||
usb_rcvctrlpipe(ar_usb->udev, 0),
|
||||
req,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR |
|
||||
USB_RECIP_DEVICE, value, index, buf,
|
||||
size, 2 * HZ);
|
||||
|
||||
if (ret < 0) {
|
||||
ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
memcpy((u8 *) data, buf, size);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_usb_ctrl_msg_exchange(struct ath6kl_usb *ar_usb,
|
||||
u8 req_val, u8 *req_buf, u32 req_len,
|
||||
u8 resp_val, u8 *resp_buf, u32 *resp_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* send command */
|
||||
ret = ath6kl_usb_submit_ctrl_out(ar_usb, req_val, 0, 0,
|
||||
req_buf, req_len);
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (resp_buf == NULL) {
|
||||
/* no expected response */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get response */
|
||||
ret = ath6kl_usb_submit_ctrl_in(ar_usb, resp_val, 0, 0,
|
||||
resp_buf, *resp_len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath6kl_usb_diag_read32(struct ath6kl *ar, u32 address, u32 *data)
|
||||
{
|
||||
struct ath6kl_usb *ar_usb = ar->hif_priv;
|
||||
struct ath6kl_usb_ctrl_diag_resp_read *resp;
|
||||
struct ath6kl_usb_ctrl_diag_cmd_read *cmd;
|
||||
u32 resp_len;
|
||||
int ret;
|
||||
|
||||
cmd = (struct ath6kl_usb_ctrl_diag_cmd_read *) ar_usb->diag_cmd_buffer;
|
||||
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
cmd->cmd = ATH6KL_USB_CTRL_DIAG_CC_READ;
|
||||
cmd->address = cpu_to_le32(address);
|
||||
resp_len = sizeof(*resp);
|
||||
|
||||
ret = ath6kl_usb_ctrl_msg_exchange(ar_usb,
|
||||
ATH6KL_USB_CONTROL_REQ_DIAG_CMD,
|
||||
(u8 *) cmd,
|
||||
sizeof(struct ath6kl_usb_ctrl_diag_cmd_write),
|
||||
ATH6KL_USB_CONTROL_REQ_DIAG_RESP,
|
||||
ar_usb->diag_resp_buffer, &resp_len);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
resp = (struct ath6kl_usb_ctrl_diag_resp_read *)
|
||||
ar_usb->diag_resp_buffer;
|
||||
|
||||
*data = le32_to_cpu(resp->value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath6kl_usb_diag_write32(struct ath6kl *ar, u32 address, __le32 data)
|
||||
{
|
||||
struct ath6kl_usb *ar_usb = ar->hif_priv;
|
||||
struct ath6kl_usb_ctrl_diag_cmd_write *cmd;
|
||||
|
||||
cmd = (struct ath6kl_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer;
|
||||
|
||||
memset(cmd, 0, sizeof(struct ath6kl_usb_ctrl_diag_cmd_write));
|
||||
cmd->cmd = cpu_to_le32(ATH6KL_USB_CTRL_DIAG_CC_WRITE);
|
||||
cmd->address = cpu_to_le32(address);
|
||||
cmd->value = data;
|
||||
|
||||
return ath6kl_usb_ctrl_msg_exchange(ar_usb,
|
||||
ATH6KL_USB_CONTROL_REQ_DIAG_CMD,
|
||||
(u8 *) cmd,
|
||||
sizeof(*cmd),
|
||||
0, NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
static int ath6kl_usb_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
|
||||
{
|
||||
struct ath6kl_usb *ar_usb = ar->hif_priv;
|
||||
int ret;
|
||||
|
||||
/* get response */
|
||||
ret = ath6kl_usb_submit_ctrl_in(ar_usb,
|
||||
ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP,
|
||||
0, 0, buf, len);
|
||||
if (ret != 0) {
|
||||
ath6kl_err("Unable to read the bmi data from the device: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
|
||||
{
|
||||
struct ath6kl_usb *ar_usb = ar->hif_priv;
|
||||
int ret;
|
||||
|
||||
/* send command */
|
||||
ret = ath6kl_usb_submit_ctrl_out(ar_usb,
|
||||
ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD,
|
||||
0, 0, buf, len);
|
||||
if (ret != 0) {
|
||||
ath6kl_err("unable to send the bmi data to the device: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_usb_power_on(struct ath6kl *ar)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_usb_power_off(struct ath6kl *ar)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ath6kl_hif_ops ath6kl_usb_ops = {
|
||||
.diag_read32 = ath6kl_usb_diag_read32,
|
||||
.diag_write32 = ath6kl_usb_diag_write32,
|
||||
.bmi_read = ath6kl_usb_bmi_read,
|
||||
.bmi_write = ath6kl_usb_bmi_write,
|
||||
.power_on = ath6kl_usb_power_on,
|
||||
.power_off = ath6kl_usb_power_off,
|
||||
};
|
||||
|
||||
/* ath6kl usb driver registered functions */
|
||||
static int ath6kl_usb_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(interface);
|
||||
struct ath6kl *ar;
|
||||
struct ath6kl_usb *ar_usb = NULL;
|
||||
int vendor_id, product_id;
|
||||
int ret = 0;
|
||||
|
||||
usb_get_dev(dev);
|
||||
|
||||
vendor_id = le16_to_cpu(dev->descriptor.idVendor);
|
||||
product_id = le16_to_cpu(dev->descriptor.idProduct);
|
||||
|
||||
ath6kl_dbg(ATH6KL_DBG_USB, "vendor_id = %04x\n", vendor_id);
|
||||
ath6kl_dbg(ATH6KL_DBG_USB, "product_id = %04x\n", product_id);
|
||||
|
||||
if (interface->cur_altsetting)
|
||||
ath6kl_dbg(ATH6KL_DBG_USB, "USB Interface %d\n",
|
||||
interface->cur_altsetting->desc.bInterfaceNumber);
|
||||
|
||||
|
||||
if (dev->speed == USB_SPEED_HIGH)
|
||||
ath6kl_dbg(ATH6KL_DBG_USB, "USB 2.0 Host\n");
|
||||
else
|
||||
ath6kl_dbg(ATH6KL_DBG_USB, "USB 1.1 Host\n");
|
||||
|
||||
ar_usb = ath6kl_usb_create(interface);
|
||||
|
||||
if (ar_usb == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_usb_put;
|
||||
}
|
||||
|
||||
ar = ath6kl_core_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);
|
@ -180,7 +180,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 +204,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 +238,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 +426,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));
|
||||
@ -786,12 +790,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;
|
||||
}
|
||||
@ -1145,9 +1151,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;
|
||||
}
|
||||
@ -2479,15 +2485,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 +2502,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 +2590,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)
|
||||
@ -2612,7 +2632,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 +2874,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 +3012,43 @@ int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac,
|
||||
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)
|
||||
{
|
||||
@ -3400,7 +3503,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");
|
||||
@ -3465,6 +3568,11 @@ 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_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID:
|
||||
ath6kl_dbg(ATH6KL_DBG_WMI,
|
||||
"WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID");
|
||||
ret = ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif);
|
||||
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);
|
||||
|
@ -149,8 +149,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 +159,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 +179,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 +213,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 +268,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 +660,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
|
||||
@ -1237,6 +1250,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 +1357,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 +1927,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 {
|
||||
@ -2105,6 +2129,19 @@ struct wmi_rx_frame_format_cmd {
|
||||
} __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 +2358,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);
|
||||
|
||||
@ -2417,7 +2454,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,13 +2463,26 @@ 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_profile_commit(struct wmi *wmip, u8 if_idx,
|
||||
struct wmi_connect_cmd *p);
|
||||
|
Loading…
Reference in New Issue
Block a user