Commit a5abbcb2 authored by John W. Linville's avatar John W. Linville

Merge branch 'for-linville' of git://github.com/kvalo/ath6kl

parents 49a59543 62c83ac4
...@@ -31,5 +31,7 @@ ath6kl-y += init.o ...@@ -31,5 +31,7 @@ ath6kl-y += init.o
ath6kl-y += main.o ath6kl-y += main.o
ath6kl-y += txrx.o ath6kl-y += txrx.o
ath6kl-y += wmi.o ath6kl-y += wmi.o
ath6kl-y += node.o
ath6kl-y += sdio.o ath6kl-y += sdio.o
ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o
ccflags-y += -D__CHECK_ENDIAN__
...@@ -62,14 +62,14 @@ static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar) ...@@ -62,14 +62,14 @@ static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar)
return 0; return 0;
} }
static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar, bool need_timeout) static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar)
{ {
unsigned long timeout; unsigned long timeout;
u32 rx_word = 0; u32 rx_word = 0;
int ret = 0; int ret = 0;
timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
while ((!need_timeout || time_before(jiffies, timeout)) && !rx_word) { while (time_before(jiffies, timeout) && !rx_word) {
ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS, ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS,
(u8 *)&rx_word, sizeof(rx_word), (u8 *)&rx_word, sizeof(rx_word),
HIF_RD_SYNC_BYTE_INC); HIF_RD_SYNC_BYTE_INC);
...@@ -109,8 +109,7 @@ static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len) ...@@ -109,8 +109,7 @@ static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len)
return ret; return ret;
} }
static int ath6kl_bmi_recv_buf(struct ath6kl *ar, static int ath6kl_bmi_recv_buf(struct ath6kl *ar, u8 *buf, u32 len)
u8 *buf, u32 len, bool want_timeout)
{ {
int ret; int ret;
u32 addr; u32 addr;
...@@ -162,7 +161,7 @@ static int ath6kl_bmi_recv_buf(struct ath6kl *ar, ...@@ -162,7 +161,7 @@ static int ath6kl_bmi_recv_buf(struct ath6kl *ar,
* a function of Host processor speed. * a function of Host processor speed.
*/ */
if (len >= 4) { /* NB: Currently, always true */ if (len >= 4) { /* NB: Currently, always true */
ret = ath6kl_bmi_get_rx_lkahd(ar, want_timeout); ret = ath6kl_bmi_get_rx_lkahd(ar);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -220,7 +219,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, ...@@ -220,7 +219,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
} }
ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version, ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version,
sizeof(targ_info->version), true); sizeof(targ_info->version));
if (ret) { if (ret) {
ath6kl_err("Unable to recv target info: %d\n", ret); ath6kl_err("Unable to recv target info: %d\n", ret);
return ret; return ret;
...@@ -230,8 +229,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, ...@@ -230,8 +229,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
/* Determine how many bytes are in the Target's targ_info */ /* Determine how many bytes are in the Target's targ_info */
ret = ath6kl_bmi_recv_buf(ar, ret = ath6kl_bmi_recv_buf(ar,
(u8 *)&targ_info->byte_count, (u8 *)&targ_info->byte_count,
sizeof(targ_info->byte_count), sizeof(targ_info->byte_count));
true);
if (ret) { if (ret) {
ath6kl_err("unable to read target info byte count: %d\n", ath6kl_err("unable to read target info byte count: %d\n",
ret); ret);
...@@ -252,8 +250,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, ...@@ -252,8 +250,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
((u8 *)targ_info) + ((u8 *)targ_info) +
sizeof(targ_info->byte_count), sizeof(targ_info->byte_count),
sizeof(*targ_info) - sizeof(*targ_info) -
sizeof(targ_info->byte_count), sizeof(targ_info->byte_count));
true);
if (ret) { if (ret) {
ath6kl_err("Unable to read target info (%d bytes): %d\n", ath6kl_err("Unable to read target info (%d bytes): %d\n",
...@@ -311,7 +308,7 @@ int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) ...@@ -311,7 +308,7 @@ int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
ret); ret);
return ret; return ret;
} }
ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len, true); ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len);
if (ret) { if (ret) {
ath6kl_err("Unable to read from the device: %d\n", ath6kl_err("Unable to read from the device: %d\n",
ret); ret);
...@@ -424,7 +421,7 @@ int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) ...@@ -424,7 +421,7 @@ int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
return ret; return ret;
} }
ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), false); ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
if (ret) { if (ret) {
ath6kl_err("Unable to read from the device: %d\n", ret); ath6kl_err("Unable to read from the device: %d\n", ret);
return ret; return ret;
...@@ -504,7 +501,7 @@ int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) ...@@ -504,7 +501,7 @@ int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
return ret; return ret;
} }
ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), true); ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
if (ret) { if (ret) {
ath6kl_err("Unable to read from the device: %d\n", ret); ath6kl_err("Unable to read from the device: %d\n", ret);
return ret; return ret;
......
...@@ -140,7 +140,7 @@ ...@@ -140,7 +140,7 @@
#define TARGET_VERSION_SENTINAL 0xffffffff #define TARGET_VERSION_SENTINAL 0xffffffff
#define TARGET_TYPE_AR6003 3 #define TARGET_TYPE_AR6003 3
#define TARGET_TYPE_AR6004 5
#define BMI_ROMPATCH_INSTALL 9 #define BMI_ROMPATCH_INSTALL 9
/* /*
* Semantics: Install a ROM Patch. * Semantics: Install a ROM Patch.
......
...@@ -17,6 +17,12 @@ ...@@ -17,6 +17,12 @@
#include "core.h" #include "core.h"
#include "cfg80211.h" #include "cfg80211.h"
#include "debug.h" #include "debug.h"
#include "hif-ops.h"
#include "testmode.h"
static unsigned int ath6kl_p2p;
module_param(ath6kl_p2p, uint, 0644);
#define RATETAB_ENT(_rate, _rateid, _flags) { \ #define RATETAB_ENT(_rate, _rateid, _flags) { \
.bitrate = (_rate), \ .bitrate = (_rate), \
...@@ -152,8 +158,7 @@ static int ath6kl_set_auth_type(struct ath6kl *ar, ...@@ -152,8 +158,7 @@ static int ath6kl_set_auth_type(struct ath6kl *ar,
break; break;
case NL80211_AUTHTYPE_AUTOMATIC: case NL80211_AUTHTYPE_AUTOMATIC:
ar->dot11_auth_mode = OPEN_AUTH; ar->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
ar->auto_auth_stage = AUTH_OPEN_IN_PROGRESS;
break; break;
default: default:
...@@ -167,7 +172,8 @@ static int ath6kl_set_auth_type(struct ath6kl *ar, ...@@ -167,7 +172,8 @@ static int ath6kl_set_auth_type(struct ath6kl *ar,
static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast) static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast)
{ {
u8 *ar_cipher = ucast ? &ar->prwise_crypto : &ar->grp_crypto; u8 *ar_cipher = ucast ? &ar->prwise_crypto : &ar->grp_crypto;
u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len : &ar->grp_crpto_len; u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len :
&ar->grp_crypto_len;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n", ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
__func__, cipher, ucast); __func__, cipher, ucast);
...@@ -354,6 +360,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, ...@@ -354,6 +360,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
} }
if (!ar->usr_bss_filter) { if (!ar->usr_bss_filter) {
clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) { if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) {
ath6kl_err("couldn't set bss filtering\n"); ath6kl_err("couldn't set bss filtering\n");
up(&ar->sem); up(&ar->sem);
...@@ -370,14 +377,14 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, ...@@ -370,14 +377,14 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
__func__, __func__,
ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto, ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
ar->prwise_crypto_len, ar->grp_crypto, ar->prwise_crypto_len, ar->grp_crypto,
ar->grp_crpto_len, ar->ch_hint); ar->grp_crypto_len, ar->ch_hint);
ar->reconnect_flag = 0; ar->reconnect_flag = 0;
status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type, status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
ar->dot11_auth_mode, ar->auth_mode, ar->dot11_auth_mode, ar->auth_mode,
ar->prwise_crypto, ar->prwise_crypto,
ar->prwise_crypto_len, ar->prwise_crypto_len,
ar->grp_crypto, ar->grp_crpto_len, ar->grp_crypto, ar->grp_crypto_len,
ar->ssid_len, ar->ssid, ar->ssid_len, ar->ssid,
ar->req_bssid, ar->ch_hint, ar->req_bssid, ar->ch_hint,
ar->connect_ctrl_flags); ar->connect_ctrl_flags);
...@@ -407,6 +414,53 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, ...@@ -407,6 +414,53 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
return 0; return 0;
} }
static int ath6kl_add_bss_if_needed(struct ath6kl *ar, const u8 *bssid,
struct ieee80211_channel *chan,
const u8 *beacon_ie, size_t beacon_ie_len)
{
struct cfg80211_bss *bss;
u8 *ie;
bss = cfg80211_get_bss(ar->wdev->wiphy, chan, bssid,
ar->ssid, ar->ssid_len, WLAN_CAPABILITY_ESS,
WLAN_CAPABILITY_ESS);
if (bss == NULL) {
/*
* Since cfg80211 may not yet know about the BSS,
* generate a partial entry until the first BSS info
* event becomes available.
*
* Prepend SSID element since it is not included in the Beacon
* IEs from the target.
*/
ie = kmalloc(2 + ar->ssid_len + beacon_ie_len, GFP_KERNEL);
if (ie == NULL)
return -ENOMEM;
ie[0] = WLAN_EID_SSID;
ie[1] = ar->ssid_len;
memcpy(ie + 2, ar->ssid, ar->ssid_len);
memcpy(ie + 2 + ar->ssid_len, beacon_ie, beacon_ie_len);
bss = cfg80211_inform_bss(ar->wdev->wiphy, chan,
bssid, 0, WLAN_CAPABILITY_ESS, 100,
ie, 2 + ar->ssid_len + beacon_ie_len,
0, GFP_KERNEL);
if (bss)
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for "
"%pM prior to indicating connect/roamed "
"event\n", bssid);
kfree(ie);
} else
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
"entry\n");
if (bss == NULL)
return -ENOMEM;
cfg80211_put_bss(bss);
return 0;
}
void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
u8 *bssid, u16 listen_intvl, u8 *bssid, u16 listen_intvl,
u16 beacon_intvl, u16 beacon_intvl,
...@@ -414,19 +468,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, ...@@ -414,19 +468,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
u8 beacon_ie_len, u8 assoc_req_len, u8 beacon_ie_len, u8 assoc_req_len,
u8 assoc_resp_len, u8 *assoc_info) u8 assoc_resp_len, u8 *assoc_info)
{ {
u16 size = 0; struct ieee80211_channel *chan;
u16 capability = 0;
struct cfg80211_bss *bss = NULL;
struct ieee80211_mgmt *mgmt = NULL;
struct ieee80211_channel *ibss_ch = NULL;
s32 signal = 50 * 100;
u8 ie_buf_len = 0;
unsigned char ie_buf[256];
unsigned char *ptr_ie_buf = ie_buf;
unsigned char *ieeemgmtbuf = NULL;
u8 source_mac[ETH_ALEN];
u16 capa_mask;
u16 capa_val;
/* capinfo + listen interval */ /* capinfo + listen interval */
u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16); u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
...@@ -441,7 +483,12 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, ...@@ -441,7 +483,12 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
assoc_req_len -= assoc_req_ie_offset; assoc_req_len -= assoc_req_ie_offset;
assoc_resp_len -= assoc_resp_ie_offset; assoc_resp_len -= assoc_resp_ie_offset;
ar->auto_auth_stage = AUTH_IDLE; /*
* Store Beacon interval here; DTIM period will be available only once
* a Beacon frame from the AP is seen.
*/
ar->assoc_bss_beacon_int = beacon_intvl;
clear_bit(DTIM_PERIOD_AVAIL, &ar->flag);
if (nw_type & ADHOC_NETWORK) { if (nw_type & ADHOC_NETWORK) {
if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) { if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
...@@ -452,110 +499,26 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, ...@@ -452,110 +499,26 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
} }
if (nw_type & INFRA_NETWORK) { if (nw_type & INFRA_NETWORK) {
if (ar->wdev->iftype != NL80211_IFTYPE_STATION) { if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in station mode\n", __func__); "%s: ath6k not in station mode\n", __func__);
return; return;
} }
} }
if (nw_type & ADHOC_NETWORK) { chan = ieee80211_get_channel(ar->wdev->wiphy, (int) channel);
capa_mask = WLAN_CAPABILITY_IBSS;
capa_val = WLAN_CAPABILITY_IBSS;
} else {
capa_mask = WLAN_CAPABILITY_ESS;
capa_val = WLAN_CAPABILITY_ESS;
}
/* Before informing the join/connect event, make sure that
* bss entry is present in scan list, if it not present
* construct and insert into scan list, otherwise that
* event will be dropped on the way by cfg80211, due to
* this keys will not be plumbed in case of WEP and
* application will not be aware of join/connect status. */
bss = cfg80211_get_bss(ar->wdev->wiphy, NULL, bssid,
ar->wdev->ssid, ar->wdev->ssid_len,
capa_mask, capa_val);
/*
* Earlier we were updating the cfg about bss by making a beacon frame
* only if the entry for bss is not there. This can have some issue if
* ROAM event is generated and a heavy traffic is ongoing. The ROAM
* event is handled through a work queue and by the time it really gets
* handled, BSS would have been aged out. So it is better to update the
* cfg about BSS irrespective of its entry being present right now or
* not.
*/
if (nw_type & ADHOC_NETWORK) { if (nw_type & ADHOC_NETWORK) {
/* construct 802.11 mgmt beacon */ cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
if (ptr_ie_buf) {
*ptr_ie_buf++ = WLAN_EID_SSID;
*ptr_ie_buf++ = ar->ssid_len;
memcpy(ptr_ie_buf, ar->ssid, ar->ssid_len);
ptr_ie_buf += ar->ssid_len;
*ptr_ie_buf++ = WLAN_EID_IBSS_PARAMS;
*ptr_ie_buf++ = 2; /* length */
*ptr_ie_buf++ = 0; /* ATIM window */
*ptr_ie_buf++ = 0; /* ATIM window */
/* TODO: update ibss params and include supported rates,
* DS param set, extened support rates, wmm. */
ie_buf_len = ptr_ie_buf - ie_buf;
}
capability |= WLAN_CAPABILITY_IBSS;
if (ar->prwise_crypto == WEP_CRYPT)
capability |= WLAN_CAPABILITY_PRIVACY;
memcpy(source_mac, ar->net_dev->dev_addr, ETH_ALEN);
ptr_ie_buf = ie_buf;
} else {
capability = *(u16 *) (&assoc_info[beacon_ie_len]);
memcpy(source_mac, bssid, ETH_ALEN);
ptr_ie_buf = assoc_req_ie;
ie_buf_len = assoc_req_len;
}
size = offsetof(struct ieee80211_mgmt, u)
+ sizeof(mgmt->u.beacon)
+ ie_buf_len;
ieeemgmtbuf = kzalloc(size, GFP_ATOMIC);
if (!ieeemgmtbuf) {
ath6kl_err("ieee mgmt buf alloc error\n");
cfg80211_put_bss(bss);
return; return;
} }
mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf; if (ath6kl_add_bss_if_needed(ar, bssid, chan, assoc_info,
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | beacon_ie_len) < 0) {
IEEE80211_STYPE_BEACON); ath6kl_err("could not add cfg80211 bss entry for "
memset(mgmt->da, 0xff, ETH_ALEN); /* broadcast addr */ "connect/roamed notification\n");
memcpy(mgmt->sa, source_mac, ETH_ALEN);
memcpy(mgmt->bssid, bssid, ETH_ALEN);
mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_intvl);
mgmt->u.beacon.capab_info = cpu_to_le16(capability);
memcpy(mgmt->u.beacon.variable, ptr_ie_buf, ie_buf_len);
ibss_ch = ieee80211_get_channel(ar->wdev->wiphy, (int)channel);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: inform bss with bssid %pM channel %d beacon_intvl %d capability 0x%x\n",
__func__, mgmt->bssid, ibss_ch->hw_value,
beacon_intvl, capability);
bss = cfg80211_inform_bss_frame(ar->wdev->wiphy,
ibss_ch, mgmt,
size, signal, GFP_KERNEL);
kfree(ieeemgmtbuf);
cfg80211_put_bss(bss);
if (nw_type & ADHOC_NETWORK) {
cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
return; return;
} }
...@@ -568,7 +531,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, ...@@ -568,7 +531,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
WLAN_STATUS_SUCCESS, GFP_KERNEL); WLAN_STATUS_SUCCESS, GFP_KERNEL);
} else if (ar->sme_state == SME_CONNECTED) { } else if (ar->sme_state == SME_CONNECTED) {
/* inform roam event to cfg80211 */ /* inform roam event to cfg80211 */
cfg80211_roamed(ar->net_dev, ibss_ch, bssid, cfg80211_roamed(ar->net_dev, chan, bssid,
assoc_req_ie, assoc_req_len, assoc_req_ie, assoc_req_len,
assoc_resp_ie, assoc_resp_len, GFP_KERNEL); assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
} }
...@@ -605,6 +568,8 @@ static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy, ...@@ -605,6 +568,8 @@ static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
up(&ar->sem); up(&ar->sem);
ar->sme_state = SME_DISCONNECTED;
return 0; return 0;
} }
...@@ -612,9 +577,6 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, ...@@ -612,9 +577,6 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
u8 *bssid, u8 assoc_resp_len, u8 *bssid, u8 assoc_resp_len,
u8 *assoc_info, u16 proto_reason) u8 *assoc_info, u16 proto_reason)
{ {
struct ath6kl_key *key = NULL;
u16 status;
if (ar->scan_req) { if (ar->scan_req) {
cfg80211_scan_done(ar->scan_req, true); cfg80211_scan_done(ar->scan_req, true);
ar->scan_req = NULL; ar->scan_req = NULL;
...@@ -632,30 +594,28 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, ...@@ -632,30 +594,28 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
} }
if (ar->nw_type & INFRA_NETWORK) { if (ar->nw_type & INFRA_NETWORK) {
if (ar->wdev->iftype != NL80211_IFTYPE_STATION) { if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in station mode\n", __func__); "%s: ath6k not in station mode\n", __func__);
return; return;
} }
} }
if (!test_bit(CONNECT_PEND, &ar->flag)) { /*
if (reason != DISCONNECT_CMD) * Send a disconnect command to target when a disconnect event is
ath6kl_wmi_disconnect_cmd(ar->wmi); * received with reason code other than 3 (DISCONNECT_CMD - disconnect
* request from host) to make the firmware stop trying to connect even
return; * after giving disconnect event. There will be one more disconnect
} * event for this disconnect command with reason code DISCONNECT_CMD
* which will be notified to cfg80211.
*/
if (reason == NO_NETWORK_AVAIL) { if (reason != DISCONNECT_CMD) {
/* connect cmd failed */
ath6kl_wmi_disconnect_cmd(ar->wmi); ath6kl_wmi_disconnect_cmd(ar->wmi);
return; return;
} }
if (reason != DISCONNECT_CMD)
return;
if (!ar->auto_auth_stage) {
clear_bit(CONNECT_PEND, &ar->flag); clear_bit(CONNECT_PEND, &ar->flag);
if (ar->sme_state == SME_CONNECTING) { if (ar->sme_state == SME_CONNECTING) {
...@@ -664,132 +624,34 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, ...@@ -664,132 +624,34 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL); GFP_KERNEL);
} else { } else if (ar->sme_state == SME_CONNECTED) {
cfg80211_disconnected(ar->net_dev, reason, cfg80211_disconnected(ar->net_dev, reason,
NULL, 0, GFP_KERNEL); NULL, 0, GFP_KERNEL);
} }
ar->sme_state = SME_DISCONNECTED; ar->sme_state = SME_DISCONNECTED;
return;
}
if (ar->dot11_auth_mode != OPEN_AUTH)
return;
/*
* If the current auth algorithm is open, try shared and
* make autoAuthStage idle. We do not make it leap for now
* being.
*/
key = &ar->keys[ar->def_txkey_index];
if (down_interruptible(&ar->sem)) {
ath6kl_err("busy, couldn't get access\n");
return;
}
ar->dot11_auth_mode = SHARED_AUTH;
ar->auto_auth_stage = AUTH_IDLE;
ath6kl_wmi_addkey_cmd(ar->wmi,
ar->def_txkey_index,
ar->prwise_crypto,
GROUP_USAGE | TX_USAGE,
key->key_len, NULL,
key->key,
KEY_OP_INIT_VAL, NULL,
NO_SYNC_WMIFLAG);
status = ath6kl_wmi_connect_cmd(ar->wmi,
ar->nw_type,
ar->dot11_auth_mode,
ar->auth_mode,
ar->prwise_crypto,
ar->prwise_crypto_len,
ar->grp_crypto,
ar->grp_crpto_len,
ar->ssid_len,
ar->ssid,
ar->req_bssid,
ar->ch_hint,
ar->connect_ctrl_flags);
up(&ar->sem);
}
static inline bool is_ch_11a(u16 ch)
{
return (!((ch >= 2412) && (ch <= 2484)));
}
/* struct ath6kl_node_table::nt_nodelock is locked when calling this */
void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni)
{
u16 size;
unsigned char *ieeemgmtbuf = NULL;
struct ieee80211_mgmt *mgmt;
struct ieee80211_channel *channel;
struct ieee80211_supported_band *band;
struct ath6kl_common_ie *cie;
s32 signal;
int freq;
cie = &ni->ni_cie;
if (is_ch_11a(cie->ie_chan))
band = wiphy->bands[IEEE80211_BAND_5GHZ]; /* 11a */
else if ((cie->ie_erp) || (cie->ie_xrates))
band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11g */
else
band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11b */
size = ni->ni_framelen + offsetof(struct ieee80211_mgmt, u);
ieeemgmtbuf = kmalloc(size, GFP_ATOMIC);
if (!ieeemgmtbuf) {
ath6kl_err("ieee mgmt buf alloc error\n");
return;
}
/*
* TODO: Update target to include 802.11 mac header while sending
* bss info. Target removes 802.11 mac header while sending the bss
* info to host, cfg80211 needs it, for time being just filling the
* da, sa and bssid fields alone.
*/
mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf;
memset(mgmt->da, 0xff, ETH_ALEN); /*broadcast addr */
memcpy(mgmt->sa, ni->ni_macaddr, ETH_ALEN);
memcpy(mgmt->bssid, ni->ni_macaddr, ETH_ALEN);
memcpy(ieeemgmtbuf + offsetof(struct ieee80211_mgmt, u),
ni->ni_buf, ni->ni_framelen);
freq = cie->ie_chan;
channel = ieee80211_get_channel(wiphy, freq);
signal = ni->ni_snr * 100;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: bssid %pM ch %d freq %d size %d\n", __func__,
mgmt->bssid, channel->hw_value, freq, size);
cfg80211_inform_bss_frame(wiphy, channel, mgmt,
size, signal, GFP_ATOMIC);
kfree(ieeemgmtbuf);
} }
static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_scan_request *request) struct cfg80211_scan_request *request)
{ {
struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
s8 n_channels = 0;
u16 *channels = NULL;
int ret = 0; int ret = 0;
if (!ath6kl_cfg80211_ready(ar)) if (!ath6kl_cfg80211_ready(ar))
return -EIO; return -EIO;
if (!ar->usr_bss_filter) { if (!ar->usr_bss_filter) {
if (ath6kl_wmi_bssfilter_cmd(ar->wmi, clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
ret = ath6kl_wmi_bssfilter_cmd(
ar->wmi,
(test_bit(CONNECTED, &ar->flag) ? (test_bit(CONNECTED, &ar->flag) ?
ALL_BUT_BSS_FILTER : ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
ALL_BSS_FILTER), 0) != 0) { if (ret) {
ath6kl_err("couldn't set bss filtering\n"); ath6kl_err("couldn't set bss filtering\n");
return -EIO; return ret;
} }
} }
...@@ -806,14 +668,47 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, ...@@ -806,14 +668,47 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
request->ssids[i].ssid); request->ssids[i].ssid);
} }
if (ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0, if (request->ie) {
false, 0, 0, 0, NULL) != 0) { ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_REQ,
ath6kl_err("wmi_startscan_cmd failed\n"); request->ie, request->ie_len);
ret = -EIO; if (ret) {
ath6kl_err("failed to set Probe Request appie for "
"scan");
return ret;
} }
}
/*
* Scan only the requested channels if the request specifies a set of
* channels. If the list is longer than the target supports, do not
* configure the list and instead, scan all available channels.
*/
if (request->n_channels > 0 &&
request->n_channels <= WMI_MAX_CHANNELS) {
u8 i;
n_channels = request->n_channels;
channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
if (channels == NULL) {
ath6kl_warn("failed to set scan channels, "
"scan all channels");
n_channels = 0;
}
for (i = 0; i < n_channels; i++)
channels[i] = request->channels[i]->center_freq;
}
ret = ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0,
false, 0, 0, n_channels, channels);
if (ret)
ath6kl_err("wmi_startscan_cmd failed\n");
else
ar->scan_req = request; ar->scan_req = request;
kfree(channels);
return ret; return ret;
} }
...@@ -831,9 +726,6 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status) ...@@ -831,9 +726,6 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status)
goto out; goto out;
} }
/* Translate data to cfg80211 mgmt format */
wlan_iterate_nodes(&ar->scan_table, ar->wdev->wiphy);
cfg80211_scan_done(ar->scan_req, false); cfg80211_scan_done(ar->scan_req, false);
if (ar->scan_req->n_ssids && ar->scan_req->ssids[0].ssid_len) { if (ar->scan_req->n_ssids && ar->scan_req->ssids[0].ssid_len) {
...@@ -918,6 +810,40 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, ...@@ -918,6 +810,40 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
key_usage, key->seq_len); key_usage, key->seq_len);
ar->def_txkey_index = key_index; ar->def_txkey_index = key_index;
if (ar->nw_type == AP_NETWORK && !pairwise &&
(key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
ar->ap_mode_bkey.valid = true;
ar->ap_mode_bkey.key_index = key_index;
ar->ap_mode_bkey.key_type = key_type;
ar->ap_mode_bkey.key_len = key->key_len;
memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
if (!test_bit(CONNECTED, &ar->flag)) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
"key configuration until AP mode has been "
"started\n");
/*
* The key will be set in ath6kl_connect_ap_mode() once
* the connected event is received from the target.
*/
return 0;
}
}
if (ar->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
!test_bit(CONNECTED, &ar->flag)) {
/*
* Store the key locally so that it can be re-configured after
* the AP mode has properly started
* (ath6kl_install_statioc_wep_keys).
*/
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
"until AP mode has been started\n");
ar->wep_key_list[key_index].key_len = key->key_len;
memcpy(ar->wep_key_list[key_index].key, key->key, key->key_len);
return 0;
}
status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
key_type, key_usage, key->key_len, key_type, key_usage, key->key_len,
key->seq, key->key, KEY_OP_INIT_VAL, key->seq, key->key, KEY_OP_INIT_VAL,
...@@ -1002,6 +928,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, ...@@ -1002,6 +928,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
struct ath6kl_key *key = NULL; struct ath6kl_key *key = NULL;
int status = 0; int status = 0;
u8 key_usage; u8 key_usage;
enum crypto_type key_type = NONE_CRYPT;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
...@@ -1026,9 +953,16 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, ...@@ -1026,9 +953,16 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
key_usage = GROUP_USAGE; key_usage = GROUP_USAGE;
if (ar->prwise_crypto == WEP_CRYPT) if (ar->prwise_crypto == WEP_CRYPT)
key_usage |= TX_USAGE; key_usage |= TX_USAGE;
if (unicast)
key_type = ar->prwise_crypto;
if (multicast)
key_type = ar->grp_crypto;
if (ar->next_mode == AP_NETWORK && !test_bit(CONNECTED, &ar->flag))
return 0; /* Delay until AP mode has been started */
status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
ar->prwise_crypto, key_usage, key_type, key_usage,
key->key_len, key->seq, key->key, key->key_len, key->seq, key->key,
KEY_OP_INIT_VAL, NULL, KEY_OP_INIT_VAL, NULL,
SYNC_BOTH_WMIFLAG); SYNC_BOTH_WMIFLAG);
...@@ -1183,6 +1117,15 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, ...@@ -1183,6 +1117,15 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
ar->next_mode = ADHOC_NETWORK; ar->next_mode = ADHOC_NETWORK;
break; break;
case NL80211_IFTYPE_AP:
ar->next_mode = AP_NETWORK;
break;
case NL80211_IFTYPE_P2P_CLIENT:
ar->next_mode = INFRA_NETWORK;
break;
case NL80211_IFTYPE_P2P_GO:
ar->next_mode = AP_NETWORK;
break;
default: default:
ath6kl_err("invalid interface type %u\n", type); ath6kl_err("invalid interface type %u\n", type);
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -1246,13 +1189,13 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, ...@@ -1246,13 +1189,13 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
__func__, __func__,
ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto, ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
ar->prwise_crypto_len, ar->grp_crypto, ar->prwise_crypto_len, ar->grp_crypto,
ar->grp_crpto_len, ar->ch_hint); ar->grp_crypto_len, ar->ch_hint);
status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type, status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
ar->dot11_auth_mode, ar->auth_mode, ar->dot11_auth_mode, ar->auth_mode,
ar->prwise_crypto, ar->prwise_crypto,
ar->prwise_crypto_len, ar->prwise_crypto_len,
ar->grp_crypto, ar->grp_crpto_len, ar->grp_crypto, ar->grp_crypto_len,
ar->ssid_len, ar->ssid, ar->ssid_len, ar->ssid,
ar->req_bssid, ar->ch_hint, ar->req_bssid, ar->ch_hint,
ar->connect_ctrl_flags); ar->connect_ctrl_flags);
...@@ -1422,12 +1365,23 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, ...@@ -1422,12 +1365,23 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
} else { } else {
ath6kl_warn("invalid rate: %d\n", rate); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"invalid rate from stats: %d\n", rate);
ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
return 0; return 0;
} }
sinfo->filled |= STATION_INFO_TX_BITRATE; sinfo->filled |= STATION_INFO_TX_BITRATE;
if (test_bit(CONNECTED, &ar->flag) &&
test_bit(DTIM_PERIOD_AVAIL, &ar->flag) &&
ar->nw_type == INFRA_NETWORK) {
sinfo->filled |= STATION_INFO_BSS_PARAM;
sinfo->bss_param.flags = 0;
sinfo->bss_param.dtim_period = ar->assoc_bss_dtim_period;
sinfo->bss_param.beacon_interval = ar->assoc_bss_beacon_int;
}
return 0; return 0;
} }
...@@ -1455,6 +1409,402 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) ...@@ -1455,6 +1409,402 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int ar6k_cfg80211_suspend(struct wiphy *wiphy,
struct cfg80211_wowlan *wow)
{
struct ath6kl *ar = wiphy_priv(wiphy);
return ath6kl_hif_suspend(ar);
}
#endif
static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type)
{
struct ath6kl *ar = ath6kl_priv(dev);
if (!ath6kl_cfg80211_ready(ar))
return -EIO;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
__func__, chan->center_freq, chan->hw_value);
ar->next_chan = chan->center_freq;
return 0;
}
static bool ath6kl_is_p2p_ie(const u8 *pos)
{
return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
pos[2] == 0x50 && pos[3] == 0x6f &&
pos[4] == 0x9a && pos[5] == 0x09;
}
static int ath6kl_set_ap_probe_resp_ies(struct ath6kl *ar, const u8 *ies,
size_t ies_len)
{
const u8 *pos;
u8 *buf = NULL;
size_t len = 0;
int ret;
/*
* Filter out P2P IE(s) since they will be included depending on
* the Probe Request frame in ath6kl_send_go_probe_resp().
*/
if (ies && ies_len) {
buf = kmalloc(ies_len, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
pos = ies;
while (pos + 1 < ies + ies_len) {
if (pos + 2 + pos[1] > ies + ies_len)
break;
if (!ath6kl_is_p2p_ie(pos)) {
memcpy(buf + len, pos, 2 + pos[1]);
len += 2 + pos[1];
}
pos += 2 + pos[1];
}
}
ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP,
buf, len);
kfree(buf);
return ret;
}
static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *info, bool add)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ieee80211_mgmt *mgmt;
u8 *ies;
int ies_len;
struct wmi_connect_cmd p;
int res;
int i;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
if (!ath6kl_cfg80211_ready(ar))
return -EIO;
if (ar->next_mode != AP_NETWORK)
return -EOPNOTSUPP;
if (info->beacon_ies) {
res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON,
info->beacon_ies,
info->beacon_ies_len);
if (res)
return res;
}
if (info->proberesp_ies) {
res = ath6kl_set_ap_probe_resp_ies(ar, info->proberesp_ies,
info->proberesp_ies_len);
if (res)
return res;
}
if (info->assocresp_ies) {
res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP,
info->assocresp_ies,
info->assocresp_ies_len);
if (res)
return res;
}
if (!add)
return 0;
ar->ap_mode_bkey.valid = false;
/* TODO:
* info->interval
* info->dtim_period
*/
if (info->head == NULL)
return -EINVAL;
mgmt = (struct ieee80211_mgmt *) info->head;
ies = mgmt->u.beacon.variable;
if (ies > info->head + info->head_len)
return -EINVAL;
ies_len = info->head + info->head_len - ies;
if (info->ssid == NULL)
return -EINVAL;
memcpy(ar->ssid, info->ssid, info->ssid_len);
ar->ssid_len = info->ssid_len;
if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
return -EOPNOTSUPP; /* TODO */
ar->dot11_auth_mode = OPEN_AUTH;
memset(&p, 0, sizeof(p));
for (i = 0; i < info->crypto.n_akm_suites; i++) {
switch (info->crypto.akm_suites[i]) {
case WLAN_AKM_SUITE_8021X:
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
p.auth_mode |= WPA_AUTH;
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
p.auth_mode |= WPA2_AUTH;
break;
case WLAN_AKM_SUITE_PSK:
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
p.auth_mode |= WPA_PSK_AUTH;
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
p.auth_mode |= WPA2_PSK_AUTH;
break;
}
}
if (p.auth_mode == 0)
p.auth_mode = NONE_AUTH;
ar->auth_mode = p.auth_mode;
for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
switch (info->crypto.ciphers_pairwise[i]) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
p.prwise_crypto_type |= WEP_CRYPT;
break;
case WLAN_CIPHER_SUITE_TKIP:
p.prwise_crypto_type |= TKIP_CRYPT;
break;
case WLAN_CIPHER_SUITE_CCMP:
p.prwise_crypto_type |= AES_CRYPT;
break;
}
}
if (p.prwise_crypto_type == 0) {
p.prwise_crypto_type = NONE_CRYPT;
ath6kl_set_cipher(ar, 0, true);
} else if (info->crypto.n_ciphers_pairwise == 1)
ath6kl_set_cipher(ar, info->crypto.ciphers_pairwise[0], true);
switch (info->crypto.cipher_group) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
p.grp_crypto_type = WEP_CRYPT;
break;
case WLAN_CIPHER_SUITE_TKIP:
p.grp_crypto_type = TKIP_CRYPT;
break;
case WLAN_CIPHER_SUITE_CCMP:
p.grp_crypto_type = AES_CRYPT;
break;
default:
p.grp_crypto_type = NONE_CRYPT;
break;
}
ath6kl_set_cipher(ar, info->crypto.cipher_group, false);
p.nw_type = AP_NETWORK;
ar->nw_type = ar->next_mode;
p.ssid_len = ar->ssid_len;
memcpy(p.ssid, ar->ssid, ar->ssid_len);
p.dot11_auth_mode = ar->dot11_auth_mode;
p.ch = cpu_to_le16(ar->next_chan);
res = ath6kl_wmi_ap_profile_commit(ar->wmi, &p);
if (res < 0)
return res;
return 0;
}
static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *info)
{
return ath6kl_ap_beacon(wiphy, dev, info, true);
}
static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *info)
{
return ath6kl_ap_beacon(wiphy, dev, info, false);
}
static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
{
struct ath6kl *ar = ath6kl_priv(dev);
if (ar->nw_type != AP_NETWORK)
return -EOPNOTSUPP;
if (!test_bit(CONNECTED, &ar->flag))
return -ENOTCONN;
ath6kl_wmi_disconnect_cmd(ar->wmi);
clear_bit(CONNECTED, &ar->flag);
return 0;
}
static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_parameters *params)
{
struct ath6kl *ar = ath6kl_priv(dev);
if (ar->nw_type != AP_NETWORK)
return -EOPNOTSUPP;
/* Use this only for authorizing/unauthorizing a station */
if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
return -EOPNOTSUPP;
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_AUTHORIZE,
mac, 0);
return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_UNAUTHORIZE, mac,
0);
}
static int ath6kl_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie)
{
struct ath6kl *ar = ath6kl_priv(dev);
/* TODO: if already pending or ongoing remain-on-channel,
* return -EBUSY */
*cookie = 1; /* only a single pending request is supported */
return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, chan->center_freq,
duration);
}
static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
u64 cookie)
{
struct ath6kl *ar = ath6kl_priv(dev);
if (cookie != 1)
return -ENOENT;
return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi);
}
static int ath6kl_send_go_probe_resp(struct ath6kl *ar, const u8 *buf,
size_t len, unsigned int freq)
{
const u8 *pos;
u8 *p2p;
int p2p_len;
int ret;
const struct ieee80211_mgmt *mgmt;
mgmt = (const struct ieee80211_mgmt *) buf;
/* Include P2P IE(s) from the frame generated in user space. */
p2p = kmalloc(len, GFP_KERNEL);
if (p2p == NULL)
return -ENOMEM;
p2p_len = 0;
pos = mgmt->u.probe_resp.variable;
while (pos + 1 < buf + len) {
if (pos + 2 + pos[1] > buf + len)
break;
if (ath6kl_is_p2p_ie(pos)) {
memcpy(p2p + p2p_len, pos, 2 + pos[1]);
p2p_len += 2 + pos[1];
}
pos += 2 + pos[1];
}
ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, freq, mgmt->da,
p2p, p2p_len);
kfree(p2p);
return ret;
}
static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, u64 *cookie)
{
struct ath6kl *ar = ath6kl_priv(dev);
u32 id;
const struct ieee80211_mgmt *mgmt;
mgmt = (const struct ieee80211_mgmt *) buf;
if (buf + len >= mgmt->u.probe_resp.variable &&
ar->nw_type == AP_NETWORK && test_bit(CONNECTED, &ar->flag) &&
ieee80211_is_probe_resp(mgmt->frame_control)) {
/*
* Send Probe Response frame in AP mode using a separate WMI
* command to allow the target to fill in the generic IEs.
*/
*cookie = 0; /* TX status not supported */
return ath6kl_send_go_probe_resp(ar, buf, len,
chan->center_freq);
}
id = ar->send_action_id++;
if (id == 0) {
/*
* 0 is a reserved value in the WMI command and shall not be
* used for the command.
*/
id = ar->send_action_id++;
}
*cookie = id;
return ath6kl_wmi_send_action_cmd(ar->wmi, id, chan->center_freq, wait,
buf, len);
}
static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
struct net_device *dev,
u16 frame_type, bool reg)
{
struct ath6kl *ar = ath6kl_priv(dev);
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
__func__, frame_type, reg);
if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
/*
* Note: This notification callback is not allowed to sleep, so
* we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
* hardcode target to report Probe Request frames all the time.
*/
ar->probe_req_report = reg;
}
}
static const struct ieee80211_txrx_stypes
ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
.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),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_P2P_GO] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
};
static struct cfg80211_ops ath6kl_cfg80211_ops = { static struct cfg80211_ops ath6kl_cfg80211_ops = {
.change_virtual_intf = ath6kl_cfg80211_change_iface, .change_virtual_intf = ath6kl_cfg80211_change_iface,
.scan = ath6kl_cfg80211_scan, .scan = ath6kl_cfg80211_scan,
...@@ -1474,12 +1824,26 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { ...@@ -1474,12 +1824,26 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
.set_pmksa = ath6kl_set_pmksa, .set_pmksa = ath6kl_set_pmksa,
.del_pmksa = ath6kl_del_pmksa, .del_pmksa = ath6kl_del_pmksa,
.flush_pmksa = ath6kl_flush_pmksa, .flush_pmksa = ath6kl_flush_pmksa,
CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
#ifdef CONFIG_PM
.suspend = ar6k_cfg80211_suspend,
#endif
.set_channel = ath6kl_set_channel,
.add_beacon = ath6kl_add_beacon,
.set_beacon = ath6kl_set_beacon,
.del_beacon = ath6kl_del_beacon,
.change_station = ath6kl_change_station,
.remain_on_channel = ath6kl_remain_on_channel,
.cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
.mgmt_tx = ath6kl_mgmt_tx,
.mgmt_frame_register = ath6kl_mgmt_frame_register,
}; };
struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
{ {
int ret = 0; int ret = 0;
struct wireless_dev *wdev; struct wireless_dev *wdev;
struct ath6kl *ar;
wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
if (!wdev) { if (!wdev) {
...@@ -1495,13 +1859,25 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) ...@@ -1495,13 +1859,25 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
return NULL; return NULL;
} }
ar = wiphy_priv(wdev->wiphy);
ar->p2p = !!ath6kl_p2p;
wdev->wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
wdev->wiphy->max_remain_on_channel_duration = 5000;
/* set device pointer for wiphy */ /* set device pointer for wiphy */
set_wiphy_dev(wdev->wiphy, dev); set_wiphy_dev(wdev->wiphy, dev);
wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC); BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
if (ar->p2p) {
wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT);
}
/* max num of ssids that can be probed during scanning */ /* max num of ssids that can be probed during scanning */
wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
wdev->wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
......
...@@ -75,94 +75,11 @@ enum crypto_type { ...@@ -75,94 +75,11 @@ enum crypto_type {
AES_CRYPT = 0x08, AES_CRYPT = 0x08,
}; };
#define ATH6KL_NODE_HASHSIZE 32
/* simple hash is enough for variation of macaddr */
#define ATH6KL_NODE_HASH(addr) \
(((const u8 *)(addr))[ETH_ALEN - 1] % \
ATH6KL_NODE_HASHSIZE)
/*
* Table of ath6kl_node instances. Each ieee80211com
* has at least one for holding the scan candidates.
* When operating as an access point or in ibss mode there
* is a second table for associated stations or neighbors.
*/
struct ath6kl_node_table {
spinlock_t nt_nodelock; /* on node table */
struct bss *nt_node_first; /* information of all nodes */
struct bss *nt_node_last; /* information of all nodes */
struct bss *nt_hash[ATH6KL_NODE_HASHSIZE];
const char *nt_name; /* for debugging */
u32 nt_node_age; /* node aging time */
};
#define WLAN_NODE_INACT_TIMEOUT_MSEC 120000
#define WLAN_NODE_INACT_CNT 4
struct ath6kl_common_ie {
u16 ie_chan;
u8 *ie_tstamp;
u8 *ie_ssid;
u8 *ie_rates;
u8 *ie_xrates;
u8 *ie_country;
u8 *ie_wpa;
u8 *ie_rsn;
u8 *ie_wmm;
u8 *ie_ath;
u16 ie_capInfo;
u16 ie_beaconInt;
u8 *ie_tim;
u8 *ie_chswitch;
u8 ie_erp;
u8 *ie_wsc;
u8 *ie_htcap;
u8 *ie_htop;
};
struct bss {
u8 ni_macaddr[ETH_ALEN];
u8 ni_snr;
s16 ni_rssi;
struct bss *ni_list_next;
struct bss *ni_list_prev;
struct bss *ni_hash_next;
struct bss *ni_hash_prev;
struct ath6kl_common_ie ni_cie;
u8 *ni_buf;
u16 ni_framelen;
struct ath6kl_node_table *ni_table;
u32 ni_refcnt;
u32 ni_tstamp;
u32 ni_actcnt;
};
struct htc_endpoint_credit_dist; struct htc_endpoint_credit_dist;
struct ath6kl; struct ath6kl;
enum htc_credit_dist_reason; enum htc_credit_dist_reason;
struct htc_credit_state_info; struct htc_credit_state_info;
struct bss *wlan_node_alloc(int wh_size);
void wlan_node_free(struct bss *ni);
void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni,
const u8 *mac_addr);
struct bss *wlan_find_node(struct ath6kl_node_table *nt,
const u8 *mac_addr);
void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni);
void wlan_free_allnodes(struct ath6kl_node_table *nt);
void wlan_iterate_nodes(struct ath6kl_node_table *nt, void *arg);
void wlan_node_table_init(struct ath6kl_node_table *nt);
void wlan_node_table_cleanup(struct ath6kl_node_table *nt);
void wlan_refresh_inactive_nodes(struct ath6kl *ar);
struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 *ssid,
u32 ssid_len, bool is_wpa2, bool match_ssid);
void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni);
int ath6k_setup_credit_dist(void *htc_handle, int ath6k_setup_credit_dist(void *htc_handle,
struct htc_credit_state_info *cred_info); struct htc_credit_state_info *cred_info);
void ath6k_credit_distribute(struct htc_credit_state_info *cred_inf, void ath6k_credit_distribute(struct htc_credit_state_info *cred_inf,
......
...@@ -21,10 +21,12 @@ ...@@ -21,10 +21,12 @@
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/circ_buf.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include "htc.h" #include "htc.h"
#include "wmi.h" #include "wmi.h"
#include "bmi.h" #include "bmi.h"
#include "target.h"
#define MAX_ATH6KL 1 #define MAX_ATH6KL 1
#define ATH6KL_MAX_RX_BUFFERS 16 #define ATH6KL_MAX_RX_BUFFERS 16
...@@ -42,6 +44,9 @@ ...@@ -42,6 +44,9 @@
#define ATH6KL_MAX_ENDPOINTS 4 #define ATH6KL_MAX_ENDPOINTS 4
#define MAX_NODE_NUM 15 #define MAX_NODE_NUM 15
/* Extra bytes for htc header alignment */
#define ATH6KL_HTC_ALIGN_BYTES 3
/* MAX_HI_COOKIE_NUM are reserved for high priority traffic */ /* MAX_HI_COOKIE_NUM are reserved for high priority traffic */
#define MAX_DEF_COOKIE_NUM 180 #define MAX_DEF_COOKIE_NUM 180
#define MAX_HI_COOKIE_NUM 18 /* 10% of MAX_COOKIE_NUM */ #define MAX_HI_COOKIE_NUM 18 /* 10% of MAX_COOKIE_NUM */
...@@ -53,6 +58,35 @@ ...@@ -53,6 +58,35 @@
#define A_DEFAULT_LISTEN_INTERVAL 100 #define A_DEFAULT_LISTEN_INTERVAL 100
#define A_MAX_WOW_LISTEN_INTERVAL 1000 #define A_MAX_WOW_LISTEN_INTERVAL 1000
/* includes also the null byte */
#define ATH6KL_FIRMWARE_MAGIC "QCA-ATH6KL"
enum ath6kl_fw_ie_type {
ATH6KL_FW_IE_FW_VERSION = 0,
ATH6KL_FW_IE_TIMESTAMP = 1,
ATH6KL_FW_IE_OTP_IMAGE = 2,
ATH6KL_FW_IE_FW_IMAGE = 3,
ATH6KL_FW_IE_PATCH_IMAGE = 4,
ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5,
ATH6KL_FW_IE_CAPABILITIES = 6,
ATH6KL_FW_IE_PATCH_ADDR = 7,
};
enum ath6kl_fw_capability {
ATH6KL_FW_CAPABILITY_HOST_P2P = 0,
/* this needs to be last */
ATH6KL_FW_CAPABILITY_MAX,
};
#define ATH6KL_CAPABILITY_LEN (ALIGN(ATH6KL_FW_CAPABILITY_MAX, 32) / 32)
struct ath6kl_fw_ie {
__le32 id;
__le32 len;
u8 data[0];
};
/* AR6003 1.0 definitions */ /* AR6003 1.0 definitions */
#define AR6003_REV1_VERSION 0x300002ba #define AR6003_REV1_VERSION 0x300002ba
...@@ -61,7 +95,9 @@ ...@@ -61,7 +95,9 @@
#define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS 0x57e910 #define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS 0x57e910
#define AR6003_REV2_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77" #define AR6003_REV2_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77"
#define AR6003_REV2_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77" #define AR6003_REV2_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77"
#define AR6003_REV2_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athtcmd_ram.bin"
#define AR6003_REV2_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin" #define AR6003_REV2_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin"
#define AR6003_REV2_FIRMWARE_2_FILE "ath6k/AR6003/hw2.0/fw-2.bin"
#define AR6003_REV2_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin" #define AR6003_REV2_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin"
#define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin" #define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin"
...@@ -69,11 +105,21 @@ ...@@ -69,11 +105,21 @@
#define AR6003_REV3_VERSION 0x30000582 #define AR6003_REV3_VERSION 0x30000582
#define AR6003_REV3_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin" #define AR6003_REV3_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin"
#define AR6003_REV3_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin" #define AR6003_REV3_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin"
#define AR6003_REV3_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin"
#define AR6003_REV3_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin" #define AR6003_REV3_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin"
#define AR6003_REV3_FIRMWARE_2_FILE "ath6k/AR6003/hw2.1.1/fw-2.bin"
#define AR6003_REV3_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin" #define AR6003_REV3_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin"
#define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \ #define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \
"ath6k/AR6003/hw2.1.1/bdata.SD31.bin" "ath6k/AR6003/hw2.1.1/bdata.SD31.bin"
/* AR6004 1.0 definitions */
#define AR6004_REV1_VERSION 0x30000623
#define AR6004_REV1_FIRMWARE_FILE "ath6k/AR6004/hw6.1/fw.ram.bin"
#define AR6004_REV1_FIRMWARE_2_FILE "ath6k/AR6004/hw6.1/fw-2.bin"
#define AR6004_REV1_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.bin"
#define AR6004_REV1_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.DB132.bin"
#define AR6004_REV1_EPPING_FIRMWARE_FILE "ath6k/AR6004/hw6.1/endpointping.bin"
/* Per STA data, used in AP mode */ /* Per STA data, used in AP mode */
#define STA_PS_AWAKE BIT(0) #define STA_PS_AWAKE BIT(0)
#define STA_PS_SLEEP BIT(1) #define STA_PS_SLEEP BIT(1)
...@@ -325,26 +371,13 @@ struct ath6kl_mbox_info { ...@@ -325,26 +371,13 @@ struct ath6kl_mbox_info {
#define ATH6KL_KEY_RECV 0x02 #define ATH6KL_KEY_RECV 0x02
#define ATH6KL_KEY_DEFAULT 0x80 /* default xmit key */ #define ATH6KL_KEY_DEFAULT 0x80 /* default xmit key */
/* /* Initial group key for AP mode */
* WPA/RSN get/set key request. Specify the key/cipher
* type and whether the key is to be used for sending and/or
* receiving. The key index should be set only when working
* with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
* Otherwise a unicast/pairwise key is specified by the bssid
* (on a station) or mac address (on an ap). They key length
* must include any MIC key data; otherwise it should be no
* more than ATH6KL_KEYBUF_SIZE.
*/
struct ath6kl_req_key { struct ath6kl_req_key {
u8 ik_type; /* key/cipher type */ bool valid;
u8 ik_pad; u8 key_index;
u16 ik_keyix; /* key index */ int key_type;
u8 ik_keylen; /* key length in bytes */ u8 key[WLAN_MAX_KEY_LEN];
u8 ik_flags; u8 key_len;
u8 ik_macaddr[ETH_ALEN];
u64 ik_keyrsc; /* key receive sequence counter */
u64 ik_keytsc; /* key transmit sequence counter */
u8 ik_keydata[ATH6KL_KEYBUF_SIZE + ATH6KL_MICBUF_SIZE];
}; };
/* Flag info */ /* Flag info */
...@@ -361,6 +394,9 @@ struct ath6kl_req_key { ...@@ -361,6 +394,9 @@ struct ath6kl_req_key {
#define NETDEV_REGISTERED 10 #define NETDEV_REGISTERED 10
#define SKIP_SCAN 11 #define SKIP_SCAN 11
#define WLAN_ENABLED 12 #define WLAN_ENABLED 12
#define TESTMODE 13
#define CLEAR_BSSFILTER_ON_BEACON 14
#define DTIM_PERIOD_AVAIL 15
struct ath6kl { struct ath6kl {
struct device *dev; struct device *dev;
...@@ -383,7 +419,7 @@ struct ath6kl { ...@@ -383,7 +419,7 @@ struct ath6kl {
u8 prwise_crypto; u8 prwise_crypto;
u8 prwise_crypto_len; u8 prwise_crypto_len;
u8 grp_crypto; u8 grp_crypto;
u8 grp_crpto_len; u8 grp_crypto_len;
u8 def_txkey_index; u8 def_txkey_index;
struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
u8 bssid[ETH_ALEN]; u8 bssid[ETH_ALEN];
...@@ -392,6 +428,7 @@ struct ath6kl { ...@@ -392,6 +428,7 @@ struct ath6kl {
u16 bss_ch; u16 bss_ch;
u16 listen_intvl_b; u16 listen_intvl_b;
u16 listen_intvl_t; u16 listen_intvl_t;
u8 lrssi_roam_threshold;
struct ath6kl_version version; struct ath6kl_version version;
u32 target_type; u32 target_type;
u8 tx_pwr; u8 tx_pwr;
...@@ -432,7 +469,18 @@ struct ath6kl { ...@@ -432,7 +469,18 @@ struct ath6kl {
enum wlan_low_pwr_state wlan_pwr_state; enum wlan_low_pwr_state wlan_pwr_state;
struct wmi_scan_params_cmd sc_params; struct wmi_scan_params_cmd sc_params;
#define AR_MCAST_FILTER_MAC_ADDR_SIZE 4 #define AR_MCAST_FILTER_MAC_ADDR_SIZE 4
u8 auto_auth_stage; struct {
void *rx_report;
size_t rx_report_len;
} tm;
struct {
u32 dataset_patch_addr;
u32 app_load_addr;
u32 app_start_override_addr;
u32 board_ext_data_addr;
u32 reserved_ram_size;
} hw;
u16 conf_flags; u16 conf_flags;
wait_queue_head_t event_wq; wait_queue_head_t event_wq;
...@@ -454,9 +502,35 @@ struct ath6kl { ...@@ -454,9 +502,35 @@ struct ath6kl {
u8 *fw_patch; u8 *fw_patch;
size_t fw_patch_len; size_t fw_patch_len;
unsigned long fw_capabilities[ATH6KL_CAPABILITY_LEN];
struct workqueue_struct *ath6kl_wq; struct workqueue_struct *ath6kl_wq;
struct ath6kl_node_table scan_table; struct dentry *debugfs_phy;
u32 send_action_id;
bool probe_req_report;
u16 next_chan;
bool p2p;
u16 assoc_bss_beacon_int;
u8 assoc_bss_dtim_period;
#ifdef CONFIG_ATH6KL_DEBUG
struct {
struct circ_buf fwlog_buf;
spinlock_t fwlog_lock;
void *fwlog_tmp;
u32 fwlog_mask;
unsigned int dbgfs_diag_reg;
u32 diag_reg_addr_wr;
u32 diag_reg_val_wr;
struct {
unsigned int invalid_rate;
} war_stats;
} debug;
#endif /* CONFIG_ATH6KL_DEBUG */
}; };
static inline void *ath6kl_priv(struct net_device *dev) static inline void *ath6kl_priv(struct net_device *dev)
...@@ -474,6 +548,19 @@ static inline void ath6kl_deposit_credit_to_ep(struct htc_credit_state_info ...@@ -474,6 +548,19 @@ static inline void ath6kl_deposit_credit_to_ep(struct htc_credit_state_info
cred_info->cur_free_credits -= credits; cred_info->cur_free_credits -= credits;
} }
static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
u32 item_offset)
{
u32 addr = 0;
if (ar->target_type == TARGET_TYPE_AR6003)
addr = ATH6KL_AR6003_HI_START_ADDR + item_offset;
else if (ar->target_type == TARGET_TYPE_AR6004)
addr = ATH6KL_AR6004_HI_START_ADDR + item_offset;
return addr;
}
void ath6kl_destroy(struct net_device *dev, unsigned int unregister); void ath6kl_destroy(struct net_device *dev, unsigned int unregister);
int ath6kl_configure_target(struct ath6kl *ar); int ath6kl_configure_target(struct ath6kl *ar);
void ath6kl_detect_error(unsigned long ptr); void ath6kl_detect_error(unsigned long ptr);
...@@ -487,9 +574,11 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, ...@@ -487,9 +574,11 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
struct htc_packet *packet); struct htc_packet *packet);
void ath6kl_stop_txrx(struct ath6kl *ar); void ath6kl_stop_txrx(struct ath6kl *ar);
void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar); void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar);
int ath6kl_access_datadiag(struct ath6kl *ar, u32 address, int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value);
u8 *data, u32 length, bool read); int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length);
int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data); int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value);
int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length);
int ath6kl_read_fwlogs(struct ath6kl *ar);
void ath6kl_init_profile_info(struct ath6kl *ar); void ath6kl_init_profile_info(struct ath6kl *ar);
void ath6kl_tx_data_cleanup(struct ath6kl *ar); void ath6kl_tx_data_cleanup(struct ath6kl *ar);
void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile, void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
...@@ -520,6 +609,10 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, ...@@ -520,6 +609,10 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel,
u16 beacon_int, enum network_type net_type, u16 beacon_int, enum network_type net_type,
u8 beacon_ie_len, u8 assoc_req_len, u8 beacon_ie_len, u8 assoc_req_len,
u8 assoc_resp_len, u8 *assoc_info); u8 assoc_resp_len, u8 *assoc_info);
void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel);
void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
u8 keymgmt, u8 ucipher, u8 auth,
u8 assoc_req_len, u8 *assoc_info);
void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason,
u8 *bssid, u8 assoc_resp_len, u8 *bssid, u8 assoc_resp_len,
u8 *assoc_info, u16 prot_reason_status); u8 *assoc_info, u16 prot_reason_status);
...@@ -534,11 +627,11 @@ void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid); ...@@ -534,11 +627,11 @@ void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid);
void ath6kl_dtimexpiry_event(struct ath6kl *ar); void ath6kl_dtimexpiry_event(struct ath6kl *ar);
void ath6kl_disconnect(struct ath6kl *ar); void ath6kl_disconnect(struct ath6kl *ar);
void ath6kl_deep_sleep_enable(struct ath6kl *ar);
void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid); void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid);
void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no, void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no,
u8 win_sz); u8 win_sz);
void ath6kl_wakeup_event(void *dev); void ath6kl_wakeup_event(void *dev);
void ath6kl_target_failure(struct ath6kl *ar); void ath6kl_target_failure(struct ath6kl *ar);
void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni);
#endif /* CORE_H */ #endif /* CORE_H */
...@@ -15,7 +15,26 @@ ...@@ -15,7 +15,26 @@
*/ */
#include "core.h" #include "core.h"
#include <linux/circ_buf.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include "debug.h" #include "debug.h"
#include "target.h"
struct ath6kl_fwlog_slot {
__le32 timestamp;
__le32 length;
/* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */
u8 payload[0];
};
#define ATH6KL_FWLOG_SIZE 32768
#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
ATH6KL_FWLOG_PAYLOAD_SIZE)
#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
int ath6kl_printk(const char *level, const char *fmt, ...) int ath6kl_printk(const char *level, const char *fmt, ...)
{ {
...@@ -36,6 +55,27 @@ int ath6kl_printk(const char *level, const char *fmt, ...) ...@@ -36,6 +55,27 @@ int ath6kl_printk(const char *level, const char *fmt, ...)
} }
#ifdef CONFIG_ATH6KL_DEBUG #ifdef CONFIG_ATH6KL_DEBUG
#define REG_OUTPUT_LEN_PER_LINE 25
#define REGTYPE_STR_LEN 100
struct ath6kl_diag_reg_info {
u32 reg_start;
u32 reg_end;
const char *reg_info;
};
static const struct ath6kl_diag_reg_info diag_reg[] = {
{ 0x20000, 0x200fc, "General DMA and Rx registers" },
{ 0x28000, 0x28900, "MAC PCU register & keycache" },
{ 0x20800, 0x20a40, "QCU" },
{ 0x21000, 0x212f0, "DCU" },
{ 0x4000, 0x42e4, "RTC" },
{ 0x540000, 0x540000 + (256 * 1024), "RAM" },
{ 0x29800, 0x2B210, "Base Band" },
{ 0x1C000, 0x1C748, "Analog" },
};
void ath6kl_dump_registers(struct ath6kl_device *dev, void ath6kl_dump_registers(struct ath6kl_device *dev,
struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_proc_registers *irq_proc_reg,
struct ath6kl_irq_enable_reg *irq_enable_reg) struct ath6kl_irq_enable_reg *irq_enable_reg)
...@@ -147,4 +187,748 @@ void dump_cred_dist_stats(struct htc_target *target) ...@@ -147,4 +187,748 @@ void dump_cred_dist_stats(struct htc_target *target)
target->cred_dist_cntxt->cur_free_credits); target->cred_dist_cntxt->cur_free_credits);
} }
static int ath6kl_debugfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
{
switch (war) {
case ATH6KL_WAR_INVALID_RATE:
ar->debug.war_stats.invalid_rate++;
break;
}
}
static ssize_t read_file_war_stats(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
char *buf;
unsigned int len = 0, buf_len = 1500;
ssize_t ret_cnt;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%25s\n",
"Workaround stats");
len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
"=================");
len += scnprintf(buf + len, buf_len - len, "%20s %10u\n",
"Invalid rates", ar->debug.war_stats.invalid_rate);
if (WARN_ON(len > buf_len))
len = buf_len;
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
return ret_cnt;
}
static const struct file_operations fops_war_stats = {
.read = read_file_war_stats,
.open = ath6kl_debugfs_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf,
size_t buf_len)
{
struct circ_buf *fwlog = &ar->debug.fwlog_buf;
size_t space;
int i;
/* entries must all be equal size */
if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE))
return;
space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE);
if (space < buf_len)
/* discard oldest slot */
fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) &
(ATH6KL_FWLOG_SIZE - 1);
for (i = 0; i < buf_len; i += space) {
space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail,
ATH6KL_FWLOG_SIZE);
if ((size_t) space > buf_len - i)
space = buf_len - i;
memcpy(&fwlog->buf[fwlog->head], buf, space);
fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1);
}
}
void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
{
struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp;
size_t slot_len;
if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
return;
spin_lock_bh(&ar->debug.fwlog_lock);
slot->timestamp = cpu_to_le32(jiffies);
slot->length = cpu_to_le32(len);
memcpy(slot->payload, buf, len);
slot_len = sizeof(*slot) + len;
if (slot_len < ATH6KL_FWLOG_SLOT_SIZE)
memset(slot->payload + len, 0,
ATH6KL_FWLOG_SLOT_SIZE - slot_len);
ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE);
spin_unlock_bh(&ar->debug.fwlog_lock);
}
static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar)
{
return CIRC_CNT(ar->debug.fwlog_buf.head,
ar->debug.fwlog_buf.tail,
ATH6KL_FWLOG_SLOT_SIZE) == 0;
}
static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
struct circ_buf *fwlog = &ar->debug.fwlog_buf;
size_t len = 0, buf_len = count;
ssize_t ret_cnt;
char *buf;
int ccnt;
buf = vmalloc(buf_len);
if (!buf)
return -ENOMEM;
/* read undelivered logs from firmware */
ath6kl_read_fwlogs(ar);
spin_lock_bh(&ar->debug.fwlog_lock);
while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail,
ATH6KL_FWLOG_SIZE);
if ((size_t) ccnt > buf_len - len)
ccnt = buf_len - len;
memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt);
len += ccnt;
fwlog->tail = (fwlog->tail + ccnt) &
(ATH6KL_FWLOG_SIZE - 1);
}
spin_unlock_bh(&ar->debug.fwlog_lock);
if (WARN_ON(len > buf_len))
len = buf_len;
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
vfree(buf);
return ret_cnt;
}
static const struct file_operations fops_fwlog = {
.open = ath6kl_debugfs_open,
.read = ath6kl_fwlog_read,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
char buf[16];
int len;
len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t ath6kl_fwlog_mask_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
int ret;
ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask);
if (ret)
return ret;
ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi,
ATH6KL_FWLOG_VALID_MASK,
ar->debug.fwlog_mask);
if (ret)
return ret;
return count;
}
static const struct file_operations fops_fwlog_mask = {
.open = ath6kl_debugfs_open,
.read = ath6kl_fwlog_mask_read,
.write = ath6kl_fwlog_mask_write,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
struct target_stats *tgt_stats = &ar->target_stats;
char *buf;
unsigned int len = 0, buf_len = 1500;
int i;
long left;
ssize_t ret_cnt;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (down_interruptible(&ar->sem)) {
kfree(buf);
return -EBUSY;
}
set_bit(STATS_UPDATE_PEND, &ar->flag);
if (ath6kl_wmi_get_stats_cmd(ar->wmi)) {
up(&ar->sem);
kfree(buf);
return -EIO;
}
left = wait_event_interruptible_timeout(ar->event_wq,
!test_bit(STATS_UPDATE_PEND,
&ar->flag), WMI_TIMEOUT);
up(&ar->sem);
if (left <= 0) {
kfree(buf);
return -ETIMEDOUT;
}
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%25s\n",
"Target Tx stats");
len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
"=================");
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Ucast packets", tgt_stats->tx_ucast_pkt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Bcast packets", tgt_stats->tx_bcast_pkt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Ucast byte", tgt_stats->tx_ucast_byte);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Bcast byte", tgt_stats->tx_bcast_byte);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Rts success cnt", tgt_stats->tx_rts_success_cnt);
for (i = 0; i < 4; i++)
len += scnprintf(buf + len, buf_len - len,
"%18s %d %10llu\n", "PER on ac",
i, tgt_stats->tx_pkt_per_ac[i]);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Error", tgt_stats->tx_err);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Fail count", tgt_stats->tx_fail_cnt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Retry count", tgt_stats->tx_retry_cnt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Multi retry cnt", tgt_stats->tx_mult_retry_cnt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Rts fail cnt", tgt_stats->tx_rts_fail_cnt);
len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n",
"TKIP counter measure used",
tgt_stats->tkip_cnter_measures_invoked);
len += scnprintf(buf + len, buf_len - len, "%25s\n",
"Target Rx stats");
len += scnprintf(buf + len, buf_len - len, "%25s\n",
"=================");
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Ucast packets", tgt_stats->rx_ucast_pkt);
len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
"Ucast Rate", tgt_stats->rx_ucast_rate);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Bcast packets", tgt_stats->rx_bcast_pkt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Ucast byte", tgt_stats->rx_ucast_byte);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Bcast byte", tgt_stats->rx_bcast_byte);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Fragmented pkt", tgt_stats->rx_frgment_pkt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Error", tgt_stats->rx_err);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"CRC Err", tgt_stats->rx_crc_err);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Key chache miss", tgt_stats->rx_key_cache_miss);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Decrypt Err", tgt_stats->rx_decrypt_err);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Duplicate frame", tgt_stats->rx_dupl_frame);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Tkip Mic failure", tgt_stats->tkip_local_mic_fail);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"TKIP format err", tgt_stats->tkip_fmt_err);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"CCMP format Err", tgt_stats->ccmp_fmt_err);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n",
"CCMP Replay Err", tgt_stats->ccmp_replays);
len += scnprintf(buf + len, buf_len - len, "%25s\n",
"Misc Target stats");
len += scnprintf(buf + len, buf_len - len, "%25s\n",
"=================");
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Beacon Miss count", tgt_stats->cs_bmiss_cnt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Num Connects", tgt_stats->cs_connect_cnt);
len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
"Num disconnects", tgt_stats->cs_discon_cnt);
len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
"Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi);
if (len > buf_len)
len = buf_len;
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
return ret_cnt;
}
static const struct file_operations fops_tgt_stats = {
.read = read_file_tgt_stats,
.open = ath6kl_debugfs_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
#define print_credit_info(fmt_str, ep_list_field) \
(len += scnprintf(buf + len, buf_len - len, fmt_str, \
ep_list->ep_list_field))
#define CREDIT_INFO_DISPLAY_STRING_LEN 200
#define CREDIT_INFO_LEN 128
static ssize_t read_file_credit_dist_stats(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
struct htc_target *target = ar->htc_target;
struct htc_endpoint_credit_dist *ep_list;
char *buf;
unsigned int buf_len, len = 0;
ssize_t ret_cnt;
buf_len = CREDIT_INFO_DISPLAY_STRING_LEN +
get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
"Total Avail Credits: ",
target->cred_dist_cntxt->total_avail_credits);
len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
"Free credits :",
target->cred_dist_cntxt->cur_free_credits);
len += scnprintf(buf + len, buf_len - len,
" Epid Flags Cred_norm Cred_min Credits Cred_assngd"
" Seek_cred Cred_sz Cred_per_msg Cred_to_dist"
" qdepth\n");
list_for_each_entry(ep_list, &target->cred_dist_list, list) {
print_credit_info(" %2d", endpoint);
print_credit_info("%10x", dist_flags);
print_credit_info("%8d", cred_norm);
print_credit_info("%9d", cred_min);
print_credit_info("%9d", credits);
print_credit_info("%10d", cred_assngd);
print_credit_info("%13d", seek_cred);
print_credit_info("%12d", cred_sz);
print_credit_info("%9d", cred_per_msg);
print_credit_info("%14d", cred_to_dist);
len += scnprintf(buf + len, buf_len - len, "%12d\n",
get_queue_depth(&((struct htc_endpoint *)
ep_list->htc_rsvd)->txq));
}
if (len > buf_len)
len = buf_len;
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
return ret_cnt;
}
static const struct file_operations fops_credit_dist_stats = {
.read = read_file_credit_dist_stats,
.open = ath6kl_debugfs_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static unsigned long ath6kl_get_num_reg(void)
{
int i;
unsigned long n_reg = 0;
for (i = 0; i < ARRAY_SIZE(diag_reg); i++)
n_reg = n_reg +
(diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1;
return n_reg;
}
static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr)
{
int i;
for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
if (reg_addr >= diag_reg[i].reg_start &&
reg_addr <= diag_reg[i].reg_end)
return true;
}
return false;
}
static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
u8 buf[50];
unsigned int len = 0;
if (ar->debug.dbgfs_diag_reg)
len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n",
ar->debug.dbgfs_diag_reg);
else
len += scnprintf(buf + len, sizeof(buf) - len,
"All diag registers\n");
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t ath6kl_regread_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
u8 buf[50];
unsigned int len;
unsigned long reg_addr;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
if (strict_strtoul(buf, 0, &reg_addr))
return -EINVAL;
if ((reg_addr % 4) != 0)
return -EINVAL;
if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr))
return -EINVAL;
ar->debug.dbgfs_diag_reg = reg_addr;
return count;
}
static const struct file_operations fops_diag_reg_read = {
.read = ath6kl_regread_read,
.write = ath6kl_regread_write,
.open = ath6kl_debugfs_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static int ath6kl_regdump_open(struct inode *inode, struct file *file)
{
struct ath6kl *ar = inode->i_private;
u8 *buf;
unsigned long int reg_len;
unsigned int len = 0, n_reg;
u32 addr;
__le32 reg_val;
int i, status;
/* Dump all the registers if no register is specified */
if (!ar->debug.dbgfs_diag_reg)
n_reg = ath6kl_get_num_reg();
else
n_reg = 1;
reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE;
if (n_reg > 1)
reg_len += REGTYPE_STR_LEN;
buf = vmalloc(reg_len);
if (!buf)
return -ENOMEM;
if (n_reg == 1) {
addr = ar->debug.dbgfs_diag_reg;
status = ath6kl_diag_read32(ar,
TARG_VTOP(ar->target_type, addr),
(u32 *)&reg_val);
if (status)
goto fail_reg_read;
len += scnprintf(buf + len, reg_len - len,
"0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val));
goto done;
}
for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
len += scnprintf(buf + len, reg_len - len,
"%s\n", diag_reg[i].reg_info);
for (addr = diag_reg[i].reg_start;
addr <= diag_reg[i].reg_end; addr += 4) {
status = ath6kl_diag_read32(ar,
TARG_VTOP(ar->target_type, addr),
(u32 *)&reg_val);
if (status)
goto fail_reg_read;
len += scnprintf(buf + len, reg_len - len,
"0x%06x 0x%08x\n",
addr, le32_to_cpu(reg_val));
}
}
done:
file->private_data = buf;
return 0;
fail_reg_read:
ath6kl_warn("Unable to read memory:%u\n", addr);
vfree(buf);
return -EIO;
}
static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
u8 *buf = file->private_data;
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
}
static int ath6kl_regdump_release(struct inode *inode, struct file *file)
{
vfree(file->private_data);
return 0;
}
static const struct file_operations fops_reg_dump = {
.open = ath6kl_regdump_open,
.read = ath6kl_regdump_read,
.release = ath6kl_regdump_release,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath6kl_lrssi_roam_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
unsigned long lrssi_roam_threshold;
char buf[32];
ssize_t len;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
if (strict_strtoul(buf, 0, &lrssi_roam_threshold))
return -EINVAL;
ar->lrssi_roam_threshold = lrssi_roam_threshold;
ath6kl_wmi_set_roam_lrssi_cmd(ar->wmi, ar->lrssi_roam_threshold);
return count;
}
static ssize_t ath6kl_lrssi_roam_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
char buf[32];
unsigned int len;
len = snprintf(buf, sizeof(buf), "%u\n", ar->lrssi_roam_threshold);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static const struct file_operations fops_lrssi_roam_threshold = {
.read = ath6kl_lrssi_roam_read,
.write = ath6kl_lrssi_roam_write,
.open = ath6kl_debugfs_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath6kl_regwrite_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
u8 buf[32];
unsigned int len = 0;
len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n",
ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t ath6kl_regwrite_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
char buf[32];
char *sptr, *token;
unsigned int len = 0;
u32 reg_addr, reg_val;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
sptr = buf;
token = strsep(&sptr, "=");
if (!token)
return -EINVAL;
if (kstrtou32(token, 0, &reg_addr))
return -EINVAL;
if (!ath6kl_dbg_is_diag_reg_valid(reg_addr))
return -EINVAL;
if (kstrtou32(sptr, 0, &reg_val))
return -EINVAL;
ar->debug.diag_reg_addr_wr = reg_addr;
ar->debug.diag_reg_val_wr = reg_val;
if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr,
cpu_to_le32(ar->debug.diag_reg_val_wr)))
return -EIO;
return count;
}
static const struct file_operations fops_diag_reg_write = {
.read = ath6kl_regwrite_read,
.write = ath6kl_regwrite_write,
.open = ath6kl_debugfs_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
int ath6kl_debug_init(struct ath6kl *ar)
{
ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE);
if (ar->debug.fwlog_buf.buf == NULL)
return -ENOMEM;
ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL);
if (ar->debug.fwlog_tmp == NULL) {
vfree(ar->debug.fwlog_buf.buf);
return -ENOMEM;
}
spin_lock_init(&ar->debug.fwlog_lock);
/*
* Actually we are lying here but don't know how to read the mask
* value from the firmware.
*/
ar->debug.fwlog_mask = 0;
ar->debugfs_phy = debugfs_create_dir("ath6kl",
ar->wdev->wiphy->debugfsdir);
if (!ar->debugfs_phy) {
vfree(ar->debug.fwlog_buf.buf);
kfree(ar->debug.fwlog_tmp);
return -ENOMEM;
}
debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
&fops_tgt_stats);
debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar,
&fops_credit_dist_stats);
debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
&fops_fwlog);
debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
ar, &fops_fwlog_mask);
debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
&fops_diag_reg_read);
debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar,
&fops_reg_dump);
debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR,
ar->debugfs_phy, ar, &fops_lrssi_roam_threshold);
debugfs_create_file("reg_write", S_IRUSR | S_IWUSR,
ar->debugfs_phy, ar, &fops_diag_reg_write);
debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar,
&fops_war_stats);
return 0;
}
void ath6kl_debug_cleanup(struct ath6kl *ar)
{
vfree(ar->debug.fwlog_buf.buf);
kfree(ar->debug.fwlog_tmp);
}
#endif #endif
...@@ -34,8 +34,12 @@ enum ATH6K_DEBUG_MASK { ...@@ -34,8 +34,12 @@ enum ATH6K_DEBUG_MASK {
ATH6KL_DBG_TRC = BIT(11), /* generic func tracing */ ATH6KL_DBG_TRC = BIT(11), /* generic func tracing */
ATH6KL_DBG_SCATTER = BIT(12), /* hif scatter tracing */ ATH6KL_DBG_SCATTER = BIT(12), /* hif scatter tracing */
ATH6KL_DBG_WLAN_CFG = BIT(13), /* cfg80211 i/f file tracing */ ATH6KL_DBG_WLAN_CFG = BIT(13), /* cfg80211 i/f file tracing */
ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx and wmi frames */ ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx frames */
ATH6KL_DBG_AGGR = BIT(15), /* aggregation */ ATH6KL_DBG_AGGR = BIT(15), /* aggregation */
ATH6KL_DBG_SDIO = BIT(16),
ATH6KL_DBG_SDIO_DUMP = BIT(17),
ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */
ATH6KL_DBG_WMI_DUMP = BIT(19),
ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */
}; };
...@@ -52,6 +56,10 @@ extern int ath6kl_printk(const char *level, const char *fmt, ...) ...@@ -52,6 +56,10 @@ extern int ath6kl_printk(const char *level, const char *fmt, ...)
#define AR_DBG_LVL_CHECK(mask) (debug_mask & mask) #define AR_DBG_LVL_CHECK(mask) (debug_mask & mask)
enum ath6kl_war {
ATH6KL_WAR_INVALID_RATE,
};
#ifdef CONFIG_ATH6KL_DEBUG #ifdef CONFIG_ATH6KL_DEBUG
#define ath6kl_dbg(mask, fmt, ...) \ #define ath6kl_dbg(mask, fmt, ...) \
({ \ ({ \
...@@ -65,12 +73,14 @@ extern int ath6kl_printk(const char *level, const char *fmt, ...) ...@@ -65,12 +73,14 @@ extern int ath6kl_printk(const char *level, const char *fmt, ...)
}) })
static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
const char *msg, const void *buf, const char *msg, const char *prefix,
size_t len) const void *buf, size_t len)
{ {
if (debug_mask & mask) { if (debug_mask & mask) {
if (msg)
ath6kl_dbg(mask, "%s\n", msg); ath6kl_dbg(mask, "%s\n", msg);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
} }
} }
...@@ -78,6 +88,11 @@ void ath6kl_dump_registers(struct ath6kl_device *dev, ...@@ -78,6 +88,11 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_proc_registers *irq_proc_reg,
struct ath6kl_irq_enable_reg *irq_en_reg); struct ath6kl_irq_enable_reg *irq_en_reg);
void dump_cred_dist_stats(struct htc_target *target); void dump_cred_dist_stats(struct htc_target *target);
void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len);
void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war);
int ath6kl_debug_init(struct ath6kl *ar);
void ath6kl_debug_cleanup(struct ath6kl *ar);
#else #else
static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
const char *fmt, ...) const char *fmt, ...)
...@@ -86,8 +101,8 @@ static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, ...@@ -86,8 +101,8 @@ static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
} }
static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
const char *msg, const void *buf, const char *msg, const char *prefix,
size_t len) const void *buf, size_t len)
{ {
} }
...@@ -100,6 +115,24 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev, ...@@ -100,6 +115,24 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
static inline void dump_cred_dist_stats(struct htc_target *target) static inline void dump_cred_dist_stats(struct htc_target *target)
{ {
} }
#endif
static inline void ath6kl_debug_fwlog_event(struct ath6kl *ar,
const void *buf, size_t len)
{
}
static inline void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
{
}
static inline int ath6kl_debug_init(struct ath6kl *ar)
{
return 0;
}
static inline void ath6kl_debug_cleanup(struct ath6kl *ar)
{
}
#endif
#endif #endif
...@@ -69,4 +69,9 @@ static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar) ...@@ -69,4 +69,9 @@ static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar)
return ar->hif_ops->cleanup_scatter(ar); return ar->hif_ops->cleanup_scatter(ar);
} }
static inline int ath6kl_hif_suspend(struct ath6kl *ar)
{
return ar->hif_ops->suspend(ar);
}
#endif #endif
...@@ -202,6 +202,7 @@ struct ath6kl_hif_ops { ...@@ -202,6 +202,7 @@ struct ath6kl_hif_ops {
int (*scat_req_rw) (struct ath6kl *ar, int (*scat_req_rw) (struct ath6kl *ar,
struct hif_scatter_req *scat_req); struct hif_scatter_req *scat_req);
void (*cleanup_scatter)(struct ath6kl *ar); void (*cleanup_scatter)(struct ath6kl *ar);
int (*suspend)(struct ath6kl *ar);
}; };
#endif #endif
...@@ -22,8 +22,19 @@ ...@@ -22,8 +22,19 @@
#define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask))
static void htc_prep_send_pkt(struct htc_packet *packet, u8 flags, int ctrl0, static void ath6kl_htc_tx_buf_align(u8 **buf, unsigned long len)
int ctrl1) {
u8 *align_addr;
if (!IS_ALIGNED((unsigned long) *buf, 4)) {
align_addr = PTR_ALIGN(*buf - 4, 4);
memmove(align_addr, *buf, len);
*buf = align_addr;
}
}
static void ath6kl_htc_tx_prep_pkt(struct htc_packet *packet, u8 flags,
int ctrl0, int ctrl1)
{ {
struct htc_frame_hdr *hdr; struct htc_frame_hdr *hdr;
...@@ -167,7 +178,8 @@ static void htc_async_tx_scat_complete(struct htc_target *target, ...@@ -167,7 +178,8 @@ static void htc_async_tx_scat_complete(struct htc_target *target,
htc_tx_complete(endpoint, &tx_compq); htc_tx_complete(endpoint, &tx_compq);
} }
static int htc_issue_send(struct htc_target *target, struct htc_packet *packet) static int ath6kl_htc_tx_issue(struct htc_target *target,
struct htc_packet *packet)
{ {
int status; int status;
bool sync = false; bool sync = false;
...@@ -265,7 +277,7 @@ static int htc_check_credits(struct htc_target *target, ...@@ -265,7 +277,7 @@ static int htc_check_credits(struct htc_target *target,
return 0; return 0;
} }
static void htc_tx_pkts_get(struct htc_target *target, static void ath6kl_htc_tx_pkts_get(struct htc_target *target,
struct htc_endpoint *endpoint, struct htc_endpoint *endpoint,
struct list_head *queue) struct list_head *queue)
{ {
...@@ -346,7 +358,7 @@ static int htc_get_credit_padding(unsigned int cred_sz, int *len, ...@@ -346,7 +358,7 @@ static int htc_get_credit_padding(unsigned int cred_sz, int *len,
return cred_pad; return cred_pad;
} }
static int htc_setup_send_scat_list(struct htc_target *target, static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target,
struct htc_endpoint *endpoint, struct htc_endpoint *endpoint,
struct hif_scatter_req *scat_req, struct hif_scatter_req *scat_req,
int n_scat, int n_scat,
...@@ -370,27 +382,23 @@ static int htc_setup_send_scat_list(struct htc_target *target, ...@@ -370,27 +382,23 @@ static int htc_setup_send_scat_list(struct htc_target *target,
cred_pad = htc_get_credit_padding(target->tgt_cred_sz, cred_pad = htc_get_credit_padding(target->tgt_cred_sz,
&len, endpoint); &len, endpoint);
if (cred_pad < 0) { if (cred_pad < 0 || rem_scat < len) {
status = -EINVAL;
break;
}
if (rem_scat < len) {
/* exceeds what we can transfer */
status = -ENOSPC; status = -ENOSPC;
break; break;
} }
rem_scat -= len; rem_scat -= len;
/* now remove it from the queue */ /* now remove it from the queue */
packet = list_first_entry(queue, struct htc_packet, list);
list_del(&packet->list); list_del(&packet->list);
scat_req->scat_list[i].packet = packet; scat_req->scat_list[i].packet = packet;
/* prepare packet and flag message as part of a send bundle */ /* prepare packet and flag message as part of a send bundle */
htc_prep_send_pkt(packet, ath6kl_htc_tx_prep_pkt(packet,
packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE, packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE,
cred_pad, packet->info.tx.seqno); cred_pad, packet->info.tx.seqno);
/* Make sure the buffer is 4-byte aligned */
ath6kl_htc_tx_buf_align(&packet->buf,
packet->act_len + HTC_HDR_LENGTH);
scat_req->scat_list[i].buf = packet->buf; scat_req->scat_list[i].buf = packet->buf;
scat_req->scat_list[i].len = len; scat_req->scat_list[i].len = len;
...@@ -402,7 +410,7 @@ static int htc_setup_send_scat_list(struct htc_target *target, ...@@ -402,7 +410,7 @@ static int htc_setup_send_scat_list(struct htc_target *target,
} }
/* Roll back scatter setup in case of any failure */ /* Roll back scatter setup in case of any failure */
if (status || (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE)) { if (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE) {
for (i = scat_req->scat_entries - 1; i >= 0; i--) { for (i = scat_req->scat_entries - 1; i >= 0; i--) {
packet = scat_req->scat_list[i].packet; packet = scat_req->scat_list[i].packet;
if (packet) { if (packet) {
...@@ -410,31 +418,32 @@ static int htc_setup_send_scat_list(struct htc_target *target, ...@@ -410,31 +418,32 @@ static int htc_setup_send_scat_list(struct htc_target *target,
list_add(&packet->list, queue); list_add(&packet->list, queue);
} }
} }
return -EINVAL; return -EAGAIN;
} }
return 0; return status;
} }
/* /*
* htc_issue_send_bundle: drain a queue and send as bundles * Drain a queue and send as bundles this function may return without fully
* this function may return without fully draining the queue * draining the queue when
* when
* *
* 1. scatter resources are exhausted * 1. scatter resources are exhausted
* 2. a message that will consume a partial credit will stop the * 2. a message that will consume a partial credit will stop the
* bundling process early * bundling process early
* 3. we drop below the minimum number of messages for a bundle * 3. we drop below the minimum number of messages for a bundle
*/ */
static void htc_issue_send_bundle(struct htc_endpoint *endpoint, static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
struct list_head *queue, struct list_head *queue,
int *sent_bundle, int *n_bundle_pkts) int *sent_bundle, int *n_bundle_pkts)
{ {
struct htc_target *target = endpoint->target; struct htc_target *target = endpoint->target;
struct hif_scatter_req *scat_req = NULL; struct hif_scatter_req *scat_req = NULL;
int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0; int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0;
int status;
while (true) { while (true) {
status = 0;
n_scat = get_queue_depth(queue); n_scat = get_queue_depth(queue);
n_scat = min(n_scat, target->msg_per_bndl_max); n_scat = min(n_scat, target->msg_per_bndl_max);
...@@ -457,8 +466,10 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint, ...@@ -457,8 +466,10 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint,
scat_req->len = 0; scat_req->len = 0;
scat_req->scat_entries = 0; scat_req->scat_entries = 0;
if (htc_setup_send_scat_list(target, endpoint, scat_req, status = ath6kl_htc_tx_setup_scat_list(target, endpoint,
n_scat, queue)) { scat_req, n_scat,
queue);
if (status == -EAGAIN) {
hif_scatter_req_add(target->dev->ar, scat_req); hif_scatter_req_add(target->dev->ar, scat_req);
break; break;
} }
...@@ -472,17 +483,20 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint, ...@@ -472,17 +483,20 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint,
"send scatter total bytes: %d , entries: %d\n", "send scatter total bytes: %d , entries: %d\n",
scat_req->len, scat_req->scat_entries); scat_req->len, scat_req->scat_entries);
ath6kldev_submit_scat_req(target->dev, scat_req, false); ath6kldev_submit_scat_req(target->dev, scat_req, false);
if (status)
break;
} }
*sent_bundle = n_sent_bundle; *sent_bundle = n_sent_bundle;
*n_bundle_pkts = tot_pkts_bundle; *n_bundle_pkts = tot_pkts_bundle;
ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "htc_issue_send_bundle (sent:%d)\n", ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "%s (sent:%d)\n",
n_sent_bundle); __func__, n_sent_bundle);
return; return;
} }
static void htc_tx_from_ep_txq(struct htc_target *target, static void ath6kl_htc_tx_from_queue(struct htc_target *target,
struct htc_endpoint *endpoint) struct htc_endpoint *endpoint)
{ {
struct list_head txq; struct list_head txq;
...@@ -511,7 +525,7 @@ static void htc_tx_from_ep_txq(struct htc_target *target, ...@@ -511,7 +525,7 @@ static void htc_tx_from_ep_txq(struct htc_target *target,
if (list_empty(&endpoint->txq)) if (list_empty(&endpoint->txq))
break; break;
htc_tx_pkts_get(target, endpoint, &txq); ath6kl_htc_tx_pkts_get(target, endpoint, &txq);
if (list_empty(&txq)) if (list_empty(&txq))
break; break;
...@@ -528,7 +542,7 @@ static void htc_tx_from_ep_txq(struct htc_target *target, ...@@ -528,7 +542,7 @@ static void htc_tx_from_ep_txq(struct htc_target *target,
HTC_MIN_HTC_MSGS_TO_BUNDLE)) { HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
int temp1 = 0, temp2 = 0; int temp1 = 0, temp2 = 0;
htc_issue_send_bundle(endpoint, &txq, ath6kl_htc_tx_bundle(endpoint, &txq,
&temp1, &temp2); &temp1, &temp2);
bundle_sent += temp1; bundle_sent += temp1;
n_pkts_bundle += temp2; n_pkts_bundle += temp2;
...@@ -541,9 +555,9 @@ static void htc_tx_from_ep_txq(struct htc_target *target, ...@@ -541,9 +555,9 @@ static void htc_tx_from_ep_txq(struct htc_target *target,
list); list);
list_del(&packet->list); list_del(&packet->list);
htc_prep_send_pkt(packet, packet->info.tx.flags, ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags,
0, packet->info.tx.seqno); 0, packet->info.tx.seqno);
htc_issue_send(target, packet); ath6kl_htc_tx_issue(target, packet);
} }
spin_lock_bh(&target->tx_lock); spin_lock_bh(&target->tx_lock);
...@@ -556,7 +570,7 @@ static void htc_tx_from_ep_txq(struct htc_target *target, ...@@ -556,7 +570,7 @@ static void htc_tx_from_ep_txq(struct htc_target *target,
spin_unlock_bh(&target->tx_lock); spin_unlock_bh(&target->tx_lock);
} }
static bool htc_try_send(struct htc_target *target, static bool ath6kl_htc_tx_try(struct htc_target *target,
struct htc_endpoint *endpoint, struct htc_endpoint *endpoint,
struct htc_packet *tx_pkt) struct htc_packet *tx_pkt)
{ {
...@@ -594,7 +608,7 @@ static bool htc_try_send(struct htc_target *target, ...@@ -594,7 +608,7 @@ static bool htc_try_send(struct htc_target *target,
list_add_tail(&tx_pkt->list, &endpoint->txq); list_add_tail(&tx_pkt->list, &endpoint->txq);
spin_unlock_bh(&target->tx_lock); spin_unlock_bh(&target->tx_lock);
htc_tx_from_ep_txq(target, endpoint); ath6kl_htc_tx_from_queue(target, endpoint);
return true; return true;
} }
...@@ -628,7 +642,7 @@ static void htc_chk_ep_txq(struct htc_target *target) ...@@ -628,7 +642,7 @@ static void htc_chk_ep_txq(struct htc_target *target)
* chance to reclaim credits from lower priority * chance to reclaim credits from lower priority
* ones. * ones.
*/ */
htc_tx_from_ep_txq(target, endpoint); ath6kl_htc_tx_from_queue(target, endpoint);
spin_lock_bh(&target->tx_lock); spin_lock_bh(&target->tx_lock);
} }
spin_unlock_bh(&target->tx_lock); spin_unlock_bh(&target->tx_lock);
...@@ -680,8 +694,8 @@ static int htc_setup_tx_complete(struct htc_target *target) ...@@ -680,8 +694,8 @@ static int htc_setup_tx_complete(struct htc_target *target)
/* we want synchronous operation */ /* we want synchronous operation */
send_pkt->completion = NULL; send_pkt->completion = NULL;
htc_prep_send_pkt(send_pkt, 0, 0, 0); ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0);
status = htc_issue_send(target, send_pkt); status = ath6kl_htc_tx_issue(target, send_pkt);
if (send_pkt != NULL) if (send_pkt != NULL)
htc_reclaim_txctrl_buf(target, send_pkt); htc_reclaim_txctrl_buf(target, send_pkt);
...@@ -733,7 +747,7 @@ int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet) ...@@ -733,7 +747,7 @@ int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet)
endpoint = &target->endpoint[packet->endpoint]; endpoint = &target->endpoint[packet->endpoint];
if (!htc_try_send(target, endpoint, packet)) { if (!ath6kl_htc_tx_try(target, endpoint, packet)) {
packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ? packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ?
-ECANCELED : -ENOSPC; -ECANCELED : -ENOSPC;
INIT_LIST_HEAD(&queue); INIT_LIST_HEAD(&queue);
...@@ -846,7 +860,7 @@ void ath6kl_htc_indicate_activity_change(struct htc_target *target, ...@@ -846,7 +860,7 @@ void ath6kl_htc_indicate_activity_change(struct htc_target *target,
/* HTC Rx */ /* HTC Rx */
static inline void htc_update_rx_stats(struct htc_endpoint *endpoint, static inline void ath6kl_htc_rx_update_stats(struct htc_endpoint *endpoint,
int n_look_ahds) int n_look_ahds)
{ {
endpoint->ep_st.rx_pkts++; endpoint->ep_st.rx_pkts++;
...@@ -894,7 +908,8 @@ static void reclaim_rx_ctrl_buf(struct htc_target *target, ...@@ -894,7 +908,8 @@ static void reclaim_rx_ctrl_buf(struct htc_target *target,
spin_unlock_bh(&target->htc_lock); spin_unlock_bh(&target->htc_lock);
} }
static int dev_rx_pkt(struct htc_target *target, struct htc_packet *packet, static int ath6kl_htc_rx_packet(struct htc_target *target,
struct htc_packet *packet,
u32 rx_len) u32 rx_len)
{ {
struct ath6kl_device *dev = target->dev; struct ath6kl_device *dev = target->dev;
...@@ -929,7 +944,7 @@ static int dev_rx_pkt(struct htc_target *target, struct htc_packet *packet, ...@@ -929,7 +944,7 @@ static int dev_rx_pkt(struct htc_target *target, struct htc_packet *packet,
* "hint" that there are more single-packets to fetch * "hint" that there are more single-packets to fetch
* on this endpoint. * on this endpoint.
*/ */
static void set_rxpkt_indication_flag(u32 lk_ahd, static void ath6kl_htc_rx_set_indicate(u32 lk_ahd,
struct htc_endpoint *endpoint, struct htc_endpoint *endpoint,
struct htc_packet *packet) struct htc_packet *packet)
{ {
...@@ -942,7 +957,7 @@ static void set_rxpkt_indication_flag(u32 lk_ahd, ...@@ -942,7 +957,7 @@ static void set_rxpkt_indication_flag(u32 lk_ahd,
} }
} }
static void chk_rx_water_mark(struct htc_endpoint *endpoint) static void ath6kl_htc_rx_chk_water_mark(struct htc_endpoint *endpoint)
{ {
struct htc_ep_callbacks ep_cb = endpoint->ep_cb; struct htc_ep_callbacks ep_cb = endpoint->ep_cb;
...@@ -959,7 +974,8 @@ static void chk_rx_water_mark(struct htc_endpoint *endpoint) ...@@ -959,7 +974,8 @@ static void chk_rx_water_mark(struct htc_endpoint *endpoint)
} }
/* This function is called with rx_lock held */ /* This function is called with rx_lock held */
static int htc_setup_rxpkts(struct htc_target *target, struct htc_endpoint *ep, static int ath6kl_htc_rx_setup(struct htc_target *target,
struct htc_endpoint *ep,
u32 *lk_ahds, struct list_head *queue, int n_msg) u32 *lk_ahds, struct list_head *queue, int n_msg)
{ {
struct htc_packet *packet; struct htc_packet *packet;
...@@ -1060,7 +1076,7 @@ static int htc_setup_rxpkts(struct htc_target *target, struct htc_endpoint *ep, ...@@ -1060,7 +1076,7 @@ static int htc_setup_rxpkts(struct htc_target *target, struct htc_endpoint *ep,
return status; return status;
} }
static int alloc_and_prep_rxpkts(struct htc_target *target, static int ath6kl_htc_rx_alloc(struct htc_target *target,
u32 lk_ahds[], int msg, u32 lk_ahds[], int msg,
struct htc_endpoint *endpoint, struct htc_endpoint *endpoint,
struct list_head *queue) struct list_head *queue)
...@@ -1129,8 +1145,8 @@ static int alloc_and_prep_rxpkts(struct htc_target *target, ...@@ -1129,8 +1145,8 @@ static int alloc_and_prep_rxpkts(struct htc_target *target,
n_msg = 1; n_msg = 1;
/* Setup packet buffers for each message */ /* Setup packet buffers for each message */
status = htc_setup_rxpkts(target, endpoint, &lk_ahds[i], queue, status = ath6kl_htc_rx_setup(target, endpoint, &lk_ahds[i],
n_msg); queue, n_msg);
/* /*
* This is due to unavailabilty of buffers to rx entire data. * This is due to unavailabilty of buffers to rx entire data.
...@@ -1176,7 +1192,7 @@ static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets) ...@@ -1176,7 +1192,7 @@ static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets)
packets->act_len + HTC_HDR_LENGTH); packets->act_len + HTC_HDR_LENGTH);
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
"Unexpected ENDPOINT 0 Message", "Unexpected ENDPOINT 0 Message", "",
packets->buf - HTC_HDR_LENGTH, packets->buf - HTC_HDR_LENGTH,
packets->act_len + HTC_HDR_LENGTH); packets->act_len + HTC_HDR_LENGTH);
} }
...@@ -1312,7 +1328,7 @@ static int htc_parse_trailer(struct htc_target *target, ...@@ -1312,7 +1328,7 @@ static int htc_parse_trailer(struct htc_target *target,
memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4); memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4);
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Next Look Ahead", ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Next Look Ahead",
next_lk_ahds, 4); "", next_lk_ahds, 4);
*n_lk_ahds = 1; *n_lk_ahds = 1;
} }
...@@ -1331,7 +1347,7 @@ static int htc_parse_trailer(struct htc_target *target, ...@@ -1331,7 +1347,7 @@ static int htc_parse_trailer(struct htc_target *target,
(struct htc_bundle_lkahd_rpt *) record_buf; (struct htc_bundle_lkahd_rpt *) record_buf;
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Bundle lk_ahd", ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Bundle lk_ahd",
record_buf, record->len); "", record_buf, record->len);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
memcpy((u8 *)&next_lk_ahds[i], memcpy((u8 *)&next_lk_ahds[i],
...@@ -1364,7 +1380,8 @@ static int htc_proc_trailer(struct htc_target *target, ...@@ -1364,7 +1380,8 @@ static int htc_proc_trailer(struct htc_target *target,
ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "+htc_proc_trailer (len:%d)\n", len); ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "+htc_proc_trailer (len:%d)\n", len);
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Recv Trailer", buf, len); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Recv Trailer", "",
buf, len);
orig_buf = buf; orig_buf = buf;
orig_len = len; orig_len = len;
...@@ -1402,12 +1419,12 @@ static int htc_proc_trailer(struct htc_target *target, ...@@ -1402,12 +1419,12 @@ static int htc_proc_trailer(struct htc_target *target,
if (status) if (status)
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD Recv Trailer", ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD Recv Trailer",
orig_buf, orig_len); "", orig_buf, orig_len);
return status; return status;
} }
static int htc_proc_rxhdr(struct htc_target *target, static int ath6kl_htc_rx_process_hdr(struct htc_target *target,
struct htc_packet *packet, struct htc_packet *packet,
u32 *next_lkahds, int *n_lkahds) u32 *next_lkahds, int *n_lkahds)
{ {
...@@ -1419,8 +1436,8 @@ static int htc_proc_rxhdr(struct htc_target *target, ...@@ -1419,8 +1436,8 @@ static int htc_proc_rxhdr(struct htc_target *target,
if (n_lkahds != NULL) if (n_lkahds != NULL)
*n_lkahds = 0; *n_lkahds = 0;
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", packet->buf, ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", "htc ",
packet->act_len); packet->buf, packet->act_len);
/* /*
* NOTE: we cannot assume the alignment of buf, so we use the safe * NOTE: we cannot assume the alignment of buf, so we use the safe
...@@ -1461,12 +1478,12 @@ static int htc_proc_rxhdr(struct htc_target *target, ...@@ -1461,12 +1478,12 @@ static int htc_proc_rxhdr(struct htc_target *target,
} }
if (lk_ahd != packet->info.rx.exp_hdr) { if (lk_ahd != packet->info.rx.exp_hdr) {
ath6kl_err("htc_proc_rxhdr, lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n", ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n",
packet, packet->info.rx.rx_flags); __func__, packet, packet->info.rx.rx_flags);
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Expected Message lk_ahd", ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Expected Message lk_ahd",
&packet->info.rx.exp_hdr, 4); "", &packet->info.rx.exp_hdr, 4);
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Current Frame Header", ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Current Frame Header",
(u8 *)&lk_ahd, sizeof(lk_ahd)); "", (u8 *)&lk_ahd, sizeof(lk_ahd));
status = -ENOMEM; status = -ENOMEM;
goto fail_rx; goto fail_rx;
} }
...@@ -1474,8 +1491,8 @@ static int htc_proc_rxhdr(struct htc_target *target, ...@@ -1474,8 +1491,8 @@ static int htc_proc_rxhdr(struct htc_target *target,
if (htc_hdr->flags & HTC_FLG_RX_TRAILER) { if (htc_hdr->flags & HTC_FLG_RX_TRAILER) {
if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) || if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) ||
htc_hdr->ctrl[0] > payload_len) { htc_hdr->ctrl[0] > payload_len) {
ath6kl_err("htc_proc_rxhdr, invalid hdr (payload len should be :%d, CB[0] is:%d)\n", ath6kl_err("%s(): invalid hdr (payload len should be :%d, CB[0] is:%d)\n",
payload_len, htc_hdr->ctrl[0]); __func__, payload_len, htc_hdr->ctrl[0]);
status = -ENOMEM; status = -ENOMEM;
goto fail_rx; goto fail_rx;
} }
...@@ -1502,19 +1519,19 @@ static int htc_proc_rxhdr(struct htc_target *target, ...@@ -1502,19 +1519,19 @@ static int htc_proc_rxhdr(struct htc_target *target,
fail_rx: fail_rx:
if (status) if (status)
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD HTC Recv PKT", ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD HTC Recv PKT",
packet->buf, "", packet->buf,
packet->act_len < 256 ? packet->act_len : 256); packet->act_len < 256 ? packet->act_len : 256);
else { else {
if (packet->act_len > 0) if (packet->act_len > 0)
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
"HTC - Application Msg", "HTC - Application Msg", "",
packet->buf, packet->act_len); packet->buf, packet->act_len);
} }
return status; return status;
} }
static void do_rx_completion(struct htc_endpoint *endpoint, static void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint,
struct htc_packet *packet) struct htc_packet *packet)
{ {
ath6kl_dbg(ATH6KL_DBG_HTC_RECV, ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
...@@ -1523,7 +1540,7 @@ static void do_rx_completion(struct htc_endpoint *endpoint, ...@@ -1523,7 +1540,7 @@ static void do_rx_completion(struct htc_endpoint *endpoint,
endpoint->ep_cb.rx(endpoint->target, packet); endpoint->ep_cb.rx(endpoint->target, packet);
} }
static int htc_issue_rxpkt_bundle(struct htc_target *target, static int ath6kl_htc_rx_bundle(struct htc_target *target,
struct list_head *rxq, struct list_head *rxq,
struct list_head *sync_compq, struct list_head *sync_compq,
int *n_pkt_fetched, bool part_bundle) int *n_pkt_fetched, bool part_bundle)
...@@ -1548,15 +1565,15 @@ static int htc_issue_rxpkt_bundle(struct htc_target *target, ...@@ -1548,15 +1565,15 @@ static int htc_issue_rxpkt_bundle(struct htc_target *target,
* This would only happen if the target ignored our max * This would only happen if the target ignored our max
* bundle limit. * bundle limit.
*/ */
ath6kl_warn("htc_issue_rxpkt_bundle : partial bundle detected num:%d , %d\n", ath6kl_warn("%s(): partial bundle detected num:%d , %d\n",
get_queue_depth(rxq), n_scat_pkt); __func__, get_queue_depth(rxq), n_scat_pkt);
} }
len = 0; len = 0;
ath6kl_dbg(ATH6KL_DBG_HTC_RECV, ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
"htc_issue_rxpkt_bundle (numpackets: %d , actual : %d)\n", "%s(): (numpackets: %d , actual : %d)\n",
get_queue_depth(rxq), n_scat_pkt); __func__, get_queue_depth(rxq), n_scat_pkt);
scat_req = hif_scatter_req_get(target->dev->ar); scat_req = hif_scatter_req_get(target->dev->ar);
...@@ -1616,8 +1633,9 @@ static int htc_issue_rxpkt_bundle(struct htc_target *target, ...@@ -1616,8 +1633,9 @@ static int htc_issue_rxpkt_bundle(struct htc_target *target,
return status; return status;
} }
static int htc_proc_fetched_rxpkts(struct htc_target *target, static int ath6kl_htc_rx_process_packets(struct htc_target *target,
struct list_head *comp_pktq, u32 lk_ahds[], struct list_head *comp_pktq,
u32 lk_ahds[],
int *n_lk_ahd) int *n_lk_ahd)
{ {
struct htc_packet *packet, *tmp_pkt; struct htc_packet *packet, *tmp_pkt;
...@@ -1629,7 +1647,8 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target, ...@@ -1629,7 +1647,8 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target,
ep = &target->endpoint[packet->endpoint]; ep = &target->endpoint[packet->endpoint];
/* process header for each of the recv packet */ /* process header for each of the recv packet */
status = htc_proc_rxhdr(target, packet, lk_ahds, n_lk_ahd); status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds,
n_lk_ahd);
if (status) if (status)
return status; return status;
...@@ -1639,7 +1658,7 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target, ...@@ -1639,7 +1658,7 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target,
* based on the lookahead. * based on the lookahead.
*/ */
if (*n_lk_ahd > 0) if (*n_lk_ahd > 0)
set_rxpkt_indication_flag(lk_ahds[0], ath6kl_htc_rx_set_indicate(lk_ahds[0],
ep, packet); ep, packet);
} else } else
/* /*
...@@ -1649,18 +1668,18 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target, ...@@ -1649,18 +1668,18 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target,
packet->info.rx.indicat_flags |= packet->info.rx.indicat_flags |=
HTC_RX_FLAGS_INDICATE_MORE_PKTS; HTC_RX_FLAGS_INDICATE_MORE_PKTS;
htc_update_rx_stats(ep, *n_lk_ahd); ath6kl_htc_rx_update_stats(ep, *n_lk_ahd);
if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE) if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE)
ep->ep_st.rx_bundl += 1; ep->ep_st.rx_bundl += 1;
do_rx_completion(ep, packet); ath6kl_htc_rx_complete(ep, packet);
} }
return status; return status;
} }
static int htc_fetch_rxpkts(struct htc_target *target, static int ath6kl_htc_rx_fetch(struct htc_target *target,
struct list_head *rx_pktq, struct list_head *rx_pktq,
struct list_head *comp_pktq) struct list_head *comp_pktq)
{ {
...@@ -1678,7 +1697,7 @@ static int htc_fetch_rxpkts(struct htc_target *target, ...@@ -1678,7 +1697,7 @@ static int htc_fetch_rxpkts(struct htc_target *target,
* bundle transfer and recv bundling is * bundle transfer and recv bundling is
* allowed. * allowed.
*/ */
status = htc_issue_rxpkt_bundle(target, rx_pktq, status = ath6kl_htc_rx_bundle(target, rx_pktq,
comp_pktq, comp_pktq,
&fetched_pkts, &fetched_pkts,
part_bundle); part_bundle);
...@@ -1710,7 +1729,8 @@ static int htc_fetch_rxpkts(struct htc_target *target, ...@@ -1710,7 +1729,8 @@ static int htc_fetch_rxpkts(struct htc_target *target,
HTC_RX_PKT_IGNORE_LOOKAHEAD; HTC_RX_PKT_IGNORE_LOOKAHEAD;
/* go fetch the packet */ /* go fetch the packet */
status = dev_rx_pkt(target, packet, packet->act_len); status = ath6kl_htc_rx_packet(target, packet,
packet->act_len);
if (status) if (status)
return status; return status;
...@@ -1764,7 +1784,7 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, ...@@ -1764,7 +1784,7 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
* Try to allocate as many HTC RX packets indicated by the * Try to allocate as many HTC RX packets indicated by the
* look_aheads. * look_aheads.
*/ */
status = alloc_and_prep_rxpkts(target, look_aheads, status = ath6kl_htc_rx_alloc(target, look_aheads,
num_look_ahead, endpoint, num_look_ahead, endpoint,
&rx_pktq); &rx_pktq);
if (status) if (status)
...@@ -1781,14 +1801,15 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, ...@@ -1781,14 +1801,15 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
num_look_ahead = 0; num_look_ahead = 0;
status = htc_fetch_rxpkts(target, &rx_pktq, &comp_pktq); status = ath6kl_htc_rx_fetch(target, &rx_pktq, &comp_pktq);
if (!status) if (!status)
chk_rx_water_mark(endpoint); ath6kl_htc_rx_chk_water_mark(endpoint);
/* Process fetched packets */ /* Process fetched packets */
status = htc_proc_fetched_rxpkts(target, &comp_pktq, status = ath6kl_htc_rx_process_packets(target, &comp_pktq,
look_aheads, &num_look_ahead); look_aheads,
&num_look_ahead);
if (!num_look_ahead || status) if (!num_look_ahead || status)
break; break;
...@@ -1881,14 +1902,14 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target) ...@@ -1881,14 +1902,14 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target)
packet->completion = NULL; packet->completion = NULL;
/* get the message from the device, this will block */ /* get the message from the device, this will block */
if (dev_rx_pkt(target, packet, packet->act_len)) if (ath6kl_htc_rx_packet(target, packet, packet->act_len))
goto fail_ctrl_rx; goto fail_ctrl_rx;
/* process receive header */ /* process receive header */
packet->status = htc_proc_rxhdr(target, packet, NULL, NULL); packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL);
if (packet->status) { if (packet->status) {
ath6kl_err("htc_wait_for_ctrl_msg, htc_proc_rxhdr failed (status = %d)\n", ath6kl_err("htc_wait_for_ctrl_msg, ath6kl_htc_rx_process_hdr failed (status = %d)\n",
packet->status); packet->status);
goto fail_ctrl_rx; goto fail_ctrl_rx;
} }
...@@ -1935,7 +1956,7 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, ...@@ -1935,7 +1956,7 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) {
packet->status = -ECANCELED; packet->status = -ECANCELED;
list_del(&packet->list); list_del(&packet->list);
do_rx_completion(endpoint, packet); ath6kl_htc_rx_complete(endpoint, packet);
} }
return status; return status;
...@@ -2034,8 +2055,8 @@ int ath6kl_htc_conn_service(struct htc_target *target, ...@@ -2034,8 +2055,8 @@ int ath6kl_htc_conn_service(struct htc_target *target,
/* we want synchronous operation */ /* we want synchronous operation */
tx_pkt->completion = NULL; tx_pkt->completion = NULL;
htc_prep_send_pkt(tx_pkt, 0, 0, 0); ath6kl_htc_tx_prep_pkt(tx_pkt, 0, 0, 0);
status = htc_issue_send(target, tx_pkt); status = ath6kl_htc_tx_issue(target, tx_pkt);
if (status) if (status)
goto fail_tx; goto fail_tx;
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <linux/moduleparam.h>
#include <linux/of.h>
#include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_func.h>
#include "core.h" #include "core.h"
#include "cfg80211.h" #include "cfg80211.h"
...@@ -23,8 +25,10 @@ ...@@ -23,8 +25,10 @@
#include "hif-ops.h" #include "hif-ops.h"
unsigned int debug_mask; unsigned int debug_mask;
static unsigned int testmode;
module_param(debug_mask, uint, 0644); module_param(debug_mask, uint, 0644);
module_param(testmode, uint, 0644);
/* /*
* Include definitions here that can be used to tune the WLAN module * Include definitions here that can be used to tune the WLAN module
...@@ -53,12 +57,6 @@ module_param(debug_mask, uint, 0644); ...@@ -53,12 +57,6 @@ module_param(debug_mask, uint, 0644);
#define CONFIG_AR600x_DEBUG_UART_TX_PIN 8 #define CONFIG_AR600x_DEBUG_UART_TX_PIN 8
enum addr_type {
DATASET_PATCH_ADDR,
APP_LOAD_ADDR,
APP_START_OVERRIDE_ADDR,
};
#define ATH6KL_DATA_OFFSET 64 #define ATH6KL_DATA_OFFSET 64
struct sk_buff *ath6kl_buf_alloc(int size) struct sk_buff *ath6kl_buf_alloc(int size)
{ {
...@@ -67,7 +65,7 @@ struct sk_buff *ath6kl_buf_alloc(int size) ...@@ -67,7 +65,7 @@ struct sk_buff *ath6kl_buf_alloc(int size)
/* Add chacheline space at front and back of buffer */ /* Add chacheline space at front and back of buffer */
reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET +
sizeof(struct htc_packet); sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES;
skb = dev_alloc_skb(size + reserved); skb = dev_alloc_skb(size + reserved);
if (skb) if (skb)
...@@ -85,7 +83,7 @@ void ath6kl_init_profile_info(struct ath6kl *ar) ...@@ -85,7 +83,7 @@ void ath6kl_init_profile_info(struct ath6kl *ar)
ar->prwise_crypto = NONE_CRYPT; ar->prwise_crypto = NONE_CRYPT;
ar->prwise_crypto_len = 0; ar->prwise_crypto_len = 0;
ar->grp_crypto = NONE_CRYPT; ar->grp_crypto = NONE_CRYPT;
ar->grp_crpto_len = 0; ar->grp_crypto_len = 0;
memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list)); memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
memset(ar->req_bssid, 0, sizeof(ar->req_bssid)); memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
memset(ar->bssid, 0, sizeof(ar->bssid)); memset(ar->bssid, 0, sizeof(ar->bssid));
...@@ -108,17 +106,6 @@ static u8 ath6kl_get_fw_iftype(struct ath6kl *ar) ...@@ -108,17 +106,6 @@ static u8 ath6kl_get_fw_iftype(struct ath6kl *ar)
} }
} }
static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
u32 item_offset)
{
u32 addr = 0;
if (ar->target_type == TARGET_TYPE_AR6003)
addr = ATH6KL_HI_START_ADDR + item_offset;
return addr;
}
static int ath6kl_set_host_app_area(struct ath6kl *ar) static int ath6kl_set_host_app_area(struct ath6kl *ar)
{ {
u32 address, data; u32 address, data;
...@@ -127,16 +114,15 @@ static int ath6kl_set_host_app_area(struct ath6kl *ar) ...@@ -127,16 +114,15 @@ static int ath6kl_set_host_app_area(struct ath6kl *ar)
/* Fetch the address of the host_app_area_s /* Fetch the address of the host_app_area_s
* instance in the host interest area */ * instance in the host interest area */
address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest));
address = TARG_VTOP(address); address = TARG_VTOP(ar->target_type, address);
if (ath6kl_read_reg_diag(ar, &address, &data)) if (ath6kl_diag_read32(ar, address, &data))
return -EIO; return -EIO;
address = TARG_VTOP(data); address = TARG_VTOP(ar->target_type, data);
host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION; host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION;
if (ath6kl_access_datadiag(ar, address, if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area,
(u8 *)&host_app_area, sizeof(struct host_app_area)))
sizeof(struct host_app_area), false))
return -EIO; return -EIO;
return 0; return 0;
...@@ -290,6 +276,7 @@ static void ath6kl_init_control_info(struct ath6kl *ar) ...@@ -290,6 +276,7 @@ static void ath6kl_init_control_info(struct ath6kl *ar)
memset(&ar->sc_params, 0, sizeof(ar->sc_params)); memset(&ar->sc_params, 0, sizeof(ar->sc_params));
ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT; ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT;
ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS; ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS;
ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
memset((u8 *)ar->sta_list, 0, memset((u8 *)ar->sta_list, 0,
AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
...@@ -370,10 +357,10 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar) ...@@ -370,10 +357,10 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar)
/* the reg dump pointer is copied to the host interest area */ /* the reg dump pointer is copied to the host interest area */
address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state));
address = TARG_VTOP(address); address = TARG_VTOP(ar->target_type, address);
/* read RAM location through diagnostic window */ /* read RAM location through diagnostic window */
status = ath6kl_read_reg_diag(ar, &address, &regdump_loc); status = ath6kl_diag_read32(ar, address, &regdump_loc);
if (status || !regdump_loc) { if (status || !regdump_loc) {
ath6kl_err("failed to get ptr to register dump area\n"); ath6kl_err("failed to get ptr to register dump area\n");
...@@ -382,15 +369,11 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar) ...@@ -382,15 +369,11 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar)
ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n", ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n",
regdump_loc); regdump_loc);
regdump_loc = TARG_VTOP(ar->target_type, regdump_loc);
regdump_loc = TARG_VTOP(regdump_loc);
/* fetch register dump data */ /* fetch register dump data */
status = ath6kl_access_datadiag(ar, status = ath6kl_diag_read(ar, regdump_loc, (u8 *)&regdump_val[0],
regdump_loc, REG_DUMP_COUNT_AR6003 * (sizeof(u32)));
(u8 *)&regdump_val[0],
REG_DUMP_COUNT_AR6003 * (sizeof(u32)),
true);
if (status) { if (status) {
ath6kl_err("failed to get register dump\n"); ath6kl_err("failed to get register dump\n");
...@@ -416,6 +399,7 @@ void ath6kl_target_failure(struct ath6kl *ar) ...@@ -416,6 +399,7 @@ void ath6kl_target_failure(struct ath6kl *ar)
static int ath6kl_target_config_wlan_params(struct ath6kl *ar) static int ath6kl_target_config_wlan_params(struct ath6kl *ar)
{ {
int status = 0; int status = 0;
int ret;
/* /*
* Configure the device for rx dot11 header rules. "0,0" are the * Configure the device for rx dot11 header rules. "0,0" are the
...@@ -460,6 +444,28 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar) ...@@ -460,6 +444,28 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar)
status = -EIO; status = -EIO;
} }
if (ar->p2p) {
ret = ath6kl_wmi_info_req_cmd(ar->wmi,
P2P_FLAG_CAPABILITIES_REQ |
P2P_FLAG_MACADDR_REQ |
P2P_FLAG_HMODEL_REQ);
if (ret) {
ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P "
"capabilities (%d) - assuming P2P not "
"supported\n", ret);
ar->p2p = 0;
}
}
if (ar->p2p) {
/* Enable Probe Request reporting for P2P */
ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true);
if (ret) {
ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe "
"Request reporting (%d)\n", ret);
}
}
return status; return status;
} }
...@@ -495,6 +501,10 @@ int ath6kl_configure_target(struct ath6kl *ar) ...@@ -495,6 +501,10 @@ int ath6kl_configure_target(struct ath6kl *ar)
param |= (1 << HI_OPTION_NUM_DEV_SHIFT); param |= (1 << HI_OPTION_NUM_DEV_SHIFT);
param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT); param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT);
if (ar->p2p && fw_iftype == HI_OPTION_FW_MODE_BSS_STA) {
param |= HI_OPTION_FW_SUBMODE_P2PDEV <<
HI_OPTION_FW_SUBMODE_SHIFT;
}
param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
...@@ -518,30 +528,22 @@ int ath6kl_configure_target(struct ath6kl *ar) ...@@ -518,30 +528,22 @@ int ath6kl_configure_target(struct ath6kl *ar)
* but possible in theory. * but possible in theory.
*/ */
if (ar->target_type == TARGET_TYPE_AR6003) { param = ar->hw.board_ext_data_addr;
if (ar->version.target_ver == AR6003_REV2_VERSION) { ram_reserved_size = ar->hw.reserved_ram_size;
param = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
ram_reserved_size = AR6003_REV2_RAM_RESERVE_SIZE;
} else {
param = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
ram_reserved_size = AR6003_REV3_RAM_RESERVE_SIZE;
}
if (ath6kl_bmi_write(ar, if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar,
ath6kl_get_hi_item_addr(ar,
HI_ITEM(hi_board_ext_data)), HI_ITEM(hi_board_ext_data)),
(u8 *)&param, 4) != 0) { (u8 *)&param, 4) != 0) {
ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n");
return -EIO; return -EIO;
} }
if (ath6kl_bmi_write(ar,
ath6kl_get_hi_item_addr(ar, if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar,
HI_ITEM(hi_end_ram_reserve_sz)), HI_ITEM(hi_end_ram_reserve_sz)),
(u8 *)&ram_reserved_size, 4) != 0) { (u8 *)&ram_reserved_size, 4) != 0) {
ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n");
return -EIO; return -EIO;
} }
}
/* set the block size for the target */ /* set the block size for the target */
if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0)) if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0))
...@@ -568,6 +570,12 @@ struct ath6kl *ath6kl_core_alloc(struct device *sdev) ...@@ -568,6 +570,12 @@ struct ath6kl *ath6kl_core_alloc(struct device *sdev)
ar->wdev = wdev; ar->wdev = wdev;
wdev->iftype = NL80211_IFTYPE_STATION; wdev->iftype = NL80211_IFTYPE_STATION;
if (ath6kl_debug_init(ar)) {
ath6kl_err("Failed to initialize debugfs\n");
ath6kl_cfg80211_deinit(ar);
return NULL;
}
dev = alloc_netdev(0, "wlan%d", ether_setup); dev = alloc_netdev(0, "wlan%d", ether_setup);
if (!dev) { if (!dev) {
ath6kl_err("no memory for network device instance\n"); ath6kl_err("no memory for network device instance\n");
...@@ -579,7 +587,6 @@ struct ath6kl *ath6kl_core_alloc(struct device *sdev) ...@@ -579,7 +587,6 @@ struct ath6kl *ath6kl_core_alloc(struct device *sdev)
SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
wdev->netdev = dev; wdev->netdev = dev;
ar->sme_state = SME_DISCONNECTED; ar->sme_state = SME_DISCONNECTED;
ar->auto_auth_stage = AUTH_IDLE;
init_netdev(dev); init_netdev(dev);
...@@ -611,29 +618,6 @@ int ath6kl_unavail_ev(struct ath6kl *ar) ...@@ -611,29 +618,6 @@ int ath6kl_unavail_ev(struct ath6kl *ar)
} }
/* firmware upload */ /* firmware upload */
static u32 ath6kl_get_load_address(u32 target_ver, enum addr_type type)
{
WARN_ON(target_ver != AR6003_REV2_VERSION &&
target_ver != AR6003_REV3_VERSION);
switch (type) {
case DATASET_PATCH_ADDR:
return (target_ver == AR6003_REV2_VERSION) ?
AR6003_REV2_DATASET_PATCH_ADDRESS :
AR6003_REV3_DATASET_PATCH_ADDRESS;
case APP_LOAD_ADDR:
return (target_ver == AR6003_REV2_VERSION) ?
AR6003_REV2_APP_LOAD_ADDRESS :
0x1234;
case APP_START_OVERRIDE_ADDR:
return (target_ver == AR6003_REV2_VERSION) ?
AR6003_REV2_APP_START_OVERRIDE :
AR6003_REV3_APP_START_OVERRIDE;
default:
return 0;
}
}
static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, static int ath6kl_get_fw(struct ath6kl *ar, const char *filename,
u8 **fw, size_t *fw_len) u8 **fw, size_t *fw_len)
{ {
...@@ -655,15 +639,79 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, ...@@ -655,15 +639,79 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename,
return ret; return ret;
} }
#ifdef CONFIG_OF
static const char *get_target_ver_dir(const struct ath6kl *ar)
{
switch (ar->version.target_ver) {
case AR6003_REV1_VERSION:
return "ath6k/AR6003/hw1.0";
case AR6003_REV2_VERSION:
return "ath6k/AR6003/hw2.0";
case AR6003_REV3_VERSION:
return "ath6k/AR6003/hw2.1.1";
}
ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__,
ar->version.target_ver);
return NULL;
}
/*
* Check the device tree for a board-id and use it to construct
* the pathname to the firmware file. Used (for now) to find a
* fallback to the "bdata.bin" file--typically a symlink to the
* appropriate board-specific file.
*/
static bool check_device_tree(struct ath6kl *ar)
{
static const char *board_id_prop = "atheros,board-id";
struct device_node *node;
char board_filename[64];
const char *board_id;
int ret;
for_each_compatible_node(node, NULL, "atheros,ath6kl") {
board_id = of_get_property(node, board_id_prop, NULL);
if (board_id == NULL) {
ath6kl_warn("No \"%s\" property on %s node.\n",
board_id_prop, node->name);
continue;
}
snprintf(board_filename, sizeof(board_filename),
"%s/bdata.%s.bin", get_target_ver_dir(ar), board_id);
ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board,
&ar->fw_board_len);
if (ret) {
ath6kl_err("Failed to get DT board file %s: %d\n",
board_filename, ret);
continue;
}
return true;
}
return false;
}
#else
static bool check_device_tree(struct ath6kl *ar)
{
return false;
}
#endif /* CONFIG_OF */
static int ath6kl_fetch_board_file(struct ath6kl *ar) static int ath6kl_fetch_board_file(struct ath6kl *ar)
{ {
const char *filename; const char *filename;
int ret; int ret;
if (ar->fw_board != NULL)
return 0;
switch (ar->version.target_ver) { switch (ar->version.target_ver) {
case AR6003_REV2_VERSION: case AR6003_REV2_VERSION:
filename = AR6003_REV2_BOARD_DATA_FILE; filename = AR6003_REV2_BOARD_DATA_FILE;
break; break;
case AR6004_REV1_VERSION:
filename = AR6004_REV1_BOARD_DATA_FILE;
break;
default: default:
filename = AR6003_REV3_BOARD_DATA_FILE; filename = AR6003_REV3_BOARD_DATA_FILE;
break; break;
...@@ -676,6 +724,11 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) ...@@ -676,6 +724,11 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
return 0; return 0;
} }
if (check_device_tree(ar)) {
/* got board file from device tree */
return 0;
}
/* there was no proper board file, try to use default instead */ /* there was no proper board file, try to use default instead */
ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n", ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",
filename, ret); filename, ret);
...@@ -684,6 +737,9 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) ...@@ -684,6 +737,9 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
case AR6003_REV2_VERSION: case AR6003_REV2_VERSION:
filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE; filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE;
break; break;
case AR6004_REV1_VERSION:
filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE;
break;
default: default:
filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE; filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE;
break; break;
...@@ -703,25 +759,346 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) ...@@ -703,25 +759,346 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
return 0; return 0;
} }
static int ath6kl_fetch_otp_file(struct ath6kl *ar)
{
const char *filename;
int ret;
if (ar->fw_otp != NULL)
return 0;
static int ath6kl_upload_board_file(struct ath6kl *ar) switch (ar->version.target_ver) {
case AR6003_REV2_VERSION:
filename = AR6003_REV2_OTP_FILE;
break;
case AR6004_REV1_VERSION:
ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n");
return 0;
break;
default:
filename = AR6003_REV3_OTP_FILE;
break;
}
ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
&ar->fw_otp_len);
if (ret) {
ath6kl_err("Failed to get OTP file %s: %d\n",
filename, ret);
return ret;
}
return 0;
}
static int ath6kl_fetch_fw_file(struct ath6kl *ar)
{
const char *filename;
int ret;
if (ar->fw != NULL)
return 0;
if (testmode) {
switch (ar->version.target_ver) {
case AR6003_REV2_VERSION:
filename = AR6003_REV2_TCMD_FIRMWARE_FILE;
break;
case AR6003_REV3_VERSION:
filename = AR6003_REV3_TCMD_FIRMWARE_FILE;
break;
case AR6004_REV1_VERSION:
ath6kl_warn("testmode not supported with ar6004\n");
return -EOPNOTSUPP;
default:
ath6kl_warn("unknown target version: 0x%x\n",
ar->version.target_ver);
return -EINVAL;
}
set_bit(TESTMODE, &ar->flag);
goto get_fw;
}
switch (ar->version.target_ver) {
case AR6003_REV2_VERSION:
filename = AR6003_REV2_FIRMWARE_FILE;
break;
case AR6004_REV1_VERSION:
filename = AR6004_REV1_FIRMWARE_FILE;
break;
default:
filename = AR6003_REV3_FIRMWARE_FILE;
break;
}
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",
filename, ret);
return ret;
}
return 0;
}
static int ath6kl_fetch_patch_file(struct ath6kl *ar)
{
const char *filename;
int ret;
switch (ar->version.target_ver) {
case AR6003_REV2_VERSION:
filename = AR6003_REV2_PATCH_FILE;
break;
case AR6004_REV1_VERSION:
/* FIXME: implement for AR6004 */
return 0;
break;
default:
filename = AR6003_REV3_PATCH_FILE;
break;
}
if (ar->fw_patch == NULL) {
ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
&ar->fw_patch_len);
if (ret) {
ath6kl_err("Failed to get patch file %s: %d\n",
filename, ret);
return ret;
}
}
return 0;
}
static int ath6kl_fetch_fw_api1(struct ath6kl *ar)
{
int ret;
ret = ath6kl_fetch_otp_file(ar);
if (ret)
return ret;
ret = ath6kl_fetch_fw_file(ar);
if (ret)
return ret;
ret = ath6kl_fetch_patch_file(ar);
if (ret)
return ret;
return 0;
}
static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
{
size_t magic_len, len, ie_len;
const struct firmware *fw;
struct ath6kl_fw_ie *hdr;
const char *filename;
const u8 *data;
int ret, ie_id, i, index, bit;
__le32 *val;
switch (ar->version.target_ver) {
case AR6003_REV2_VERSION:
filename = AR6003_REV2_FIRMWARE_2_FILE;
break;
case AR6003_REV3_VERSION:
filename = AR6003_REV3_FIRMWARE_2_FILE;
break;
case AR6004_REV1_VERSION:
filename = AR6004_REV1_FIRMWARE_2_FILE;
break;
default:
return -EOPNOTSUPP;
}
ret = request_firmware(&fw, filename, ar->dev);
if (ret)
return ret;
data = fw->data;
len = fw->size;
/* magic also includes the null byte, check that as well */
magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1;
if (len < magic_len) {
ret = -EINVAL;
goto out;
}
if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) {
ret = -EINVAL;
goto out;
}
len -= magic_len;
data += magic_len;
/* loop elements */
while (len > sizeof(struct ath6kl_fw_ie)) {
/* hdr is unaligned! */
hdr = (struct ath6kl_fw_ie *) data;
ie_id = le32_to_cpup(&hdr->id);
ie_len = le32_to_cpup(&hdr->len);
len -= sizeof(*hdr);
data += sizeof(*hdr);
if (len < ie_len) {
ret = -EINVAL;
goto out;
}
switch (ie_id) {
case ATH6KL_FW_IE_OTP_IMAGE:
ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n",
ie_len);
ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL);
if (ar->fw_otp == NULL) {
ret = -ENOMEM;
goto out;
}
ar->fw_otp_len = ie_len;
break;
case ATH6KL_FW_IE_FW_IMAGE:
ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n",
ie_len);
ar->fw = kmemdup(data, ie_len, GFP_KERNEL);
if (ar->fw == NULL) {
ret = -ENOMEM;
goto out;
}
ar->fw_len = ie_len;
break;
case ATH6KL_FW_IE_PATCH_IMAGE:
ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n",
ie_len);
ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL);
if (ar->fw_patch == NULL) {
ret = -ENOMEM;
goto out;
}
ar->fw_patch_len = ie_len;
break;
case ATH6KL_FW_IE_RESERVED_RAM_SIZE:
val = (__le32 *) data;
ar->hw.reserved_ram_size = le32_to_cpup(val);
ath6kl_dbg(ATH6KL_DBG_BOOT,
"found reserved ram size ie 0x%d\n",
ar->hw.reserved_ram_size);
break;
case ATH6KL_FW_IE_CAPABILITIES:
ath6kl_dbg(ATH6KL_DBG_BOOT,
"found firmware capabilities ie (%zd B)\n",
ie_len);
for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) {
index = ALIGN(i, 8) / 8;
bit = i % 8;
if (data[index] & (1 << bit))
__set_bit(i, ar->fw_capabilities);
}
ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "",
ar->fw_capabilities,
sizeof(ar->fw_capabilities));
break;
case ATH6KL_FW_IE_PATCH_ADDR:
if (ie_len != sizeof(*val))
break;
val = (__le32 *) data;
ar->hw.dataset_patch_addr = le32_to_cpup(val);
ath6kl_dbg(ATH6KL_DBG_BOOT,
"found patch address ie 0x%d\n",
ar->hw.dataset_patch_addr);
break;
default:
ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n",
le32_to_cpup(&hdr->id));
break;
}
len -= ie_len;
data += ie_len;
};
ret = 0;
out:
release_firmware(fw);
return ret;
}
static int ath6kl_fetch_firmwares(struct ath6kl *ar)
{ {
u32 board_address, board_ext_address, param;
int ret; int ret;
if (ar->fw_board == NULL) {
ret = ath6kl_fetch_board_file(ar); ret = ath6kl_fetch_board_file(ar);
if (ret) if (ret)
return ret; return ret;
ret = ath6kl_fetch_fw_api2(ar);
if (ret == 0) {
ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n");
return 0;
} }
/* Determine where in Target RAM to write Board Data */ ret = ath6kl_fetch_fw_api1(ar);
if (ret)
return ret;
ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n");
return 0;
}
static int ath6kl_upload_board_file(struct ath6kl *ar)
{
u32 board_address, board_ext_address, param;
u32 board_data_size, board_ext_data_size;
int ret;
if (WARN_ON(ar->fw_board == NULL))
return -ENOENT;
/*
* Determine where in Target RAM to write Board Data.
* For AR6004, host determine Target RAM address for
* writing board data.
*/
if (ar->target_type == TARGET_TYPE_AR6004) {
board_address = AR6004_REV1_BOARD_DATA_ADDRESS;
ath6kl_bmi_write(ar,
ath6kl_get_hi_item_addr(ar,
HI_ITEM(hi_board_data)),
(u8 *) &board_address, 4);
} else {
ath6kl_bmi_read(ar, ath6kl_bmi_read(ar,
ath6kl_get_hi_item_addr(ar, ath6kl_get_hi_item_addr(ar,
HI_ITEM(hi_board_data)), HI_ITEM(hi_board_data)),
(u8 *) &board_address, 4); (u8 *) &board_address, 4);
ath6kl_dbg(ATH6KL_DBG_TRC, "board data download addr: 0x%x\n", }
board_address);
/* determine where in target ram to write extended board data */ /* determine where in target ram to write extended board data */
ath6kl_bmi_read(ar, ath6kl_bmi_read(ar,
...@@ -729,21 +1106,37 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) ...@@ -729,21 +1106,37 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
HI_ITEM(hi_board_ext_data)), HI_ITEM(hi_board_ext_data)),
(u8 *) &board_ext_address, 4); (u8 *) &board_ext_address, 4);
ath6kl_dbg(ATH6KL_DBG_TRC, "board file download addr: 0x%x\n",
board_ext_address);
if (board_ext_address == 0) { if (board_ext_address == 0) {
ath6kl_err("Failed to get board file target address.\n"); ath6kl_err("Failed to get board file target address.\n");
return -EINVAL; return -EINVAL;
} }
if (ar->fw_board_len == (AR6003_BOARD_DATA_SZ + switch (ar->target_type) {
AR6003_BOARD_EXT_DATA_SZ)) { case TARGET_TYPE_AR6003:
board_data_size = AR6003_BOARD_DATA_SZ;
board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ;
break;
case TARGET_TYPE_AR6004:
board_data_size = AR6004_BOARD_DATA_SZ;
board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ;
break;
default:
WARN_ON(1);
return -EINVAL;
break;
}
if (ar->fw_board_len == (board_data_size +
board_ext_data_size)) {
/* write extended board data */ /* write extended board data */
ret = ath6kl_bmi_write(ar, board_ext_address, ath6kl_dbg(ATH6KL_DBG_BOOT,
ar->fw_board + AR6003_BOARD_DATA_SZ, "writing extended board data to 0x%x (%d B)\n",
AR6003_BOARD_EXT_DATA_SZ); board_ext_address, board_ext_data_size);
ret = ath6kl_bmi_write(ar, board_ext_address,
ar->fw_board + board_data_size,
board_ext_data_size);
if (ret) { if (ret) {
ath6kl_err("Failed to write extended board data: %d\n", ath6kl_err("Failed to write extended board data: %d\n",
ret); ret);
...@@ -751,21 +1144,25 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) ...@@ -751,21 +1144,25 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
} }
/* record that extended board data is initialized */ /* record that extended board data is initialized */
param = (AR6003_BOARD_EXT_DATA_SZ << 16) | 1; param = (board_ext_data_size << 16) | 1;
ath6kl_bmi_write(ar, ath6kl_bmi_write(ar,
ath6kl_get_hi_item_addr(ar, ath6kl_get_hi_item_addr(ar,
HI_ITEM(hi_board_ext_data_config)), HI_ITEM(hi_board_ext_data_config)),
(unsigned char *) &param, 4); (unsigned char *) &param, 4);
} }
if (ar->fw_board_len < AR6003_BOARD_DATA_SZ) { if (ar->fw_board_len < board_data_size) {
ath6kl_err("Too small board file: %zu\n", ar->fw_board_len); ath6kl_err("Too small board file: %zu\n", ar->fw_board_len);
ret = -EINVAL; ret = -EINVAL;
return ret; return ret;
} }
ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n",
board_address, board_data_size);
ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, ret = ath6kl_bmi_write(ar, board_address, ar->fw_board,
AR6003_BOARD_DATA_SZ); board_data_size);
if (ret) { if (ret) {
ath6kl_err("Board file bmi write failed: %d\n", ret); ath6kl_err("Board file bmi write failed: %d\n", ret);
...@@ -784,31 +1181,16 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) ...@@ -784,31 +1181,16 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
static int ath6kl_upload_otp(struct ath6kl *ar) static int ath6kl_upload_otp(struct ath6kl *ar)
{ {
const char *filename;
u32 address, param; u32 address, param;
int ret; int ret;
switch (ar->version.target_ver) { if (WARN_ON(ar->fw_otp == NULL))
case AR6003_REV2_VERSION: return -ENOENT;
filename = AR6003_REV2_OTP_FILE;
break;
default:
filename = AR6003_REV3_OTP_FILE;
break;
}
if (ar->fw_otp == NULL) { address = ar->hw.app_load_addr;
ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
&ar->fw_otp_len);
if (ret) {
ath6kl_err("Failed to get OTP file %s: %d\n",
filename, ret);
return ret;
}
}
address = ath6kl_get_load_address(ar->version.target_ver, ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address,
APP_LOAD_ADDR); ar->fw_otp_len);
ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp,
ar->fw_otp_len); ar->fw_otp_len);
...@@ -817,10 +1199,25 @@ static int ath6kl_upload_otp(struct ath6kl *ar) ...@@ -817,10 +1199,25 @@ static int ath6kl_upload_otp(struct ath6kl *ar)
return ret; return ret;
} }
/* read firmware start address */
ret = ath6kl_bmi_read(ar,
ath6kl_get_hi_item_addr(ar,
HI_ITEM(hi_app_start)),
(u8 *) &address, sizeof(address));
if (ret) {
ath6kl_err("Failed to read hi_app_start: %d\n", ret);
return ret;
}
ar->hw.app_start_override_addr = address;
ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr 0x%x\n",
ar->hw.app_start_override_addr);
/* execute the OTP code */ /* execute the OTP code */
ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", address);
param = 0; param = 0;
address = ath6kl_get_load_address(ar->version.target_ver,
APP_START_OVERRIDE_ADDR);
ath6kl_bmi_execute(ar, address, &param); ath6kl_bmi_execute(ar, address, &param);
return ret; return ret;
...@@ -828,30 +1225,16 @@ static int ath6kl_upload_otp(struct ath6kl *ar) ...@@ -828,30 +1225,16 @@ static int ath6kl_upload_otp(struct ath6kl *ar)
static int ath6kl_upload_firmware(struct ath6kl *ar) static int ath6kl_upload_firmware(struct ath6kl *ar)
{ {
const char *filename;
u32 address; u32 address;
int ret; int ret;
switch (ar->version.target_ver) { if (WARN_ON(ar->fw == NULL))
case AR6003_REV2_VERSION: return -ENOENT;
filename = AR6003_REV2_FIRMWARE_FILE;
break;
default:
filename = AR6003_REV3_FIRMWARE_FILE;
break;
}
if (ar->fw == NULL) { address = ar->hw.app_load_addr;
ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
if (ret) {
ath6kl_err("Failed to get firmware file %s: %d\n",
filename, ret);
return ret;
}
}
address = ath6kl_get_load_address(ar->version.target_ver, ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n",
APP_LOAD_ADDR); address, ar->fw_len);
ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len);
...@@ -860,41 +1243,29 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) ...@@ -860,41 +1243,29 @@ static int ath6kl_upload_firmware(struct ath6kl *ar)
return ret; return ret;
} }
/* Set starting address for firmware */ /*
address = ath6kl_get_load_address(ar->version.target_ver, * Set starting address for firmware
APP_START_OVERRIDE_ADDR); * Don't need to setup app_start override addr on AR6004
*/
if (ar->target_type != TARGET_TYPE_AR6004) {
address = ar->hw.app_start_override_addr;
ath6kl_bmi_set_app_start(ar, address); ath6kl_bmi_set_app_start(ar, address);
}
return ret; return ret;
} }
static int ath6kl_upload_patch(struct ath6kl *ar) static int ath6kl_upload_patch(struct ath6kl *ar)
{ {
const char *filename;
u32 address, param; u32 address, param;
int ret; int ret;
switch (ar->version.target_ver) { if (WARN_ON(ar->fw_patch == NULL))
case AR6003_REV2_VERSION: return -ENOENT;
filename = AR6003_REV2_PATCH_FILE;
break;
default:
filename = AR6003_REV3_PATCH_FILE;
break;
}
if (ar->fw_patch == NULL) { address = ar->hw.dataset_patch_addr;
ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
&ar->fw_patch_len);
if (ret) {
ath6kl_err("Failed to get patch file %s: %d\n",
filename, ret);
return ret;
}
}
address = ath6kl_get_load_address(ar->version.target_ver, ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n",
DATASET_PATCH_ADDR); address, ar->fw_patch_len);
ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len);
if (ret) { if (ret) {
...@@ -916,7 +1287,8 @@ static int ath6kl_init_upload(struct ath6kl *ar) ...@@ -916,7 +1287,8 @@ static int ath6kl_init_upload(struct ath6kl *ar)
u32 param, options, sleep, address; u32 param, options, sleep, address;
int status = 0; int status = 0;
if (ar->target_type != TARGET_TYPE_AR6003) if (ar->target_type != TARGET_TYPE_AR6003 &&
ar->target_type != TARGET_TYPE_AR6004)
return -EINVAL; return -EINVAL;
/* temporarily disable system sleep */ /* temporarily disable system sleep */
...@@ -948,8 +1320,11 @@ static int ath6kl_init_upload(struct ath6kl *ar) ...@@ -948,8 +1320,11 @@ static int ath6kl_init_upload(struct ath6kl *ar)
options, sleep); options, sleep);
/* program analog PLL register */ /* program analog PLL register */
/* no need to control 40/44MHz clock on AR6004 */
if (ar->target_type != TARGET_TYPE_AR6004) {
status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER,
0xF9104001); 0xF9104001);
if (status) if (status)
return status; return status;
...@@ -960,6 +1335,7 @@ static int ath6kl_init_upload(struct ath6kl *ar) ...@@ -960,6 +1335,7 @@ static int ath6kl_init_upload(struct ath6kl *ar)
status = ath6kl_bmi_reg_write(ar, address, param); status = ath6kl_bmi_reg_write(ar, address, param);
if (status) if (status)
return status; return status;
}
param = 0; param = 0;
address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS; address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS;
...@@ -1036,6 +1412,45 @@ static int ath6kl_init_upload(struct ath6kl *ar) ...@@ -1036,6 +1412,45 @@ static int ath6kl_init_upload(struct ath6kl *ar)
return status; return status;
} }
static int ath6kl_init_hw_params(struct ath6kl *ar)
{
switch (ar->version.target_ver) {
case AR6003_REV2_VERSION:
ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS;
ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE;
break;
case AR6003_REV3_VERSION:
ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS;
ar->hw.app_load_addr = 0x1234;
ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE;
break;
case AR6004_REV1_VERSION:
ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS;
ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS;
ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE;
break;
default:
ath6kl_err("Unsupported hardware version: 0x%x\n",
ar->version.target_ver);
return -EINVAL;
}
ath6kl_dbg(ATH6KL_DBG_BOOT,
"target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n",
ar->version.target_ver, ar->target_type,
ar->hw.dataset_patch_addr, ar->hw.app_load_addr);
ath6kl_dbg(ATH6KL_DBG_BOOT,
"app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x",
ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr,
ar->hw.reserved_ram_size);
return 0;
}
static int ath6kl_init(struct net_device *dev) static int ath6kl_init(struct net_device *dev)
{ {
struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl *ar = ath6kl_priv(dev);
...@@ -1062,8 +1477,6 @@ static int ath6kl_init(struct net_device *dev) ...@@ -1062,8 +1477,6 @@ static int ath6kl_init(struct net_device *dev)
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi);
wlan_node_table_init(&ar->scan_table);
/* /*
* The reason we have to wait for the target here is that the * The reason we have to wait for the target here is that the
* driver layer has to init BMI in order to set the host block * driver layer has to init BMI in order to set the host block
...@@ -1111,6 +1524,8 @@ static int ath6kl_init(struct net_device *dev) ...@@ -1111,6 +1524,8 @@ static int ath6kl_init(struct net_device *dev)
&ar->flag), &ar->flag),
WMI_TIMEOUT); WMI_TIMEOUT);
ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { if (ar->version.abi_ver != ATH6KL_ABI_VERSION) {
ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n",
ATH6KL_ABI_VERSION, ar->version.abi_ver); ATH6KL_ABI_VERSION, ar->version.abi_ver);
...@@ -1133,6 +1548,8 @@ static int ath6kl_init(struct net_device *dev) ...@@ -1133,6 +1548,8 @@ static int ath6kl_init(struct net_device *dev)
ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER |
ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST;
ar->wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
status = ath6kl_target_config_wlan_params(ar); status = ath6kl_target_config_wlan_params(ar);
if (!status) if (!status)
goto ath6kl_init_done; goto ath6kl_init_done;
...@@ -1145,7 +1562,6 @@ static int ath6kl_init(struct net_device *dev) ...@@ -1145,7 +1562,6 @@ static int ath6kl_init(struct net_device *dev)
err_cleanup_scatter: err_cleanup_scatter:
ath6kl_hif_cleanup_scatter(ar); ath6kl_hif_cleanup_scatter(ar);
err_node_cleanup: err_node_cleanup:
wlan_node_table_cleanup(&ar->scan_table);
ath6kl_wmi_shutdown(ar->wmi); ath6kl_wmi_shutdown(ar->wmi);
clear_bit(WMI_ENABLED, &ar->flag); clear_bit(WMI_ENABLED, &ar->flag);
ar->wmi = NULL; ar->wmi = NULL;
...@@ -1175,6 +1591,10 @@ int ath6kl_core_init(struct ath6kl *ar) ...@@ -1175,6 +1591,10 @@ int ath6kl_core_init(struct ath6kl *ar)
ar->target_type = le32_to_cpu(targ_info.type); ar->target_type = le32_to_cpu(targ_info.type);
ar->wdev->wiphy->hw_version = le32_to_cpu(targ_info.version); ar->wdev->wiphy->hw_version = le32_to_cpu(targ_info.version);
ret = ath6kl_init_hw_params(ar);
if (ret)
goto err_bmi_cleanup;
ret = ath6kl_configure_target(ar); ret = ath6kl_configure_target(ar);
if (ret) if (ret)
goto err_bmi_cleanup; goto err_bmi_cleanup;
...@@ -1193,6 +1613,10 @@ int ath6kl_core_init(struct ath6kl *ar) ...@@ -1193,6 +1613,10 @@ int ath6kl_core_init(struct ath6kl *ar)
goto err_htc_cleanup; goto err_htc_cleanup;
} }
ret = ath6kl_fetch_firmwares(ar);
if (ret)
goto err_htc_cleanup;
ret = ath6kl_init_upload(ar); ret = ath6kl_init_upload(ar);
if (ret) if (ret)
goto err_htc_cleanup; goto err_htc_cleanup;
...@@ -1285,6 +1709,8 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister) ...@@ -1285,6 +1709,8 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister)
ath6kl_bmi_cleanup(ar); ath6kl_bmi_cleanup(ar);
ath6kl_debug_cleanup(ar);
if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) { if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) {
unregister_netdev(dev); unregister_netdev(dev);
clear_bit(NETDEV_REGISTERED, &ar->flag); clear_bit(NETDEV_REGISTERED, &ar->flag);
...@@ -1292,8 +1718,6 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister) ...@@ -1292,8 +1718,6 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister)
free_netdev(dev); free_netdev(dev);
wlan_node_table_cleanup(&ar->scan_table);
kfree(ar->fw_board); kfree(ar->fw_board);
kfree(ar->fw_otp); kfree(ar->fw_otp);
kfree(ar->fw); kfree(ar->fw);
......
...@@ -61,6 +61,7 @@ static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie, ...@@ -61,6 +61,7 @@ static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie,
sta = &ar->sta_list[free_slot]; sta = &ar->sta_list[free_slot];
memcpy(sta->mac, mac, ETH_ALEN); memcpy(sta->mac, mac, ETH_ALEN);
if (ielen <= ATH6KL_MAX_IE)
memcpy(sta->wpa_ie, wpaie, ielen); memcpy(sta->wpa_ie, wpaie, ielen);
sta->aid = aid; sta->aid = aid;
sta->keymgmt = keymgmt; sta->keymgmt = keymgmt;
...@@ -177,8 +178,8 @@ void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie) ...@@ -177,8 +178,8 @@ void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie)
static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
{ {
int status; int status;
u8 addr_val[4];
s32 i; s32 i;
__le32 addr_val;
/* /*
* Write bytes 1,2,3 of the register to set the upper address bytes, * Write bytes 1,2,3 of the register to set the upper address bytes,
...@@ -188,16 +189,18 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) ...@@ -188,16 +189,18 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
for (i = 1; i <= 3; i++) { for (i = 1; i <= 3; i++) {
/* /*
* Fill the buffer with the address byte value we want to * Fill the buffer with the address byte value we want to
* hit 4 times. * hit 4 times. No need to worry about endianness as the
* same byte is copied to all four bytes of addr_val at
* any time.
*/ */
memset(addr_val, ((u8 *)&addr)[i], 4); memset((u8 *)&addr_val, ((u8 *)&addr)[i], 4);
/* /*
* Hit each byte of the register address with a 4-byte * Hit each byte of the register address with a 4-byte
* write operation to the same address, this is a harmless * write operation to the same address, this is a harmless
* operation. * operation.
*/ */
status = hif_read_write_sync(ar, reg_addr + i, addr_val, status = hif_read_write_sync(ar, reg_addr + i, (u8 *)&addr_val,
4, HIF_WR_SYNC_BYTE_FIX); 4, HIF_WR_SYNC_BYTE_FIX);
if (status) if (status)
break; break;
...@@ -215,7 +218,9 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) ...@@ -215,7 +218,9 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
* cycle to start, the extra 3 byte write to bytes 1,2,3 has no * cycle to start, the extra 3 byte write to bytes 1,2,3 has no
* effect since we are writing the same values again * effect since we are writing the same values again
*/ */
status = hif_read_write_sync(ar, reg_addr, (u8 *)(&addr), addr_val = cpu_to_le32(addr);
status = hif_read_write_sync(ar, reg_addr,
(u8 *)&(addr_val),
4, HIF_WR_SYNC_BYTE_INC); 4, HIF_WR_SYNC_BYTE_INC);
if (status) { if (status) {
...@@ -228,90 +233,193 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) ...@@ -228,90 +233,193 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
} }
/* /*
* Read from the ATH6KL through its diagnostic window. No cooperation from * Read from the hardware through its diagnostic window. No cooperation
* the Target is required for this. * from the firmware is required for this.
*/ */
int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data) int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value)
{ {
int status; int ret;
/* set window register to start read cycle */ /* set window register to start read cycle */
status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, ret = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, address);
*address); if (ret)
return ret;
if (status)
return status;
/* read the data */ /* read the data */
status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data, ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) value,
sizeof(u32), HIF_RD_SYNC_BYTE_INC); sizeof(*value), HIF_RD_SYNC_BYTE_INC);
if (status) { if (ret) {
ath6kl_err("failed to read from window data addr\n"); ath6kl_warn("failed to read32 through diagnose window: %d\n",
return status; ret);
return ret;
} }
return status; return 0;
} }
/* /*
* Write to the ATH6KL through its diagnostic window. No cooperation from * Write to the ATH6KL through its diagnostic window. No cooperation from
* the Target is required for this. * the Target is required for this.
*/ */
static int ath6kl_write_reg_diag(struct ath6kl *ar, u32 *address, u32 *data) int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value)
{ {
int status; int ret;
/* set write data */ /* set write data */
status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data, ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) &value,
sizeof(u32), HIF_WR_SYNC_BYTE_INC); sizeof(value), HIF_WR_SYNC_BYTE_INC);
if (status) { if (ret) {
ath6kl_err("failed to write 0x%x to window data addr\n", *data); ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n",
return status; address, value);
return ret;
} }
/* set window register, which starts the write cycle */ /* set window register, which starts the write cycle */
return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS, return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
*address); address);
}
int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length)
{
u32 count, *buf = data;
int ret;
if (WARN_ON(length % 4))
return -EINVAL;
for (count = 0; count < length / 4; count++, address += 4) {
ret = ath6kl_diag_read32(ar, address, &buf[count]);
if (ret)
return ret;
}
return 0;
} }
int ath6kl_access_datadiag(struct ath6kl *ar, u32 address, int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length)
u8 *data, u32 length, bool read)
{ {
u32 count; u32 count;
int status = 0; __le32 *buf = data;
int ret;
for (count = 0; count < length; count += 4, address += 4) { if (WARN_ON(length % 4))
if (read) { return -EINVAL;
status = ath6kl_read_reg_diag(ar, &address,
(u32 *) &data[count]); for (count = 0; count < length / 4; count++, address += 4) {
if (status) ret = ath6kl_diag_write32(ar, address, buf[count]);
break; if (ret)
} else { return ret;
status = ath6kl_write_reg_diag(ar, &address, }
(u32 *) &data[count]);
if (status) return 0;
break; }
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;
u8 *buf;
buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
address = TARG_VTOP(ar->target_type,
ath6kl_get_hi_item_addr(ar,
HI_ITEM(hi_dbglog_hdr)));
ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr);
if (ret)
goto out;
/* Get the contents of the ring buffer */
if (debug_hdr_addr == 0) {
ath6kl_warn("Invalid address for debug_hdr_addr\n");
ret = -EINVAL;
goto out;
} }
address = TARG_VTOP(ar->target_type, debug_hdr_addr);
ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr));
address = TARG_VTOP(ar->target_type,
le32_to_cpu(debug_hdr.dbuf_addr));
firstbuf = address;
dropped = le32_to_cpu(debug_hdr.dropped);
ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
loop = 100;
do {
address = TARG_VTOP(ar->target_type,
le32_to_cpu(debug_buf.buffer_addr));
length = le32_to_cpu(debug_buf.length);
if (length != 0 && (le32_to_cpu(debug_buf.length) <=
le32_to_cpu(debug_buf.bufsize))) {
length = ALIGN(length, 4);
ret = ath6kl_diag_read(ar, address,
buf, length);
if (ret)
goto out;
ath6kl_debug_fwlog_event(ar, buf, length);
} }
return status; address = TARG_VTOP(ar->target_type,
le32_to_cpu(debug_buf.next));
ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
if (ret)
goto out;
loop--;
if (WARN_ON(loop == 0)) {
ret = -ETIMEDOUT;
goto out;
}
} while (address != firstbuf);
out:
kfree(buf);
return ret;
} }
/* FIXME: move to a better place, target.h? */
#define AR6003_RESET_CONTROL_ADDRESS 0x00004000
#define AR6004_RESET_CONTROL_ADDRESS 0x00004000
static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
bool wait_fot_compltn, bool cold_reset) bool wait_fot_compltn, bool cold_reset)
{ {
int status = 0; int status = 0;
u32 address; u32 address;
u32 data; __le32 data;
if (target_type != TARGET_TYPE_AR6003) if (target_type != TARGET_TYPE_AR6003 &&
target_type != TARGET_TYPE_AR6004)
return; return;
data = cold_reset ? RESET_CONTROL_COLD_RST : RESET_CONTROL_MBOX_RST; data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) :
cpu_to_le32(RESET_CONTROL_MBOX_RST);
address = RTC_BASE_ADDRESS; switch (target_type) {
status = ath6kl_write_reg_diag(ar, &address, &data); case TARGET_TYPE_AR6003:
address = AR6003_RESET_CONTROL_ADDRESS;
break;
case TARGET_TYPE_AR6004:
address = AR6004_RESET_CONTROL_ADDRESS;
break;
default:
address = AR6003_RESET_CONTROL_ADDRESS;
break;
}
status = ath6kl_diag_write32(ar, address, data);
if (status) if (status)
ath6kl_err("failed to reset target\n"); ath6kl_err("failed to reset target\n");
...@@ -411,18 +519,16 @@ static void ath6kl_install_static_wep_keys(struct ath6kl *ar) ...@@ -411,18 +519,16 @@ static void ath6kl_install_static_wep_keys(struct ath6kl *ar)
} }
} }
static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid, void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel)
u16 listen_int, u16 beacon_int,
u8 assoc_resp_len, u8 *assoc_info)
{ {
struct net_device *dev = ar->net_dev;
struct station_info sinfo;
struct ath6kl_req_key *ik; struct ath6kl_req_key *ik;
enum crypto_type keyType = NONE_CRYPT; int res;
u8 key_rsc[ATH6KL_KEY_SEQ_LEN];
if (memcmp(dev->dev_addr, bssid, ETH_ALEN) == 0) {
ik = &ar->ap_mode_bkey; ik = &ar->ap_mode_bkey;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel);
switch (ar->auth_mode) { switch (ar->auth_mode) {
case NONE_AUTH: case NONE_AUTH:
if (ar->prwise_crypto == WEP_CRYPT) if (ar->prwise_crypto == WEP_CRYPT)
...@@ -430,49 +536,90 @@ static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid, ...@@ -430,49 +536,90 @@ static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid,
break; break;
case WPA_PSK_AUTH: case WPA_PSK_AUTH:
case WPA2_PSK_AUTH: case WPA2_PSK_AUTH:
case (WPA_PSK_AUTH|WPA2_PSK_AUTH): case (WPA_PSK_AUTH | WPA2_PSK_AUTH):
switch (ik->ik_type) { if (!ik->valid)
case ATH6KL_CIPHER_TKIP:
keyType = TKIP_CRYPT;
break; break;
case ATH6KL_CIPHER_AES_CCM:
keyType = AES_CRYPT; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for "
break; "the initial group key for AP mode\n");
default: memset(key_rsc, 0, sizeof(key_rsc));
goto skip_key; res = ath6kl_wmi_addkey_cmd(
ar->wmi, ik->key_index, ik->key_type,
GROUP_USAGE, ik->key_len, key_rsc, ik->key,
KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG);
if (res) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed "
"addkey failed: %d\n", res);
} }
ath6kl_wmi_addkey_cmd(ar->wmi, ik->ik_keyix, keyType,
GROUP_USAGE, ik->ik_keylen,
(u8 *)&ik->ik_keyrsc,
ik->ik_keydata,
KEY_OP_INIT_VAL, ik->ik_macaddr,
SYNC_BOTH_WMIFLAG);
break; break;
} }
skip_key:
ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
set_bit(CONNECTED, &ar->flag); set_bit(CONNECTED, &ar->flag);
return; netif_carrier_on(ar->net_dev);
}
void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
u8 keymgmt, u8 ucipher, u8 auth,
u8 assoc_req_len, u8 *assoc_info)
{
u8 *ies = NULL, *wpa_ie = NULL, *pos;
size_t ies_len = 0;
struct station_info sinfo;
ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid);
if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) {
struct ieee80211_mgmt *mgmt =
(struct ieee80211_mgmt *) assoc_info;
if (ieee80211_is_assoc_req(mgmt->frame_control) &&
assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) +
sizeof(mgmt->u.assoc_req)) {
ies = mgmt->u.assoc_req.variable;
ies_len = assoc_info + assoc_req_len - ies;
} else if (ieee80211_is_reassoc_req(mgmt->frame_control) &&
assoc_req_len >= sizeof(struct ieee80211_hdr_3addr)
+ sizeof(mgmt->u.reassoc_req)) {
ies = mgmt->u.reassoc_req.variable;
ies_len = assoc_info + assoc_req_len - ies;
}
} }
ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", pos = ies;
bssid, channel); while (pos && pos + 1 < ies + ies_len) {
if (pos + 2 + pos[1] > ies + ies_len)
break;
if (pos[0] == WLAN_EID_RSN)
wpa_ie = pos; /* RSN IE */
else if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
pos[1] >= 4 &&
pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2) {
if (pos[5] == 0x01)
wpa_ie = pos; /* WPA IE */
else if (pos[5] == 0x04) {
wpa_ie = pos; /* WPS IE */
break; /* overrides WPA/RSN IE */
}
}
pos += 2 + pos[1];
}
ath6kl_add_new_sta(ar, bssid, channel, assoc_info, assoc_resp_len, ath6kl_add_new_sta(ar, mac_addr, aid, wpa_ie,
listen_int & 0xFF, beacon_int, wpa_ie ? 2 + wpa_ie[1] : 0,
(listen_int >> 8) & 0xFF); keymgmt, ucipher, auth);
/* send event to application */ /* send event to application */
memset(&sinfo, 0, sizeof(sinfo)); memset(&sinfo, 0, sizeof(sinfo));
/* TODO: sinfo.generation */ /* TODO: sinfo.generation */
/* TODO: need to deliver (Re)AssocReq IEs somehow.. change in
* cfg80211 needed, e.g., by adding those into sinfo
*/
cfg80211_new_sta(ar->net_dev, bssid, &sinfo, GFP_KERNEL);
netif_wake_queue(ar->net_dev); sinfo.assoc_req_ies = ies;
sinfo.assoc_req_ies_len = ies_len;
sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
return; cfg80211_new_sta(ar->net_dev, mac_addr, &sinfo, GFP_KERNEL);
netif_wake_queue(ar->net_dev);
} }
/* Functions for Tx credit handling */ /* Functions for Tx credit handling */
...@@ -779,6 +926,41 @@ void ath6kl_disconnect(struct ath6kl *ar) ...@@ -779,6 +926,41 @@ void ath6kl_disconnect(struct ath6kl *ar)
} }
} }
void ath6kl_deep_sleep_enable(struct ath6kl *ar)
{
switch (ar->sme_state) {
case SME_CONNECTING:
cfg80211_connect_result(ar->net_dev, ar->bssid, NULL, 0,
NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
break;
case SME_CONNECTED:
default:
/*
* FIXME: oddly enough smeState is in DISCONNECTED during
* suspend, why? Need to send disconnected event in that
* state.
*/
cfg80211_disconnected(ar->net_dev, 0, NULL, 0, GFP_KERNEL);
break;
}
if (test_bit(CONNECTED, &ar->flag) ||
test_bit(CONNECT_PEND, &ar->flag))
ath6kl_wmi_disconnect_cmd(ar->wmi);
ar->sme_state = SME_DISCONNECTED;
/* disable scanning */
if (ath6kl_wmi_scanparams_cmd(ar->wmi, 0xFFFF, 0, 0, 0, 0, 0, 0, 0,
0, 0) != 0)
printk(KERN_WARNING "ath6kl: failed to disable scan "
"during suspend\n");
ath6kl_cfg80211_scan_complete_event(ar, -ECANCELED);
}
/* WMI Event handlers */ /* WMI Event handlers */
static const char *get_hw_id_string(u32 id) static const char *get_hw_id_string(u32 id)
...@@ -819,17 +1001,20 @@ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) ...@@ -819,17 +1001,20 @@ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver)
set_bit(WMI_READY, &ar->flag); set_bit(WMI_READY, &ar->flag);
wake_up(&ar->event_wq); wake_up(&ar->event_wq);
ath6kl_info("hw %s fw %s\n", ath6kl_info("hw %s fw %s%s\n",
get_hw_id_string(ar->wdev->wiphy->hw_version), get_hw_id_string(ar->wdev->wiphy->hw_version),
ar->wdev->wiphy->fw_version); ar->wdev->wiphy->fw_version,
test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
} }
void ath6kl_scan_complete_evt(struct ath6kl *ar, int status) void ath6kl_scan_complete_evt(struct ath6kl *ar, int status)
{ {
ath6kl_cfg80211_scan_complete_event(ar, status); ath6kl_cfg80211_scan_complete_event(ar, status);
if (!ar->usr_bss_filter) if (!ar->usr_bss_filter) {
clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0); ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
}
ath6kl_dbg(ATH6KL_DBG_WLAN_SCAN, "scan complete: %d\n", status); ath6kl_dbg(ATH6KL_DBG_WLAN_SCAN, "scan complete: %d\n", status);
} }
...@@ -842,13 +1027,6 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid, ...@@ -842,13 +1027,6 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid,
{ {
unsigned long flags; unsigned long flags;
if (ar->nw_type == AP_NETWORK) {
ath6kl_connect_ap_mode(ar, channel, bssid, listen_int,
beacon_int, assoc_resp_len,
assoc_info);
return;
}
ath6kl_cfg80211_connect_event(ar, channel, bssid, ath6kl_cfg80211_connect_event(ar, channel, bssid,
listen_int, beacon_int, listen_int, beacon_int,
net_type, beacon_ie_len, net_type, beacon_ie_len,
...@@ -880,8 +1058,10 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid, ...@@ -880,8 +1058,10 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid,
ar->next_ep_id = ENDPOINT_2; ar->next_ep_id = ENDPOINT_2;
} }
if (!ar->usr_bss_filter) if (!ar->usr_bss_filter) {
ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0); set_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
ath6kl_wmi_bssfilter_cmd(ar->wmi, CURRENT_BSS_FILTER, 0);
}
} }
void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast) void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast)
...@@ -915,26 +1095,11 @@ static void ath6kl_update_target_stats(struct ath6kl *ar, u8 *ptr, u32 len) ...@@ -915,26 +1095,11 @@ static void ath6kl_update_target_stats(struct ath6kl *ar, u8 *ptr, u32 len)
(struct wmi_target_stats *) ptr; (struct wmi_target_stats *) ptr;
struct target_stats *stats = &ar->target_stats; struct target_stats *stats = &ar->target_stats;
struct tkip_ccmp_stats *ccmp_stats; struct tkip_ccmp_stats *ccmp_stats;
struct bss *conn_bss = NULL;
struct cserv_stats *c_stats;
u8 ac; u8 ac;
if (len < sizeof(*tgt_stats)) if (len < sizeof(*tgt_stats))
return; return;
/* update the RSSI of the connected bss */
if (test_bit(CONNECTED, &ar->flag)) {
conn_bss = ath6kl_wmi_find_node(ar->wmi, ar->bssid);
if (conn_bss) {
c_stats = &tgt_stats->cserv_stats;
conn_bss->ni_rssi =
a_sle16_to_cpu(c_stats->cs_ave_beacon_rssi);
conn_bss->ni_snr =
tgt_stats->cserv_stats.cs_ave_beacon_snr;
ath6kl_wmi_node_return(ar->wmi, conn_bss);
}
}
ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n"); ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n");
stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt); stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt);
...@@ -1165,7 +1330,6 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, ...@@ -1165,7 +1330,6 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
u8 assoc_resp_len, u8 *assoc_info, u8 assoc_resp_len, u8 *assoc_info,
u16 prot_reason_status) u16 prot_reason_status)
{ {
struct bss *wmi_ssid_node = NULL;
unsigned long flags; unsigned long flags;
if (ar->nw_type == AP_NETWORK) { if (ar->nw_type == AP_NETWORK) {
...@@ -1188,7 +1352,10 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, ...@@ -1188,7 +1352,10 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
cfg80211_del_sta(ar->net_dev, bssid, GFP_KERNEL); cfg80211_del_sta(ar->net_dev, bssid, GFP_KERNEL);
} }
if (memcmp(ar->net_dev->dev_addr, bssid, ETH_ALEN) == 0) {
memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
clear_bit(CONNECTED, &ar->flag); clear_bit(CONNECTED, &ar->flag);
}
return; return;
} }
...@@ -1222,33 +1389,6 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, ...@@ -1222,33 +1389,6 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
} }
} }
if ((reason == NO_NETWORK_AVAIL) && test_bit(WMI_READY, &ar->flag)) {
ath6kl_wmi_node_free(ar->wmi, bssid);
/*
* In case any other same SSID nodes are present remove it,
* since those nodes also not available now.
*/
do {
/*
* Find the nodes based on SSID and remove it
*
* Note: This case will not work out for
* Hidden-SSID
*/
wmi_ssid_node = ath6kl_wmi_find_ssid_node(ar->wmi,
ar->ssid,
ar->ssid_len,
false,
true);
if (wmi_ssid_node)
ath6kl_wmi_node_free(ar->wmi,
wmi_ssid_node->ni_macaddr);
} while (wmi_ssid_node);
}
/* update connect & link status atomically */ /* update connect & link status atomically */
spin_lock_irqsave(&ar->lock, flags); spin_lock_irqsave(&ar->lock, flags);
clear_bit(CONNECTED, &ar->flag); clear_bit(CONNECTED, &ar->flag);
...@@ -1331,7 +1471,7 @@ void init_netdev(struct net_device *dev) ...@@ -1331,7 +1471,7 @@ void init_netdev(struct net_device *dev)
dev->needed_headroom = ETH_HLEN; dev->needed_headroom = ETH_HLEN;
dev->needed_headroom += sizeof(struct ath6kl_llc_snap_hdr) + dev->needed_headroom += sizeof(struct ath6kl_llc_snap_hdr) +
sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH
+ WMI_MAX_TX_META_SZ; + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES;
return; return;
} }
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "htc.h"
#include "wmi.h"
#include "debug.h"
struct bss *wlan_node_alloc(int wh_size)
{
struct bss *ni;
ni = kzalloc(sizeof(struct bss), GFP_ATOMIC);
if ((ni != NULL) && wh_size) {
ni->ni_buf = kmalloc(wh_size, GFP_ATOMIC);
if (ni->ni_buf == NULL) {
kfree(ni);
return NULL;
}
}
return ni;
}
void wlan_node_free(struct bss *ni)
{
kfree(ni->ni_buf);
kfree(ni);
}
void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni,
const u8 *mac_addr)
{
int hash;
memcpy(ni->ni_macaddr, mac_addr, ETH_ALEN);
hash = ATH6KL_NODE_HASH(mac_addr);
ni->ni_refcnt = 1;
ni->ni_tstamp = jiffies_to_msecs(jiffies);
ni->ni_actcnt = WLAN_NODE_INACT_CNT;
spin_lock_bh(&nt->nt_nodelock);
/* insert at the end of the node list */
ni->ni_list_next = NULL;
ni->ni_list_prev = nt->nt_node_last;
if (nt->nt_node_last != NULL)
nt->nt_node_last->ni_list_next = ni;
nt->nt_node_last = ni;
if (nt->nt_node_first == NULL)
nt->nt_node_first = ni;
/* insert into the hash list */
ni->ni_hash_next = nt->nt_hash[hash];
if (ni->ni_hash_next != NULL)
nt->nt_hash[hash]->ni_hash_prev = ni;
ni->ni_hash_prev = NULL;
nt->nt_hash[hash] = ni;
spin_unlock_bh(&nt->nt_nodelock);
}
struct bss *wlan_find_node(struct ath6kl_node_table *nt,
const u8 *mac_addr)
{
struct bss *ni, *found_ni = NULL;
int hash;
spin_lock_bh(&nt->nt_nodelock);
hash = ATH6KL_NODE_HASH(mac_addr);
for (ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
if (memcmp(ni->ni_macaddr, mac_addr, ETH_ALEN) == 0) {
ni->ni_refcnt++;
found_ni = ni;
break;
}
}
spin_unlock_bh(&nt->nt_nodelock);
return found_ni;
}
void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni)
{
int hash;
spin_lock_bh(&nt->nt_nodelock);
if (ni->ni_list_prev == NULL)
/* fix list head */
nt->nt_node_first = ni->ni_list_next;
else
ni->ni_list_prev->ni_list_next = ni->ni_list_next;
if (ni->ni_list_next == NULL)
/* fix list tail */
nt->nt_node_last = ni->ni_list_prev;
else
ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
if (ni->ni_hash_prev == NULL) {
/* first in list so fix the list head */
hash = ATH6KL_NODE_HASH(ni->ni_macaddr);
nt->nt_hash[hash] = ni->ni_hash_next;
} else {
ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
}
if (ni->ni_hash_next != NULL)
ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
wlan_node_free(ni);
spin_unlock_bh(&nt->nt_nodelock);
}
static void wlan_node_dec_free(struct bss *ni)
{
if ((ni->ni_refcnt--) == 1)
wlan_node_free(ni);
}
void wlan_free_allnodes(struct ath6kl_node_table *nt)
{
struct bss *ni;
while ((ni = nt->nt_node_first) != NULL)
wlan_node_reclaim(nt, ni);
}
void wlan_iterate_nodes(struct ath6kl_node_table *nt, void *arg)
{
struct bss *ni;
spin_lock_bh(&nt->nt_nodelock);
for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
ni->ni_refcnt++;
ath6kl_cfg80211_scan_node(arg, ni);
wlan_node_dec_free(ni);
}
spin_unlock_bh(&nt->nt_nodelock);
}
void wlan_node_table_init(struct ath6kl_node_table *nt)
{
ath6kl_dbg(ATH6KL_DBG_WLAN_NODE, "node table = 0x%lx\n",
(unsigned long)nt);
memset(nt, 0, sizeof(struct ath6kl_node_table));
spin_lock_init(&nt->nt_nodelock);
nt->nt_node_age = WLAN_NODE_INACT_TIMEOUT_MSEC;
}
void wlan_refresh_inactive_nodes(struct ath6kl *ar)
{
struct ath6kl_node_table *nt = &ar->scan_table;
struct bss *bss;
u32 now;
now = jiffies_to_msecs(jiffies);
bss = nt->nt_node_first;
while (bss != NULL) {
/* refresh all nodes except the current bss */
if (memcmp(ar->bssid, bss->ni_macaddr, ETH_ALEN) != 0) {
if (((now - bss->ni_tstamp) > nt->nt_node_age)
|| --bss->ni_actcnt == 0) {
wlan_node_reclaim(nt, bss);
}
}
bss = bss->ni_list_next;
}
}
void wlan_node_table_cleanup(struct ath6kl_node_table *nt)
{
wlan_free_allnodes(nt);
}
struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 * ssid,
u32 ssid_len, bool is_wpa2, bool match_ssid)
{
struct bss *ni, *found_ni = NULL;
u8 *ie_ssid;
spin_lock_bh(&nt->nt_nodelock);
for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
ie_ssid = ni->ni_cie.ie_ssid;
if ((ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) &&
(memcmp(ssid, &ie_ssid[2], ssid_len) == 0)) {
if (match_ssid ||
(is_wpa2 && ni->ni_cie.ie_rsn != NULL) ||
(!is_wpa2 && ni->ni_cie.ie_wpa != NULL)) {
ni->ni_refcnt++;
found_ni = ni;
break;
}
}
}
spin_unlock_bh(&nt->nt_nodelock);
return found_ni;
}
void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni)
{
spin_lock_bh(&nt->nt_nodelock);
wlan_node_dec_free(ni);
spin_unlock_bh(&nt->nt_nodelock);
}
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "hif-ops.h" #include "hif-ops.h"
#include "target.h" #include "target.h"
#include "debug.h" #include "debug.h"
#include "cfg80211.h"
struct ath6kl_sdio { struct ath6kl_sdio {
struct sdio_func *func; struct sdio_func *func;
...@@ -134,10 +135,12 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr, ...@@ -134,10 +135,12 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
int ret = 0; int ret = 0;
if (request & HIF_WRITE) { if (request & HIF_WRITE) {
/* FIXME: looks like ugly workaround for something */
if (addr >= HIF_MBOX_BASE_ADDR && if (addr >= HIF_MBOX_BASE_ADDR &&
addr <= HIF_MBOX_END_ADDR) addr <= HIF_MBOX_END_ADDR)
addr += (HIF_MBOX_WIDTH - len); addr += (HIF_MBOX_WIDTH - len);
/* FIXME: this also looks like ugly workaround */
if (addr == HIF_MBOX0_EXT_BASE_ADDR) if (addr == HIF_MBOX0_EXT_BASE_ADDR)
addr += HIF_MBOX0_EXT_WIDTH - len; addr += HIF_MBOX0_EXT_WIDTH - len;
...@@ -152,6 +155,11 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr, ...@@ -152,6 +155,11 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
ret = sdio_memcpy_fromio(func, buf, addr, len); ret = sdio_memcpy_fromio(func, buf, addr, len);
} }
ath6kl_dbg(ATH6KL_DBG_SDIO, "%s addr 0x%x%s buf 0x%p len %d\n",
request & HIF_WRITE ? "wr" : "rd", addr,
request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len);
ath6kl_dbg_dump(ATH6KL_DBG_SDIO_DUMP, NULL, "sdio ", buf, len);
return ret; return ret;
} }
...@@ -172,7 +180,8 @@ static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio) ...@@ -172,7 +180,8 @@ static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio)
list_del(&bus_req->list); list_del(&bus_req->list);
spin_unlock_irqrestore(&ar_sdio->lock, flag); spin_unlock_irqrestore(&ar_sdio->lock, flag);
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req); ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n",
__func__, bus_req);
return bus_req; return bus_req;
} }
...@@ -182,7 +191,8 @@ static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio, ...@@ -182,7 +191,8 @@ static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio,
{ {
unsigned long flag; unsigned long flag;
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req); ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n",
__func__, bus_req);
spin_lock_irqsave(&ar_sdio->lock, flag); spin_lock_irqsave(&ar_sdio->lock, flag);
list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq); list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
...@@ -213,16 +223,6 @@ static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req, ...@@ -213,16 +223,6 @@ static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req,
/* assemble SG list */ /* assemble SG list */
for (i = 0; i < scat_req->scat_entries; i++, sg++) { for (i = 0; i < scat_req->scat_entries; i++, sg++) {
if ((unsigned long)scat_req->scat_list[i].buf & 0x3)
/*
* Some scatter engines can handle unaligned
* buffers, print this as informational only.
*/
ath6kl_dbg(ATH6KL_DBG_SCATTER,
"(%s) scatter buffer is unaligned 0x%p\n",
scat_req->req & HIF_WRITE ? "WR" : "RD",
scat_req->scat_list[i].buf);
ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n", ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n",
i, scat_req->scat_list[i].buf, i, scat_req->scat_list[i].buf,
scat_req->scat_list[i].len); scat_req->scat_list[i].len);
...@@ -447,6 +447,8 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func) ...@@ -447,6 +447,8 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func)
int status; int status;
struct ath6kl_sdio *ar_sdio; struct ath6kl_sdio *ar_sdio;
ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n");
ar_sdio = sdio_get_drvdata(func); ar_sdio = sdio_get_drvdata(func);
atomic_set(&ar_sdio->irq_handling, 1); atomic_set(&ar_sdio->irq_handling, 1);
...@@ -684,7 +686,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) ...@@ -684,7 +686,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
MAX_SCATTER_REQUESTS, virt_scat); MAX_SCATTER_REQUESTS, virt_scat);
if (!ret) { if (!ret) {
ath6kl_dbg(ATH6KL_DBG_ANY, ath6kl_dbg(ATH6KL_DBG_SCATTER,
"hif-scatter enabled: max scatter req : %d entries: %d\n", "hif-scatter enabled: max scatter req : %d entries: %d\n",
MAX_SCATTER_REQUESTS, MAX_SCATTER_REQUESTS,
MAX_SCATTER_ENTRIES_PER_REQ); MAX_SCATTER_ENTRIES_PER_REQ);
...@@ -709,7 +711,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) ...@@ -709,7 +711,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
return ret; return ret;
} }
ath6kl_dbg(ATH6KL_DBG_ANY, ath6kl_dbg(ATH6KL_DBG_SCATTER,
"Vitual scatter enabled, max_scat_req:%d, entries:%d\n", "Vitual scatter enabled, max_scat_req:%d, entries:%d\n",
ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ); ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ);
...@@ -721,6 +723,34 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) ...@@ -721,6 +723,34 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
return 0; return 0;
} }
static int ath6kl_sdio_suspend(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);
if (!(flags & MMC_PM_KEEP_POWER))
/* as host doesn't support keep power we need to bail out */
ath6kl_dbg(ATH6KL_DBG_SDIO,
"func %d doesn't support MMC_PM_KEEP_POWER\n",
func->num);
return -EINVAL;
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret) {
printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n",
ret);
return ret;
}
ath6kl_deep_sleep_enable(ar);
return 0;
}
static const struct ath6kl_hif_ops ath6kl_sdio_ops = { static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
.read_write_sync = ath6kl_sdio_read_write_sync, .read_write_sync = ath6kl_sdio_read_write_sync,
.write_async = ath6kl_sdio_write_async, .write_async = ath6kl_sdio_write_async,
...@@ -731,6 +761,7 @@ static const struct ath6kl_hif_ops ath6kl_sdio_ops = { ...@@ -731,6 +761,7 @@ static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
.enable_scatter = ath6kl_sdio_enable_scatter, .enable_scatter = ath6kl_sdio_enable_scatter,
.scat_req_rw = ath6kl_sdio_async_rw_scatter, .scat_req_rw = ath6kl_sdio_async_rw_scatter,
.cleanup_scatter = ath6kl_sdio_cleanup_scatter, .cleanup_scatter = ath6kl_sdio_cleanup_scatter,
.suspend = ath6kl_sdio_suspend,
}; };
static int ath6kl_sdio_probe(struct sdio_func *func, static int ath6kl_sdio_probe(struct sdio_func *func,
...@@ -741,10 +772,10 @@ static int ath6kl_sdio_probe(struct sdio_func *func, ...@@ -741,10 +772,10 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
struct ath6kl *ar; struct ath6kl *ar;
int count; int count;
ath6kl_dbg(ATH6KL_DBG_TRC, ath6kl_dbg(ATH6KL_DBG_SDIO,
"%s: func: 0x%X, vendor id: 0x%X, dev id: 0x%X, block size: 0x%X/0x%X\n", "new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
__func__, func->num, func->vendor, func->num, func->vendor, func->device,
func->device, func->max_blksize, func->cur_blksize); func->max_blksize, func->cur_blksize);
ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL); ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL);
if (!ar_sdio) if (!ar_sdio)
...@@ -800,10 +831,10 @@ static int ath6kl_sdio_probe(struct sdio_func *func, ...@@ -800,10 +831,10 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
ath6kl_err("Failed to enable 4-bit async irq mode %d\n", ath6kl_err("Failed to enable 4-bit async irq mode %d\n",
ret); ret);
sdio_release_host(func); sdio_release_host(func);
goto err_dma; goto err_cfg80211;
} }
ath6kl_dbg(ATH6KL_DBG_TRC, "4-bit async irq mode enabled\n"); ath6kl_dbg(ATH6KL_DBG_SDIO, "4-bit async irq mode enabled\n");
} }
/* give us some time to enable, in ms */ /* give us some time to enable, in ms */
...@@ -813,7 +844,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, ...@@ -813,7 +844,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
ret = ath6kl_sdio_power_on(ar_sdio); ret = ath6kl_sdio_power_on(ar_sdio);
if (ret) if (ret)
goto err_dma; goto err_cfg80211;
sdio_claim_host(func); sdio_claim_host(func);
...@@ -837,6 +868,8 @@ static int ath6kl_sdio_probe(struct sdio_func *func, ...@@ -837,6 +868,8 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
err_off: err_off:
ath6kl_sdio_power_off(ar_sdio); ath6kl_sdio_power_off(ar_sdio);
err_cfg80211:
ath6kl_cfg80211_deinit(ar_sdio->ar);
err_dma: err_dma:
kfree(ar_sdio->dma_buffer); kfree(ar_sdio->dma_buffer);
err_hif: err_hif:
...@@ -849,6 +882,10 @@ static void ath6kl_sdio_remove(struct sdio_func *func) ...@@ -849,6 +882,10 @@ static void ath6kl_sdio_remove(struct sdio_func *func)
{ {
struct ath6kl_sdio *ar_sdio; struct ath6kl_sdio *ar_sdio;
ath6kl_dbg(ATH6KL_DBG_SDIO,
"removed func %d vendor 0x%x device 0x%x\n",
func->num, func->vendor, func->device);
ar_sdio = sdio_get_drvdata(func); ar_sdio = sdio_get_drvdata(func);
ath6kl_stop_txrx(ar_sdio->ar); ath6kl_stop_txrx(ar_sdio->ar);
......
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
#define AR6003_BOARD_DATA_SZ 1024 #define AR6003_BOARD_DATA_SZ 1024
#define AR6003_BOARD_EXT_DATA_SZ 768 #define AR6003_BOARD_EXT_DATA_SZ 768
#define AR6004_BOARD_DATA_SZ 7168
#define AR6004_BOARD_EXT_DATA_SZ 0
#define RESET_CONTROL_ADDRESS 0x00000000 #define RESET_CONTROL_ADDRESS 0x00000000
#define RESET_CONTROL_COLD_RST 0x00000100 #define RESET_CONTROL_COLD_RST 0x00000100
#define RESET_CONTROL_MBOX_RST 0x00000004 #define RESET_CONTROL_MBOX_RST 0x00000004
...@@ -135,7 +138,8 @@ ...@@ -135,7 +138,8 @@
* between the two, and is intended to remain constant (with additions only * between the two, and is intended to remain constant (with additions only
* at the end). * at the end).
*/ */
#define ATH6KL_HI_START_ADDR 0x00540600 #define ATH6KL_AR6003_HI_START_ADDR 0x00540600
#define ATH6KL_AR6004_HI_START_ADDR 0x00400800
/* /*
* These are items that the Host may need to access * These are items that the Host may need to access
...@@ -300,6 +304,11 @@ struct host_interest { ...@@ -300,6 +304,11 @@ struct host_interest {
#define HI_OPTION_FW_MODE_BSS_STA 0x1 #define HI_OPTION_FW_MODE_BSS_STA 0x1
#define HI_OPTION_FW_MODE_AP 0x2 #define HI_OPTION_FW_MODE_AP 0x2
#define HI_OPTION_FW_SUBMODE_NONE 0x0
#define HI_OPTION_FW_SUBMODE_P2PDEV 0x1
#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2
#define HI_OPTION_FW_SUBMODE_P2PGO 0x3
#define HI_OPTION_NUM_DEV_SHIFT 0x9 #define HI_OPTION_NUM_DEV_SHIFT 0x9
#define HI_OPTION_FW_BRIDGE_SHIFT 0x04 #define HI_OPTION_FW_BRIDGE_SHIFT 0x04
...@@ -312,20 +321,44 @@ struct host_interest { ...@@ -312,20 +321,44 @@ struct host_interest {
|------------------------------------------------------------------------------| |------------------------------------------------------------------------------|
*/ */
#define HI_OPTION_FW_MODE_SHIFT 0xC #define HI_OPTION_FW_MODE_SHIFT 0xC
#define HI_OPTION_FW_SUBMODE_SHIFT 0x14
/* Convert a Target virtual address into a Target physical address */ /* Convert a Target virtual address into a Target physical address */
#define TARG_VTOP(vaddr) (vaddr & 0x001fffff) #define AR6003_VTOP(vaddr) ((vaddr) & 0x001fffff)
#define AR6004_VTOP(vaddr) (vaddr)
#define TARG_VTOP(target_type, vaddr) \
(((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \
(((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : 0))
#define AR6003_REV2_APP_START_OVERRIDE 0x944C00
#define AR6003_REV2_APP_LOAD_ADDRESS 0x543180 #define AR6003_REV2_APP_LOAD_ADDRESS 0x543180
#define AR6003_REV2_BOARD_EXT_DATA_ADDRESS 0x57E500 #define AR6003_REV2_BOARD_EXT_DATA_ADDRESS 0x57E500
#define AR6003_REV2_DATASET_PATCH_ADDRESS 0x57e884 #define AR6003_REV2_DATASET_PATCH_ADDRESS 0x57e884
#define AR6003_REV2_RAM_RESERVE_SIZE 6912 #define AR6003_REV2_RAM_RESERVE_SIZE 6912
#define AR6003_REV3_APP_START_OVERRIDE 0x945d00
#define AR6003_REV3_APP_LOAD_ADDRESS 0x545000 #define AR6003_REV3_APP_LOAD_ADDRESS 0x545000
#define AR6003_REV3_BOARD_EXT_DATA_ADDRESS 0x542330 #define AR6003_REV3_BOARD_EXT_DATA_ADDRESS 0x542330
#define AR6003_REV3_DATASET_PATCH_ADDRESS 0x57FF74 #define AR6003_REV3_DATASET_PATCH_ADDRESS 0x57FF74
#define AR6003_REV3_RAM_RESERVE_SIZE 512 #define AR6003_REV3_RAM_RESERVE_SIZE 512
#define AR6004_REV1_BOARD_DATA_ADDRESS 0x435400
#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS 0x437000
#define AR6004_REV1_RAM_RESERVE_SIZE 11264
#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500
struct ath6kl_dbglog_buf {
__le32 next;
__le32 buffer_addr;
__le32 bufsize;
__le32 length;
__le32 count;
__le32 free;
} __packed;
struct ath6kl_dbglog_hdr {
__le32 dbuf_addr;
__le32 dropped;
} __packed;
#endif #endif
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "testmode.h"
#include <net/netlink.h>
enum ath6kl_tm_attr {
__ATH6KL_TM_ATTR_INVALID = 0,
ATH6KL_TM_ATTR_CMD = 1,
ATH6KL_TM_ATTR_DATA = 2,
/* keep last */
__ATH6KL_TM_ATTR_AFTER_LAST,
ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1,
};
enum ath6kl_tm_cmd {
ATH6KL_TM_CMD_TCMD = 0,
ATH6KL_TM_CMD_RX_REPORT = 1,
};
#define ATH6KL_TM_DATA_MAX_LEN 5000
static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = {
[ATH6KL_TM_ATTR_CMD] = { .type = NLA_U32 },
[ATH6KL_TM_ATTR_DATA] = { .type = NLA_BINARY,
.len = ATH6KL_TM_DATA_MAX_LEN },
};
void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len)
{
if (down_interruptible(&ar->sem))
return;
kfree(ar->tm.rx_report);
ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL);
ar->tm.rx_report_len = buf_len;
up(&ar->sem);
wake_up(&ar->event_wq);
}
static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len,
struct sk_buff *skb)
{
int ret = 0;
long left;
if (down_interruptible(&ar->sem))
return -ERESTARTSYS;
if (!test_bit(WMI_READY, &ar->flag)) {
ret = -EIO;
goto out;
}
if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
ret = -EBUSY;
goto out;
}
if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) {
up(&ar->sem);
return -EIO;
}
left = wait_event_interruptible_timeout(ar->event_wq,
ar->tm.rx_report != NULL,
WMI_TIMEOUT);
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_failure:
ret = -ENOBUFS;
goto out;
}
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;
void *buf;
err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len,
ath6kl_tm_policy);
if (err)
return err;
if (!tb[ATH6KL_TM_ATTR_CMD])
return -EINVAL;
switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) {
case ATH6KL_TM_CMD_TCMD:
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]);
ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len);
return 0;
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;
}
}
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "core.h"
#ifdef CONFIG_NL80211_TESTMODE
void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len);
int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len);
#else
static inline void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf,
size_t buf_len)
{
}
static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
{
return 0;
}
#endif
...@@ -239,7 +239,6 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) ...@@ -239,7 +239,6 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
u16 htc_tag = ATH6KL_DATA_PKT_TAG; u16 htc_tag = ATH6KL_DATA_PKT_TAG;
u8 ac = 99 ; /* initialize to unmapped ac */ u8 ac = 99 ; /* initialize to unmapped ac */
bool chk_adhoc_ps_mapping = false, more_data = false; bool chk_adhoc_ps_mapping = false, more_data = false;
struct wmi_tx_meta_v2 meta_v2;
int ret; int ret;
ath6kl_dbg(ATH6KL_DBG_WLAN_TX, ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
...@@ -262,8 +261,6 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) ...@@ -262,8 +261,6 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
} }
if (test_bit(WMI_ENABLED, &ar->flag)) { if (test_bit(WMI_ENABLED, &ar->flag)) {
memset(&meta_v2, 0, sizeof(meta_v2));
if (skb_headroom(skb) < dev->needed_headroom) { if (skb_headroom(skb) < dev->needed_headroom) {
WARN_ON(1); WARN_ON(1);
goto fail_tx; goto fail_tx;
...@@ -320,12 +317,31 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) ...@@ -320,12 +317,31 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
spin_unlock_bh(&ar->lock); spin_unlock_bh(&ar->lock);
if (!IS_ALIGNED((unsigned long) skb->data - HTC_HDR_LENGTH, 4) &&
skb_cloned(skb)) {
/*
* We will touch (move the buffer data to align it. Since the
* skb buffer is cloned and not only the header is changed, we
* have to copy it to allow the changes. Since we are copying
* the data here, we may as well align it by reserving suitable
* headroom to avoid the memmove in ath6kl_htc_tx_buf_align().
*/
struct sk_buff *nskb;
nskb = skb_copy_expand(skb, HTC_HDR_LENGTH, 0, GFP_ATOMIC);
if (nskb == NULL)
goto fail_tx;
kfree_skb(skb);
skb = nskb;
}
cookie->skb = skb; cookie->skb = skb;
cookie->map_no = map_no; cookie->map_no = map_no;
set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
eid, htc_tag); eid, htc_tag);
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ",
skb->data, skb->len);
/* /*
* HTC interface is asynchronous, if this fails, cleanup will * HTC interface is asynchronous, if this fails, cleanup will
...@@ -689,6 +705,8 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) ...@@ -689,6 +705,8 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint)
break; break;
packet = (struct htc_packet *) skb->head; packet = (struct htc_packet *) skb->head;
if (!IS_ALIGNED((unsigned long) skb->data, 4))
skb->data = PTR_ALIGN(skb->data - 4, 4);
set_htc_rxpkt_info(packet, skb, skb->data, set_htc_rxpkt_info(packet, skb, skb->data,
ATH6KL_BUFFER_SIZE, endpoint); ATH6KL_BUFFER_SIZE, endpoint);
list_add_tail(&packet->list, &queue); list_add_tail(&packet->list, &queue);
...@@ -709,6 +727,8 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) ...@@ -709,6 +727,8 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count)
return; return;
packet = (struct htc_packet *) skb->head; packet = (struct htc_packet *) skb->head;
if (!IS_ALIGNED((unsigned long) skb->data, 4))
skb->data = PTR_ALIGN(skb->data - 4, 4);
set_htc_rxpkt_info(packet, skb, skb->data, set_htc_rxpkt_info(packet, skb, skb->data,
ATH6KL_AMSDU_BUFFER_SIZE, 0); ATH6KL_AMSDU_BUFFER_SIZE, 0);
spin_lock_bh(&ar->lock); spin_lock_bh(&ar->lock);
...@@ -812,7 +832,7 @@ static void aggr_slice_amsdu(struct aggr_info *p_aggr, ...@@ -812,7 +832,7 @@ static void aggr_slice_amsdu(struct aggr_info *p_aggr,
/* Add the length of A-MSDU subframe padding bytes - /* Add the length of A-MSDU subframe padding bytes -
* Round to nearest word. * Round to nearest word.
*/ */
frame_8023_len = ALIGN(frame_8023_len + 3, 3); frame_8023_len = ALIGN(frame_8023_len, 4);
framep += frame_8023_len; framep += frame_8023_len;
amsdu_len -= frame_8023_len; amsdu_len -= frame_8023_len;
...@@ -1044,12 +1064,13 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) ...@@ -1044,12 +1064,13 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
ar->net_stats.rx_packets++; ar->net_stats.rx_packets++;
ar->net_stats.rx_bytes += packet->act_len; ar->net_stats.rx_bytes += packet->act_len;
spin_unlock_bh(&ar->lock);
skb_put(skb, packet->act_len + HTC_HDR_LENGTH); skb_put(skb, packet->act_len + HTC_HDR_LENGTH);
skb_pull(skb, HTC_HDR_LENGTH); skb_pull(skb, HTC_HDR_LENGTH);
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ",
skb->data, skb->len);
spin_unlock_bh(&ar->lock);
skb->dev = ar->net_dev; skb->dev = ar->net_dev;
...@@ -1065,8 +1086,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) ...@@ -1065,8 +1086,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
return; return;
} }
min_hdr_len = sizeof(struct ethhdr); min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) +
min_hdr_len += sizeof(struct wmi_data_hdr) +
sizeof(struct ath6kl_llc_snap_hdr); sizeof(struct ath6kl_llc_snap_hdr);
dhdr = (struct wmi_data_hdr *) skb->data; dhdr = (struct wmi_data_hdr *) skb->data;
...@@ -1163,8 +1183,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) ...@@ -1163,8 +1183,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
seq_no = wmi_data_hdr_get_seqno(dhdr); seq_no = wmi_data_hdr_get_seqno(dhdr);
meta_type = wmi_data_hdr_get_meta(dhdr); meta_type = wmi_data_hdr_get_meta(dhdr);
dot11_hdr = wmi_data_hdr_get_dot11(dhdr); dot11_hdr = wmi_data_hdr_get_dot11(dhdr);
skb_pull(skb, sizeof(struct wmi_data_hdr));
ath6kl_wmi_data_hdr_remove(ar->wmi, skb);
switch (meta_type) { switch (meta_type) {
case WMI_META_VERSION_1: case WMI_META_VERSION_1:
...@@ -1231,8 +1250,14 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) ...@@ -1231,8 +1250,14 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
ath6kl_data_tx(skb1, ar->net_dev); ath6kl_data_tx(skb1, ar->net_dev);
} }
if (!aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no, datap = (struct ethhdr *) skb->data;
if (is_unicast_ether_addr(datap->h_dest) &&
aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no,
is_amsdu, skb)) is_amsdu, skb))
/* aggregation code will handle the skb */
return;
ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb); ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb);
} }
...@@ -1250,10 +1275,6 @@ static void aggr_timeout(unsigned long arg) ...@@ -1250,10 +1275,6 @@ static void aggr_timeout(unsigned long arg)
if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress) if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress)
continue; continue;
/*
* FIXME: these timeouts happen quite fruently, something
* line once within 60 seconds. Investigate why.
*/
stats->num_timeouts++; stats->num_timeouts++;
ath6kl_dbg(ATH6KL_DBG_AGGR, ath6kl_dbg(ATH6KL_DBG_AGGR,
"aggr timeout (st %d end %d)\n", "aggr timeout (st %d end %d)\n",
......
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
#include <linux/ip.h> #include <linux/ip.h>
#include "core.h" #include "core.h"
#include "debug.h" #include "debug.h"
#include "testmode.h"
#include "../regd.h"
#include "../regd_common.h"
static int ath6kl_wmi_sync_point(struct wmi *wmi); static int ath6kl_wmi_sync_point(struct wmi *wmi);
...@@ -167,9 +170,11 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, ...@@ -167,9 +170,11 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
if (WARN_ON(skb == NULL)) if (WARN_ON(skb == NULL))
return -EINVAL; return -EINVAL;
if (tx_meta_info) {
ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info); ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info);
if (ret) if (ret)
return ret; return ret;
}
skb_push(skb, sizeof(struct wmi_data_hdr)); skb_push(skb, sizeof(struct wmi_data_hdr));
...@@ -376,35 +381,6 @@ int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb) ...@@ -376,35 +381,6 @@ int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb)
return 0; return 0;
} }
int ath6kl_wmi_data_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
{
if (WARN_ON(skb == NULL))
return -EINVAL;
skb_pull(skb, sizeof(struct wmi_data_hdr));
return 0;
}
static void ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(struct sk_buff *skb,
u8 *datap)
{
struct wmi_bss_info_hdr2 bih2;
struct wmi_bss_info_hdr *bih;
memcpy(&bih2, datap, sizeof(struct wmi_bss_info_hdr2));
skb_push(skb, 4);
bih = (struct wmi_bss_info_hdr *) skb->data;
bih->ch = bih2.ch;
bih->frame_type = bih2.frame_type;
bih->snr = bih2.snr;
bih->rssi = a_cpu_to_sle16(bih2.snr - 95);
bih->ie_mask = cpu_to_le32(le16_to_cpu(bih2.ie_mask));
memcpy(bih->bssid, bih2.bssid, ETH_ALEN);
}
static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
{ {
struct tx_complete_msg_v1 *msg_v1; struct tx_complete_msg_v1 *msg_v1;
...@@ -433,6 +409,201 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) ...@@ -433,6 +409,201 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
return 0; return 0;
} }
static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap,
int len)
{
struct wmi_remain_on_chnl_event *ev;
u32 freq;
u32 dur;
struct ieee80211_channel *chan;
struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(*ev))
return -EINVAL;
ev = (struct wmi_remain_on_chnl_event *) datap;
freq = le32_to_cpu(ev->freq);
dur = le32_to_cpu(ev->duration);
ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n",
freq, dur);
chan = ieee80211_get_channel(ar->wdev->wiphy, freq);
if (!chan) {
ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: Unknown channel "
"(freq=%u)\n", freq);
return -EINVAL;
}
cfg80211_ready_on_channel(ar->net_dev, 1, chan, NL80211_CHAN_NO_HT,
dur, GFP_ATOMIC);
return 0;
}
static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi,
u8 *datap, int len)
{
struct wmi_cancel_remain_on_chnl_event *ev;
u32 freq;
u32 dur;
struct ieee80211_channel *chan;
struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(*ev))
return -EINVAL;
ev = (struct wmi_cancel_remain_on_chnl_event *) datap;
freq = le32_to_cpu(ev->freq);
dur = le32_to_cpu(ev->duration);
ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u "
"status=%u\n", freq, dur, ev->status);
chan = ieee80211_get_channel(ar->wdev->wiphy, freq);
if (!chan) {
ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: Unknown "
"channel (freq=%u)\n", freq);
return -EINVAL;
}
cfg80211_remain_on_channel_expired(ar->net_dev, 1, chan,
NL80211_CHAN_NO_HT, GFP_ATOMIC);
return 0;
}
static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len)
{
struct wmi_tx_status_event *ev;
u32 id;
struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(*ev))
return -EINVAL;
ev = (struct wmi_tx_status_event *) datap;
id = le32_to_cpu(ev->id);
ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n",
id, ev->ack_status);
if (wmi->last_mgmt_tx_frame) {
cfg80211_mgmt_tx_status(ar->net_dev, id,
wmi->last_mgmt_tx_frame,
wmi->last_mgmt_tx_frame_len,
!!ev->ack_status, GFP_ATOMIC);
kfree(wmi->last_mgmt_tx_frame);
wmi->last_mgmt_tx_frame = NULL;
wmi->last_mgmt_tx_frame_len = 0;
}
return 0;
}
static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len)
{
struct wmi_p2p_rx_probe_req_event *ev;
u32 freq;
u16 dlen;
struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(*ev))
return -EINVAL;
ev = (struct wmi_p2p_rx_probe_req_event *) datap;
freq = le32_to_cpu(ev->freq);
dlen = le16_to_cpu(ev->len);
if (datap + len < ev->data + dlen) {
ath6kl_err("invalid wmi_p2p_rx_probe_req_event: "
"len=%d dlen=%u\n", len, dlen);
return -EINVAL;
}
ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u freq=%u "
"probe_req_report=%d\n",
dlen, freq, ar->probe_req_report);
if (ar->probe_req_report || ar->nw_type == AP_NETWORK)
cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC);
return 0;
}
static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len)
{
struct wmi_p2p_capabilities_event *ev;
u16 dlen;
if (len < sizeof(*ev))
return -EINVAL;
ev = (struct wmi_p2p_capabilities_event *) datap;
dlen = le16_to_cpu(ev->len);
ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_capab: len=%u\n", dlen);
return 0;
}
static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len)
{
struct wmi_rx_action_event *ev;
u32 freq;
u16 dlen;
struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(*ev))
return -EINVAL;
ev = (struct wmi_rx_action_event *) datap;
freq = le32_to_cpu(ev->freq);
dlen = le16_to_cpu(ev->len);
if (datap + len < ev->data + dlen) {
ath6kl_err("invalid wmi_rx_action_event: "
"len=%d dlen=%u\n", len, dlen);
return -EINVAL;
}
ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq);
cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC);
return 0;
}
static int ath6kl_wmi_p2p_info_event_rx(u8 *datap, int len)
{
struct wmi_p2p_info_event *ev;
u32 flags;
u16 dlen;
if (len < sizeof(*ev))
return -EINVAL;
ev = (struct wmi_p2p_info_event *) datap;
flags = le32_to_cpu(ev->info_req_flags);
dlen = le16_to_cpu(ev->len);
ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: flags=%x len=%d\n", flags, dlen);
if (flags & P2P_FLAG_CAPABILITIES_REQ) {
struct wmi_p2p_capabilities *cap;
if (dlen < sizeof(*cap))
return -EINVAL;
cap = (struct wmi_p2p_capabilities *) ev->data;
ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: GO Power Save = %d\n",
cap->go_power_save);
}
if (flags & P2P_FLAG_MACADDR_REQ) {
struct wmi_p2p_macaddr *mac;
if (dlen < sizeof(*mac))
return -EINVAL;
mac = (struct wmi_p2p_macaddr *) ev->data;
ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: MAC Address = %pM\n",
mac->mac_addr);
}
if (flags & P2P_FLAG_HMODEL_REQ) {
struct wmi_p2p_hmodel *mod;
if (dlen < sizeof(*mod))
return -EINVAL;
mod = (struct wmi_p2p_hmodel *) ev->data;
ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: P2P Model = %d (%s)\n",
mod->p2p_model,
mod->p2p_model ? "host" : "firmware");
}
return 0;
}
static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size) static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size)
{ {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -478,18 +649,84 @@ static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len) ...@@ -478,18 +649,84 @@ static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len)
return 0; return 0;
} }
/*
* Mechanism to modify the roaming behavior in the firmware. The lower rssi
* at which the station has to roam can be passed with
* WMI_SET_LRSSI_SCAN_PARAMS. Subtract 96 from RSSI to get the signal level
* in dBm.
*/
int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi)
{
struct sk_buff *skb;
struct roam_ctrl_cmd *cmd;
skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
if (!skb)
return -ENOMEM;
cmd = (struct roam_ctrl_cmd *) skb->data;
cmd->info.params.lrssi_scan_period = cpu_to_le16(DEF_LRSSI_SCAN_PERIOD);
cmd->info.params.lrssi_scan_threshold = a_cpu_to_sle16(lrssi +
DEF_SCAN_FOR_ROAM_INTVL);
cmd->info.params.lrssi_roam_threshold = a_cpu_to_sle16(lrssi);
cmd->info.params.roam_rssi_floor = DEF_LRSSI_ROAM_FLOOR;
cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS;
ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG);
return 0;
}
static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len) static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
{ {
struct wmi_connect_event *ev; struct wmi_connect_event *ev;
u8 *pie, *peie; u8 *pie, *peie;
struct ath6kl *ar = wmi->parent_dev;
if (len < sizeof(struct wmi_connect_event)) if (len < sizeof(struct wmi_connect_event))
return -EINVAL; return -EINVAL;
ev = (struct wmi_connect_event *) datap; ev = (struct wmi_connect_event *) datap;
ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM\n", if (ar->nw_type == AP_NETWORK) {
__func__, ev->ch, ev->bssid); /* AP mode start/STA connected event */
struct net_device *dev = ar->net_dev;
if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) {
ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM "
"(AP started)\n",
__func__, le16_to_cpu(ev->u.ap_bss.ch),
ev->u.ap_bss.bssid);
ath6kl_connect_ap_mode_bss(
ar, le16_to_cpu(ev->u.ap_bss.ch));
} else {
ath6kl_dbg(ATH6KL_DBG_WMI, "%s: aid %u mac_addr %pM "
"auth=%u keymgmt=%u cipher=%u apsd_info=%u "
"(STA connected)\n",
__func__, ev->u.ap_sta.aid,
ev->u.ap_sta.mac_addr,
ev->u.ap_sta.auth,
ev->u.ap_sta.keymgmt,
le16_to_cpu(ev->u.ap_sta.cipher),
ev->u.ap_sta.apsd_info);
ath6kl_connect_ap_mode_sta(
ar, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr,
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);
}
return 0;
}
/* STA/IBSS mode connection event */
ath6kl_dbg(ATH6KL_DBG_WMI,
"wmi event connect freq %d bssid %pM listen_intvl %d beacon_intvl %d type %d\n",
le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid,
le16_to_cpu(ev->u.sta.listen_intvl),
le16_to_cpu(ev->u.sta.beacon_intvl),
le32_to_cpu(ev->u.sta.nw_type));
/* Start of assoc rsp IEs */ /* Start of assoc rsp IEs */
pie = ev->assoc_info + ev->beacon_ie_len + pie = ev->assoc_info + ev->beacon_ie_len +
...@@ -518,16 +755,92 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len) ...@@ -518,16 +755,92 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
pie += pie[1] + 2; pie += pie[1] + 2;
} }
ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->ch), ev->bssid, ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->u.sta.ch),
le16_to_cpu(ev->listen_intvl), ev->u.sta.bssid,
le16_to_cpu(ev->beacon_intvl), le16_to_cpu(ev->u.sta.listen_intvl),
le32_to_cpu(ev->nw_type), le16_to_cpu(ev->u.sta.beacon_intvl),
le32_to_cpu(ev->u.sta.nw_type),
ev->beacon_ie_len, ev->assoc_req_len, ev->beacon_ie_len, ev->assoc_req_len,
ev->assoc_resp_len, ev->assoc_info); ev->assoc_resp_len, ev->assoc_info);
return 0; return 0;
} }
static struct country_code_to_enum_rd *
ath6kl_regd_find_country(u16 countryCode)
{
int i;
for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
if (allCountries[i].countryCode == countryCode)
return &allCountries[i];
}
return NULL;
}
static struct reg_dmn_pair_mapping *
ath6kl_get_regpair(u16 regdmn)
{
int i;
if (regdmn == NO_ENUMRD)
return NULL;
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
if (regDomainPairs[i].regDmnEnum == regdmn)
return &regDomainPairs[i];
}
return NULL;
}
static struct country_code_to_enum_rd *
ath6kl_regd_find_country_by_rd(u16 regdmn)
{
int i;
for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
if (allCountries[i].regDmnEnum == regdmn)
return &allCountries[i];
}
return NULL;
}
static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
{
struct ath6kl_wmi_regdomain *ev;
struct country_code_to_enum_rd *country = NULL;
struct reg_dmn_pair_mapping *regpair = NULL;
char alpha2[2];
u32 reg_code;
ev = (struct ath6kl_wmi_regdomain *) datap;
reg_code = le32_to_cpu(ev->reg_code);
if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG)
country = ath6kl_regd_find_country((u16) reg_code);
else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
regpair = ath6kl_get_regpair((u16) reg_code);
country = ath6kl_regd_find_country_by_rd((u16) reg_code);
ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n",
regpair->regDmnEnum);
}
if (country) {
alpha2[0] = country->isoName[0];
alpha2[1] = country->isoName[1];
regulatory_hint(wmi->parent_dev->wdev->wiphy, alpha2);
ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n",
alpha2[0], alpha2[1]);
}
}
static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len) static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len)
{ {
struct wmi_disconnect_event *ev; struct wmi_disconnect_event *ev;
...@@ -538,6 +851,11 @@ static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len) ...@@ -538,6 +851,11 @@ static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len)
ev = (struct wmi_disconnect_event *) datap; ev = (struct wmi_disconnect_event *) datap;
ath6kl_dbg(ATH6KL_DBG_WMI,
"wmi event disconnect proto_reason %d bssid %pM wmi_reason %d assoc_resp_len %d\n",
le16_to_cpu(ev->proto_reason_status), ev->bssid,
ev->disconn_reason, ev->assoc_resp_len);
wmi->is_wmm_enabled = false; wmi->is_wmm_enabled = false;
wmi->pair_crypto_type = NONE_CRYPT; wmi->pair_crypto_type = NONE_CRYPT;
wmi->grp_crypto_type = NONE_CRYPT; wmi->grp_crypto_type = NONE_CRYPT;
...@@ -582,315 +900,92 @@ static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len) ...@@ -582,315 +900,92 @@ static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len)
return 0; return 0;
} }
static int ath6kl_wlan_parse_beacon(u8 *buf, int frame_len, static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
struct ath6kl_common_ie *cie)
{ {
u8 *frm, *efrm; struct wmi_bss_info_hdr2 *bih;
u8 elemid_ssid = false; u8 *buf;
struct ieee80211_channel *channel;
frm = buf; struct ath6kl *ar = wmi->parent_dev;
efrm = (u8 *) (frm + frame_len); struct ieee80211_mgmt *mgmt;
struct cfg80211_bss *bss;
/* if (len <= sizeof(struct wmi_bss_info_hdr2))
* beacon/probe response frame format
* [8] time stamp
* [2] beacon interval
* [2] capability information
* [tlv] ssid
* [tlv] supported rates
* [tlv] country information
* [tlv] parameter set (FH/DS)
* [tlv] erp information
* [tlv] extended supported rates
* [tlv] WMM
* [tlv] WPA or RSN
* [tlv] Atheros Advanced Capabilities
*/
if ((efrm - frm) < 12)
return -EINVAL; return -EINVAL;
memset(cie, 0, sizeof(*cie)); bih = (struct wmi_bss_info_hdr2 *) datap;
buf = datap + sizeof(struct wmi_bss_info_hdr2);
cie->ie_tstamp = frm; len -= sizeof(struct wmi_bss_info_hdr2);
frm += 8;
cie->ie_beaconInt = *(u16 *) frm;
frm += 2;
cie->ie_capInfo = *(u16 *) frm;
frm += 2;
cie->ie_chan = 0;
while (frm < efrm) { ath6kl_dbg(ATH6KL_DBG_WMI,
switch (*frm) { "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" "
case WLAN_EID_SSID: "frame_type=%d\n",
if (!elemid_ssid) { bih->ch, bih->snr, bih->snr - 95, bih->bssid,
cie->ie_ssid = frm; bih->frame_type);
elemid_ssid = true;
}
break;
case WLAN_EID_SUPP_RATES:
cie->ie_rates = frm;
break;
case WLAN_EID_COUNTRY:
cie->ie_country = frm;
break;
case WLAN_EID_FH_PARAMS:
break;
case WLAN_EID_DS_PARAMS:
cie->ie_chan = frm[2];
break;
case WLAN_EID_TIM:
cie->ie_tim = frm;
break;
case WLAN_EID_IBSS_PARAMS:
break;
case WLAN_EID_EXT_SUPP_RATES:
cie->ie_xrates = frm;
break;
case WLAN_EID_ERP_INFO:
if (frm[1] != 1)
return -EINVAL;
cie->ie_erp = frm[2];
break;
case WLAN_EID_RSN:
cie->ie_rsn = frm;
break;
case WLAN_EID_HT_CAPABILITY:
cie->ie_htcap = frm;
break;
case WLAN_EID_HT_INFORMATION:
cie->ie_htop = frm;
break;
case WLAN_EID_VENDOR_SPECIFIC:
if (frm[1] > 3 && frm[2] == 0x00 && frm[3] == 0x50 &&
frm[4] == 0xf2) {
/* OUT Type (00:50:F2) */
if (frm[5] == WPA_OUI_TYPE) { if (bih->frame_type != BEACON_FTYPE &&
/* WPA OUT */ bih->frame_type != PROBERESP_FTYPE)
cie->ie_wpa = frm; return 0; /* Only update BSS table for now */
} else if (frm[5] == WMM_OUI_TYPE) {
/* WMM OUT */
cie->ie_wmm = frm;
} else if (frm[5] == WSC_OUT_TYPE) {
/* WSC OUT */
cie->ie_wsc = frm;
}
} else if (frm[1] > 3 && frm[2] == 0x00 if (bih->frame_type == BEACON_FTYPE &&
&& frm[3] == 0x03 && frm[4] == 0x7f test_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag)) {
&& frm[5] == ATH_OUI_TYPE) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
/* Atheros OUI (00:03:7f) */ ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
cie->ie_ath = frm;
}
break;
default:
break;
}
frm += frm[1] + 2;
} }
if ((cie->ie_rates == NULL) channel = ieee80211_get_channel(ar->wdev->wiphy, le16_to_cpu(bih->ch));
|| (cie->ie_rates[1] > ATH6KL_RATE_MAXSIZE)) if (channel == NULL)
return -EINVAL; return -EINVAL;
if ((cie->ie_ssid == NULL) if (len < 8 + 2 + 2)
|| (cie->ie_ssid[1] > IEEE80211_MAX_SSID_LEN))
return -EINVAL; return -EINVAL;
return 0; if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &ar->flag) &&
} memcmp(bih->bssid, ar->bssid, ETH_ALEN) == 0) {
const u8 *tim;
static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len) tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2,
{ len - 8 - 2 - 2);
struct bss *bss = NULL; if (tim && tim[1] >= 2) {
struct wmi_bss_info_hdr *bih; ar->assoc_bss_dtim_period = tim[3];
u8 cached_ssid_len = 0; set_bit(DTIM_PERIOD_AVAIL, &ar->flag);
u8 cached_ssid[IEEE80211_MAX_SSID_LEN] = { 0 };
u8 beacon_ssid_len = 0;
u8 *buf, *ie_ssid;
u8 *ni_buf;
int buf_len;
int ret;
if (len <= sizeof(struct wmi_bss_info_hdr))
return -EINVAL;
bih = (struct wmi_bss_info_hdr *) datap;
bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid);
if (a_sle16_to_cpu(bih->rssi) > 0) {
if (bss == NULL)
return 0;
else
bih->rssi = a_cpu_to_sle16(bss->ni_rssi);
} }
buf = datap + sizeof(struct wmi_bss_info_hdr);
len -= sizeof(struct wmi_bss_info_hdr);
ath6kl_dbg(ATH6KL_DBG_WMI,
"bss info evt - ch %u, rssi %02x, bssid \"%pM\"\n",
bih->ch, a_sle16_to_cpu(bih->rssi), bih->bssid);
if (bss != NULL) {
/*
* Free up the node. We are about to allocate a new node.
* In case of hidden AP, beacon will not have ssid,
* but a directed probe response will have it,
* so cache the probe-resp-ssid if already present.
*/
if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE)) {
ie_ssid = bss->ni_cie.ie_ssid;
if (ie_ssid && (ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) &&
(ie_ssid[2] != 0)) {
cached_ssid_len = ie_ssid[1];
memcpy(cached_ssid, ie_ssid + 2,
cached_ssid_len);
}
}
/*
* Use the current average rssi of associated AP base on
* assumption
* 1. Most os with GUI will update RSSI by
* ath6kl_wmi_get_stats_cmd() periodically.
* 2. ath6kl_wmi_get_stats_cmd(..) will be called when calling
* ath6kl_wmi_startscan_cmd(...)
* The average value of RSSI give end-user better feeling for
* instance value of scan result. It also sync up RSSI info
* in GUI between scan result and RSSI signal icon.
*/
if (memcmp(wmi->parent_dev->bssid, bih->bssid, ETH_ALEN) == 0) {
bih->rssi = a_cpu_to_sle16(bss->ni_rssi);
bih->snr = bss->ni_snr;
}
wlan_node_reclaim(&wmi->parent_dev->scan_table, bss);
} }
/* /*
* beacon/probe response frame format * In theory, use of cfg80211_inform_bss() would be more natural here
* [8] time stamp * since we do not have the full frame. However, at least for now,
* [2] beacon interval * cfg80211 can only distinguish Beacon and Probe Response frames from
* [2] capability information * each other when using cfg80211_inform_bss_frame(), so let's build a
* [tlv] ssid * fake IEEE 802.11 header to be able to take benefit of this.
*/
beacon_ssid_len = buf[SSID_IE_LEN_INDEX];
/*
* If ssid is cached for this hidden AP, then change
* buffer len accordingly.
*/ */
if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) && mgmt = kmalloc(24 + len, GFP_ATOMIC);
(cached_ssid_len != 0) && if (mgmt == NULL)
(beacon_ssid_len == 0 || (cached_ssid_len > beacon_ssid_len &&
buf[SSID_IE_LEN_INDEX + 1] == 0))) {
len += (cached_ssid_len - beacon_ssid_len);
}
bss = wlan_node_alloc(len);
if (!bss)
return -ENOMEM;
bss->ni_snr = bih->snr;
bss->ni_rssi = a_sle16_to_cpu(bih->rssi);
if (WARN_ON(!bss->ni_buf))
return -EINVAL; return -EINVAL;
/* if (bih->frame_type == BEACON_FTYPE) {
* In case of hidden AP, beacon will not have ssid, mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
* but a directed probe response will have it, IEEE80211_STYPE_BEACON);
* so place the cached-ssid(probe-resp) in the bss info. memset(mgmt->da, 0xff, ETH_ALEN);
*/ } else {
if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) && struct net_device *dev = ar->net_dev;
(cached_ssid_len != 0) &&
(beacon_ssid_len == 0 || (beacon_ssid_len &&
buf[SSID_IE_LEN_INDEX + 1] == 0))) {
ni_buf = bss->ni_buf;
buf_len = len;
/*
* Copy the first 14 bytes:
* time-stamp(8), beacon-interval(2),
* cap-info(2), ssid-id(1), ssid-len(1).
*/
memcpy(ni_buf, buf, SSID_IE_LEN_INDEX + 1);
ni_buf[SSID_IE_LEN_INDEX] = cached_ssid_len;
ni_buf += (SSID_IE_LEN_INDEX + 1);
buf += (SSID_IE_LEN_INDEX + 1);
buf_len -= (SSID_IE_LEN_INDEX + 1);
memcpy(ni_buf, cached_ssid, cached_ssid_len);
ni_buf += cached_ssid_len;
buf += beacon_ssid_len;
buf_len -= beacon_ssid_len;
if (cached_ssid_len > beacon_ssid_len)
buf_len -= (cached_ssid_len - beacon_ssid_len);
memcpy(ni_buf, buf, buf_len);
} else
memcpy(bss->ni_buf, buf, len);
bss->ni_framelen = len;
ret = ath6kl_wlan_parse_beacon(bss->ni_buf, len, &bss->ni_cie); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
if (ret) { IEEE80211_STYPE_PROBE_RESP);
wlan_node_free(bss); memcpy(mgmt->da, dev->dev_addr, ETH_ALEN);
return -EINVAL;
} }
mgmt->duration = cpu_to_le16(0);
memcpy(mgmt->sa, bih->bssid, ETH_ALEN);
memcpy(mgmt->bssid, bih->bssid, ETH_ALEN);
mgmt->seq_ctrl = cpu_to_le16(0);
/* memcpy(&mgmt->u.beacon, buf, len);
* Update the frequency in ie_chan, overwriting of channel number
* which is done in ath6kl_wlan_parse_beacon
*/
bss->ni_cie.ie_chan = le16_to_cpu(bih->ch);
wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid);
return 0;
}
static int ath6kl_wmi_opt_frame_event_rx(struct wmi *wmi, u8 *datap, int len)
{
struct bss *bss;
struct wmi_opt_rx_info_hdr *bih;
u8 *buf;
if (len <= sizeof(struct wmi_opt_rx_info_hdr))
return -EINVAL;
bih = (struct wmi_opt_rx_info_hdr *) datap; bss = cfg80211_inform_bss_frame(ar->wdev->wiphy, channel, mgmt,
buf = datap + sizeof(struct wmi_opt_rx_info_hdr); 24 + len, (bih->snr - 95) * 100,
len -= sizeof(struct wmi_opt_rx_info_hdr); GFP_ATOMIC);
kfree(mgmt);
ath6kl_dbg(ATH6KL_DBG_WMI, "opt frame event %2.2x:%2.2x\n", if (bss == NULL)
bih->bssid[4], bih->bssid[5]);
bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid);
if (bss != NULL) {
/* Free up the node. We are about to allocate a new node. */
wlan_node_reclaim(&wmi->parent_dev->scan_table, bss);
}
bss = wlan_node_alloc(len);
if (!bss)
return -ENOMEM; return -ENOMEM;
cfg80211_put_bss(bss);
bss->ni_snr = bih->snr;
bss->ni_cie.ie_chan = le16_to_cpu(bih->ch);
if (WARN_ON(!bss->ni_buf))
return -EINVAL;
memcpy(bss->ni_buf, buf, len);
wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid);
return 0; return 0;
} }
...@@ -949,6 +1044,13 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len) ...@@ -949,6 +1044,13 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len)
return 0; return 0;
} }
static int ath6kl_wmi_tcmd_test_report_rx(struct wmi *wmi, u8 *datap, int len)
{
ath6kl_tm_rx_report_event(wmi->parent_dev, datap, len);
return 0;
}
static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len) static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len)
{ {
if (len < sizeof(struct wmi_fix_rates_reply)) if (len < sizeof(struct wmi_fix_rates_reply))
...@@ -998,15 +1100,41 @@ static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len) ...@@ -998,15 +1100,41 @@ static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len)
ev = (struct wmi_scan_complete_event *) datap; ev = (struct wmi_scan_complete_event *) datap;
if (a_sle32_to_cpu(ev->status) == 0)
wlan_refresh_inactive_nodes(wmi->parent_dev);
ath6kl_scan_complete_evt(wmi->parent_dev, a_sle32_to_cpu(ev->status)); ath6kl_scan_complete_evt(wmi->parent_dev, a_sle32_to_cpu(ev->status));
wmi->is_probe_ssid = false; wmi->is_probe_ssid = false;
return 0; return 0;
} }
static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap,
int len)
{
struct wmi_neighbor_report_event *ev;
u8 i;
if (len < sizeof(*ev))
return -EINVAL;
ev = (struct wmi_neighbor_report_event *) datap;
if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info)
> len) {
ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event "
"(num=%d len=%d)\n", ev->num_neighbors, len);
return -EINVAL;
}
for (i = 0; i < ev->num_neighbors; i++) {
ath6kl_dbg(ATH6KL_DBG_WMI, "neighbor %d/%d - %pM 0x%x\n",
i + 1, ev->num_neighbors, ev->neighbor[i].bssid,
ev->neighbor[i].bss_flags);
cfg80211_pmksa_candidate_notify(wmi->parent_dev->net_dev, i,
ev->neighbor[i].bssid,
!!(ev->neighbor[i].bss_flags &
WMI_PREAUTH_CAPABLE_BSS),
GFP_ATOMIC);
}
return 0;
}
/* /*
* Target is reporting a programming error. This is for * Target is reporting a programming error. This is for
* developer aid only. Target only checks a few common violations * developer aid only. Target only checks a few common violations
...@@ -1410,6 +1538,11 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb, ...@@ -1410,6 +1538,11 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
if (WARN_ON(skb == NULL)) if (WARN_ON(skb == NULL))
return -EINVAL; return -EINVAL;
ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n",
cmd_id, skb->len, sync_flag);
ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi tx ",
skb->data, skb->len);
if (sync_flag >= END_WMIFLAG) { if (sync_flag >= END_WMIFLAG) {
dev_kfree_skb(skb); dev_kfree_skb(skb);
return -EINVAL; return -EINVAL;
...@@ -1468,6 +1601,13 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type, ...@@ -1468,6 +1601,13 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type,
struct wmi_connect_cmd *cc; struct wmi_connect_cmd *cc;
int ret; int ret;
ath6kl_dbg(ATH6KL_DBG_WMI,
"wmi connect bssid %pM freq %d flags 0x%x ssid_len %d "
"type %d dot11_auth %d auth %d pairwise %d group %d\n",
bssid, channel, ctrl_flags, ssid_len, nw_type,
dot11_auth_mode, auth_mode, pairwise_crypto, group_crypto);
ath6kl_dbg_dump(ATH6KL_DBG_WMI, NULL, "ssid ", ssid, ssid_len);
wmi->traffic_class = 100; wmi->traffic_class = 100;
if ((pairwise_crypto == NONE_CRYPT) && (group_crypto != NONE_CRYPT)) if ((pairwise_crypto == NONE_CRYPT) && (group_crypto != NONE_CRYPT))
...@@ -1513,6 +1653,9 @@ int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel) ...@@ -1513,6 +1653,9 @@ int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel)
struct wmi_reconnect_cmd *cc; struct wmi_reconnect_cmd *cc;
int ret; int ret;
ath6kl_dbg(ATH6KL_DBG_WMI, "wmi reconnect bssid %pM freq %d\n",
bssid, channel);
wmi->traffic_class = 100; wmi->traffic_class = 100;
skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_reconnect_cmd)); skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_reconnect_cmd));
...@@ -1535,6 +1678,8 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi) ...@@ -1535,6 +1678,8 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi)
{ {
int ret; int ret;
ath6kl_dbg(ATH6KL_DBG_WMI, "wmi disconnect\n");
wmi->traffic_class = 100; wmi->traffic_class = 100;
/* Disconnect command does not need to do a SYNC before. */ /* Disconnect command does not need to do a SYNC before. */
...@@ -1551,7 +1696,7 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type, ...@@ -1551,7 +1696,7 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
struct sk_buff *skb; struct sk_buff *skb;
struct wmi_start_scan_cmd *sc; struct wmi_start_scan_cmd *sc;
s8 size; s8 size;
int ret; int i, ret;
size = sizeof(struct wmi_start_scan_cmd); size = sizeof(struct wmi_start_scan_cmd);
...@@ -1576,8 +1721,8 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type, ...@@ -1576,8 +1721,8 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
sc->force_scan_intvl = cpu_to_le32(force_scan_interval); sc->force_scan_intvl = cpu_to_le32(force_scan_interval);
sc->num_ch = num_chan; sc->num_ch = num_chan;
if (num_chan) for (i = 0; i < num_chan; i++)
memcpy(sc->ch_list, ch_list, num_chan * sizeof(u16)); sc->ch_list[i] = cpu_to_le16(ch_list[i]);
ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_START_SCAN_CMDID, ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_START_SCAN_CMDID,
NO_SYNC_WMIFLAG); NO_SYNC_WMIFLAG);
...@@ -1770,6 +1915,10 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index, ...@@ -1770,6 +1915,10 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
struct wmi_add_cipher_key_cmd *cmd; struct wmi_add_cipher_key_cmd *cmd;
int ret; int ret;
ath6kl_dbg(ATH6KL_DBG_WMI, "addkey cmd: key_index=%u key_type=%d "
"key_usage=%d key_len=%d key_op_ctrl=%d\n",
key_index, key_type, key_usage, key_len, key_op_ctrl);
if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) || if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) ||
(key_material == NULL)) (key_material == NULL))
return -EINVAL; return -EINVAL;
...@@ -2211,6 +2360,25 @@ int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source) ...@@ -2211,6 +2360,25 @@ int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source)
return ret; return ret;
} }
int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config)
{
struct ath6kl_wmix_dbglog_cfg_module_cmd *cmd;
struct sk_buff *skb;
int ret;
skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
if (!skb)
return -ENOMEM;
cmd = (struct ath6kl_wmix_dbglog_cfg_module_cmd *) skb->data;
cmd->valid = cpu_to_le32(valid);
cmd->config = cpu_to_le32(config);
ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_DBGLOG_CFG_MODULE_CMDID,
NO_SYNC_WMIFLAG);
return ret;
}
int ath6kl_wmi_get_stats_cmd(struct wmi *wmi) int ath6kl_wmi_get_stats_cmd(struct wmi *wmi)
{ {
return ath6kl_wmi_simple_cmd(wmi, WMI_GET_STATISTICS_CMDID); return ath6kl_wmi_simple_cmd(wmi, WMI_GET_STATISTICS_CMDID);
...@@ -2316,49 +2484,29 @@ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl) ...@@ -2316,49 +2484,29 @@ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl)
return ret; return ret;
} }
s32 ath6kl_wmi_get_rate(s8 rate_index) int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len)
{ {
if (rate_index == RATE_AUTO) struct sk_buff *skb;
return 0; int ret;
return wmi_rate_tbl[(u32) rate_index][0]; skb = ath6kl_wmi_get_new_buf(len);
} if (!skb)
return -ENOMEM;
void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss) memcpy(skb->data, buf, len);
{
if (bss)
wlan_node_return(&wmi->parent_dev->scan_table, bss);
}
struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 * ssid, ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_TEST_CMDID, NO_SYNC_WMIFLAG);
u32 ssid_len, bool is_wpa2,
bool match_ssid)
{
struct bss *node = NULL;
node = wlan_find_ssid_node(&wmi->parent_dev->scan_table, ssid, return ret;
ssid_len, is_wpa2, match_ssid);
return node;
} }
struct bss *ath6kl_wmi_find_node(struct wmi *wmi, const u8 * mac_addr)
{
struct bss *ni = NULL;
ni = wlan_find_node(&wmi->parent_dev->scan_table, mac_addr);
return ni; s32 ath6kl_wmi_get_rate(s8 rate_index)
}
void ath6kl_wmi_node_free(struct wmi *wmi, const u8 * mac_addr)
{ {
struct bss *ni = NULL; if (rate_index == RATE_AUTO)
return 0;
ni = wlan_find_node(&wmi->parent_dev->scan_table, mac_addr);
if (ni != NULL)
wlan_node_reclaim(&wmi->parent_dev->scan_table, ni);
return; return wmi_rate_tbl[(u32) rate_index][0];
} }
static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap, static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
...@@ -2400,6 +2548,47 @@ static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len) ...@@ -2400,6 +2548,47 @@ static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
} }
/* AP mode functions */ /* AP mode functions */
int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p)
{
struct sk_buff *skb;
struct wmi_connect_cmd *cm;
int res;
skb = ath6kl_wmi_get_new_buf(sizeof(*cm));
if (!skb)
return -ENOMEM;
cm = (struct wmi_connect_cmd *) skb->data;
memcpy(cm, p, sizeof(*cm));
res = ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_CONFIG_COMMIT_CMDID,
NO_SYNC_WMIFLAG);
ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u "
"ctrl_flags=0x%x-> res=%d\n",
__func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch),
le32_to_cpu(p->ctrl_flags), res);
return res;
}
int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason)
{
struct sk_buff *skb;
struct wmi_ap_set_mlme_cmd *cm;
skb = ath6kl_wmi_get_new_buf(sizeof(*cm));
if (!skb)
return -ENOMEM;
cm = (struct wmi_ap_set_mlme_cmd *) skb->data;
memcpy(cm->mac, mac, ETH_ALEN);
cm->reason = cpu_to_le16(reason);
cm->cmd = cmd;
return ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_SET_MLME_CMDID,
NO_SYNC_WMIFLAG);
}
static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len) static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len)
{ {
struct wmi_pspoll_event *ev; struct wmi_pspoll_event *ev;
...@@ -2433,6 +2622,7 @@ int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag) ...@@ -2433,6 +2622,7 @@ int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag)
cmd = (struct wmi_ap_set_pvb_cmd *) skb->data; cmd = (struct wmi_ap_set_pvb_cmd *) skb->data;
cmd->aid = cpu_to_le16(aid); cmd->aid = cpu_to_le16(aid);
cmd->rsvd = cpu_to_le16(0);
cmd->flag = cpu_to_le32(flag); cmd->flag = cpu_to_le32(flag);
ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_AP_SET_PVB_CMDID, ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_AP_SET_PVB_CMDID,
...@@ -2464,6 +2654,160 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver, ...@@ -2464,6 +2654,160 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver,
return ret; return ret;
} }
int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
u8 ie_len)
{
struct sk_buff *skb;
struct wmi_set_appie_cmd *p;
skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len);
if (!skb)
return -ENOMEM;
ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u "
"ie_len=%u\n", mgmt_frm_type, ie_len);
p = (struct wmi_set_appie_cmd *) skb->data;
p->mgmt_frm_type = mgmt_frm_type;
p->ie_len = ie_len;
memcpy(p->ie_info, ie, ie_len);
return ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_APPIE_CMDID,
NO_SYNC_WMIFLAG);
}
int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable)
{
struct sk_buff *skb;
struct wmi_disable_11b_rates_cmd *cmd;
skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
if (!skb)
return -ENOMEM;
ath6kl_dbg(ATH6KL_DBG_WMI, "disable_11b_rates_cmd: disable=%u\n",
disable);
cmd = (struct wmi_disable_11b_rates_cmd *) skb->data;
cmd->disable = disable ? 1 : 0;
return ath6kl_wmi_cmd_send(wmi, skb, WMI_DISABLE_11B_RATES_CMDID,
NO_SYNC_WMIFLAG);
}
int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur)
{
struct sk_buff *skb;
struct wmi_remain_on_chnl_cmd *p;
skb = ath6kl_wmi_get_new_buf(sizeof(*p));
if (!skb)
return -ENOMEM;
ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl_cmd: freq=%u dur=%u\n",
freq, dur);
p = (struct wmi_remain_on_chnl_cmd *) skb->data;
p->freq = cpu_to_le32(freq);
p->duration = cpu_to_le32(dur);
return ath6kl_wmi_cmd_send(wmi, skb, WMI_REMAIN_ON_CHNL_CMDID,
NO_SYNC_WMIFLAG);
}
int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
const u8 *data, u16 data_len)
{
struct sk_buff *skb;
struct wmi_send_action_cmd *p;
u8 *buf;
if (wait)
return -EINVAL; /* Offload for wait not supported */
buf = kmalloc(data_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
if (!skb) {
kfree(buf);
return -ENOMEM;
}
kfree(wmi->last_mgmt_tx_frame);
wmi->last_mgmt_tx_frame = buf;
wmi->last_mgmt_tx_frame_len = data_len;
ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u "
"len=%u\n", id, freq, wait, data_len);
p = (struct wmi_send_action_cmd *) skb->data;
p->id = cpu_to_le32(id);
p->freq = cpu_to_le32(freq);
p->wait = cpu_to_le32(wait);
p->len = cpu_to_le16(data_len);
memcpy(p->data, data, data_len);
return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_ACTION_CMDID,
NO_SYNC_WMIFLAG);
}
int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq,
const u8 *dst,
const u8 *data, u16 data_len)
{
struct sk_buff *skb;
struct wmi_p2p_probe_response_cmd *p;
skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
if (!skb)
return -ENOMEM;
ath6kl_dbg(ATH6KL_DBG_WMI, "send_probe_response_cmd: freq=%u dst=%pM "
"len=%u\n", freq, dst, data_len);
p = (struct wmi_p2p_probe_response_cmd *) skb->data;
p->freq = cpu_to_le32(freq);
memcpy(p->destination_addr, dst, ETH_ALEN);
p->len = cpu_to_le16(data_len);
memcpy(p->data, data, data_len);
return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_PROBE_RESPONSE_CMDID,
NO_SYNC_WMIFLAG);
}
int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable)
{
struct sk_buff *skb;
struct wmi_probe_req_report_cmd *p;
skb = ath6kl_wmi_get_new_buf(sizeof(*p));
if (!skb)
return -ENOMEM;
ath6kl_dbg(ATH6KL_DBG_WMI, "probe_report_req_cmd: enable=%u\n",
enable);
p = (struct wmi_probe_req_report_cmd *) skb->data;
p->enable = enable ? 1 : 0;
return ath6kl_wmi_cmd_send(wmi, skb, WMI_PROBE_REQ_REPORT_CMDID,
NO_SYNC_WMIFLAG);
}
int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags)
{
struct sk_buff *skb;
struct wmi_get_p2p_info *p;
skb = ath6kl_wmi_get_new_buf(sizeof(*p));
if (!skb)
return -ENOMEM;
ath6kl_dbg(ATH6KL_DBG_WMI, "info_req_cmd: flags=%x\n",
info_req_flags);
p = (struct wmi_get_p2p_info *) skb->data;
p->info_req_flags = cpu_to_le32(info_req_flags);
return ath6kl_wmi_cmd_send(wmi, skb, WMI_GET_P2P_INFO_CMDID,
NO_SYNC_WMIFLAG);
}
int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi)
{
ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl_cmd\n");
return ath6kl_wmi_simple_cmd(wmi, WMI_CANCEL_REMAIN_ON_CHNL_CMDID);
}
static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
{ {
struct wmix_cmd_hdr *cmd; struct wmix_cmd_hdr *cmd;
...@@ -2488,11 +2832,14 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) ...@@ -2488,11 +2832,14 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
switch (id) { switch (id) {
case WMIX_HB_CHALLENGE_RESP_EVENTID: case WMIX_HB_CHALLENGE_RESP_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n");
break; break;
case WMIX_DBGLOG_EVENTID: case WMIX_DBGLOG_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len);
ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len);
break; break;
default: default:
ath6kl_err("unknown cmd id 0x%x\n", id); ath6kl_warn("unknown cmd id 0x%x\n", id);
wmi->stat.cmd_id_err++; wmi->stat.cmd_id_err++;
ret = -EINVAL; ret = -EINVAL;
break; break;
...@@ -2528,8 +2875,9 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ...@@ -2528,8 +2875,9 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
datap = skb->data; datap = skb->data;
len = skb->len; len = skb->len;
ath6kl_dbg(ATH6KL_DBG_WMI, "%s: wmi id: %d\n", __func__, id); ath6kl_dbg(ATH6KL_DBG_WMI, "wmi rx id %d len %d\n", id, len);
ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "msg payload ", datap, len); ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ",
datap, len);
switch (id) { switch (id) {
case WMI_GET_BITRATE_CMDID: case WMI_GET_BITRATE_CMDID:
...@@ -2566,11 +2914,11 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ...@@ -2566,11 +2914,11 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
break; break;
case WMI_BSSINFO_EVENTID: case WMI_BSSINFO_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n");
ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(skb, datap); ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len);
ret = ath6kl_wmi_bssinfo_event_rx(wmi, skb->data, skb->len);
break; break;
case WMI_REGDOMAIN_EVENTID: case WMI_REGDOMAIN_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n");
ath6kl_wmi_regdomain_event(wmi, datap, len);
break; break;
case WMI_PSTREAM_TIMEOUT_EVENTID: case WMI_PSTREAM_TIMEOUT_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n");
...@@ -2578,6 +2926,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ...@@ -2578,6 +2926,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
break; break;
case WMI_NEIGHBOR_REPORT_EVENTID: case WMI_NEIGHBOR_REPORT_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n");
ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len);
break; break;
case WMI_SCAN_COMPLETE_EVENTID: case WMI_SCAN_COMPLETE_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
...@@ -2600,7 +2949,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ...@@ -2600,7 +2949,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
break; break;
case WMI_OPT_RX_FRAME_EVENTID: case WMI_OPT_RX_FRAME_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n");
ret = ath6kl_wmi_opt_frame_event_rx(wmi, datap, len); /* this event has been deprecated */
break; break;
case WMI_REPORT_ROAM_TBL_EVENTID: case WMI_REPORT_ROAM_TBL_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n");
...@@ -2619,6 +2968,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ...@@ -2619,6 +2968,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
case WMI_REPORT_ROAM_DATA_EVENTID: case WMI_REPORT_ROAM_DATA_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_DATA_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_DATA_EVENTID\n");
break; break;
case WMI_TEST_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TEST_EVENTID\n");
ret = ath6kl_wmi_tcmd_test_report_rx(wmi, datap, len);
break;
case WMI_GET_FIXRATES_CMDID: case WMI_GET_FIXRATES_CMDID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n");
ret = ath6kl_wmi_ratemask_reply_rx(wmi, datap, len); ret = ath6kl_wmi_ratemask_reply_rx(wmi, datap, len);
...@@ -2683,6 +3036,36 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ...@@ -2683,6 +3036,36 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n"); ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n");
ret = ath6kl_wmi_tx_complete_event_rx(datap, len); ret = ath6kl_wmi_tx_complete_event_rx(datap, len);
break; break;
case WMI_REMAIN_ON_CHNL_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n");
ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len);
break;
case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI,
"WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n");
ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap,
len);
break;
case WMI_TX_STATUS_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n");
ret = ath6kl_wmi_tx_status_event_rx(wmi, datap, len);
break;
case WMI_RX_PROBE_REQ_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n");
ret = ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len);
break;
case WMI_P2P_CAPABILITIES_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n");
ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len);
break;
case WMI_RX_ACTION_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n");
ret = ath6kl_wmi_rx_action_event_rx(wmi, datap, len);
break;
case WMI_P2P_INFO_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n");
ret = ath6kl_wmi_p2p_info_event_rx(datap, len);
break;
default: default:
ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id); ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id);
wmi->stat.cmd_id_err++; wmi->stat.cmd_id_err++;
...@@ -2739,5 +3122,6 @@ void ath6kl_wmi_shutdown(struct wmi *wmi) ...@@ -2739,5 +3122,6 @@ void ath6kl_wmi_shutdown(struct wmi *wmi)
if (!wmi) if (!wmi)
return; return;
kfree(wmi->last_mgmt_tx_frame);
kfree(wmi); kfree(wmi);
} }
...@@ -129,6 +129,9 @@ struct wmi { ...@@ -129,6 +129,9 @@ struct wmi {
u8 ht_allowed[A_NUM_BANDS]; u8 ht_allowed[A_NUM_BANDS];
u8 traffic_class; u8 traffic_class;
bool is_probe_ssid; bool is_probe_ssid;
u8 *last_mgmt_tx_frame;
size_t last_mgmt_tx_frame_len;
}; };
struct host_app_area { struct host_app_area {
...@@ -490,17 +493,61 @@ enum wmi_cmd_id { ...@@ -490,17 +493,61 @@ enum wmi_cmd_id {
WMI_SET_PASSPHRASE_CMDID, WMI_SET_PASSPHRASE_CMDID,
WMI_SEND_ASSOC_RES_CMDID, WMI_SEND_ASSOC_RES_CMDID,
WMI_SET_ASSOC_REQ_RELAY_CMDID, WMI_SET_ASSOC_REQ_RELAY_CMDID,
WMI_GET_RFKILL_MODE_CMDID,
/* ACS command, consists of sub-commands */ /* ACS command, consists of sub-commands */
WMI_ACS_CTRL_CMDID, WMI_ACS_CTRL_CMDID,
WMI_SET_EXCESS_TX_RETRY_THRES_CMDID,
WMI_SET_TBD_TIME_CMDID, /*added for wmiconfig command for TBD */
/* Pktlog cmds */
WMI_PKTLOG_ENABLE_CMDID,
WMI_PKTLOG_DISABLE_CMDID,
/* More P2P Cmds */
WMI_P2P_GO_NEG_REQ_RSP_CMDID,
WMI_P2P_GRP_INIT_CMDID,
WMI_P2P_GRP_FORMATION_DONE_CMDID,
WMI_P2P_INVITE_CMDID,
WMI_P2P_INVITE_REQ_RSP_CMDID,
WMI_P2P_PROV_DISC_REQ_CMDID,
WMI_P2P_SET_CMDID,
WMI_GET_RFKILL_MODE_CMDID,
WMI_SET_RFKILL_MODE_CMDID,
WMI_AP_SET_APSD_CMDID,
WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID,
WMI_P2P_SDPD_TX_CMDID, /* F05C */
WMI_P2P_STOP_SDPD_CMDID,
WMI_P2P_CANCEL_CMDID,
/* Ultra low power store / recall commands */ /* Ultra low power store / recall commands */
WMI_STORERECALL_CONFIGURE_CMDID, WMI_STORERECALL_CONFIGURE_CMDID,
WMI_STORERECALL_RECALL_CMDID, WMI_STORERECALL_RECALL_CMDID,
WMI_STORERECALL_HOST_READY_CMDID, WMI_STORERECALL_HOST_READY_CMDID,
WMI_FORCE_TARGET_ASSERT_CMDID, WMI_FORCE_TARGET_ASSERT_CMDID,
WMI_SET_EXCESS_TX_RETRY_THRES_CMDID,
WMI_SET_PROBED_SSID_EX_CMDID,
WMI_SET_NETWORK_LIST_OFFLOAD_CMDID,
WMI_SET_ARP_NS_OFFLOAD_CMDID,
WMI_ADD_WOW_EXT_PATTERN_CMDID,
WMI_GTK_OFFLOAD_OP_CMDID,
WMI_REMAIN_ON_CHNL_CMDID,
WMI_CANCEL_REMAIN_ON_CHNL_CMDID,
WMI_SEND_ACTION_CMDID,
WMI_PROBE_REQ_REPORT_CMDID,
WMI_DISABLE_11B_RATES_CMDID,
WMI_SEND_PROBE_RESPONSE_CMDID,
WMI_GET_P2P_INFO_CMDID,
WMI_AP_JOIN_BSS_CMDID,
};
enum wmi_mgmt_frame_type {
WMI_FRAME_BEACON = 0,
WMI_FRAME_PROBE_REQ,
WMI_FRAME_PROBE_RESP,
WMI_FRAME_ASSOC_REQ,
WMI_FRAME_ASSOC_RESP,
WMI_NUM_MGMT_FRAME
}; };
/* WMI_CONNECT_CMDID */ /* WMI_CONNECT_CMDID */
...@@ -519,11 +566,6 @@ enum dot11_auth_mode { ...@@ -519,11 +566,6 @@ enum dot11_auth_mode {
LEAP_AUTH = 0x04, LEAP_AUTH = 0x04,
}; };
enum {
AUTH_IDLE,
AUTH_OPEN_IN_PROGRESS,
};
enum auth_mode { enum auth_mode {
NONE_AUTH = 0x01, NONE_AUTH = 0x01,
WPA_AUTH = 0x02, WPA_AUTH = 0x02,
...@@ -1179,15 +1221,26 @@ enum wmi_event_id { ...@@ -1179,15 +1221,26 @@ enum wmi_event_id {
WMI_WAC_START_WPS_EVENTID, WMI_WAC_START_WPS_EVENTID,
WMI_WAC_CTRL_REQ_REPLY_EVENTID, WMI_WAC_CTRL_REQ_REPLY_EVENTID,
WMI_REPORT_WMM_PARAMS_EVENTID,
WMI_WAC_REJECT_WPS_EVENTID,
/* More P2P Events */
WMI_P2P_GO_NEG_REQ_EVENTID,
WMI_P2P_INVITE_REQ_EVENTID,
WMI_P2P_INVITE_RCVD_RESULT_EVENTID,
WMI_P2P_INVITE_SENT_RESULT_EVENTID,
WMI_P2P_PROV_DISC_RESP_EVENTID,
WMI_P2P_PROV_DISC_REQ_EVENTID,
/* RFKILL Events */ /* RFKILL Events */
WMI_RFKILL_STATE_CHANGE_EVENTID, WMI_RFKILL_STATE_CHANGE_EVENTID,
WMI_RFKILL_GET_MODE_CMD_EVENTID, WMI_RFKILL_GET_MODE_CMD_EVENTID,
WMI_THIN_RESERVED_START_EVENTID = 0x8000,
/* WMI_P2P_START_SDPD_EVENTID,
* Events in this range are reserved for thinmode WMI_P2P_SDPD_RX_EVENTID,
* See wmi_thin.h for actual definitions
*/ WMI_THIN_RESERVED_START_EVENTID = 0x8000,
/* Events in this range are reserved for thinmode */
WMI_THIN_RESERVED_END_EVENTID = 0x8fff, WMI_THIN_RESERVED_END_EVENTID = 0x8fff,
WMI_SET_CHANNEL_EVENTID, WMI_SET_CHANNEL_EVENTID,
...@@ -1195,7 +1248,17 @@ enum wmi_event_id { ...@@ -1195,7 +1248,17 @@ enum wmi_event_id {
/* Generic ACS event */ /* Generic ACS event */
WMI_ACS_EVENTID, WMI_ACS_EVENTID,
WMI_REPORT_WMM_PARAMS_EVENTID WMI_STORERECALL_STORE_EVENTID,
WMI_WOW_EXT_WAKE_EVENTID,
WMI_GTK_OFFLOAD_STATUS_EVENTID,
WMI_NETWORK_LIST_OFFLOAD_EVENTID,
WMI_REMAIN_ON_CHNL_EVENTID,
WMI_CANCEL_REMAIN_ON_CHNL_EVENTID,
WMI_TX_STATUS_EVENTID,
WMI_RX_PROBE_REQ_EVENTID,
WMI_P2P_CAPABILITIES_EVENTID,
WMI_RX_ACTION_EVENTID,
WMI_P2P_INFO_EVENTID,
}; };
struct wmi_ready_event_2 { struct wmi_ready_event_2 {
...@@ -1207,11 +1270,30 @@ struct wmi_ready_event_2 { ...@@ -1207,11 +1270,30 @@ struct wmi_ready_event_2 {
/* Connect Event */ /* Connect Event */
struct wmi_connect_event { struct wmi_connect_event {
union {
struct {
__le16 ch; __le16 ch;
u8 bssid[ETH_ALEN]; u8 bssid[ETH_ALEN];
__le16 listen_intvl; __le16 listen_intvl;
__le16 beacon_intvl; __le16 beacon_intvl;
__le32 nw_type; __le32 nw_type;
} sta;
struct {
u8 phymode;
u8 aid;
u8 mac_addr[ETH_ALEN];
u8 auth;
u8 keymgmt;
__le16 cipher;
u8 apsd_info;
u8 unused[3];
} ap_sta;
struct {
__le16 ch;
u8 bssid[ETH_ALEN];
u8 unused[8];
} ap_bss;
} u;
u8 beacon_ie_len; u8 beacon_ie_len;
u8 assoc_req_len; u8 assoc_req_len;
u8 assoc_resp_len; u8 assoc_resp_len;
...@@ -1238,6 +1320,12 @@ enum wmi_disconnect_reason { ...@@ -1238,6 +1320,12 @@ enum wmi_disconnect_reason {
IBSS_MERGE = 0xe, IBSS_MERGE = 0xe,
}; };
#define ATH6KL_COUNTRY_RD_SHIFT 16
struct ath6kl_wmi_regdomain {
__le32 reg_code;
};
struct wmi_disconnect_event { struct wmi_disconnect_event {
/* reason code, see 802.11 spec. */ /* reason code, see 802.11 spec. */
__le16 proto_reason_status; __le16 proto_reason_status;
...@@ -1265,33 +1353,54 @@ enum wmi_bi_ftype { ...@@ -1265,33 +1353,54 @@ enum wmi_bi_ftype {
PROBEREQ_FTYPE, PROBEREQ_FTYPE,
}; };
struct wmi_bss_info_hdr { #define DEF_LRSSI_SCAN_PERIOD 5
__le16 ch; #define DEF_LRSSI_ROAM_THRESHOLD 20
#define DEF_LRSSI_ROAM_FLOOR 60
#define DEF_SCAN_FOR_ROAM_INTVL 2
/* see, enum wmi_bi_ftype */ enum wmi_roam_ctrl {
u8 frame_type; WMI_FORCE_ROAM = 1,
WMI_SET_ROAM_MODE,
WMI_SET_HOST_BIAS,
WMI_SET_LRSSI_SCAN_PARAMS,
};
u8 snr; struct bss_bias {
a_sle16 rssi;
u8 bssid[ETH_ALEN]; u8 bssid[ETH_ALEN];
__le32 ie_mask; u8 bias;
} __packed; } __packed;
/* struct bss_bias_info {
* BSS INFO HDR version 2.0 u8 num_bss;
* With 6 bytes HTC header and 6 bytes of WMI header struct bss_bias bss_bias[1];
* WMI_BSS_INFO_HDR cannot be accommodated in the removed 802.11 management } __packed;
* header space.
* - Reduce the ie_mask to 2 bytes as only two bit flags are used struct low_rssi_scan_params {
* - Remove rssi and compute it on the host. rssi = snr - 95 __le16 lrssi_scan_period;
*/ a_sle16 lrssi_scan_threshold;
a_sle16 lrssi_roam_threshold;
u8 roam_rssi_floor;
u8 reserved[1];
} __packed;
struct roam_ctrl_cmd {
union {
u8 bssid[ETH_ALEN];
u8 roam_mode;
struct bss_bias_info bss;
struct low_rssi_scan_params params;
} __packed info;
u8 roam_ctrl;
} __packed;
/* BSS INFO HDR version 2.0 */
struct wmi_bss_info_hdr2 { struct wmi_bss_info_hdr2 {
__le16 ch; __le16 ch; /* frequency in MHz */
/* see, enum wmi_bi_ftype */ /* see, enum wmi_bi_ftype */
u8 frame_type; u8 frame_type;
u8 snr; u8 snr; /* note: rssi = snr - 95 dBm */
u8 bssid[ETH_ALEN]; u8 bssid[ETH_ALEN];
__le16 ie_mask; __le16 ie_mask;
} __packed; } __packed;
...@@ -1330,6 +1439,16 @@ enum wmi_bss_flags { ...@@ -1330,6 +1439,16 @@ enum wmi_bss_flags {
WMI_PMKID_VALID_BSS = 0x02, WMI_PMKID_VALID_BSS = 0x02,
}; };
struct wmi_neighbor_info {
u8 bssid[ETH_ALEN];
u8 bss_flags; /* enum wmi_bss_flags */
} __packed;
struct wmi_neighbor_report_event {
u8 num_neighbors;
struct wmi_neighbor_info neighbor[0];
} __packed;
/* TKIP MIC Error Event */ /* TKIP MIC Error Event */
struct wmi_tkip_micerr_event { struct wmi_tkip_micerr_event {
u8 key_id; u8 key_id;
...@@ -1642,6 +1761,12 @@ struct wmi_get_keepalive_cmd { ...@@ -1642,6 +1761,12 @@ struct wmi_get_keepalive_cmd {
u8 keep_alive_intvl; u8 keep_alive_intvl;
} __packed; } __packed;
struct wmi_set_appie_cmd {
u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */
u8 ie_len;
u8 ie_info[0];
} __packed;
/* Notify the WSC registration status to the target */ /* Notify the WSC registration status to the target */
#define WSC_REG_ACTIVE 1 #define WSC_REG_ACTIVE 1
#define WSC_REG_INACTIVE 0 #define WSC_REG_INACTIVE 0
...@@ -1789,8 +1914,26 @@ struct wmi_tx_complete_event { ...@@ -1789,8 +1914,26 @@ struct wmi_tx_complete_event {
/* Used with WMI_AP_SET_NUM_STA_CMDID */ /* Used with WMI_AP_SET_NUM_STA_CMDID */
/*
* Used with WMI_AP_SET_MLME_CMDID
*/
/* MLME Commands */
#define WMI_AP_MLME_ASSOC 1 /* associate station */
#define WMI_AP_DISASSOC 2 /* disassociate station */
#define WMI_AP_DEAUTH 3 /* deauthenticate station */
#define WMI_AP_MLME_AUTHORIZE 4 /* authorize station */
#define WMI_AP_MLME_UNAUTHORIZE 5 /* unauthorize station */
struct wmi_ap_set_mlme_cmd {
u8 mac[ETH_ALEN];
__le16 reason; /* 802.11 reason code */
u8 cmd; /* operation to perform (WMI_AP_*) */
} __packed;
struct wmi_ap_set_pvb_cmd { struct wmi_ap_set_pvb_cmd {
__le32 flag; __le32 flag;
__le16 rsvd;
__le16 aid; __le16 aid;
} __packed; } __packed;
...@@ -1840,6 +1983,100 @@ struct wmi_ap_mode_stat { ...@@ -1840,6 +1983,100 @@ struct wmi_ap_mode_stat {
/* End of AP mode definitions */ /* End of AP mode definitions */
struct wmi_remain_on_chnl_cmd {
__le32 freq;
__le32 duration;
} __packed;
struct wmi_send_action_cmd {
__le32 id;
__le32 freq;
__le32 wait;
__le16 len;
u8 data[0];
} __packed;
struct wmi_tx_status_event {
__le32 id;
u8 ack_status;
} __packed;
struct wmi_probe_req_report_cmd {
u8 enable;
} __packed;
struct wmi_disable_11b_rates_cmd {
u8 disable;
} __packed;
struct wmi_set_appie_extended_cmd {
u8 role_id;
u8 mgmt_frm_type;
u8 ie_len;
u8 ie_info[0];
} __packed;
struct wmi_remain_on_chnl_event {
__le32 freq;
__le32 duration;
} __packed;
struct wmi_cancel_remain_on_chnl_event {
__le32 freq;
__le32 duration;
u8 status;
} __packed;
struct wmi_rx_action_event {
__le32 freq;
__le16 len;
u8 data[0];
} __packed;
struct wmi_p2p_capabilities_event {
__le16 len;
u8 data[0];
} __packed;
struct wmi_p2p_rx_probe_req_event {
__le32 freq;
__le16 len;
u8 data[0];
} __packed;
#define P2P_FLAG_CAPABILITIES_REQ (0x00000001)
#define P2P_FLAG_MACADDR_REQ (0x00000002)
#define P2P_FLAG_HMODEL_REQ (0x00000002)
struct wmi_get_p2p_info {
__le32 info_req_flags;
} __packed;
struct wmi_p2p_info_event {
__le32 info_req_flags;
__le16 len;
u8 data[0];
} __packed;
struct wmi_p2p_capabilities {
u8 go_power_save;
} __packed;
struct wmi_p2p_macaddr {
u8 mac_addr[ETH_ALEN];
} __packed;
struct wmi_p2p_hmodel {
u8 p2p_model;
} __packed;
struct wmi_p2p_probe_response_cmd {
__le32 freq;
u8 destination_addr[ETH_ALEN];
__le16 len;
u8 data[0];
} __packed;
/* Extended WMI (WMIX) /* Extended WMI (WMIX)
* *
* Extended WMIX commands are encapsulated in a WMI message with * Extended WMIX commands are encapsulated in a WMI message with
...@@ -1898,6 +2135,11 @@ struct wmix_hb_challenge_resp_cmd { ...@@ -1898,6 +2135,11 @@ struct wmix_hb_challenge_resp_cmd {
__le32 source; __le32 source;
} __packed; } __packed;
struct ath6kl_wmix_dbglog_cfg_module_cmd {
__le32 valid;
__le32 config;
} __packed;
/* End of Extended WMI (WMIX) */ /* End of Extended WMI (WMIX) */
enum wmi_sync_flag { enum wmi_sync_flag {
...@@ -1925,14 +2167,11 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, ...@@ -1925,14 +2167,11 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb); int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb);
int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb); int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb);
int ath6kl_wmi_data_hdr_remove(struct wmi *wmi, struct sk_buff *skb);
int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb, int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
u32 layer2_priority, bool wmm_enabled, u32 layer2_priority, bool wmm_enabled,
u8 *ac); u8 *ac);
int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb); int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb);
struct bss *ath6kl_wmi_find_node(struct wmi *wmi, const u8 *mac_addr);
void ath6kl_wmi_node_free(struct wmi *wmi, const u8 *mac_addr);
int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb, int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag); enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag);
...@@ -1978,6 +2217,7 @@ int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status, ...@@ -1978,6 +2217,7 @@ int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status,
u8 preamble_policy); u8 preamble_policy);
int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source); int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source);
int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config);
int ath6kl_wmi_get_stats_cmd(struct wmi *wmi); int ath6kl_wmi_get_stats_cmd(struct wmi *wmi);
int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index, int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
...@@ -1995,23 +2235,47 @@ int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi); ...@@ -1995,23 +2235,47 @@ int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi);
int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg); int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg);
int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl); int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl);
int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len);
s32 ath6kl_wmi_get_rate(s8 rate_index); 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, struct wmi_set_ip_cmd *ip_cmd);
int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi);
struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 *ssid, /* AP mode */
u32 ssid_len, bool is_wpa2, int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p);
bool match_ssid);
void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss); int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason);
/* AP mode */
int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag); int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag);
int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version, int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version,
bool rx_dot11_hdr, bool defrag_on_host); bool rx_dot11_hdr, bool defrag_on_host);
int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
u8 ie_len);
/* P2P */
int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable);
int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur);
int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
const u8 *data, u16 data_len);
int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq,
const u8 *dst,
const u8 *data, u16 data_len);
int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable);
int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags);
int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi);
int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
u8 ie_len);
void *ath6kl_wmi_init(struct ath6kl *devt); void *ath6kl_wmi_init(struct ath6kl *devt);
void ath6kl_wmi_shutdown(struct wmi *wmi); void ath6kl_wmi_shutdown(struct wmi *wmi);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment