SmartAudio/lichee/linux-4.9/drivers/net/wireless/xr829/wlan/txrx.c

3294 lines
89 KiB
C
Executable File

/*
* Datapath implementation for XRadio drivers
*
* Copyright (c) 2013
* Xradio Technology Co., Ltd. <www.xradiotech.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include "xradio.h"
#include "wsm.h"
#include "bh.h"
#include "ap.h"
#include "sta.h"
#include "sbus.h"
#ifndef SUPPORT_HT40
#define B_RATE_INDEX 0 /* 11b rate for important short frames in 2.4G. */
#define AG_RATE_INDEX 6 /* 11a/g rate for important short frames in 5G. */
#define XRADIO_INVALID_RATE_ID (0xFF)
#endif
/* rate should fall quickly to avoid dropping frames by aps.*/
#ifdef ENHANCE_ANTI_INTERFERE
#define HIGH_RATE_MAX_RETRY 9
#else
#define HIGH_RATE_MAX_RETRY 7
#endif
#ifdef CONFIG_XRADIO_TESTMODE
#include "nl80211_testmode_msg_copy.h"
#endif /* CONFIG_XRADIO_TESTMODE */
#ifdef TES_P2P_0002_ROC_RESTART
#include <linux/time.h>
#endif
static const struct ieee80211_rate *xradio_get_tx_rate(
const struct xradio_common *hw_priv,
const struct ieee80211_tx_rate *rate);
#ifdef SUPPORT_HT40
u32 TxedHtofdmRateMap[4][8] = { {0x0} };
u32 TxedLegacyRateMap[2][8] = { {0x0} };
u32 RxedHtofdmRateMap[4][8] = { {0x0} };
u32 RxedLegacyRateMap[2][8] = { {0x0} };
u8 LegacyRxedRateLut[2][14] = {
{
0, 1, 2, 3, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11
},
{
0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7
}
};
#else
u32 TxedRateIdx_Map[24] = { 0 };
u32 RxedRateIdx_Map[24] = { 0 };
#endif
/* ******************************************************************** */
/* TX policy cache implementation */
static void tx_policy_dump(struct tx_policy *policy)
{
txrx_printk(XRADIO_DBG_MSG, "[TX policy] "
"%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X"
"%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X"
"%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n",
policy->raw[0] & 0x0F, policy->raw[0] >> 4,
policy->raw[1] & 0x0F, policy->raw[1] >> 4,
policy->raw[2] & 0x0F, policy->raw[2] >> 4,
policy->raw[3] & 0x0F, policy->raw[3] >> 4,
policy->raw[4] & 0x0F, policy->raw[4] >> 4,
policy->raw[5] & 0x0F, policy->raw[5] >> 4,
policy->raw[6] & 0x0F, policy->raw[6] >> 4,
policy->raw[7] & 0x0F, policy->raw[7] >> 4,
policy->raw[8] & 0x0F, policy->raw[8] >> 4,
policy->raw[9] & 0x0F, policy->raw[9] >> 4,
policy->raw[10] & 0x0F, policy->raw[10] >> 4,
policy->raw[11] & 0x0F, policy->raw[11] >> 4,
policy->defined);
}
static void xradio_check_go_neg_conf_success(struct xradio_common *hw_priv,
u8 *action)
{
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A &&
action[5] == 0x09 && action[6] == 0x02) {
if (action[17] == 0) {
hw_priv->is_go_thru_go_neg = true;
} else {
hw_priv->is_go_thru_go_neg = false;
}
}
}
#ifdef TES_P2P_0002_ROC_RESTART
/*
* TES_P2P_0002 WorkAround:
* P2P GO Neg Process and P2P FIND may be collision.
* When P2P Device is waiting for GO NEG CFM in 30ms,
* P2P FIND may end with p2p listen, and then goes to p2p search.
* Then xradio scan will occupy phy on other channel in 3+ seconds.
* P2P Device will not be able to receive the GO NEG CFM.
* We extend the roc period to remaind phy to receive
* GO NEG CFM as WorkAround.
*/
s32 TES_P2P_0002_roc_dur;
s32 TES_P2P_0002_roc_sec;
s32 TES_P2P_0002_roc_usec;
u32 TES_P2P_0002_packet_id;
u32 TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
static void xradio_frame_monitor(struct xradio_common *hw_priv,
struct sk_buff *skb, bool tx)
{
struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
u8 *action = (u8 *) &mgmt->u.action.category;
u8 *category_code = &(action[0]);
u8 *action_code = &(action[1]);
u8 *oui = &(action[2]);
u8 *subtype = &(action[5]);
u8 *oui_subtype = &(action[6]);
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (ieee80211_is_action(frame->frame_control) &&
*category_code == WLAN_CATEGORY_PUBLIC &&
*action_code == 0x09) {
if ((oui[0] == 0x50) && (oui[1] == 0x6F) &&
(oui[2] == 0x9A) && (*subtype == 0x09)) {
/* w, GO Negotiation Response */
if (*oui_subtype == 0x01) {
if ((TES_P2P_0002_state == TES_P2P_0002_STATE_IDLE) &&
(tx == true)) { /* w, p2p atturbute:status,id=0 */
u8 *go_neg_resp_res = &(action[17]);
if (*go_neg_resp_res == 0x0) {
TES_P2P_0002_state = TES_P2P_0002_STATE_SEND_RESP;
txrx_printk(XRADIO_DBG_NIY,
"[ROC_RESTART_STATE_SEND_RESP]\n");
}
}
/* w, GO Negotiation Confirmation */
} else if (*oui_subtype == 0x02) {
if (tx == false) {
TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
txrx_printk(XRADIO_DBG_NIY, "[ROC_RESTART_STATE_IDLE]"
"[GO Negotiation Confirmation]\n");
}
/* w, Provision Discovery Response */
} else if (*oui_subtype == 0x08) {
if (tx == false) {
TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
txrx_printk(XRADIO_DBG_NIY, "[ROC_RESTART_STATE_IDLE]"
"[Provision Discovery Response]\n");
}
}
}
}
}
#endif
static void xradio_check_prov_desc_req(struct xradio_common *hw_priv,
u8 *action)
{
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A &&
action[5] == 0x09 && action[6] == 0x07) {
hw_priv->is_go_thru_go_neg = false;
}
}
#ifdef AP_HT_COMPAT_FIX
#define AP_COMPAT_THRESHOLD 2000
#define AP_COMPAT_MIN_CNT 200
u8 ap_compat_bssid[ETH_ALEN] = { 0 };
#ifdef SUPPORT_HT40
static int xradio_apcompat_detect(struct xradio_vif *priv, u16 rxedRateEntry)
#else
static int xradio_apcompat_detect(struct xradio_vif *priv, u8 rx_rate)
#endif
{
#ifdef SUPPORT_HT40
if ((GET_RATE_ENTRY_MODEM(rxedRateEntry) == FW_RATE_MODEM_LEGACY)
&& (GET_RATE_ENTRY_RATEINDEX(rxedRateEntry) < 6)) {
#else
if (rx_rate < AG_RATE_INDEX) {
#endif
priv->ht_compat_cnt++;
#ifdef SUPPORT_HT40
txrx_printk(XRADIO_DBG_MSG, "%s:rate=%d.\n",
__func__, GET_RATE_ENTRY_RATEINDEX(rxedRateEntry));
#else
txrx_printk(XRADIO_DBG_MSG, "%s:rate=%d.\n", __func__, rx_rate);
#endif
} else {
priv->ht_compat_det |= 1;
priv->ht_compat_cnt = 0;
txrx_printk(XRADIO_DBG_NIY, "%s:HT compat detect\n", __func__);
return 0;
}
/* Enhance compatibility with some illegal APs.*/
if (priv->ht_compat_cnt > AP_COMPAT_THRESHOLD ||
(priv->ht_compat_cnt > AP_COMPAT_MIN_CNT &&
priv->bssid[0] == 0xC8 &&
priv->bssid[1] == 0x3A &&
priv->bssid[2] == 0x35)) {
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
memcpy(ap_compat_bssid, priv->bssid, ETH_ALEN);
wsm_send_disassoc_to_self(hw_priv, priv);
txrx_printk(XRADIO_DBG_WARN, "%s:SSID=%s, BSSID=" \
"%02x:%02x:%02x:%02x:%02x:%02x\n", __func__, priv->ssid,
ap_compat_bssid[0], ap_compat_bssid[1],
ap_compat_bssid[2], ap_compat_bssid[3],
ap_compat_bssid[4], ap_compat_bssid[5]);
return 1;
}
return 0;
}
static void xradio_remove_ht_ie(struct xradio_vif *priv, struct sk_buff *skb)
{
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
u8 *ies = NULL;
size_t ies_len = 0;
u8 *ht_ie = NULL;
if (!mgmt || memcmp(ap_compat_bssid, mgmt->bssid, ETH_ALEN))
return;
if (ieee80211_is_probe_resp(mgmt->frame_control))
ies = mgmt->u.probe_resp.variable;
else if (ieee80211_is_beacon(mgmt->frame_control))
ies = mgmt->u.beacon.variable;
else if (ieee80211_is_assoc_resp(mgmt->frame_control))
ies = mgmt->u.assoc_resp.variable;
else if (ieee80211_is_assoc_req(mgmt->frame_control))
ies = mgmt->u.assoc_req.variable;
else
return;
ies_len = skb->len - (ies - (u8 *)(skb->data));
ht_ie = (u8 *)xradio_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY);
if (ht_ie) {
u8 ht_len = *(ht_ie + 1) + 2;
u8 move_len = (ies + ies_len) - (ht_ie + ht_len);
memmove(ht_ie, (ht_ie + ht_len), move_len);
skb_trim(skb, skb->len - ht_len);
ies_len = skb->len - (ies - (u8 *)(skb->data));
ht_ie = (u8 *)xradio_get_ie(ies, ies_len, WLAN_EID_HT_INFORMATION);
if (ht_ie) {
ht_len = *(ht_ie + 1) + 2;
move_len = (ies + ies_len) - (ht_ie + ht_len);
memmove(ht_ie, (ht_ie + ht_len), move_len);
skb_trim(skb, skb->len - ht_len);
}
}
txrx_printk(XRADIO_DBG_WARN, "%s: BSSID=%02x:%02x:%02x:%02x:%02x:%02x\n",
__func__,
mgmt->bssid[0], mgmt->bssid[1],
mgmt->bssid[2], mgmt->bssid[3],
mgmt->bssid[4], mgmt->bssid[5]);
}
#endif /*AP_HT_COMPAT_FIX*/
#ifdef SUPPORT_HT40
static inline u8 xradio_rate_to_entry_idx(u8 flags, u8 hw_value)
{
u8 entryIndex = 0xF;
if (flags & IEEE80211_TX_RC_MCS)
entryIndex = hw_value - MCS_RATES_OFF;
else
entryIndex = hw_value;
return entryIndex;
}
#endif
static void tx_policy_build(struct xradio_vif *priv,
/* [out] */ struct tx_policy *policy,
struct ieee80211_tx_rate *rates, size_t count)
{
int i, j;
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
struct ieee80211_rate *tmp_rate = NULL;
unsigned limit = hw_priv->short_frame_max_tx_count;
unsigned max_rates_cnt = count;
unsigned total = 0;
u8 lowest_rate_idx = 0;
SYS_BUG(rates[0].idx < 0);
memset(policy, 0, sizeof(*policy));
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
txrx_printk(XRADIO_DBG_MSG, "============================");
#ifdef SUPPORT_HT40
/* calculate the tx policy count and max retry times */
total = rates[0].count;
for (i = 1; i < count; ++i) {
if (rates[i].idx < 0 || i >= limit) {
count = i;
break;
} else {
total += rates[i].count;
}
}
#else
/*
* minstrel is buggy a little bit, so distille
* incoming rates first.
*/
/* Sort rates in descending order. */
total = rates[0].count;
for (i = 1; i < count; ++i) {
if (rates[i].idx > rates[i-1].idx) {
rates[i].idx = rates[i-1].idx > 0 ? (rates[i-1].idx - 1) : -1;
}
if (rates[i].idx < 0 || i >= limit) {
count = i;
break;
} else {
total += rates[i].count;
}
}
#endif
/*
* Add lowest rate to the end.
* TODO: it's better to do this in rate control of mac80211.
*/
if (unlikely(!(rates[0].flags & IEEE80211_TX_RC_MCS)) &&
hw_priv->channel->band == NL80211_BAND_2GHZ) {
u32 rateset = (priv->oper_rates | priv->base_rates) & ~0xf;
if (rateset)
lowest_rate_idx = __ffs(rateset);
else
lowest_rate_idx = 0xff;
txrx_printk(XRADIO_DBG_MSG, "rateset=0x%x, lowest_rate_idx=%d\n",
rateset, lowest_rate_idx);
}
if (count < max_rates_cnt && rates[count-1].idx > lowest_rate_idx) {
rates[count].idx = lowest_rate_idx;
rates[count].count = rates[0].count;
rates[count].flags = rates[0].flags;
rates[count].flags &= (~IEEE80211_TX_RC_40_MHZ_WIDTH);
rates[count].flags &= (~IEEE80211_TX_RC_SHORT_GI);
total += rates[count].count;
count++;
}
/*
* Adjust tx count to limit, rates should fall quickly
* and lower rates should be more retry, because reorder
* buffer of reciever will be timeout and clear probably.
*/
if (count < 2) {
rates[0].count = limit;
total = limit;
} else {
u8 end_retry = 0; /* the retry should be add to last rate. */
if (limit > HIGH_RATE_MAX_RETRY) {
end_retry = limit - HIGH_RATE_MAX_RETRY;
limit = HIGH_RATE_MAX_RETRY;
}
/* i<100 to avoid dead loop */
for (i = 0; (limit != total) && (i < 100); ++i) {
j = i % count;
if (limit < total) {
total += (rates[j].count > 1 ? -1 : 0);
rates[j].count += (rates[j].count > 1 ? -1 : 0);
} else {
j = count - 1 - j;
if (rates[j].count > 0) {
total++;
rates[j].count++;
}
}
}
if (end_retry) {
rates[count-1].count += end_retry;
limit += end_retry;
}
}
#ifdef SUPPORT_HT40
/* Eliminate duplicates. */
total = rates[0].count;
for (i = 0, j = 1; j < count; ++j) {
if ((rates[j].idx == rates[i].idx) &&
(rates[j].flags == rates[i].flags)) {
rates[i].count += rates[j].count;
} else {
++i;
if (i != j)
rates[i] = rates[j];
}
total += rates[j].count;
}
for (j = i + 1; j < count; j++) {
rates[j].idx = -1;
rates[j].count = 0;
}
count = i + 1;
#else
/* Eliminate duplicates. */
total = rates[0].count;
for (i = 0, j = 1; j < count; ++j) {
if (rates[j].idx < 0 || rates[j].idx > rates[i].idx)
break;
if (rates[j].idx == rates[i].idx) {
rates[i].count += rates[j].count;
} else {
++i;
if (i != j)
rates[i] = rates[j];
}
total += rates[j].count;
}
count = i + 1;
#endif
/*
* Re-fill policy trying to keep every requested rate and with
* respect to the global max tx retransmission count.
*/
if (limit < count)
limit = count;
if (total > limit) {
for (i = 0; i < count; ++i) {
int left = count - i - 1;
if (rates[i].count > limit - left)
rates[i].count = limit - left;
limit -= rates[i].count;
}
}
/*
* HACK!!! Device has problems (at least) switching from
* 54Mbps CTS to 1Mbps. This switch takes enormous amount
* of time (100-200 ms), leading to valuable throughput drop.
* As a workaround, additional g-rates are injected to the
* policy.
*/
if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
rates[0].idx > 4 && rates[0].count > 2 &&
rates[1].idx < 2) {
/* ">> 1" is an equivalent of "/ 2", but faster */
int mid_rate = (rates[0].idx + 4) >> 1;
/* Decrease number of retries for the initial rate */
rates[0].count -= 2;
if (mid_rate != 4) {
/* Keep fallback rate at 1Mbps. */
rates[3] = rates[1];
/* Inject 1 transmission on lowest g-rate */
rates[2].idx = 4;
rates[2].count = 1;
rates[2].flags = rates[1].flags;
/* Inject 1 transmission on mid-rate */
rates[1].idx = mid_rate;
rates[1].count = 1;
/* Fallback to 1 Mbps is a really bad thing,
* so let's try to increase probability of
* successful transmission on the lowest g rate
* even more */
if (rates[0].count >= 3) {
--rates[0].count;
++rates[2].count;
}
/* Adjust amount of rates defined */
count += 2;
} else {
/* Keep fallback rate at 1Mbps. */
rates[2] = rates[1];
/* Inject 2 transmissions on lowest g-rate */
rates[1].idx = 4;
rates[1].count = 2;
/* Adjust amount of rates defined */
count += 1;
}
}
#ifdef SUPPORT_HT40
/* set rate_entrys */
for (i = 0; i < count; ++i) {
register u8 entry_idx = 0;
register u8 modem_type = RATE_MODEM_LEGACY;
register u8 bandwidth = RATE_BANDWIDTH_20M;
register u8 flag = rates[i].flags;
if (rates[i].idx < 0) {
policy->rate_entrys[i] = XRADIO_INVALID_RATE_ENTRY;
continue;
}
/* set rate index and retry counter. */
tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[i]);
entry_idx = xradio_rate_to_entry_idx(flag, tmp_rate->hw_value);
policy->rate_entrys[i] = ((entry_idx & 0xf) << RATEINDEX_SHIFT)
| (rates[i].count & 0xf);
/* set modem_type and bandwidth. */
if (flag & IEEE80211_TX_RC_MCS)
modem_type = RATE_MODEM_HTOFDM;
if (flag & IEEE80211_TX_RC_40_MHZ_WIDTH)
bandwidth = RATE_BANDWIDTH_40M;
policy->rate_entrys[i] |= ((modem_type << MODEMTYPE_SHIFT) |
(bandwidth << BANDWIDTH_SHIFT));
/* set short preamble in DSSS */
policy->rate_entrys[i] |= MAKE_PREAMBLE_S(flag
& IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
/* set short GI */
policy->rate_entrys[i] |= MAKE_SGI(flag
& IEEE80211_TX_RC_SHORT_GI);
/*
* set green field mode.
* there is a conflict between sgi and green field in one stream,
* and we prefer to use sgi instead of green field
*/
if (priv->association_mode.PhyModeCfg.GF_Enable
&& (modem_type == RATE_MODEM_HTOFDM)) {
if (!(GET_RATE_ENTRY_FLAGS(policy->rate_entrys[i]) & FW_RATE_F_SGI))
policy->rate_entrys[i] |= MAKE_GF(1);
}
/* set STBC, but unsurported in xradio yet. */
policy->rate_entrys[i] |= MAKE_STBC(0);
policy->defined++;
policy->retry_count += (rates[i].count & 0xf);
txrx_printk(XRADIO_DBG_NIY, "[TX policy] rate_entrys=0x%04x",
policy->rate_entrys[i]);
}
#else
tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[0]);
if (tmp_rate)
policy->defined = tmp_rate->hw_value + 1;
#ifdef ENHANCE_ANTI_INTERFERE
/*
* We need to check if 11b rate be supported.
* We only add 11bg rate when no rate below 5.5Mbps is set,
* and last rate has enough reties. Add we need check fw version too.
*/
if (WSM_CAPS_11N_TO_11BG(hw_priv->wsm_caps) && !priv->vif->p2p &&
((priv->oper_rates | priv->base_rates) & 0xf) == 0xf &&
(rates[0].flags & IEEE80211_TX_RC_MCS) &&
rates[count-1].idx == 0 && rates[count-1].count >= 3) {
u8 mcs0_cnt = rates[count-1].count;
u8 cnt1 = (mcs0_cnt>>2);
u8 cnt2 = ((mcs0_cnt - (cnt1<<1))>>1);
if (count <= 1) {
mcs0_cnt -= (cnt1 + cnt1 + cnt2);
rates[count-1].count = 0;
policy->tbl[0] |= ((cnt1&0xf)<<(3<<2));
policy->tbl[0] |= ((cnt2&0xf)<<(2<<2));
policy->tbl[0] |= ((cnt1&0xf)<<(1<<2));
policy->tbl[0] |= ((mcs0_cnt&0xf)<<(0<<2));
policy->retry_count = cnt1 + cnt2 + cnt1 + mcs0_cnt;
txrx_printk(XRADIO_DBG_MSG,
"[TX policy]to11b=%d(f=%d),%d, 11=%d, 5.5=%d, 2=%d, 1=%d\n",
rates[count-1].idx, rates[0].flags,
rates[count-1].count, cnt1, cnt2, cnt1, mcs0_cnt);
} else {
mcs0_cnt -= (cnt1 + cnt1 + cnt2);
rates[count-1].count = cnt1;
policy->tbl[0] |= ((cnt1&0xf)<<(3<<2));
policy->tbl[0] |= ((cnt2&0xf)<<(2<<2));
policy->tbl[0] |= ((mcs0_cnt&0xf)<<(0<<2));
policy->retry_count = cnt1 + cnt2 + mcs0_cnt;
txrx_printk(XRADIO_DBG_MSG,
"[TX policy]to11b=%d(f=%d),%d, 11=%d, 5.5=%d, 2=%d, 1=%d\n",
rates[count-1].idx, rates[0].flags,
rates[count-1].count, cnt1, cnt2, 0, mcs0_cnt);
}
} else {
txrx_printk(XRADIO_DBG_MSG,
"[TX policy]WSM_CAPS to11b=%d, p2p=%d, MSC=%d, rates=0x%08x, count(%d)=%d\n",
!!WSM_CAPS_11N_TO_11BG(hw_priv->wsm_caps), priv->vif->p2p,
!!(rates[0].flags & IEEE80211_TX_RC_MCS),
(priv->oper_rates | priv->base_rates),
count-1, rates[count-1].count);
}
#endif
for (i = 0; i < count; ++i) {
register unsigned rateid, off, shift, retries;
tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[i]);
if (tmp_rate) {
rateid = tmp_rate->hw_value;
} else {
break;
}
off = rateid >> 3; /* eq. rateid / 8 */
shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */
retries = rates[i].count;
if (unlikely(retries > 0x0F))
rates[i].count = retries = 0x0F;
policy->tbl[off] |= __cpu_to_le32(retries << shift);
policy->retry_count += retries;
txrx_printk(XRADIO_DBG_MSG, "[TX policy] %d.%dMps=%d",
tmp_rate->bitrate/10, tmp_rate->bitrate%10, retries);
}
#endif
txrx_printk(XRADIO_DBG_MSG, "[TX policy] Dst Policy (%zu): " \
"%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n",
count,
rates[0].idx, rates[0].count,
rates[1].idx, rates[1].count,
rates[2].idx, rates[2].count,
rates[3].idx, rates[3].count,
rates[4].idx, rates[4].count);
}
#ifdef SUPPORT_HT40
static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
const struct tx_policy *cached)
{
size_t count = wanted->defined << 1;
if (wanted->defined != cached->defined)
return false;
if (count) {
if (memcmp(wanted->raw, cached->raw, count))
return false;
}
return true;
}
#else
static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
const struct tx_policy *cached)
{
size_t count = wanted->defined >> 1;
if (wanted->defined > cached->defined)
return false;
if (count) {
if (memcmp(wanted->raw, cached->raw, count))
return false;
}
if (wanted->defined & 1) {
if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F))
return false;
}
return true;
}
#endif
static int tx_policy_find(struct tx_policy_cache *cache,
const struct tx_policy *wanted)
{
/* O(n) complexity. Not so good, but there's only 8 entries in
* the cache.
* Also lru helps to reduce search time. */
struct tx_policy_cache_entry *it;
/* Search for policy in "used" list */
list_for_each_entry(it, &cache->used, link) {
if (tx_policy_is_equal(wanted, &it->policy))
return it - cache->cache;
}
/* Then - in "free list" */
list_for_each_entry(it, &cache->free, link) {
if (tx_policy_is_equal(wanted, &it->policy))
return it - cache->cache;
}
return -1;
}
static inline void tx_policy_use(struct tx_policy_cache *cache,
struct tx_policy_cache_entry *entry)
{
++entry->policy.usage_count;
list_move(&entry->link, &cache->used);
}
static inline int tx_policy_release(struct tx_policy_cache *cache,
struct tx_policy_cache_entry *entry)
{
int ret = --entry->policy.usage_count;
if (!ret)
list_move(&entry->link, &cache->free);
return ret;
}
/* ******************************************************************** */
/* External TX policy cache API */
void tx_policy_init(struct xradio_common *hw_priv)
{
struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
int i;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
memset(cache, 0, sizeof(*cache));
spin_lock_init(&cache->lock);
INIT_LIST_HEAD(&cache->used);
INIT_LIST_HEAD(&cache->free);
for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i)
list_add(&cache->cache[i].link, &cache->free);
}
static int tx_policy_get(struct xradio_vif *priv,
struct ieee80211_tx_rate *rates,
u8 use_bg_rate, bool *renew)
{
int idx;
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
struct tx_policy wanted;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (use_bg_rate) {
#ifdef SUPPORT_HT40
memset(&wanted, 0, sizeof(wanted));
wanted.retry_count = (hw_priv->short_frame_max_tx_count&0xf);
wanted.defined = 1;
/* set rate index and retry counter. */
wanted.rate_entrys[0] = (((use_bg_rate & 0xf)<<4)
| (wanted.retry_count & 0xf));
/* set modem_type and bandwidth. */
wanted.rate_entrys[0] |=
((RATE_MODEM_LEGACY << MODEMTYPE_SHIFT)
| (RATE_BANDWIDTH_20M << BANDWIDTH_SHIFT));
/* set short preamble in DSSS */
wanted.rate_entrys[0] |= MAKE_PREAMBLE_S(rates[0].flags
& IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
txrx_printk(XRADIO_DBG_NIY, "[TX policy] robust rate=0x%04x\n",
wanted.rate_entrys[0]);
#else
u8 rate = (u8)(use_bg_rate & 0x3f);
u8 shitf = ((rate&0x7)<<2);
u8 off = (rate>>3);
memset(&wanted, 0, sizeof(wanted));
wanted.defined = rate + 1;
wanted.retry_count = (hw_priv->short_frame_max_tx_count&0xf);
wanted.tbl[off] = wanted.retry_count<<shitf;
txrx_printk(XRADIO_DBG_NIY, "[TX policy] robust rate=%d\n", rate);
#endif
} else
tx_policy_build(priv, &wanted, rates, IEEE80211_TX_MAX_RATES);
/* use rate policy instead of minstel policy in debug mode*/
#ifdef CONFIG_XRADIO_DEBUGFS
if (rates_dbg_en & 0x2) {
memset(&wanted, 0, sizeof(wanted));
#ifdef SUPPORT_HT40
wanted.defined = Ratecnt_dbg;
wanted.retry_count = (hw_priv->short_frame_max_tx_count&0xf);
memcpy(&wanted.rate_entrys[0], &rates_debug[0],
sizeof(wanted.rate_entrys));
#else
wanted.defined = maxRate_dbg + 1;
wanted.retry_count = (hw_priv->short_frame_max_tx_count&0xf);
memcpy(&wanted.tbl[0], &rates_debug[0], sizeof(wanted.tbl));
#endif
}
#endif
spin_lock_bh(&cache->lock);
idx = tx_policy_find(cache, &wanted);
if (idx >= 0) {
txrx_printk(XRADIO_DBG_MSG, "[TX policy] Used TX policy: %d\n",
idx);
*renew = false;
} else if (WARN_ON_ONCE(list_empty(&cache->free))) {
spin_unlock_bh(&cache->lock);
txrx_printk(XRADIO_DBG_ERROR, "[TX policy] no policy cache\n");
return XRADIO_INVALID_RATE_ID;
} else {
struct tx_policy_cache_entry *entry;
/* If policy is not found create a new one
* using the oldest entry in "free" list */
*renew = true;
entry = list_entry(cache->free.prev,
struct tx_policy_cache_entry, link);
entry->policy = wanted;
idx = entry - cache->cache;
txrx_printk(XRADIO_DBG_MSG, "[TX policy] New TX policy: %d\n",
idx);
tx_policy_dump(&entry->policy);
}
tx_policy_use(cache, &cache->cache[idx]);
if (unlikely(list_empty(&cache->free)) &&
!cache->queue_locked) {
/* Lock TX queues. */
DBG_INT_ADD(policy_lock_cnt);
txrx_printk(XRADIO_DBG_WARN, "[TX policy] policy cache used up\n");
xradio_tx_queues_lock(hw_priv);
cache->queue_locked = true;
}
spin_unlock_bh(&cache->lock);
/*force to upload retry limit when using debug rate policy */
#ifdef CONFIG_XRADIO_DEBUGFS
if (retry_dbg & 0x2) {
retry_dbg &= ~0x2;
/* retry dgb need to be applied to policy. */
*renew = true;
cache->cache[idx].policy.uploaded = 0;
}
#endif
return idx;
}
static void tx_policy_put(struct xradio_common *hw_priv, int idx)
{
int usage;
/*int locked;*/
struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
spin_lock_bh(&cache->lock);
/*locked = list_empty(&cache->free);*/
usage = tx_policy_release(cache, &cache->cache[idx]);
if (unlikely(cache->queue_locked) && !list_empty(&cache->free)) {
/* Unlock TX queues. */
xradio_tx_queues_unlock(hw_priv);
cache->queue_locked = false;
}
spin_unlock_bh(&cache->lock);
}
/*
bool tx_policy_cache_full(struct xradio_common *hw_priv)
{
bool ret;
struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
spin_lock_bh(&cache->lock);
ret = list_empty(&cache->free);
spin_unlock_bh(&cache->lock);
return ret;
}
*/
extern u32 policy_upload;
extern u32 policy_num;
static int tx_policy_upload(struct xradio_common *hw_priv)
{
struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
int i;
struct wsm_set_tx_rate_retry_policy arg = {
.hdr = {
.numTxRatePolicies = 0,
}
};
int if_id = 0;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
spin_lock_bh(&cache->lock);
/* Upload only modified entries. */
for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) {
struct tx_policy *src = &cache->cache[i].policy;
if (src->retry_count && !src->uploaded) {
struct wsm_set_tx_rate_retry_policy_policy *dst =
&arg.tbl[arg.hdr.numTxRatePolicies];
dst->policyIndex = i;
dst->shortRetryCount = hw_priv->short_frame_max_tx_count-1;
/* only RTS need use longRetryCount, should be short_frame. */
dst->longRetryCount = hw_priv->short_frame_max_tx_count-1;
/* BIT(2) - Terminate retries when Tx rate retry policy
* finishes.
* BIT(3) - Count initial frame transmission as part of
* rate retry counting but not as a retry
* attempt */
dst->policyFlags = BIT(2) | BIT(3);
#ifdef SUPPORT_HT40
memcpy(dst->rate_entrys, src->rate_entrys,
sizeof(dst->rate_entrys));
#else
memcpy(dst->rateCountIndices, src->tbl,
sizeof(dst->rateCountIndices));
#endif
src->uploaded = 1;
++arg.hdr.numTxRatePolicies;
}
}
spin_unlock_bh(&cache->lock);
atomic_set(&hw_priv->upload_count, 0);
xradio_debug_tx_cache_miss(hw_priv);
txrx_printk(XRADIO_DBG_MSG, "[TX policy] Upload %d policies\n",
arg.hdr.numTxRatePolicies);
#ifdef CONFIG_XRADIO_DEBUGFS
#ifndef SUPPORT_HT40
if (arg.tbl[0].policyIndex == 7)
txrx_printk(XRADIO_DBG_MSG, "rate:0x%08x, 0x%08x, 0x%08x\n",
arg.tbl[0].rateCountIndices[2],
arg.tbl[0].rateCountIndices[1],
arg.tbl[0].rateCountIndices[0]);
#endif
policy_upload++;
policy_num += arg.hdr.numTxRatePolicies;
#endif
/*TODO: COMBO*/
return wsm_set_tx_rate_retry_policy(hw_priv, &arg, if_id);
}
void tx_policy_upload_work(struct work_struct *work)
{
struct xradio_common *hw_priv =
container_of(work, struct xradio_common, tx_policy_upload_work);
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
SYS_WARN(tx_policy_upload(hw_priv));
wsm_unlock_tx(hw_priv);
}
/* ******************************************************************** */
/* xradio TX implementation */
struct xradio_txinfo {
struct sk_buff *skb;
unsigned queue;
struct ieee80211_tx_info *tx_info;
const struct ieee80211_rate *rate;
struct ieee80211_hdr *hdr;
size_t hdrlen;
const u8 *da;
struct xradio_sta_priv *sta_priv;
struct xradio_txpriv txpriv;
};
u32 xradio_rate_mask_to_wsm(struct xradio_common *hw_priv, u32 rates)
{
u32 ret = 0;
int i;
u32 n_bitrates =
hw_priv->hw->wiphy->bands[hw_priv->channel->band]->n_bitrates;
struct ieee80211_rate *bitrates =
hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates;
for (i = 0; i < n_bitrates; ++i) {
if (rates & BIT(i))
ret |= BIT(bitrates[i].hw_value);
}
return ret;
}
static const struct ieee80211_rate *
xradio_get_tx_rate(const struct xradio_common *hw_priv,
const struct ieee80211_tx_rate *rate)
{
if (rate->idx < 0)
return NULL;
if (rate->flags & IEEE80211_TX_RC_MCS)
return &hw_priv->mcs_rates[rate->idx];
return &hw_priv->hw->wiphy->bands[hw_priv->channel->band]->
bitrates[rate->idx];
}
#ifdef SUPPORT_HT40
u16 xradio_get_rate_entry(const struct xradio_common *hw_priv,
u8 Bandwidth, u16 FormatFlag, u8 hw_value)
{
u16 rate_entry = XRADIO_INVALID_RATE_ENTRY;
u8 modem_type = 0;
s8 rate_idx = hw_value;
if (rate_idx <= MAX_RATES_IDX) {
if (rate_idx >= MCS_RATES_OFF) {
rate_idx -= MCS_RATES_OFF;
modem_type = RATE_MODEM_HTOFDM;
}
rate_entry = rate_idx << RATEINDEX_SHIFT;
rate_entry |= (modem_type & MODEMTYPE_MASK) << MODEMTYPE_SHIFT;
rate_entry |= (Bandwidth & BANDWIDTH_MASK) << BANDWIDTH_SHIFT;
rate_entry |= (FormatFlag & RATE_F_MASK);
}
return rate_entry;
}
#else
static inline s8 xradio_get_rate_idx(const struct xradio_common *hw_priv,
u8 flag, u16 hw_value)
{
s16 ret = (s16)hw_value;
if (flag & IEEE80211_TX_RC_MCS) { /* 11n */
if (hw_value <= hw_priv->mcs_rates[7].hw_value &&
hw_value >= hw_priv->mcs_rates[0].hw_value)
ret -= hw_priv->mcs_rates[0].hw_value;
else
ret = -1;
} else { /* 11b/g */
if (hw_value > 5 && hw_value < hw_priv->mcs_rates[0].hw_value) {
ret -= hw_priv->hw->wiphy-> \
bands[hw_priv->channel->band]->bitrates[0].hw_value;
if (hw_priv->hw->wiphy-> \
bands[hw_priv->channel->band]->bitrates[0].hw_value < 5) /* 11a*/
ret -= 2;
} else if (hw_value < 4) {
ret -= hw_priv->hw->wiphy-> \
bands[hw_priv->channel->band]->bitrates[0].hw_value;
} else {
ret = -1;
}
}
return (s8)ret;
}
#endif
static int
xradio_tx_h_calc_link_ids(struct xradio_vif *priv,
struct xradio_txinfo *t)
{
#ifndef P2P_MULTIVIF
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
#endif
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
#ifndef P2P_MULTIVIF
if ((t->tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
(hw_priv->roc_if_id == priv->if_id))
t->txpriv.offchannel_if_id = 2;
else
t->txpriv.offchannel_if_id = 0;
#endif
if (likely(t->tx_info->control.sta && t->sta_priv->link_id))
t->txpriv.raw_link_id =
t->txpriv.link_id =
t->sta_priv->link_id;
else if (priv->mode != NL80211_IFTYPE_AP)
t->txpriv.raw_link_id =
t->txpriv.link_id = 0;
else if (is_multicast_ether_addr(t->da)) {
if (priv->enable_beacon) {
t->txpriv.raw_link_id = 0;
t->txpriv.link_id = priv->link_id_after_dtim;
} else {
t->txpriv.raw_link_id = 0;
t->txpriv.link_id = 0;
}
} else {
t->txpriv.link_id =
xradio_find_link_id(priv, t->da);
/* Do not assign valid link id for deauth/disassoc frame being
transmitted to an unassociated STA */
if (!(t->txpriv.link_id) &&
(ieee80211_is_deauth(t->hdr->frame_control) ||
ieee80211_is_disassoc(t->hdr->frame_control))) {
t->txpriv.link_id = 0;
} else {
if (!t->txpriv.link_id)
t->txpriv.link_id = xradio_alloc_link_id(priv, t->da);
if (!t->txpriv.link_id) {
txrx_printk(XRADIO_DBG_ERROR,
"%s: No more link IDs available.\n", __func__);
return -ENOENT;
}
}
t->txpriv.raw_link_id = t->txpriv.link_id;
}
if (t->txpriv.raw_link_id)
priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp =
jiffies;
#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
if (t->tx_info->control.sta &&
(t->tx_info->control.sta->uapsd_queues & BIT(t->queue)))
t->txpriv.link_id = priv->link_id_uapsd;
#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
return 0;
}
static void
xradio_tx_h_pm(struct xradio_vif *priv,
struct xradio_txinfo *t)
{
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (unlikely(ieee80211_is_auth(t->hdr->frame_control))) {
u32 mask = ~BIT(t->txpriv.raw_link_id);
spin_lock_bh(&priv->ps_state_lock);
priv->sta_asleep_mask &= mask;
priv->pspoll_mask &= mask;
spin_unlock_bh(&priv->ps_state_lock);
}
}
static void
xradio_tx_h_calc_tid(struct xradio_vif *priv,
struct xradio_txinfo *t)
{
if (ieee80211_is_data_qos(t->hdr->frame_control)) {
u8 *qos = ieee80211_get_qos_ctl(t->hdr);
t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK;
} else if (ieee80211_is_data(t->hdr->frame_control)) {
t->txpriv.tid = 0;
}
}
/* IV/ICV injection. */
/* TODO: Quite unoptimal. It's better co modify mac80211
* to reserve space for IV */
static int
xradio_tx_h_crypt(struct xradio_vif *priv,
struct xradio_txinfo *t)
{
size_t iv_len;
size_t icv_len;
u8 *icv;
u8 *newhdr;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (!t->tx_info->control.hw_key ||
!(t->hdr->frame_control &
__cpu_to_le32(IEEE80211_FCTL_PROTECTED)))
return 0;
iv_len = t->tx_info->control.hw_key->iv_len;
icv_len = t->tx_info->control.hw_key->icv_len;
#ifdef AP_ARP_COMPAT_FIX
t->txpriv.iv_len = iv_len;
#endif
if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
icv_len += 8; /* MIC */
if ((skb_headroom(t->skb) + skb_tailroom(t->skb) <
iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) ||
(skb_headroom(t->skb) <
iv_len + WSM_TX_EXTRA_HEADROOM)) {
txrx_printk(XRADIO_DBG_ERROR,
"Bug: no space allocated for crypto headers.\n"
"headroom: %d, tailroom: %d, "
"req_headroom: %zu, req_tailroom: %zu\n"
"Please fix it in xradio_get_skb().\n",
skb_headroom(t->skb), skb_tailroom(t->skb),
iv_len + WSM_TX_EXTRA_HEADROOM, icv_len);
return -ENOMEM;
} else if (skb_tailroom(t->skb) < icv_len) {
size_t offset = icv_len - skb_tailroom(t->skb);
u8 *p;
txrx_printk(XRADIO_DBG_ERROR,
"Slowpath: tailroom is not big enough. "
"Req: %zu, got: %d.\n",
icv_len, skb_tailroom(t->skb));
p = skb_push(t->skb, offset);
memmove(p, &p[offset], t->skb->len - offset);
skb_trim(t->skb, t->skb->len - offset);
}
newhdr = skb_push(t->skb, iv_len);
memmove(newhdr, newhdr + iv_len, t->hdrlen);
t->hdr = (struct ieee80211_hdr *) newhdr;
t->hdrlen += iv_len;
icv = skb_put(t->skb, icv_len);
return 0;
}
#ifdef SUPPORT_HT40
static int xradio_tx_h_align(struct xradio_vif *priv, struct xradio_txinfo *t)
#else
static int
xradio_tx_h_align(struct xradio_vif *priv, struct xradio_txinfo *t,
u8 *flags)
#endif
{
size_t offset = (size_t)t->skb->data & 3;
u8 *newhdr;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (!offset)
return 0;
if (skb_headroom(t->skb) < offset) {
txrx_printk(XRADIO_DBG_ERROR,
"Bug: no space allocated "
"for DMA alignment.\n"
"headroom: %d\n",
skb_headroom(t->skb));
return -ENOMEM;
}
/* offset = 1or3 process */
if (offset & 1) {
newhdr = skb_push(t->skb, offset);
memmove(newhdr, newhdr + offset, t->skb->len-offset);
skb_trim(t->skb, t->skb->len-offset);
t->hdr = (struct ieee80211_hdr *) newhdr;
xradio_debug_tx_align(priv);
return 0;
}
/* offset=2 process */
skb_push(t->skb, offset);
t->hdrlen += offset;
t->txpriv.offset += offset;
#ifndef SUPPORT_HT40
*flags |= WSM_TX_2BYTES_SHIFT;
#endif
xradio_debug_tx_align(priv);
return 0;
}
static int
xradio_tx_h_action(struct xradio_vif *priv, struct xradio_txinfo *t)
{
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)t->hdr;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (ieee80211_is_action(t->hdr->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK)
return 1;
else
return 0;
}
/* Add WSM header */
static struct wsm_tx *
xradio_tx_h_wsm(struct xradio_vif *priv, struct xradio_txinfo *t)
{
struct wsm_tx *wsm;
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) {
txrx_printk(XRADIO_DBG_ERROR,
"Bug: no space allocated "
"for WSM header.\n"
"headroom: %d\n",
skb_headroom(t->skb));
return NULL;
}
wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx));
t->txpriv.offset += sizeof(struct wsm_tx);
memset(wsm, 0, sizeof(*wsm));
#ifdef SUPPORT_HT40
wsm->DataOffset = t->txpriv.offset;
#endif
wsm->hdr.len = __cpu_to_le16(t->skb->len);
wsm->hdr.id = __cpu_to_le16(0x0004);
wsm->queueId = (t->txpriv.raw_link_id << 2) | wsm_queue_id_to_wsm(t->queue);
if (wsm->hdr.len > hw_priv->wsm_caps.sizeInpChBuf) {
txrx_printk(XRADIO_DBG_ERROR, "%s,msg length too big=%d\n",
__func__, wsm->hdr.len);
skb_pull(t->skb, sizeof(struct wsm_tx)); //skb revert.
wsm = NULL;
}
return wsm;
}
/* BT Coex specific handling */
static void xradio_tx_h_bt(struct xradio_vif *priv, struct xradio_txinfo *t,
struct wsm_tx *wsm)
{
u8 priority = 0;
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (!hw_priv->is_BT_Present)
return;
if (unlikely(ieee80211_is_nullfunc(t->hdr->frame_control)))
priority = WSM_EPTA_PRIORITY_MGT;
else if (ieee80211_is_data(t->hdr->frame_control)) {
/* Skip LLC SNAP header (+6) */
u8 *payload = &t->skb->data[t->hdrlen];
u16 *ethertype = (u16 *) &payload[6];
if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE)))
priority = WSM_EPTA_PRIORITY_EAPOL;
} else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) ||
ieee80211_is_reassoc_req(t->hdr->frame_control))) {
struct ieee80211_mgmt *mgt_frame =
(struct ieee80211_mgmt *)t->hdr;
if (mgt_frame->u.assoc_req.listen_interval <
priv->listen_interval) {
txrx_printk(XRADIO_DBG_MSG,
"Modified Listen Interval to %d from %d\n",
priv->listen_interval,
mgt_frame->u.assoc_req.listen_interval);
/* Replace listen interval derieved from
* the one read from SDD */
mgt_frame->u.assoc_req.listen_interval =
priv->listen_interval;
}
}
if (likely(!priority)) {
if (ieee80211_is_action(t->hdr->frame_control))
priority = WSM_EPTA_PRIORITY_ACTION;
else if (ieee80211_is_mgmt(t->hdr->frame_control))
priority = WSM_EPTA_PRIORITY_MGT;
else if ((wsm->queueId == WSM_QUEUE_VOICE))
priority = WSM_EPTA_PRIORITY_VOICE;
else if ((wsm->queueId == WSM_QUEUE_VIDEO))
priority = WSM_EPTA_PRIORITY_VIDEO;
else
priority = WSM_EPTA_PRIORITY_DATA;
}
txrx_printk(XRADIO_DBG_MSG, "[TX] EPTA priority %d.\n",
priority);
#ifdef SUPPORT_HT40
wsm->EptaPriority = priority;
#else
wsm->flags |= priority << 1;
#endif
}
static int
xradio_tx_h_rate_policy(struct xradio_vif *priv,
struct xradio_txinfo *t,
struct wsm_tx *wsm)
{
bool tx_policy_renew = false;
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
/*use debug policy for data frames only*/
#ifdef CONFIG_XRADIO_DEBUGFS
if ((rates_dbg_en & 0x1) && ieee80211_is_data(t->hdr->frame_control)) {
rates_dbg_en |= 0x02;
}
#endif
t->txpriv.rate_id = tx_policy_get(priv,
t->tx_info->control.rates, t->txpriv.use_bg_rate,
&tx_policy_renew);
if (t->txpriv.rate_id == XRADIO_INVALID_RATE_ID)
return -EFAULT;
#ifdef SUPPORT_HT40
wsm->TxPolicyIndex = t->txpriv.rate_id;
wsm->TxRateEntry =
hw_priv->tx_policy_cache.cache[t->txpriv.rate_id].policy.rate_entrys[0];
#else
wsm->flags |= t->txpriv.rate_id << 4;
t->rate = xradio_get_tx_rate(hw_priv, &t->tx_info->control.rates[0]);
if (t->txpriv.use_bg_rate)
wsm->maxTxRate = (u8)(t->txpriv.use_bg_rate & 0x3f);
else
wsm->maxTxRate = t->rate->hw_value;
#endif
#ifdef SUPPORT_HT40
/* set the first TxRateEntry and
clear the dataframe flag of rates_dbg_en */
#ifdef CONFIG_XRADIO_DEBUGFS
if (rates_dbg_en & 0x02) {
wsm->TxRateEntry = rates_debug[0];
rates_dbg_en &= ~0x2;
}
#endif
#else
/*set the maxTxRate and clear the dataframe flag of rates_dbg_en */
#ifdef CONFIG_XRADIO_DEBUGFS
if (rates_dbg_en & 0x02) {
wsm->maxTxRate = maxRate_dbg;
rates_dbg_en &= ~0x2;
}
#endif
if (t->rate->flags & IEEE80211_TX_RC_MCS) {
if (priv->association_mode.greenfieldMode)
wsm->htTxParameters |=
__cpu_to_le32(WSM_HT_TX_GREENFIELD);
else
wsm->htTxParameters |=
__cpu_to_le32(WSM_HT_TX_MIXED);
}
#endif
if (tx_policy_renew) {
txrx_printk(XRADIO_DBG_MSG, "[TX] TX policy renew.\n");
/* It's not so optimal to stop TX queues every now and then.
* Maybe it's better to reimplement task scheduling with
* a counter. */
/* xradio_tx_queues_lock(priv); */
/* Definetly better. TODO. */
if (atomic_add_return(1, &hw_priv->upload_count) == 1) {
wsm_lock_tx_async(hw_priv);
if (queue_work(hw_priv->workqueue,
&hw_priv->tx_policy_upload_work) <= 0) {
atomic_set(&hw_priv->upload_count, 0);
wsm_unlock_tx(hw_priv);
}
}
}
return 0;
}
static bool
xradio_tx_h_pm_state(struct xradio_vif *priv, struct xradio_txinfo *t)
{
int was_buffered = 1;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (t->txpriv.link_id == priv->link_id_after_dtim &&
!priv->buffered_multicasts) {
priv->buffered_multicasts = true;
if (priv->sta_asleep_mask)
queue_work(priv->hw_priv->workqueue,
&priv->multicast_start_work);
}
if (t->txpriv.raw_link_id && t->txpriv.tid < XRADIO_MAX_TID)
was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1]
.buffered[t->txpriv.tid]++;
return !was_buffered;
}
static void
xradio_tx_h_ba_stat(struct xradio_vif *priv,
struct xradio_txinfo *t)
{
struct xradio_common *hw_priv = priv->hw_priv;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (priv->join_status != XRADIO_JOIN_STATUS_STA)
return;
if (!xradio_is_ht(&hw_priv->ht_info))
return;
if (!priv->setbssparams_done)
return;
if (!ieee80211_is_data(t->hdr->frame_control))
return;
spin_lock_bh(&hw_priv->ba_lock);
hw_priv->ba_acc += t->skb->len - t->hdrlen;
if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
mod_timer(&hw_priv->ba_timer,
jiffies + XRADIO_BLOCK_ACK_INTERVAL);
}
hw_priv->ba_cnt++;
spin_unlock_bh(&hw_priv->ba_lock);
}
static int
xradio_tx_h_skb_pad(struct xradio_common *priv,
struct wsm_tx *wsm,
struct sk_buff *skb)
{
size_t len = __le16_to_cpu(wsm->hdr.len);
size_t padded_len = priv->sbus_ops->align_size(priv->sbus_priv, len);
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (SYS_WARN(skb_padto(skb, padded_len) != 0)) {
return -EINVAL;
}
return 0;
}
/* ******************************************************************** */
#if (defined(CONFIG_XRADIO_DEBUG))
u16 txparse_flags;
u16 rxparse_flags;
#endif
#if PERF_INFO_TEST
struct timeval mac_start_time;
#endif
void xradio_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct xradio_common *hw_priv = dev->priv;
struct xradio_txinfo t = {
.skb = skb,
.queue = skb_get_queue_mapping(skb),
.tx_info = IEEE80211_SKB_CB(skb),
.hdr = (struct ieee80211_hdr *)skb->data,
.txpriv.tid = XRADIO_MAX_TID,
.txpriv.rate_id = XRADIO_INVALID_RATE_ID,
#ifdef P2P_MULTIVIF
.txpriv.raw_if_id = 0,
#endif
.txpriv.use_bg_rate = 0,
#ifdef AP_ARP_COMPAT_FIX
.txpriv.iv_len = 0,
#endif
};
struct ieee80211_sta *sta;
struct wsm_tx *wsm;
bool tid_update = 0;
#if PERF_INFO_TEST
int date_len = skb->len;
#endif
#ifndef SUPPORT_HT40
u8 flags = 0;
#endif
int ret = 0;
struct xradio_vif *priv;
struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
PERF_INFO_GETTIME(&mac_start_time);
if (!skb->data)
SYS_BUG(1);
#ifdef HW_RESTART
if (hw_priv->hw_restart) {
txrx_printk(XRADIO_DBG_WARN, "%s, hw in reset.\n", __func__);
ret = __LINE__;
goto drop;
}
#endif
if (!(t.tx_info->control.vif)) {
ret = __LINE__;
goto drop;
}
priv = xrwl_get_vif_from_ieee80211(t.tx_info->control.vif);
if (!priv) {
ret = __LINE__;
goto drop;
}
if (atomic_read(&priv->enabled) == 0) {
ret = __LINE__;
goto drop;
}
#if (defined(CONFIG_XRADIO_DEBUG))
/* parse frame for debug. */
if (txparse_flags)
xradio_parse_frame(skb->data, 0, txparse_flags, priv->if_id);
#endif
/*
* dhcp and 8021 frames are important, use b/g rate and delay scan.
* it can make sense, such as accelerate connect.
*/
if (ieee80211_is_auth(frame->frame_control)) {
hw_priv->scan_delay_time[priv->if_id] = jiffies;
hw_priv->scan_delay_status[priv->if_id] = XRADIO_SCAN_DELAY;
} else if (ieee80211_is_data_present(frame->frame_control)) {
u8 *llc = skb->data+ieee80211_hdrlen(frame->frame_control);
if (is_dhcp(llc) || is_8021x(llc)) {
t.txpriv.use_bg_rate =
hw_priv->hw->wiphy-> \
bands[hw_priv->channel->band]->bitrates[0].hw_value;
if (priv->vif->p2p)
t.txpriv.use_bg_rate = AG_RATE_INDEX;
t.txpriv.use_bg_rate |= 0x80;
}
if (t.txpriv.use_bg_rate) {
hw_priv->scan_delay_time[priv->if_id] = jiffies;
hw_priv->scan_delay_status[priv->if_id] = XRADIO_SCAN_DELAY;
}
} else if (ieee80211_is_deauth(frame->frame_control) ||
ieee80211_is_disassoc(frame->frame_control)) {
hw_priv->scan_delay_status[priv->if_id] = XRADIO_SCAN_ALLOW;
}
#ifdef AP_HT_COMPAT_FIX
if (ieee80211_is_assoc_req(frame->frame_control) &&
priv->if_id == 0 && !(priv->ht_compat_det & 0x10)) {
xradio_remove_ht_ie(priv, skb);
}
#endif
#ifdef CONFIG_XRADIO_TESTMODE
spin_lock_bh(&hw_priv->tsm_lock);
if (hw_priv->start_stop_tsm.start) {
if (hw_priv->tsm_info.ac == t.queue)
hw_priv->tsm_stats.txed_msdu_count++;
}
spin_unlock_bh(&hw_priv->tsm_lock);
#endif /*CONFIG_XRADIO_TESTMODE*/
#ifdef TES_P2P_0002_ROC_RESTART
xradio_frame_monitor(hw_priv, skb, true);
#endif
if (ieee80211_is_action(frame->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
u8 *action = (u8 *)&mgmt->u.action.category;
xradio_check_go_neg_conf_success(hw_priv, action);
xradio_check_prov_desc_req(hw_priv, action);
}
t.txpriv.if_id = priv->if_id;
t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control);
t.da = ieee80211_get_DA(t.hdr);
t.sta_priv =
(struct xradio_sta_priv *)&t.tx_info->control.sta->drv_priv;
if (SYS_WARN(t.queue >= 4)) {
ret = __LINE__;
goto drop;
}
/*
spin_lock_bh(&hw_priv->tx_queue[t.queue].lock);
if ((priv->if_id == 0) &&
(hw_priv->tx_queue[t.queue].num_queued_vif[0] >=
hw_priv->vif0_throttle)) {
spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
ret = __LINE__;
goto drop;
} else if ((priv->if_id == 1) &&
(hw_priv->tx_queue[t.queue].num_queued_vif[1] >=
hw_priv->vif1_throttle)) {
spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
ret = __LINE__;
goto drop;
}
spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
*/
ret = xradio_tx_h_calc_link_ids(priv, &t);
if (ret) {
ret = __LINE__;
goto drop;
}
txrx_printk(XRADIO_DBG_MSG, "[TX] TX %d bytes (if_id: %d,"
" queue: %d, link_id: %d (%d)).\n",
skb->len, priv->if_id, t.queue, t.txpriv.link_id,
t.txpriv.raw_link_id);
xradio_tx_h_pm(priv, &t);
xradio_tx_h_calc_tid(priv, &t);
ret = xradio_tx_h_crypt(priv, &t);
if (ret) {
ret = __LINE__;
goto drop;
}
#ifdef SUPPORT_HT40
ret = xradio_tx_h_align(priv, &t);
#else
ret = xradio_tx_h_align(priv, &t, &flags);
#endif
if (ret) {
ret = __LINE__;
goto drop;
}
ret = xradio_tx_h_action(priv, &t);
if (ret) {
ret = __LINE__;
goto drop;
}
wsm = xradio_tx_h_wsm(priv, &t);
if (!wsm) {
ret = __LINE__;
goto drop;
}
#ifdef SUPPORT_HT40
#ifdef CONFIG_XRADIO_TESTMODE
wsm->ExpireTimeSetting = WSM_TX_FLAG_EXPIRY_TIME;
#endif /*CONFIG_XRADIO_TESTMODE*/
#else
#ifdef CONFIG_XRADIO_TESTMODE
flags |= WSM_TX_FLAG_EXPIRY_TIME;
#endif /*CONFIG_XRADIO_TESTMODE*/
wsm->flags |= flags;
#endif
xradio_tx_h_bt(priv, &t, wsm);
ret = xradio_tx_h_rate_policy(priv, &t, wsm);
if (ret) {
ret = __LINE__;
goto drop;
}
ret = xradio_tx_h_skb_pad(hw_priv, wsm, skb);
if (ret) {
ret = __LINE__;
goto drop;
}
rcu_read_lock();
sta = rcu_dereference(t.tx_info->control.sta);
xradio_tx_h_ba_stat(priv, &t);
spin_lock_bh(&priv->ps_state_lock);
tid_update = xradio_tx_h_pm_state(priv, &t);
SYS_BUG(xradio_queue_put(&hw_priv->tx_queue[t.queue],
t.skb, &t.txpriv));
#ifdef ROC_DEBUG
txrx_printk(XRADIO_DBG_ERROR, "QPUT %x, %pM, if_id - %d\n",
t.hdr->frame_control, t.da, priv->if_id);
#endif
spin_unlock_bh(&priv->ps_state_lock);
/* To improve tcp tx in linux4.9
* skb_orphan will tell tcp that driver has processed this skb,
* so tcp can send other skb to driver.
* If this is a retransmitted frame by umac, driver do not skb_orphan it again.
*/
if (!(t.tx_info->flags & IEEE80211_TX_INTFL_RETRANSMISSION))
skb_orphan(skb);
#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
if (tid_update && sta)
mac80211_sta_set_buffered(sta,
t.txpriv.tid, true);
#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
rcu_read_unlock();
xradio_proc_wakeup(hw_priv);
PERF_INFO_STAMP(&mac_start_time, &mac_tx, date_len);
return;
drop:
txrx_printk(XRADIO_DBG_WARN, "drop=%d, fctl=0x%04x.\n",
ret, frame->frame_control);
if (!(t.tx_info->flags & IEEE80211_TX_INTFL_RETRANSMISSION))
skb_orphan(skb);
xradio_skb_post_gc(hw_priv, skb, &t.txpriv);
return;
}
/* ******************************************************************** */
static int xradio_handle_pspoll(struct xradio_vif *priv,
struct sk_buff *skb)
{
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
struct ieee80211_sta *sta;
struct ieee80211_pspoll *pspoll =
(struct ieee80211_pspoll *) skb->data;
int link_id = 0;
u32 pspoll_mask = 0;
int drop = 1;
int i;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (priv->join_status != XRADIO_JOIN_STATUS_AP)
goto done;
if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN))
goto done;
rcu_read_lock();
sta = mac80211_find_sta(priv->vif, pspoll->ta);
if (sta) {
struct xradio_sta_priv *sta_priv;
sta_priv = (struct xradio_sta_priv *)&sta->drv_priv;
link_id = sta_priv->link_id;
pspoll_mask = BIT(sta_priv->link_id);
}
rcu_read_unlock();
if (!link_id)
goto done;
priv->pspoll_mask |= pspoll_mask;
drop = 0;
/* Do not report pspols if data for given link id is
* queued already. */
for (i = 0; i < 4; ++i) {
if (xradio_queue_get_num_queued(priv,
&hw_priv->tx_queue[i],
pspoll_mask)) {
xradio_proc_wakeup(hw_priv);
drop = 1;
break;
}
}
txrx_printk(XRADIO_DBG_NIY, "[RX] PSPOLL: %s\n", drop ? "local" : "fwd");
done:
return drop;
}
/* ******************************************************************** */
extern u32 tx_retrylimit;
extern u32 tx_over_limit;
extern u32 tx_lower_limit;
extern int retry_mis;
#ifdef SUPPORT_HT40
void xradio_get_ieee80211_tx_rate(struct xradio_common *hw_priv,
u16 RateEntry, struct ieee80211_tx_rate *rates)
{
if (GET_RATE_ENTRY_RATEINDEX(RateEntry) == FW_RATE_USE_DEFAULT) {
rates->idx = -1;
rates->count = 0;
rates->flags = 0;
return;
}
if (GET_RATE_ENTRY_MODEM(RateEntry) == FW_RATE_MODEM_HTOFDM) {
rates->idx = GET_RATE_ENTRY_RATEINDEX(RateEntry);
rates->count = GET_RATE_ENTRY_MAXRETRY(RateEntry);
rates->flags = IEEE80211_TX_RC_MCS;
if (GET_RATE_ENTRY_BANDWIDTH(RateEntry) == FW_RATE_BW_40M)
rates->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (GET_RATE_ENTRY_FLAGS(RateEntry) & FW_RATE_F_GF)
rates->flags |= IEEE80211_TX_RC_GREEN_FIELD;
if (GET_RATE_ENTRY_FLAGS(RateEntry) & FW_RATE_F_SGI)
rates->flags |= IEEE80211_TX_RC_SHORT_GI;
} else {
rates->idx = LegacyRxedRateLut[hw_priv->channel->band] \
[GET_RATE_ENTRY_RATEINDEX(RateEntry)];
rates->count = GET_RATE_ENTRY_MAXRETRY(RateEntry);
if (GET_RATE_ENTRY_FLAGS(RateEntry) & FW_RATE_F_SPRE)
rates->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE;
}
return;
}
#endif
void xradio_tx_confirm_cb(struct xradio_common *hw_priv,
struct wsm_tx_confirm *arg)
{
u8 queue_id = xradio_queue_get_queue_id(arg->packetID);
struct xradio_queue *queue = &hw_priv->tx_queue[queue_id];
struct sk_buff *skb;
const struct xradio_txpriv *txpriv;
struct xradio_vif *priv;
u32 feedback_retry = 0;
#ifdef SUPPORT_HT40
if (arg->status) {
txrx_printk(XRADIO_DBG_WARN, "status=%d, retry=%d, lastRate=0x%04x\n",
arg->status, arg->ackFailures, arg->txedRateEntry);
} else {
txrx_printk(XRADIO_DBG_MSG, "status=%d, retry=%d, lastRate=0x%04x\n",
arg->status, arg->ackFailures, arg->txedRateEntry);
}
#else
if (arg->status) {
txrx_printk(XRADIO_DBG_NIY, "status=%d, retry=%d, lastRate=%d\n",
arg->status, arg->ackFailures, arg->txedRate);
} else {
txrx_printk(XRADIO_DBG_MSG, "status=%d, retry=%d, lastRate=%d\n",
arg->status, arg->ackFailures, arg->txedRate);
}
#endif
#ifdef TES_P2P_0002_ROC_RESTART
if ((TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) &&
(arg->packetID == TES_P2P_0002_packet_id)) {
if (arg->status == 0x00) {
struct timeval TES_P2P_0002_tmval;
s32 TES_P2P_0002_roc_time;
s32 TES_P2P_0002_now_sec;
s32 TES_P2P_0002_now_usec;
bool TES_P2P_0002_roc_rst_need;
do_gettimeofday(&TES_P2P_0002_tmval);
TES_P2P_0002_roc_rst_need = false;
TES_P2P_0002_now_sec = (s32)(TES_P2P_0002_tmval.tv_sec);
TES_P2P_0002_now_usec = (s32)(TES_P2P_0002_tmval.tv_usec);
TES_P2P_0002_roc_time = TES_P2P_0002_roc_dur -
(((TES_P2P_0002_now_sec - TES_P2P_0002_roc_sec) * 1000) +
((TES_P2P_0002_now_usec - TES_P2P_0002_roc_usec) / 1000));
/* tx rsp to rx cfm will need more than 60ms */
if (TES_P2P_0002_roc_time < 100) {
TES_P2P_0002_roc_time = 100;
TES_P2P_0002_roc_rst_need = true;
}
if (TES_P2P_0002_roc_rst_need == true) {
txrx_printk(XRADIO_DBG_WARN,
"[ROC RESTART ACTIVE ON][Confirm CallBack]");
cancel_delayed_work_sync(&hw_priv->rem_chan_timeout);
if (atomic_read(&hw_priv->remain_on_channel)) {
queue_delayed_work(hw_priv->spare_workqueue,
&hw_priv->rem_chan_timeout,
(TES_P2P_0002_roc_time) * HZ / 1000);
}
}
}
TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
txrx_printk(XRADIO_DBG_NIY,
"[ROC_RESTART_STATE_IDLE][Confirm CallBack]");
}
#endif
if (unlikely(xradio_itp_tx_running(hw_priv)))
return;
priv = xrwl_hwpriv_to_vifpriv(hw_priv, arg->if_id);
if (unlikely(!priv))
return;
if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
/* STA is stopped. */
spin_unlock(&priv->vif_lock);
return;
}
if (SYS_WARN(queue_id >= 4)) {
spin_unlock(&priv->vif_lock);
return;
}
#ifdef CONFIG_XRADIO_TESTMODE
spin_lock_bh(&hw_priv->tsm_lock);
if ((arg->status == WSM_STATUS_RETRY_EXCEEDED) ||
(arg->status == WSM_STATUS_TX_LIFETIME_EXCEEDED)) {
hw_priv->tsm_stats.msdu_discarded_count++;
} else if ((hw_priv->start_stop_tsm.start) &&
(arg->status == WSM_STATUS_SUCCESS)) {
if (queue_id == hw_priv->tsm_info.ac) {
struct timeval tmval;
do_gettimeofday(&tmval);
u16 pkt_delay =
hw_priv->start_stop_tsm.packetization_delay;
if (hw_priv->tsm_info.sta_roamed &&
!hw_priv->tsm_info.use_rx_roaming) {
hw_priv->tsm_info.roam_delay = tmval.tv_usec -
hw_priv->tsm_info.txconf_timestamp_vo;
if (hw_priv->tsm_info.roam_delay > pkt_delay)
hw_priv->tsm_info.roam_delay -= pkt_delay;
txrx_printk(XRADIO_DBG_MSG, "[TX] txConf"
"Roaming: roam_delay = %u\n",
hw_priv->tsm_info.roam_delay);
hw_priv->tsm_info.sta_roamed = 0;
}
hw_priv->tsm_info.txconf_timestamp_vo = tmval.tv_usec;
}
}
spin_unlock_bh(&hw_priv->tsm_lock);
#endif /*CONFIG_XRADIO_TESTMODE*/
if ((arg->status == WSM_REQUEUE) &&
(arg->flags & WSM_TX_STATUS_REQUEUE)) {
/* "Requeue" means "implicit suspend" */
struct wsm_suspend_resume suspend = {
.link_id = arg->link_id,
.stop = 1,
.multicast = !arg->link_id,
.if_id = arg->if_id,
};
xradio_suspend_resume(priv, &suspend);
txrx_printk(XRADIO_DBG_NIY, "Requeue for link_id %d (try %d)."
" STAs asleep: 0x%.8X\n",
arg->link_id,
xradio_queue_get_generation(arg->packetID) + 1,
priv->sta_asleep_mask);
#ifdef CONFIG_XRADIO_TESTMODE
SYS_WARN(xradio_queue_requeue(hw_priv, queue,
arg->packetID, true));
#else
SYS_WARN(xradio_queue_requeue(queue,
arg->packetID, true));
#endif
spin_lock_bh(&priv->ps_state_lock);
if (!arg->link_id) {
priv->buffered_multicasts = true;
if (priv->sta_asleep_mask) {
queue_work(hw_priv->workqueue,
&priv->multicast_start_work);
}
}
spin_unlock_bh(&priv->ps_state_lock);
spin_unlock(&priv->vif_lock);
} else if (!xradio_queue_get_skb(
queue, arg->packetID, &skb, &txpriv)) {
struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *frame =
(struct ieee80211_hdr *)&skb->data[txpriv->offset];
int tx_count = arg->ackFailures;
u8 ht_flags = 0;
/*
* reset if_0 in firmware when STA-unjoined,
* fix the errors when switch APs in combo mode.
*/
if (unlikely(ieee80211_is_disassoc(frame->frame_control) ||
ieee80211_is_deauth(frame->frame_control))) {
if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
wsm_send_deauth_to_self(hw_priv, priv);
/* Shedule unjoin work */
txrx_printk(XRADIO_DBG_WARN,
"Issue unjoin command(TX) by self.\n");
wsm_lock_tx_async(hw_priv);
if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
wsm_unlock_tx(hw_priv);
}
}
#ifdef ROC_DEBUG
#ifndef P2P_MULTIVIF
if (txpriv->offchannel_if_id)
txrx_printk(XRADIO_DBG_ERROR, "TX CONFIRM %x - %d - %d\n",
skb->data[txpriv->offset],
txpriv->offchannel_if_id, arg->status);
#else
if (txpriv->if_id)
txrx_printk(XRADIO_DBG_ERROR, "TX CONFIRM %x - %d - %d\n",
skb->data[txpriv->offset],
txpriv->raw_if_id, arg->status);
#endif
#endif
#ifdef SUPPORT_HT40
if (priv->association_mode.PhyModeCfg.GF_Enable)
ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
#else
if (priv->association_mode.greenfieldMode)
ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
#endif
/* bss loss confirm. */
if (unlikely(priv->bss_loss_status == XRADIO_BSS_LOSS_CONFIRMING &&
priv->bss_loss_confirm_id == arg->packetID)) {
spin_lock(&priv->bss_loss_lock);
priv->bss_loss_status = arg->status ? XRADIO_BSS_LOSS_CONFIRMED :
XRADIO_BSS_LOSS_NONE;
spin_unlock(&priv->bss_loss_lock);
}
/*when less ap can't reply arp request accidentally,
*then disconnect actively,
*and wait system trigger reconnect again.
*/
#ifdef AP_ARP_COMPAT_FIX
if (likely(!arg->status) &&
(priv->join_status == XRADIO_JOIN_STATUS_STA) &&
(ieee80211_is_data(frame->frame_control))) {
u8 machdrlen = ieee80211_hdrlen(frame->frame_control);
u8 *llc_data = (u8 *)frame + machdrlen + txpriv->iv_len;
if (is_SNAP(llc_data) && is_arp(llc_data)) {
u8 *arp_hdr = llc_data + LLC_LEN;
u16 *arp_type = (u16 *)(arp_hdr + ARP_TYPE_OFFSET);
if (*arp_type == cpu_to_be16(ARP_REQUEST))
priv->arp_compat_cnt++;
if (priv->arp_compat_cnt > 10) {
txrx_printk(XRADIO_DBG_ERROR,
"ap don't reply arp resp count=%d\n",
priv->arp_compat_cnt);
priv->arp_compat_cnt = 0;
wsm_send_disassoc_to_self(hw_priv, priv);
}
}
}
#endif
if (likely(!arg->status)) {
tx->flags |= IEEE80211_TX_STAT_ACK;
priv->cqm_tx_failure_count = 0;
++tx_count;
#ifdef SUPPORT_HT40
if (((arg->txedRateEntry >> MODEMTYPE_SHIFT)
& MODEMTYPE_MASK) == RATE_MODEM_HTOFDM) {
if (((arg->txedRateEntry >> BANDWIDTH_SHIFT)
& BANDWIDTH_MASK)
== RATE_BANDWIDTH_20M) {
if (arg->txedRateEntry & RATE_F_SGI) {
u8 index = (arg->txedRateEntry
>> RATEINDEX_SHIFT)
& RATEINDEX_MASK;
TxedHtofdmRateMap[0][index]++;
} else {
u8 index = (arg->txedRateEntry
>> RATEINDEX_SHIFT)
& RATEINDEX_MASK;
TxedHtofdmRateMap[1][index]++;
}
} else {
if (arg->txedRateEntry & RATE_F_SGI) {
u8 index = (arg->txedRateEntry
>> RATEINDEX_SHIFT)
& RATEINDEX_MASK;
TxedHtofdmRateMap[2][index]++;
} else {
u8 index = (arg->txedRateEntry
>> RATEINDEX_SHIFT)
& RATEINDEX_MASK;
TxedHtofdmRateMap[3][index]++;
}
}
} else {
u8 index =
(arg->txedRateEntry >> RATEINDEX_SHIFT)
& RATEINDEX_MASK;
if (index < 6) {
if (arg->txedRateEntry
& RATE_F_PREAMBLE_S) {
TxedLegacyRateMap[0] \
[index*2+1]++;
} else {
TxedLegacyRateMap[0] \
[index*2]++;
}
} else {
TxedLegacyRateMap[1][index-6]++;
}
}
#else
if (arg->txedRate < 24)
TxedRateIdx_Map[arg->txedRate]++;
else
SYS_WARN(1);
#endif
xradio_debug_txed(priv);
if (arg->flags & WSM_TX_STATUS_AGGREGATION) {
/* Do not report aggregation to mac80211:
* it confuses minstrel a lot. */
/* tx->flags |= IEEE80211_TX_STAT_AMPDU; */
xradio_debug_txed_agg(priv);
}
} else {
/* TODO: Update TX failure counters */
if (unlikely(priv->cqm_tx_failure_thold &&
(++priv->cqm_tx_failure_count >
priv->cqm_tx_failure_thold))) {
priv->cqm_tx_failure_thold = 0;
queue_work(hw_priv->workqueue,
&priv->tx_failure_work);
}
if (tx_count)
++tx_count;
}
spin_unlock(&priv->vif_lock);
tx->status.ampdu_len = 1;
tx->status.ampdu_ack_len = 1;
#if 0
tx_count = arg->ackFailures+1;
for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
if (tx->status.rates[i].count >= tx_count) {
tx->status.rates[i].count = tx_count;
if (likely(!arg->status)) {
s8 txed_idx = xradio_get_rate_idx(hw_priv,
tx->status.rates[i].flags,
arg->txedRate);
if (tx->status.rates[i].idx != txed_idx) {
if (i < (IEEE80211_TX_MAX_RATES-1)) {
i++;
tx->status.rates[i].idx = txed_idx;
tx->status.rates[i].count = 1;
} else if (txed_idx >= 0) {
tx->status.rates[i].idx = txed_idx;
tx->status.rates[i].count = 1;
}
}
}
break;
}
tx_count -= tx->status.rates[i].count;
if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS)
tx->status.rates[i].flags |= ht_flags;
}
for (++i; i < IEEE80211_TX_MAX_RATES; ++i) {
tx->status.rates[i].count = 0;
tx->status.rates[i].idx = -1;
}
#else
#ifdef SUPPORT_HT40
if (txpriv->use_bg_rate) {
tx->status.rates[0].count = arg->ackFailures+1;
tx->status.rates[0].idx = 0;
tx->status.rates[1].idx = -1;
tx->status.rates[2].idx = -1;
tx->status.rates[3].idx = -1;
tx->status.rates[4].idx = -1;
} else {
u8 RateTryIdx = 0x0;
u8 BreakIdx = 0x0;
bool IsFind = false;
bool IsBreak = false;
for (RateTryIdx = 0; RateTryIdx < IEEE80211_TX_MAX_RATES; RateTryIdx++) {
if (!IsBreak)
BreakIdx = RateTryIdx;
if (GET_RATE_ENTRY_RATEINDEX(arg->RateTry[RateTryIdx]) == FW_RATE_USE_DEFAULT)
IsBreak = true;
if (IsBreak) {
tx->status.rates[RateTryIdx].idx = -1;
tx->status.rates[RateTryIdx].count = 0;
tx->status.rates[RateTryIdx].flags = 0;
} else {
xradio_get_ieee80211_tx_rate(hw_priv,
arg->RateTry[RateTryIdx], &tx->status.rates[RateTryIdx]);
if ((!arg->status) && (!IsFind)) {
if ((arg->RateTry[RateTryIdx]&0xFFF0) == (arg->txedRateEntry&0xFFF0)) {
tx->status.rates[RateTryIdx].count++;
IsFind = true;
}
}
}
}
if ((!arg->status) && (!IsFind)) {
if (!IsBreak)
BreakIdx = 4;
xradio_get_ieee80211_tx_rate(hw_priv, arg->txedRateEntry, &tx->status.rates[BreakIdx]);
tx->status.rates[BreakIdx].count = 1;
}
}
#else
txrx_printk(XRADIO_DBG_MSG,
"feedback:%08x, %08x, %08x.\n",
arg->rate_try[2], arg->rate_try[1], arg->rate_try[0]);
if (txpriv->use_bg_rate) { /* bg rates */
tx->status.rates[0].count = arg->ackFailures+1;
tx->status.rates[0].idx = 0;
tx->status.rates[1].idx = -1;
tx->status.rates[2].idx = -1;
tx->status.rates[3].idx = -1;
tx->status.rates[4].idx = -1;
} else {
int i;
int j;
s8 txed_idx;
register u8 rate_num = 0, shift = 0, retries = 0;
u8 flag = tx->status.rates[0].flags;
/* get retry rate idx. */
for (i = 2; i >= 0; i--) {
if (arg->rate_try[i]) {
for (j = 7; j >= 0; j--) {
shift = j<<2;
retries = (arg->rate_try[i]>>shift) & 0xf;
if (retries) {
feedback_retry += retries;
txed_idx = xradio_get_rate_idx(hw_priv, flag,
((i<<3) + j));
txrx_printk(XRADIO_DBG_MSG,
"rate_num=%d, hw=%d, idx=%d, "
"retries=%d, flag=%d",
rate_num, ((i<<3)+j),
txed_idx, retries, flag);
if (likely(txed_idx >= 0)) {
tx->status.rates[rate_num].idx = txed_idx;
tx->status.rates[rate_num].count = retries;
if (tx->status.rates[rate_num].flags &
IEEE80211_TX_RC_MCS)
tx->status.rates[rate_num].flags |=
ht_flags;
rate_num++;
if (rate_num >= IEEE80211_TX_MAX_RATES) {
i = -1;
break;
}
}
}
}
}
}
/* If there is 11b rates in 11n mode, put it into MCS0 */
if ((arg->rate_try[0]&0xffff) && (flag & IEEE80211_TX_RC_MCS)) {
int br_retrys = 0;
for (i = 0; i < 16; i += 4)
br_retrys += ((arg->rate_try[0]>>i)&0xf);
if (rate_num > 0 && tx->status.rates[rate_num-1].idx == 0) {
tx->status.rates[rate_num-1].count += br_retrys;
} else if (rate_num < IEEE80211_TX_MAX_RATES) {
tx->status.rates[rate_num].idx = 0;
tx->status.rates[rate_num].count += br_retrys;
rate_num++;
}
}
/* clear other rate. */
for (i = rate_num; i < IEEE80211_TX_MAX_RATES; ++i) {
tx->status.rates[i].count = 0;
tx->status.rates[i].idx = -1;
}
/* get successful rate idx. */
if (!arg->status) {
txed_idx = xradio_get_rate_idx(hw_priv, flag, arg->txedRate);
if (rate_num == 0) {
tx->status.rates[0].idx = txed_idx;
tx->status.rates[0].count = 1;
} else if (rate_num <= IEEE80211_TX_MAX_RATES) {
--rate_num;
if (txed_idx == tx->status.rates[rate_num].idx) {
tx->status.rates[rate_num].count += 1;
} else if (rate_num < (IEEE80211_TX_MAX_RATES-1)) {
++rate_num;
tx->status.rates[rate_num].idx = txed_idx;
tx->status.rates[rate_num].count = 1;
} else if (txed_idx >= 0) {
tx->status.rates[rate_num].idx = txed_idx;
tx->status.rates[rate_num].count = 1;
}
}
}
}
#endif
#endif
#ifdef CONFIG_XRADIO_DEBUGFS
if (arg->status == WSM_STATUS_RETRY_EXCEEDED) {
tx_retrylimit++;
retry_mis += ((s32)hw_priv->short_frame_max_tx_count -
arg->ackFailures-1);
if (arg->ackFailures != (hw_priv->short_frame_max_tx_count-1)) {
if (arg->ackFailures < (hw_priv->short_frame_max_tx_count-1))
tx_lower_limit++;
else
tx_over_limit++;
txrx_printk(XRADIO_DBG_NIY,
"retry_err, ackFailures=%d, feedbk_retry=%d.\n",
arg->ackFailures, feedback_retry);
}
} else if (feedback_retry > hw_priv->short_frame_max_tx_count-1) {
tx_over_limit++;
txrx_printk(XRADIO_DBG_WARN,
"status=%d, ackFailures=%d, feedbk_retry=%d.\n",
arg->status, arg->ackFailures, feedback_retry);
}
#endif
txrx_printk(XRADIO_DBG_MSG, "[TX policy] Ack: " \
"%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n",
tx->status.rates[0].idx, tx->status.rates[0].count,
tx->status.rates[1].idx, tx->status.rates[1].count,
tx->status.rates[2].idx, tx->status.rates[2].count,
tx->status.rates[3].idx, tx->status.rates[3].count,
tx->status.rates[4].idx, tx->status.rates[4].count);
#ifdef CONFIG_XRADIO_TESTMODE
xradio_queue_remove(hw_priv, queue, arg->packetID);
#else
xradio_queue_remove(queue, arg->packetID);
#endif /*CONFIG_XRADIO_TESTMODE*/
} else {
spin_unlock(&priv->vif_lock);
txrx_printk(XRADIO_DBG_WARN,
"%s xradio_queue_get_skb failed.\n", __func__);
}
}
#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
static void xradio_set_skb_eosp(struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_STATUS_EOSP;
}
#endif
static void xradio_notify_buffered_tx(struct xradio_vif *priv,
struct sk_buff *skb, int link_id, int tid)
{
#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
struct ieee80211_sta *sta;
struct ieee80211_hdr *hdr;
u8 *buffered;
u8 still_buffered = 0;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (link_id && tid < XRADIO_MAX_TID) {
buffered = priv->link_id_db
[link_id - 1].buffered;
spin_lock_bh(&priv->ps_state_lock);
if (!SYS_WARN(!buffered[tid]))
still_buffered = --buffered[tid];
spin_unlock_bh(&priv->ps_state_lock);
if (!still_buffered && tid < XRADIO_MAX_TID) {
hdr = (struct ieee80211_hdr *) skb->data;
rcu_read_lock();
sta = mac80211_find_sta(priv->vif, hdr->addr1);
if (sta) {
mac80211_sta_set_buffered(sta, tid, false);
xradio_set_skb_eosp(skb);
}
rcu_read_unlock();
}
}
#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
}
void xradio_skb_dtor(struct xradio_common *hw_priv,
struct sk_buff *skb,
const struct xradio_txpriv *txpriv)
{
struct xradio_vif *priv =
__xrwl_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
skb_pull(skb, txpriv->offset);
if (priv && txpriv->rate_id != XRADIO_INVALID_RATE_ID) {
xradio_notify_buffered_tx(priv, skb,
txpriv->raw_link_id, txpriv->tid);
tx_policy_put(hw_priv, txpriv->rate_id);
}
if (likely(!xradio_is_itp(hw_priv))) {
down(&hw_priv->dtor_lock);
mac80211_tx_status(hw_priv->hw, skb);
up(&hw_priv->dtor_lock);
}
}
#ifdef CONFIG_XRADIO_TESTMODE
/* TODO It should be removed before official delivery */
static void frame_hexdump(char *prefix, u8 *data, int len)
{
int i;
txrx_printk(XRADIO_DBG_MSG, "%s hexdump:\n", prefix);
for (i = 0; i < len; i++) {
if (i + 10 < len) {
txrx_printk(XRADIO_DBG_MSG, "%.1X %.1X %.1X %.1X" \
"%.1X %.1X %.1X %.1X %.1X %.1X",
data[i], data[i+1], data[i+2],
data[i+3], data[i+4], data[i+5],
data[i+6], data[i+7], data[i+8],
data[i+9]);
i += 9;
} else {
txrx_printk(XRADIO_DBG_MSG, "%.1X ", data[i]);
}
}
}
/**
* c1200_tunnel_send_testmode_data - Send test frame to the driver
*
* @priv: pointer to xradio private structure
* @skb: skb with frame
*
* Returns: 0 on success or non zero value on failure
*/
static int xradio_tunnel_send_testmode_data(struct xradio_common *hw_priv,
struct sk_buff *skb)
{
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (xradio_tesmode_event(hw_priv->hw->wiphy, XR_MSG_EVENT_FRAME_DATA,
skb->data, skb->len, GFP_ATOMIC))
return -EINVAL;
return 0;
}
/**
* xradio_frame_test_detection - Detection frame_test
*
* @priv: pointer to xradio vif structure
* @frame: ieee80211 header
* @skb: skb with frame
*
* Returns: 1 - frame test detected, 0 - not detected
*/
static int xradio_frame_test_detection(struct xradio_vif *priv,
struct ieee80211_hdr *frame,
struct sk_buff *skb)
{
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
int hdrlen = ieee80211_hdrlen(frame->frame_control);
int detected = 0;
int ret;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (hdrlen + hw_priv->test_frame.len <= skb->len &&
memcmp(skb->data + hdrlen, hw_priv->test_frame.data,
hw_priv->test_frame.len) == 0) {
detected = 1;
txrx_printk(XRADIO_DBG_MSG, "TEST FRAME detected");
frame_hexdump("TEST FRAME original:", skb->data, skb->len);
ret = ieee80211_data_to_8023(skb, hw_priv->mac_addr,
priv->mode);
if (!ret) {
frame_hexdump("FRAME 802.3:", skb->data, skb->len);
ret = xradio_tunnel_send_testmode_data(hw_priv, skb);
}
if (ret)
txrx_printk(XRADIO_DBG_ERROR, "Send TESTFRAME failed(%d)", ret);
}
return detected;
}
#endif /* CONFIG_XRADIO_TESTMODE */
static void
xradio_rx_h_ba_stat(struct xradio_vif *priv,
size_t hdrlen, size_t skb_len)
{
struct xradio_common *hw_priv = priv->hw_priv;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (priv->join_status != XRADIO_JOIN_STATUS_STA)
return;
if (!xradio_is_ht(&hw_priv->ht_info))
return;
if (!priv->setbssparams_done)
return;
spin_lock_bh(&hw_priv->ba_lock);
hw_priv->ba_acc_rx += skb_len - hdrlen;
if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
mod_timer(&hw_priv->ba_timer,
jiffies + XRADIO_BLOCK_ACK_INTERVAL);
}
hw_priv->ba_cnt_rx++;
spin_unlock_bh(&hw_priv->ba_lock);
}
#if 0
u8 nettest_bssid[] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x06};
u8 save_rate_ie;
#endif
#if PERF_INFO_TEST
struct timeval upper_rx_time;
size_t upper_rx_size;
#endif
void xradio_rx_cb(struct xradio_vif *priv,
struct wsm_rx *arg,
struct sk_buff **skb_p)
{
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
struct sk_buff *skb = *skb_p;
struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
#endif
struct xradio_link_entry *entry = NULL;
unsigned long grace_period;
bool early_data = false;
size_t hdrlen = 0;
u8 parse_iv_len = 0;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
hdr->flag = 0;
if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
/* STA is stopped. */
goto drop;
}
#ifdef TES_P2P_0002_ROC_RESTART
xradio_frame_monitor(hw_priv, skb, false);
#endif
#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
if ((ieee80211_is_action(frame->frame_control))
&& (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
u8 *action = (u8 *)&mgmt->u.action.category;
xradio_check_go_neg_conf_success(hw_priv, action);
}
#endif
#ifdef CONFIG_XRADIO_TESTMODE
spin_lock_bh(&hw_priv->tsm_lock);
if (hw_priv->start_stop_tsm.start) {
unsigned queue_id = skb_get_queue_mapping(skb);
if (queue_id == 0) {
struct timeval tmval;
do_gettimeofday(&tmval);
if (hw_priv->tsm_info.sta_roamed &&
hw_priv->tsm_info.use_rx_roaming) {
hw_priv->tsm_info.roam_delay = tmval.tv_usec -
hw_priv->tsm_info.rx_timestamp_vo;
txrx_printk(XRADIO_DBG_NIY, "[RX] RxInd Roaming:"
"roam_delay = %u\n", hw_priv->tsm_info.roam_delay);
hw_priv->tsm_info.sta_roamed = 0;
}
hw_priv->tsm_info.rx_timestamp_vo = tmval.tv_usec;
}
}
spin_unlock_bh(&hw_priv->tsm_lock);
#endif /*CONFIG_XRADIO_TESTMODE*/
if (arg->link_id && (arg->link_id != XRADIO_LINK_ID_UNMAPPED)
&& (arg->link_id <= XRADIO_MAX_STA_IN_AP_MODE)) {
entry = &priv->link_id_db[arg->link_id - 1];
if (entry->status == XRADIO_LINK_SOFT &&
ieee80211_is_data(frame->frame_control))
early_data = true;
entry->timestamp = jiffies;
}
#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
else if ((arg->link_id == XRADIO_LINK_ID_UNMAPPED)
&& (priv->vif->p2p == WSM_START_MODE_P2P_GO)
&& ieee80211_is_action(frame->frame_control)
&& (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
txrx_printk(XRADIO_DBG_NIY, "[RX] Going to MAP&RESET link ID\n");
#if defined (DEBUG_P2P_SETTING_CRASH)
printk(KERN_ERR "[BUG12]link ID is unmapped,go to MAP&RESET link ID\n");
#endif
if (work_pending(&priv->linkid_reset_work))
SYS_WARN(1);
memcpy(&priv->action_frame_sa[0],
ieee80211_get_SA(frame), ETH_ALEN);
priv->action_linkid = 0;
schedule_work(&priv->linkid_reset_work);
}
if (arg->link_id && (arg->link_id != XRADIO_LINK_ID_UNMAPPED)
&& (priv->vif->p2p == WSM_START_MODE_P2P_GO)
&& ieee80211_is_action(frame->frame_control)
&& (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
/* Link ID already exists for the ACTION frame.
* Reset and Remap */
if (work_pending(&priv->linkid_reset_work))
SYS_WARN(1);
memcpy(&priv->action_frame_sa[0],
ieee80211_get_SA(frame), ETH_ALEN);
priv->action_linkid = arg->link_id;
schedule_work(&priv->linkid_reset_work);
}
#endif
if (unlikely(arg->status)) {
if (arg->status == WSM_STATUS_MICFAILURE) {
txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, MIC failure.\n",
priv->if_id);
hdr->flag |= RX_FLAG_MMIC_ERROR;
} else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
#ifdef MONITOR_MODE
if (hw_priv->monitor_if_id == -1) {
#endif
txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, No key found.\n",
priv->if_id);
goto drop;
#ifdef MONITOR_MODE
}
#endif
} else {
txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, Receive failure: %d.\n",
priv->if_id, arg->status);
goto drop;
}
}
if (skb->len < sizeof(struct ieee80211_pspoll)) {
txrx_printk(XRADIO_DBG_WARN, "Mailformed SDU rx'ed. "
"Size is lesser than IEEE header.\n");
goto drop;
}
if (unlikely(ieee80211_is_pspoll(frame->frame_control)))
if (xradio_handle_pspoll(priv, skb))
goto drop;
hdr->mactime = 0; /* Not supported by WSM */
hdr->band = (arg->channelNumber > 14) ?
NL80211_BAND_5GHZ : NL80211_BAND_2GHZ;
hdr->freq = ieee80211_channel_to_frequency(
arg->channelNumber,
hdr->band);
#ifdef AP_HT_COMPAT_FIX
#ifdef SUPPORT_HT40
if (!priv->ht_compat_det && priv->htcap &&
ieee80211_is_data_qos(frame->frame_control)) {
if (xradio_apcompat_detect(priv, arg->rxedRateEntry))
goto drop;
}
#else
if (!priv->ht_compat_det && priv->htcap &&
ieee80211_is_data_qos(frame->frame_control)) {
if (xradio_apcompat_detect(priv, arg->rxedRate))
goto drop;
}
#endif
#endif
#ifdef SUPPORT_HT40
if (((arg->rxedRateEntry >> MODEMTYPE_SHIFT) & MODEMTYPE_MASK) ==
RATE_MODEM_HTOFDM) {
if (((arg->rxedRateEntry >> BANDWIDTH_SHIFT) & BANDWIDTH_MASK) ==
RATE_BANDWIDTH_20M) {
if (arg->rxedRateEntry & RATE_F_SGI) {
u8 index = (arg->rxedRateEntry >> RATEINDEX_SHIFT) & RATEINDEX_MASK;
RxedHtofdmRateMap[0][index]++;
} else {
u8 index = (arg->rxedRateEntry >> RATEINDEX_SHIFT) & RATEINDEX_MASK;
RxedHtofdmRateMap[1][index]++;
}
} else {
if (arg->rxedRateEntry & RATE_F_SGI) {
u8 index = (arg->rxedRateEntry >> RATEINDEX_SHIFT) & RATEINDEX_MASK;
RxedHtofdmRateMap[2][index]++;
} else {
u8 index = (arg->rxedRateEntry >> RATEINDEX_SHIFT) & RATEINDEX_MASK;
RxedHtofdmRateMap[3][index]++;
}
}
} else {
u8 index = (arg->rxedRateEntry >> RATEINDEX_SHIFT) & RATEINDEX_MASK;
if (index < 6) {
if (arg->rxedRateEntry & RATE_F_PREAMBLE_S)
RxedLegacyRateMap[0][index*2+1]++;
else
RxedLegacyRateMap[0][index*2]++;
} else {
RxedLegacyRateMap[1][index-6]++;
}
}
if (GET_RATE_ENTRY_MODEM(arg->rxedRateEntry) == FW_RATE_MODEM_HTOFDM) {
hdr->flag |= RX_FLAG_HT;
hdr->rate_idx = GET_RATE_ENTRY_RATEINDEX(arg->rxedRateEntry);
if (GET_RATE_ENTRY_BANDWIDTH(arg->rxedRateEntry) == FW_RATE_BW_40M)
hdr->flag |= RX_FLAG_40MHZ;
if (GET_RATE_ENTRY_FLAGS(arg->rxedRateEntry)&FW_RATE_F_SGI)
hdr->flag |= RX_FLAG_SHORT_GI;
} else {
hdr->rate_idx = LegacyRxedRateLut[hdr->band][GET_RATE_ENTRY_RATEINDEX(arg->rxedRateEntry)];
if (GET_RATE_ENTRY_FLAGS(arg->rxedRateEntry)&FW_RATE_F_SPRE)
hdr->flag |= RX_FLAG_SHORTPRE;
}
#else
if (arg->rxedRate < 24)
RxedRateIdx_Map[arg->rxedRate]++;
else
SYS_WARN(1);
if (arg->rxedRate >= 14) {
hdr->flag |= RX_FLAG_HT;
hdr->rate_idx = arg->rxedRate - 14;
} else if (arg->rxedRate >= 4) {
if (hdr->band == NL80211_BAND_5GHZ)
hdr->rate_idx = arg->rxedRate - 6;
else
hdr->rate_idx = arg->rxedRate - 2;
} else {
hdr->rate_idx = arg->rxedRate;
}
#endif
hdr->signal = (s8)arg->rcpiRssi;
hdr->antenna = 0;
hdrlen = ieee80211_hdrlen(frame->frame_control);
if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
size_t iv_len = 0, icv_len = 0;
hdr->flag |= RX_FLAG_DECRYPTED;
/* Oops... There is no fast way to ask mac80211 about
* IV/ICV lengths. Even defineas are not exposed.*/
switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
case WSM_RX_STATUS_WEP:
iv_len = 4 /* WEP_IV_LEN */;
icv_len = 4 /* WEP_ICV_LEN */;
break;
case WSM_RX_STATUS_TKIP:
iv_len = 8 /* TKIP_IV_LEN */;
icv_len = 4 /* TKIP_ICV_LEN */
+ 8 /*MICHAEL_MIC_LEN*/;
break;
case WSM_RX_STATUS_AES:
iv_len = 8 /* CCMP_HDR_LEN */;
icv_len = 8 /* CCMP_MIC_LEN */;
break;
case WSM_RX_STATUS_WAPI:
iv_len = 18 /* WAPI_HDR_LEN */;
icv_len = 16 /* WAPI_MIC_LEN */;
hdr->flag |= RX_FLAG_IV_STRIPPED;
break;
default:
SYS_WARN("Unknown encryption type");
goto drop;
}
/* Firmware strips ICV in case of MIC failure. */
if (arg->status == WSM_STATUS_MICFAILURE) {
icv_len = 0;
hdr->flag |= RX_FLAG_IV_STRIPPED;
}
if (skb->len < hdrlen + iv_len + icv_len) {
txrx_printk(XRADIO_DBG_WARN, "Mailformed SDU rx'ed. "
"Size is lesser than crypto headers.\n");
goto drop;
}
if (WSM_RX_STATUS_ENCRYPTION(arg->flags) ==
WSM_RX_STATUS_TKIP) {
/* Remove TKIP MIC 8 bytes*/
memmove(skb->data + skb->len-icv_len,
skb->data + skb->len-icv_len+8, 4);
skb_trim(skb, skb->len - 8);
hdr->flag |= RX_FLAG_MMIC_STRIPPED;
} else if (unlikely(WSM_RX_STATUS_ENCRYPTION(arg->flags) ==
WSM_RX_STATUS_WAPI)) {
/* Protocols not defined in mac80211 should be
stripped/crypted in driver/firmware */
/* Remove IV, ICV and MIC */
skb_trim(skb, skb->len - icv_len);
memmove(skb->data + iv_len, skb->data, hdrlen);
skb_pull(skb, iv_len);
}
parse_iv_len = iv_len;
}
xradio_debug_rxed(priv);
if (arg->flags & WSM_RX_STATUS_AGGREGATE)
xradio_debug_rxed_agg(priv);
#if 0
/*for nettest*/
if (ieee80211_is_probe_resp(frame->frame_control) &&
!arg->status &&
!memcmp(ieee80211_get_SA(frame), nettest_bssid, ETH_ALEN)) {
const u8 *supp_rate_ie;
u8 *ies = ((struct ieee80211_mgmt *)
(skb->data))->u.probe_resp.variable;
size_t ies_len = skb->len - (ies - (u8 *)(skb->data));
supp_rate_ie = xradio_get_ie(ies, ies_len, WLAN_EID_SUPP_RATES);
save_rate_ie = supp_rate_ie[2];
txrx_printk(XRADIO_DBG_WARN, "[netest]: save_rate_ie=%2x\n",
save_rate_ie);
}
if (ieee80211_is_assoc_resp(frame->frame_control) &&
!arg->status &&
!memcmp(ieee80211_get_SA(frame), nettest_bssid, ETH_ALEN)) {
u8 *supp_rate_ie2;
size_t ies_len;
u8 *ies = ((struct ieee80211_mgmt *)
(skb->data))->u.assoc_resp.variable;
ies_len = skb->len - (ies - (u8 *)(skb->data));
supp_rate_ie2 = xradio_get_ie(ies, ies_len, WLAN_EID_SUPP_RATES);
if ((supp_rate_ie2[1] == 1) && (supp_rate_ie2[2] == 0x80)) {
supp_rate_ie2[2] = save_rate_ie;
txrx_printk(XRADIO_DBG_WARN,
"[netest]: rate_ie modified=%2x\n",
supp_rate_ie2[2]);
}
}
/*for test*/
#endif
if (ieee80211_is_beacon(frame->frame_control) &&
!arg->status &&
!memcmp(ieee80211_get_SA(frame), priv->join_bssid, ETH_ALEN)) {
const u8 *tim_ie;
u8 *ies;
size_t ies_len;
priv->disable_beacon_filter = false;
queue_work(hw_priv->workqueue, &priv->update_filtering_work);
ies = ((struct ieee80211_mgmt *)
(skb->data))->u.beacon.variable;
ies_len = skb->len - (ies - (u8 *)(skb->data));
tim_ie = xradio_get_ie(ies, ies_len, WLAN_EID_TIM);
if (tim_ie) {
struct ieee80211_tim_ie *tim =
(struct ieee80211_tim_ie *)&tim_ie[2];
if (priv->join_dtim_period != tim->dtim_period) {
priv->join_dtim_period = tim->dtim_period;
queue_work(hw_priv->workqueue,
&priv->set_beacon_wakeup_period_work);
}
}
if (unlikely(priv->disable_beacon_filter)) {
priv->disable_beacon_filter = false;
queue_work(hw_priv->workqueue,
&priv->update_filtering_work);
}
}
#ifdef AP_HT_CAP_UPDATE
if (priv->mode == NL80211_IFTYPE_AP &&
ieee80211_is_beacon(frame->frame_control) &&
((priv->ht_info&HT_INFO_MASK) != 0x0011) &&
!arg->status) {
u8 *ies;
size_t ies_len;
const u8 *ht_cap;
ies = ((struct ieee80211_mgmt *)(skb->data))->u.beacon.variable;
ies_len = skb->len - (ies - (u8 *)(skb->data));
ht_cap = xradio_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY);
if (!ht_cap) {
priv->ht_info |= 0x0011;
queue_work(hw_priv->workqueue, &priv->ht_info_update_work);
}
}
#endif
#ifdef AP_HT_COMPAT_FIX
if (ieee80211_is_mgmt(frame->frame_control) &&
priv->if_id == 0 && !(priv->ht_compat_det & 0x10)) {
xradio_remove_ht_ie(priv, skb);
}
#endif
#ifdef ROAM_OFFLOAD
if ((ieee80211_is_beacon(frame->frame_control) ||
ieee80211_is_probe_resp(frame->frame_control)) &&
!arg->status) {
if (hw_priv->auto_scanning &&
!atomic_read(&hw_priv->scan.in_progress))
hw_priv->frame_rcvd = 1;
if (!memcmp(ieee80211_get_SA(frame), priv->join_bssid, ETH_ALEN)) {
if (hw_priv->beacon)
dev_kfree_skb(hw_priv->beacon);
hw_priv->beacon = skb_copy(skb, GFP_ATOMIC);
if (!hw_priv->beacon)
txrx_printk(XRADIO_DBG_ERROR,
"sched_scan: own beacon storing failed\n");
}
}
#endif /*ROAM_OFFLOAD*/
/*scanResult.timestamp to adapt to Framework(WiFi) on Android5.0 or advanced version.*/
if ((ieee80211_is_beacon(mgmt->frame_control) ||
ieee80211_is_probe_resp(mgmt->frame_control))
&& !arg->status) {
struct timespec ts;
u64 tv_nsec;
get_monotonic_boottime(&ts);
tv_nsec = ts.tv_nsec;
do_div(tv_nsec, 1000);
if (ieee80211_is_beacon(mgmt->frame_control)) {
mgmt->u.beacon.timestamp = ((u64)ts.tv_sec * 1000000 + tv_nsec);
} else if (ieee80211_is_probe_resp(mgmt->frame_control)) {
mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec * 1000000 + tv_nsec);
}
}
/* don't delay scan before next connect */
if (ieee80211_is_deauth(frame->frame_control) ||
ieee80211_is_disassoc(frame->frame_control))
hw_priv->scan_delay_status[priv->if_id] = XRADIO_SCAN_ALLOW;
/* Stay awake for 1sec. after frame is received to give
* userspace chance to react and acquire appropriate
* wakelock. */
if (ieee80211_is_auth(frame->frame_control))
grace_period = 5 * HZ;
else if (ieee80211_is_deauth(frame->frame_control))
grace_period = 5 * HZ;
else
grace_period = HZ;
if (ieee80211_is_data(frame->frame_control))
xradio_rx_h_ba_stat(priv, hdrlen, skb->len);
#ifdef CONFIG_PM
xradio_pm_stay_awake(&hw_priv->pm_state, grace_period);
#endif
#ifdef CONFIG_XRADIO_TESTMODE
if (hw_priv->test_frame.len > 0 &&
priv->mode == NL80211_IFTYPE_STATION) {
if (xradio_frame_test_detection(priv, frame, skb) == 1) {
consume_skb(skb);
*skb_p = NULL;
return;
}
}
#endif /* CONFIG_XRADIO_TESTMODE */
#ifdef AP_ARP_COMPAT_FIX
if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
u16 fctl = frame->frame_control;
if (ieee80211_is_data(fctl)) {
u8 machdrlen = ieee80211_hdrlen(fctl);
u8 *llc_data = (u8 *)frame + machdrlen + parse_iv_len;
if (is_SNAP(llc_data) && is_arp(llc_data)) {
u8 *arp_hdr = llc_data + LLC_LEN;
u16 *arp_type = (u16 *)(arp_hdr + ARP_TYPE_OFFSET);
if (*arp_type == cpu_to_be16(ARP_RESPONSE)) {
priv->arp_compat_cnt = 0;
}
}
}
}
#endif
#if (defined(CONFIG_XRADIO_DEBUG))
/* parsse frame here for debug. */
if (rxparse_flags)
xradio_parse_frame(skb->data, parse_iv_len,
rxparse_flags|PF_RX, priv->if_id);
#endif
/* Some aps change channel to inform station by sending beacon with WLAN_EID_DS_PARAMS ie,
*then station needs to reconnect to ap.
*/
if (ieee80211_is_beacon(frame->frame_control) &&
!arg->status &&
(priv->join_status == XRADIO_JOIN_STATUS_STA) &&
!memcmp(ieee80211_get_SA(frame), priv->join_bssid, ETH_ALEN)) {
const u8 *ds_ie;
u8 *ies;
size_t ies_len;
int ds_ie_partms_chan = 0;
ies = ((struct ieee80211_mgmt *)
(skb->data))->u.beacon.variable;
ies_len = skb->len - (ies - (u8 *)(skb->data));
ds_ie = xradio_get_ie(ies, ies_len, WLAN_EID_DS_PARAMS);
if (ds_ie && (ds_ie[1] == 1)) {
ds_ie_partms_chan = ds_ie[2];
if (ds_ie_partms_chan != hw_priv->join_chan) {
txrx_printk(XRADIO_DBG_WARN, "***ap changes channel by beacon with ds ie,"
"then station reconnects to ap, %d -> %d\n",
hw_priv->join_chan, ds_ie_partms_chan);
wsm_send_disassoc_to_self(hw_priv, priv);
}
}
}
#if PERF_INFO_TEST
upper_rx_size = skb->len;
#endif
PERF_INFO_GETTIME(&upper_rx_time);
/* Try to a packet for the case dev_alloc_skb failed in bh.*/
if (unlikely(xradio_itp_rxed(hw_priv, skb)))
consume_skb(skb);
else if (unlikely(early_data)) {
spin_lock_bh(&priv->ps_state_lock);
/* Double-check status with lock held */
if (entry->status == XRADIO_LINK_SOFT) {
skb_queue_tail(&entry->rx_queue, skb);
txrx_printk(XRADIO_DBG_WARN, "***skb_queue_tail\n");
} else
mac80211_rx_irqsafe(priv->hw, skb);
spin_unlock_bh(&priv->ps_state_lock);
} else {
mac80211_rx_irqsafe(priv->hw, skb);
}
*skb_p = NULL;
PERF_INFO_STAMP(&upper_rx_time, &mac_rx, upper_rx_size);
return;
drop:
/* TODO: update failure counters */
return;
}
/* ******************************************************************** */
/* Security */
int xradio_alloc_key(struct xradio_common *hw_priv)
{
int idx;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
idx = ffs(~hw_priv->key_map) - 1;
if (idx < 0 || idx > WSM_KEY_MAX_INDEX)
return -1;
hw_priv->key_map |= BIT(idx);
hw_priv->keys[idx].entryIndex = idx;
txrx_printk(XRADIO_DBG_NIY, "%s, idx=%d\n", __func__, idx);
return idx;
}
void xradio_free_key(struct xradio_common *hw_priv, int idx)
{
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
SYS_BUG(!(hw_priv->key_map & BIT(idx)));
memset(&hw_priv->keys[idx], 0, sizeof(hw_priv->keys[idx]));
hw_priv->key_map &= ~BIT(idx);
txrx_printk(XRADIO_DBG_NIY, "%s, idx=%d\n", __func__, idx);
}
void xradio_free_keys(struct xradio_common *hw_priv)
{
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
memset(&hw_priv->keys, 0, sizeof(hw_priv->keys));
hw_priv->key_map = 0;
}
int xradio_upload_keys(struct xradio_vif *priv)
{
struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
int idx, ret = 0;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
for (idx = 0; idx <= WSM_KEY_MAX_IDX; ++idx)
if (hw_priv->key_map & BIT(idx)) {
ret = wsm_add_key(hw_priv, &hw_priv->keys[idx], priv->if_id);
if (ret < 0)
break;
}
return ret;
}
#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
/* Workaround for WFD test case 6.1.10 */
void xradio_link_id_reset(struct work_struct *work)
{
struct xradio_vif *priv =
container_of(work, struct xradio_vif, linkid_reset_work);
struct xradio_common *hw_priv = priv->hw_priv;
int temp_linkid;
txrx_printk(XRADIO_DBG_TRC, "%s\n", __func__);
if (!priv->action_linkid) {
/* In GO mode we can receive ACTION frames without a linkID */
temp_linkid = xradio_alloc_link_id(priv,
&priv->action_frame_sa[0]);
SYS_WARN(!temp_linkid);
if (temp_linkid) {
/* Make sure we execute the WQ */
flush_workqueue(hw_priv->workqueue);
/* Release the link ID */
spin_lock_bh(&priv->ps_state_lock);
priv->link_id_db[temp_linkid - 1].prev_status =
priv->link_id_db[temp_linkid - 1].status;
priv->link_id_db[temp_linkid - 1].status =
XRADIO_LINK_RESET;
spin_unlock_bh(&priv->ps_state_lock);
wsm_lock_tx_async(hw_priv);
if (queue_work(hw_priv->workqueue,
&priv->link_id_work) <= 0)
wsm_unlock_tx(hw_priv);
}
} else {
spin_lock_bh(&priv->ps_state_lock);
priv->link_id_db[priv->action_linkid - 1].prev_status =
priv->link_id_db[priv->action_linkid - 1].status;
priv->link_id_db[priv->action_linkid - 1].status =
XRADIO_LINK_RESET_REMAP;
spin_unlock_bh(&priv->ps_state_lock);
wsm_lock_tx_async(hw_priv);
if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0)
wsm_unlock_tx(hw_priv);
flush_workqueue(hw_priv->workqueue);
}
}
#endif