nt9856x/code/driver/source/net/18211_nvtfmac/nvt_wlan_priv.c
2023-03-28 15:07:53 +08:00

2699 lines
105 KiB
C
Executable File

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <net/netlink.h>
#include "nvt_wlan_linux.h"
#include "nvt_wlan_priv.h"
#ifdef CONFIG_WIFI_TUNING_PHASE_II
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
#define MAC2STR(a) ((a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
#define QP_turning_point_1 (70 * 8 * 1000) /* TBR: 70 KBps */
#define QP_turning_point_2 (50 * 8 * 1000) /* TBR: 50 KBps */
#define QP_turning_point_3 (20 * 8 * 1000) /* TBR: 20 KBps */
//#define VCLEAR_INCR_TP_THRES 4
//#define VCLEAR_MACBEDROP_THRES 3
//#define VCLEAR_MAX_TP (300 * 8 * 1024) /* TBR: 2.4 Mbps */
#define VCLEAR_MAX_TP (125 * 8 * 1000) /* TBR: 125 KBps (1Mbps) */
#define VCLEAR_MIN_TP (10 * 8 * 1000) /* TBR: 10 KBps */
#define TPUT_IN_PHY_1_0 (1000 * 1000 / 2) /* TBR: 62.5 KBps */
#define TPUT_IN_PHY_2_0 (81 * 8 * 1000) /* TBR: 81 KBps */
#define TP_MONITOR_PHYRATE_TBL_SZ 28
// {x, y} : x means the value from fw, y is phyrate being round down to the
// nearest whole value.
u32 tp_monitor_phyrate_map_tbl[TP_MONITOR_PHYRATE_TBL_SZ][2] = {
// b mode
{0x04, 1000}, {0x01, 2000}, {0x02, 5500}, {0x03, 11000},
// g mode
{0x0b, 6000}, {0x0f, 9000}, {0x0a, 12000}, {0x0e, 18000}, {0x09, 24000},
{0x0d, 36000}, {0x08, 48000}, {0x0c, 54000},
// n mode with SGI
{0xc0, 7200}, {0xc1, 14400}, {0xc2, 21700}, {0xc3, 28900}, {0xc4, 43300},
{0xc5, 57800}, {0xc6, 65000}, {0xc7, 72200},
// n mode with LGI
{0x80, 6500}, {0x81, 13000}, {0x82, 19500}, {0x83, 26000}, {0x84, 39000},
{0x85, 52000}, {0x86, 58500}, {0x87, 65000}
};
#endif
u16 g_num_ba_buffers = DEFAULT_NUM_BA_BUFFERS;
static void update_piggyback_fifocredit(struct _nvt_if *nvt_if,
struct _nvt_hwinfo_rx *info);
/* This function checks whether seq1 is less than or equal to seq2 */
static inline bool nvt_seqno_leq(u16 seq1, u16 seq2)
{
if (((seq1 <= seq2) && ((seq2 - seq1) < MAX_SEQNO_BY_TWO)) ||
((seq1 > seq2) && ((seq1 - seq2) > MAX_SEQNO_BY_TWO))) {
return true;
}
return false;
}
/* This function checks whether seq1 is greater than or equal to seq2 */
static inline bool nvt_seqno_geq(u16 seq1, u16 seq2)
{
return nvt_seqno_leq(seq2, seq1);
}
static inline bool nvt_seqno_lt(u16 seq1, u16 seq2)
{
if (((seq1 < seq2) && ((seq2 - seq1) < MAX_SEQNO_BY_TWO)) ||
((seq1 > seq2) && ((seq1 - seq2) > MAX_SEQNO_BY_TWO))) {
return true;
}
return false;
}
static inline bool nvt_seqno_gt(u16 seq1, u16 seq2)
{
return nvt_seqno_lt(seq2, seq1);
}
/*
* This function compares the given sequence number with the specified
* upper and lower bounds and returns its position relative to them.
*/
static inline u8 nvt_seqno_bound_chk(u16 seq_lo, u16 seq_hi, u16 seq)
{
bool lo_chk = nvt_seqno_leq(seq_lo, seq);
bool hi_chk = nvt_seqno_leq(seq, seq_hi);
u8 chk_res = 0;
if ((true == lo_chk) && (true == hi_chk)) {
chk_res = BETWEEN_SEQLO_SEQHI;
} else if (true == lo_chk) {
chk_res = GREATER_THAN_SEQHI;
} else {
chk_res = LESSER_THAN_SEQLO;
}
return chk_res;
}
static bool nvt_cmp_pn_val(u8 *pn_val_old, struct sk_buff *skb)
{
struct _nvt_hwinfo_rx *info = NULL;
bool ret_val = false;
u32 old_val_low = 0;
u32 old_val_high = 0;
u32 rx_val_low = 0;
u32 rx_val_high = 0;
u8 pn_val_rx[6];
info = (struct _nvt_hwinfo_rx *)skb->data;
rx_val_low = (ntohl(info->word7.value) & 0xffff0000) >> 16;
rx_val_high = ntohl(info->word6.pn_high);
memcpy(&pn_val_rx[0], (u8 *)&rx_val_low, 2);
memcpy(&pn_val_rx[2], (u8 *)&rx_val_high, 4);
old_val_low = pn_val_old[0] | (pn_val_old[1] << 8) |
(pn_val_old[2] << 16) | (pn_val_old[3] << 24);
old_val_high = pn_val_old[4] | (pn_val_old[5] << 8);
rx_val_low = pn_val_rx[0] | (pn_val_rx[1] << 8) |
(pn_val_rx[2] << 16) | (pn_val_rx[3] << 24);
rx_val_high = pn_val_rx[4] | (pn_val_rx[5] << 8);
nvt_dbg(REORDER, "New PN = 0x%x_%x\n", rx_val_high, rx_val_low);
nvt_dbg(REORDER, "Old PN = 0x%x_%x\n", old_val_high, old_val_low);
if (rx_val_low == 0 && rx_val_high == 0) {
nvt_dbg(REORDER, "PN is all zero, do not check PN value!\n");
return true;
}
if (rx_val_high > 0x0000f0ff) {
nvt_dbg(REORDER, "PN wrap-around, don't check PN/reset PN\n");
memset(pn_val_old, 0, 6);
return true;
}
/*
* If the received PN Value is newer, that the old value is updated
* and successful comparision is returned.
*/
if (((old_val_high == rx_val_high) && (old_val_low < rx_val_low)) ||
(old_val_high < rx_val_high)) {
nvt_dbg(REORDER, "After PN check, New > Old\n");
memcpy(pn_val_old, pn_val_rx, 6);
ret_val = true;
}
return ret_val;
}
bool nvt_check_sec_rx_mpdu(struct _nvt_adapter *nvt_adapter,
enum _cipher_t ct, struct sk_buff *skb, u8 *old_pn_val)
{
bool error = false;
nvt_dbg(REORDER, "%s, ct=%d\n", __func__, ct);
/* Perform replay detection. */
if ((ct == TKIP) || (ct == CCMP)) {
nvt_dbg(REORDER, "skb buffer is ciphered by TKIP/CCMP\n");
if (nvt_cmp_pn_val(old_pn_val, skb) != true) {
error = true;
}
} else {
nvt_dbg(REORDER, "not ciphered by TKIP/CCMP, don't Check PN\n");
}
return error;
}
static void nvt_process_ba_rx_buff_frame(struct _ba_rx_struct_t *ba,
struct _bfd_rx_pkt_t *pkt_ptr, u8 tid)
{
struct _nvt_adapter *nvt_adapter = ba->ba_data.entry;
struct _nvt_if *nvt_if = NULL;
struct _nvt_hwinfo_rx *info = NULL;
u8 ifidx = 0;
nvt_dbg(REORDER, "%s\n", __func__);
/* Get nvt_if pointer */
nvt_if = nvt_get_if_by_index(nvt_adapter, ifidx);
if (!nvt_if) {
nvt_dbg(REORDER,
"ifidx = %d, nvt_if not match, Drop it\n", ifidx);
dev_kfree_skb_any(pkt_ptr->pkt);
return;
}
nvt_dbg(REORDER, "ifidx=%d, nvt_if=%p, nvt_adapter=%p",
ifidx, nvt_if, nvt_adapter);
/* PN Check */
if (nvt_check_sec_rx_mpdu(nvt_adapter, pkt_ptr->ct,
pkt_ptr->pkt, *(ba->rx_pn_val)) == true) {
nvt_dbg(REORDER, "PN Check Fail, Drop it!!!!!!!!!\n");
nvt_dbg(REORDER, "seq=%d,tid=%d\n", pkt_ptr->seq_num, tid);
/* Drop it */
dev_kfree_skb_any(pkt_ptr->pkt);
} else {
nvt_dbg(REORDER, "PN Check Success, Send it to kernel\n");
nvt_dbg(REORDER, "seq=%d,tid=%d\n", pkt_ptr->seq_num, tid);
/* Debugging Purpose */
if ((nvt_seqno_gt(ba->debug_seq, pkt_ptr->seq_num) == true) ||
(ba->debug_seq == pkt_ptr->seq_num)) {
nvt_dbg(REORDER, "ERROR:tid=%d,prev=%d,current=%d\n",
tid, ba->debug_seq, pkt_ptr->seq_num);
}
if (((ba->debug_seq + 1) % 4096) != pkt_ptr->seq_num) {
nvt_dbg(REORDER, "Hole:tid=%d,prev=%d,current=%d\n",
tid, ba->debug_seq, pkt_ptr->seq_num);
}
ba->debug_seq = pkt_ptr->seq_num;
info = (struct _nvt_hwinfo_rx *)pkt_ptr->pkt->data;
/* check this skb is AMSDU or not, and send to kernel */
if ((ntohl(info->word0) & 0x08000000) == 0) {
nvt_netif_rx(nvt_if, pkt_ptr->pkt);
} else {
nvt_deaggr_amsdu(pkt_ptr->pkt, info, nvt_if);
}
}
}
static struct _bfd_rx_pkt_t *
nvt_remove_frame_from_reorder_q(struct _ba_rx_struct_t *ba,
u16 seq_num)
{
u16 idx = (seq_num & (g_num_ba_buffers - 1));
struct _bfd_rx_pkt_t *buff = &(ba->buff_list[idx]);
nvt_dbg(REORDER, "%s\n", __func__);
nvt_dbg(REORDER, "seq_num=%d, buff->in_use=%d, buff->seq_num=%d\n",
seq_num, buff->in_use, buff->seq_num);
if ((buff->in_use == 0) || (buff->seq_num != seq_num)) {
return NULL;
}
buff->in_use = 0;
return buff;
}
static u16 nvt_send_frames_in_order(struct _ba_rx_struct_t *ba, u8 tid)
{
struct _bfd_rx_pkt_t *pkt_ptr = NULL;
u16 seq_num = ba->win_start;
nvt_dbg(REORDER, "%s, win_start=%d\n", __func__, ba->win_start);
while (1) {
pkt_ptr = nvt_remove_frame_from_reorder_q(ba, seq_num);
if (pkt_ptr != NULL) {
nvt_process_ba_rx_buff_frame(ba, pkt_ptr, tid);
ba->buff_cnt--;
} else {
break;
}
seq_num = SEQNO_ADD(seq_num, 1);
}
return seq_num;
}
static u16 nvt_send_frames_with_gap(struct _ba_rx_struct_t *ba,
u8 tid, u16 last_seqno)
{
struct _bfd_rx_pkt_t *pkt_ptr = NULL;
u8 num_frms = 0;
u16 seq_num = ba->win_start;
nvt_dbg(REORDER, "%s\n", __func__);
while (seq_num != last_seqno) {
pkt_ptr = nvt_remove_frame_from_reorder_q(ba, seq_num);
if (pkt_ptr != NULL) {
nvt_process_ba_rx_buff_frame(ba, pkt_ptr, tid);
num_frms++;
ba->buff_cnt--;
} else {
/* Do nothing */
}
seq_num = SEQNO_ADD(seq_num, 1);
}
return num_frms;
}
static void nvt_flush_reorder_q(struct _ba_rx_struct_t *ba)
{
u16 idx = 0;
struct _bfd_rx_pkt_t *buff = NULL;
nvt_dbg(REORDER, "%s\n", __func__);
for (idx = 0; idx < g_num_ba_buffers; idx++) {
buff = &(ba->buff_list[idx]);
if (buff->in_use == 1) {
nvt_dbg(REORDER, "buff->seq_num=%d\n", buff->seq_num);
buff->in_use = 0;
}
}
}
static struct _bfd_rx_pkt_t *
nvt_buffer_frame_in_reorder_q(struct _ba_rx_struct_t *ba,
u16 seq_num, bool *reuse)
{
u16 idx = (seq_num & (g_num_ba_buffers - 1));
struct _bfd_rx_pkt_t *buff = &(ba->buff_list[idx]);
nvt_dbg(REORDER, "%s, idx=%d, seq_num=%d\n", __func__, idx, seq_num);
*reuse = false;
if (buff->in_use == 1) {
nvt_dbg(REORDER, "buff->in_use == 1, check it later\n");
nvt_dbg(REORDER, "buff->seq_num=%d, seq_num=%d\n",
buff->seq_num, seq_num);
*reuse = true;
}
buff->in_use = 1;
return buff;
}
static s32 nvt_buffer_ba_rx_frame(struct _ba_rx_struct_t *ba,
struct sk_buff *pkt, u16 seq_num, enum _cipher_t ct, bool *reuse)
{
struct _bfd_rx_pkt_t *pkt_ptr = NULL;
nvt_dbg(REORDER, "%s\n", __func__);
/* Get the pointer to the buffered packet */
pkt_ptr = nvt_buffer_frame_in_reorder_q(ba, seq_num, reuse);
if ((*reuse) == false) {
/* Update the buffered receive packet details */
pkt_ptr->seq_num = seq_num;
pkt_ptr->pkt = pkt;
pkt_ptr->ct = ct;
pkt_ptr->reorder_time = jiffies;
nvt_dbg(REORDER, "jiffies_now=%lu, buff->reorder_time=%lu\n",
jiffies, pkt_ptr->reorder_time);
ba->buff_cnt++;
} else {
if (seq_num == pkt_ptr->seq_num) {
return -1;
}
}
return 0;
}
static void nvt_release_reorder_frame(struct _ba_rx_struct_t *ba,
u16 idx, u8 tid)
{
struct _bfd_rx_pkt_t *buff = &(ba->buff_list[idx]);
nvt_dbg(REORDER, "%s, idx=%d, buff->seq_num=%d\n",
__func__, idx, buff->seq_num);
nvt_dbg(REORDER, "ba->win_start=%d, ba->win_end=%d, ba->buff_cnt=%d\n",
ba->win_start, ba->win_end, ba->buff_cnt);
if (buff->in_use == 0) {
nvt_dbg(REORDER, "Come here is werid, ERROR!!\n");
return;
}
buff->in_use = 0;
nvt_dbg(REORDER, "jiffies_now=%lu, buff->reorder_time=%lu\n",
jiffies, buff->reorder_time);
nvt_process_ba_rx_buff_frame(ba, buff, tid);
ba->buff_cnt--;
ba->win_start = SEQNO_ADD(ba->win_start, 1);
ba->win_end = SEQNO_ADD(ba->win_end, 1);
}
static bool nvt_rx_reorder_ready(struct _ba_rx_struct_t *ba, u16 idx)
{
struct _bfd_rx_pkt_t *buff = &(ba->buff_list[idx]);
if (buff->in_use == 1) {
return true;
} else {
return false;
}
}
static void nvt_reorder_release(struct _ba_rx_struct_t *ba,
u8 tid, bool do_check)
{
u32 index, j;
u32 skipped = 0;
struct _bfd_rx_pkt_t *buff = NULL;
nvt_dbg(REORDER, "%s, ba->win_start=%d\n", __func__, ba->win_start);
/* release the buffer until next missing frame */
index = (ba->win_start & (g_num_ba_buffers - 1));
if (!nvt_rx_reorder_ready(ba, (u16)index) && (ba->buff_cnt > 0) &&
(do_check == true)) {
/* No buffers ready to be released, but check whether any
* frames in the reorder buffer have timed out */
skipped = 1;
for (j = (index + 1) % ba->buff_size; j != index;
j = (j + 1) % ba->buff_size) {
if (!nvt_rx_reorder_ready(ba, (u16)j)) {
skipped++;
continue;
}
buff = &(ba->buff_list[(u16)j]);
if (skipped &&
!time_after(jiffies, buff->reorder_time +
RX_REORDER_BUF_TIMEOUT)) {
goto set_release_timer;
}
nvt_dbg(REORDER, "release an RX reorder frame due to timeout on earlier frames\n");
nvt_release_reorder_frame(ba, (u16)j, tid);
/* Increment the head seq also for the skipped slots */
ba->win_start = SEQNO_ADD(ba->win_start, skipped);
ba->win_end = SEQNO_ADD(ba->win_end, skipped);
skipped = 0;
}
}
if (ba->buff_cnt > 0) {
j = index = (ba->win_start & (g_num_ba_buffers - 1));
for (; j != (index - 1) % ba->buff_size;
j = (j + 1) % ba->buff_size) {
if (nvt_rx_reorder_ready(ba, (u16)j)) {
break;
}
}
set_release_timer:
nvt_dbg(REORDER, "start reorder release timer\n");
buff = &(ba->buff_list[(u16)j]);
nvt_dbg(REORDER,
"idx=%d, buff->seq_num=%d, buff->reorder_time=%lu\n",
j, buff->seq_num, buff->reorder_time);
mod_timer(&(ba->reorder_timer), buff->reorder_time + 1 +
RX_REORDER_BUF_TIMEOUT);
} else {
nvt_dbg(REORDER, "stop reorder release timer\n");
if (timer_pending(&(ba->reorder_timer))) {
del_timer(&(ba->reorder_timer));
}
}
}
static void nvt_reorder_ba_rx_buffer_data(struct _ba_rx_struct_t *ba,
struct sk_buff *pkt, u8 tid, u16 seq_num, enum _cipher_t ct, bool reuse)
{
u8 seqno_pos = 0;
u16 temp_winend;
u16 temp_winstart;
bool do_check = false;
nvt_dbg(REORDER, "%s\n", __func__);
nvt_dbg(REORDER, "win_start=%d, win_end=%d, seq_num=%d, reuse=%d\n",
ba->win_start, ba->win_end, seq_num, reuse);
if (ba->win_start == seq_num) {
/* do nothing */
} else {
do_check = true;
}
seqno_pos = nvt_seqno_bound_chk(ba->win_start, ba->win_end, seq_num);
if (BETWEEN_SEQLO_SEQHI == seqno_pos) {
nvt_dbg(REORDER, "BETWEEN_SEQLO_SEQHI\n");
ba->win_start = nvt_send_frames_in_order(ba, tid);
ba->win_end = SEQNO_ADD(ba->win_start, (ba->buff_size - 1));
} else if (GREATER_THAN_SEQHI == seqno_pos) {
nvt_dbg(REORDER, "GREATER_THAN_SEQHI\n");
temp_winend = seq_num;
temp_winstart = SEQNO_SUB(temp_winend, (ba->buff_size - 1));
nvt_send_frames_with_gap(ba, tid, temp_winstart);
if (reuse == true) {
if (nvt_buffer_ba_rx_frame(ba, pkt,
seq_num, ct, &reuse) < 0) {
dev_kfree_skb_any(pkt);
nvt_dbg(REORDER, "Shouldn't be here, ERROR!\n");
}
}
ba->win_start = temp_winstart;
ba->win_start = nvt_send_frames_in_order(ba, tid);
ba->win_end = SEQNO_ADD(ba->win_start, (ba->buff_size - 1));
} else {
nvt_dbg(REORDER, "Come here is illegal, ERROR!!!!!!!!!\n");
/* Do Nothing */
}
/* handle reorder timer release */
nvt_reorder_release(ba, tid, do_check);
}
static void nvt_free_ba_handle(void *ba_handle)
{
if (ba_handle != NULL) {
nvt_dbg(REORDER, "%s, (*ba) != NULL, free them\n", __func__);
/* buff_list element is freed */
kfree(((struct _ba_rx_struct_t *)ba_handle)->buff_list);
/* Block ACK handle is freed */
kfree(ba_handle);
return;
}
nvt_dbg(REORDER, "nvt_free_ba_handle, ba == NULL, weird!!!!!\n");
}
static void nvt_ba_handle(struct _ba_rx_struct_t *ba, int cmd,
struct _bfd_rx_pkt_t *pkt_cur, struct _nvt_if *nvt_if)
{
struct _nvt_hwinfo_rx *info = NULL;
bool reuse = false;
u8 tid = 0;
u16 temp_seqno = 0;
nvt_dbg(REORDER, "%s\n", __func__);
if (ba == NULL) {
nvt_dbg(REORDER, "%s,ba==NULL, check it out!!\n", __func__);
/* Exception */
return;
}
nvt_dbg(REORDER, "ba=%p\n", ba);
nvt_dbg(REORDER, "ba->win_start=%d, ba->win_end=%d, cmd=%d\n",
ba->win_start, ba->win_end, cmd);
tid = ba->ba_data.tid;
nvt_dbg(REORDER, "tid=%d\n", tid);
if (cmd == 1) {
temp_seqno = SEQNO_ADD(ba->win_tail, 1);
nvt_send_frames_with_gap(ba, tid, temp_seqno);
ba->win_start = temp_seqno;
ba->win_end =
SEQNO_ADD(ba->win_start, (ba->buff_size - 1));
} else if (cmd == 2) {
ba->win_start = SEQNO_ADD(ba->win_start, 1);
ba->win_end = SEQNO_ADD(ba->win_end, 1);
if (nvt_check_sec_rx_mpdu(nvt_if->nvt_adapter, pkt_cur->ct,
pkt_cur->pkt, *(ba->rx_pn_val)) == true) {
//pkt_cur->pkt, pkt_cur->rx_pn_val) == true) {
nvt_dbg(REORDER, "PN Check Fail, Drop it!!!!!!!!!\n");
nvt_dbg(REORDER,
"seq=%d,tid=%d\n", pkt_cur->seq_num, tid);
dev_kfree_skb_any(pkt_cur->pkt);
} else {
nvt_dbg(REORDER, "PN Check Success, Send to kernel\n");
nvt_dbg(REORDER,
"seq=%d,tid=%d\n", pkt_cur->seq_num, tid);
/* Debugging Purpose */
if ((nvt_seqno_gt(ba->debug_seq,
pkt_cur->seq_num) == true) ||
(ba->debug_seq == pkt_cur->seq_num)) {
nvt_dbg(REORDER,
"ERROR:tid=%d,prev=%d,current=%d\n",
tid, ba->debug_seq,
pkt_cur->seq_num);
}
if (((ba->debug_seq + 1) % 4096) !=
pkt_cur->seq_num) {
nvt_dbg(REORDER,
"Hole:tid=%d,prev=%d,current=%d\n",
tid, ba->debug_seq,
pkt_cur->seq_num);
}
ba->debug_seq = pkt_cur->seq_num;
info = (struct _nvt_hwinfo_rx *)pkt_cur->pkt->data;
/* check this skb is AMSDU or not, send to kernel */
if ((ntohl(info->word0) & 0x08000000) == 0) {
nvt_netif_rx(nvt_if, pkt_cur->pkt);
} else {
nvt_deaggr_amsdu(pkt_cur->pkt, info, nvt_if);
}
}
} else if (cmd == 3) {
/* Buffer the new MSDU */
if (nvt_buffer_ba_rx_frame(ba, pkt_cur->pkt, pkt_cur->seq_num,
pkt_cur->ct, &reuse) < 0) {
dev_kfree_skb_any(pkt_cur->pkt);
nvt_dbg(REORDER, "Receive duplicate frame drop it!!\n");
return;
}
/* Pass the reordered MSDUs up the stack */
nvt_reorder_ba_rx_buffer_data(ba, pkt_cur->pkt, tid,
pkt_cur->seq_num, pkt_cur->ct, reuse);
}
return;
}
static void nvt_reset_rx_ba_handle(struct _ba_struct_t *ba_ctxt)
{
struct _ba_rx_struct_t *ba = NULL;
ulong flags;
//u8 idx;
//AT:comment out unused variables
//struct _nvt_adapter *nvt_adapter = ba_ctxt->entry;
//u8 *pn_start_ptr;
//u8 *bcmc_pn_ptr;
nvt_dbg(REORDER, "%s\n", __func__);
spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
ba = ba_ctxt->ba_rx_ptr;
if (ba == NULL) {
nvt_dbg(REORDER, "%s,ba==NULL, check it!!\n", __func__);
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return;
}
del_timer(&(ba->timer));
del_timer(&(ba->reorder_timer));
nvt_dbg(REORDER, "Before reset,ba->buff_cnt=%d, ba->win_tail=%d\n",
ba->buff_cnt, ba->win_tail);
if (ba->buff_cnt > 0) {
nvt_ba_handle(ba, 1, NULL, NULL);
}
nvt_dbg(REORDER, "After reset,ba->buff_cnt=%d\n", ba->buff_cnt);
/* Flush out the BA-Rx Queue */
nvt_flush_reorder_q(ba);
nvt_free_ba_handle(ba);
ba_ctxt->ba_rx_ptr = NULL;
//AT:should not free PN in reset ba handle
//idx = (ba_ctxt->sta_index - 1) * NUM_TIDS + ba_ctxt->tid;
//pn_start_ptr = nvt_adapter->nvt_priv.pn_unicast[idx];
//bcmc_pn_ptr = nvt_adapter->nvt_priv.pn_bcmc;
//if (pn_start_ptr != NULL) {
// kfree(pn_start_ptr);
// nvt_adapter->nvt_priv.pn_unicast[idx] = 0;
//}
//if (bcmc_pn_ptr != NULL) {
// kfree(bcmc_pn_ptr);
// nvt_adapter->nvt_priv.pn_bcmc = 0;
//}
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
}
/**
* nvt_reset_ba_handle - clean ba handles for one STA or one AP
* @nvt_adapter: adapter structrue
* @ba: ba(block ack) handles
*
* this function will reset the 11e BA handles
*
* Return: NULL
*/
void nvt_reset_ba_handle(struct _nvt_adapter *nvt_adapter, u8 sta_index)
{
struct _ba_struct_t *ba_ctxt = NULL;
struct _nvt_priv nvt_priv = nvt_adapter->nvt_priv;
u8 idx;
u8 i = 0;
nvt_dbg(REORDER, "%s\n", __func__);
nvt_dbg(REORDER,
"nvt_adapter=%p, sta_index=%d\n", nvt_adapter, sta_index);
for (i = 0; i < NUM_TIDS; i++) {
idx = (sta_index - 1) * NUM_TIDS + i;
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
nvt_reset_rx_ba_handle(ba_ctxt);
nvt_priv.last_seq_ctl[idx] = 0xFFFF;
}
nvt_priv.last_seq_ctl[ASSOC_NUM * NUM_TIDS + sta_index - 1] = 0xFFFF;
}
void nvt_reset_unicast_pn(struct _nvt_adapter *nvt_adapter,
u8 sta_index)
{
u8 idx;
u8 i = 0;
u8 *pn_start_ptr;
ulong flags;
struct _ba_struct_t *ba_ctxt = NULL;
struct _ba_rx_struct_t *ba;
for (i = 0; i < NUM_TIDS; i++) {
idx = (sta_index - 1) * NUM_TIDS + i;
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
/*check ba ring buffer*/
ba = ba_ctxt->ba_rx_ptr;
if (ba != NULL && ba->buff_cnt > 0) {
nvt_ba_handle(ba, 1, NULL, NULL);
}
pn_start_ptr = nvt_adapter->nvt_priv.pn_unicast[idx];
if (pn_start_ptr != NULL) {
kfree(pn_start_ptr);
nvt_adapter->nvt_priv.pn_unicast[idx] = 0;
}
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
}
}
static void nvt_ba_timeout_fn(ulong arg)
{
struct _ba_struct_t *ba_ctxt = (struct _ba_struct_t *)arg;
struct _ba_rx_struct_t *ba = NULL;
ulong flags;
nvt_dbg(REORDER, "%s, sta_index=%d, tid=%d\n",
__func__, ba_ctxt->sta_index, ba_ctxt->tid);
nvt_dbg(REORDER, "is_in_interrupt=%lu\n", in_interrupt());
if (spin_is_locked(&ba_ctxt->ba_lock)) {
return;
}
spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
ba = ba_ctxt->ba_rx_ptr;
/* Check whether the Block-Ack session is still valid */
if (ba == NULL) {
nvt_dbg(REORDER, "ba == NULL, return\n");
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return;
}
nvt_dbg(REORDER, "ba->buff_cnt=%d, ba->win_tail=%d\n",
ba->buff_cnt, ba->win_tail);
if (ba->buff_cnt > 0) {
nvt_ba_handle(ba, 1, NULL, NULL);
}
nvt_dbg(REORDER, "ba->buff_cnt=%d\n", ba->buff_cnt);
if (ba->timeout != 0) {
nvt_dbg(REORDER,
"Timeout!=0, call workqueue to send Delba/reset ba\n");
schedule_work(&ba_ctxt->ba_timeout_work);
}
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
}
static void nvt_ba_timeout_worker(struct work_struct *work)
{
struct _ba_struct_t *ba_ctxt = container_of(work,
struct _ba_struct_t, ba_timeout_work);
struct _nvt_adapter *nvt_adapter = NULL;
s32 ret = 0;
u16 index = 0;
u8 buf[64] = {0};
nvt_dbg(REORDER, "%s, ba_ctxt=%p\n", __func__, ba_ctxt);
nvt_dbg(REORDER, "is_in_interrupt=%lu\n", in_interrupt());
nvt_adapter = ba_ctxt->entry;
nvt_dbg(REORDER, "nvt_adapter=%p, sta_index=%d, tid=%d\n",
nvt_adapter, ba_ctxt->sta_index, ba_ctxt->tid);
/* Prepare DELBA Command to FW & Reset BA-Session */
index = 1;
/* Block Ack Category */
buf[index++] = 3;
/* MLME_DELBA_REQ_TYPE */
buf[index++] = 2;
memcpy(&buf[index], ba_ctxt->dst_addr, ETH_ALEN);
index += ETH_ALEN;
buf[index++] = ba_ctxt->tid;
/* Recipient */
buf[index++] = 0;
/* QSTA_TIMEOUT */
buf[index++] = 39;
buf[0] = index;
nvt_reset_rx_ba_handle(ba_ctxt);
/* Send DELBA Command */
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
if (ret < 0) {
goto err_ret;
}
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
if (ret < 0) {
goto err_ret;
}
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
WID_11N_P_ACTION_REQ, (u8 *)&buf, index);
if (ret < 0) {
goto err_ret;
}
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
err_ret:
nvt_icfg_unlock(nvt_adapter->nvt_bus);
if (ret < 0) {
nvt_dbg(REORDER, "WID_SET(DELBA) has problems\n");
} else {
}
}
static void nvt_reorder_timeout_fn(ulong arg)
{
struct _ba_struct_t *ba_ctxt = (struct _ba_struct_t *)arg;
struct _ba_rx_struct_t *ba = NULL;
u8 tid = 0;
ulong flags;
nvt_dbg(REORDER, "%s, sta_index=%d, tid=%d\n",
__func__, ba_ctxt->sta_index, ba_ctxt->tid);
nvt_dbg(REORDER, "is_in_interrupt=%lu\n", in_interrupt());
if (spin_is_locked(&ba_ctxt->ba_lock)) {
return;
}
spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
ba = ba_ctxt->ba_rx_ptr;
/* Check whether the Block-Ack session is still valid */
if (ba == NULL) {
nvt_dbg(REORDER, "ba == NULL, return\n");
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return;
}
nvt_dbg(REORDER, "before win: start=%d, end=%d, buff_cnt=%d, tail=%d\n",
ba->win_start, ba->win_end, ba->buff_cnt, ba->win_tail);
if (ba->buff_cnt > 0) {
tid = ba->ba_data.tid;
nvt_reorder_release(ba, tid, true);
}
nvt_dbg(REORDER, "after win: start=%d, end=%d, buff_cnt=%d, tail=%d\n",
ba->win_start, ba->win_end, ba->buff_cnt, ba->win_tail);
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
}
/*
* This function reorders the Reciver buffer and sends frames to the higher
* layer on reception of a Block-Ack-Request frame. It also updates the
* receiver buffer window.
*/
static void nvt_reorder_ba_rx_buffer_bar(struct _ba_rx_struct_t *ba,
u8 tid, u16 start_seq_num)
{
nvt_dbg(REORDER, "%s\n", __func__);
nvt_dbg(REORDER, "start_seq_num=%d, tid=%d, ba->win_start=%d\n",
start_seq_num, tid, ba->win_start);
if (true == nvt_seqno_gt(start_seq_num, ba->win_start)) {
nvt_send_frames_with_gap(ba, tid, start_seq_num);
ba->win_start = start_seq_num;
ba->win_start = nvt_send_frames_in_order(ba, tid);
ba->win_end = SEQNO_ADD(ba->win_start, (ba->buff_size - 1));
}
}
s32 get_ba_index_by_hosthdr(struct _nvt_hwinfo_rx *info,
struct sk_buff *pkt)
{
u8 idx;
u8 sta_index = info->word4.sta_idx;
u8 tid = (u8)(ntohl(info->word0) & 0x000000F0) >> 4;
idx = (sta_index - 1) * NUM_TIDS + (tid);
if ((sta_index == 0) || (sta_index > 8)) {
nvt_dbg(REORDER, "Error Sta_index not available, drop\n");
nvt_dbg(REORDER, "info->word0 = 0x%08x\n", ntohl(info->word0));
dev_kfree_skb_any(pkt);
return -1;
}
if (tid > 7) {
nvt_dbg(REORDER, "Error tid not available, drop\n");
nvt_dbg(REORDER, "info->word0 = 0x%08x\n", ntohl(info->word0));
dev_kfree_skb_any(pkt);
return -1;
}
nvt_dbg(REORDER, "sta_index=%d, tid=%d\n", sta_index, tid);
return idx;
}
/**
* nvt_ba_process - this function checks whether the received frame
* should be filtered based on the Ack-Policy.
* @nvt_if : interface structrue
* @info : rx host header structure
* @pkt : rx skb buffer
* @ba_index : this is an array idx used to access the ba_ctxt
* If the rxd frame is under a block-ack session, and is out-of-order,
* then it is buffered. Otherwise, it is allowed to be passed to the kernel.
*
* Return: true - under ba session, need to reorder.
* false - pass to the kernel directly.
*/
bool nvt_ba_process(struct _nvt_if *nvt_if,
struct _nvt_hwinfo_rx *info, struct sk_buff *pkt, u8 idx)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _ba_struct_t *ba_ctxt = NULL;
struct _ba_rx_struct_t *ba = NULL;
enum _cipher_t ct;
struct _bfd_rx_pkt_t pkt_cur;
ulong flags;
u8 tid;
u8 sta_index;
//u8 idx;
u8 *pn_start_ptr;
u16 seq_num;
u8 is_BC;
u8 is_MC;
u8 *sa = NULL;
u32 tmp;
u32 i = 0;
/* Get the station ID entry & TID & Seq. & cipher, sanity check */
sta_index = info->word4.sta_idx;
tid = (u8)(ntohl(info->word0) & 0x000000F0) >> 4;
//idx = (sta_index - 1) * NUM_TIDS + tid;
seq_num = (u16)(ntohl(info->word7.value) & 0x00000fff);
/*if ((sta_index == 0) || (sta_index > 8)) {
nvt_dbg(REORDER, "Error Sta_index not available, drop\n");
nvt_dbg(REORDER, "info->word0 = 0x%08x\n", ntohl(info->word0));
dev_kfree_skb_any(pkt);
return true;
}
if (tid > 7) {
nvt_dbg(REORDER, "Error tid not available, drop\n");
nvt_dbg(REORDER, "info->word0 = 0x%08x\n", ntohl(info->word0));
dev_kfree_skb_any(pkt);
return true;
}*/
nvt_dbg(REORDER, "%s\n", __func__);
nvt_dbg(REORDER, "is_in_interrupt=%lu\n", in_interrupt());
nvt_dbg(REORDER, "nvt_if=%p, nvt_adapter=%p\n", nvt_if, nvt_adapter);
//nvt_dbg(REORDER, "sta_index=%d, seq_num=%d, tid=%d\n",
// sta_index, seq_num, tid);
is_BC = (ntohl(info->word0) & 0x10000000) >> 28;
is_MC = (ntohl(info->word0) & 0x20000000) >> 29;
ct = (enum _cipher_t)((ntohl(info->word7.value) & 0x0000f000) >> 12);
update_piggyback_fifocredit(nvt_if, info);
sa = (u8 *)(pkt->data);
tmp = info->word1.offset;
nvt_dbg(REORDER, "SA address: %02x:%02x:%02x:%02x:%02x:%02x\n",
sa[tmp + 6], sa[tmp + 7], sa[tmp + 8], sa[tmp + 9],
sa[tmp + 10], sa[tmp + 11]);
nvt_dbg(REORDER, "info->word0 = 0x%08x\n", ntohl(info->word0));
if ((sa[tmp + 12] == 0x88) && (sa[tmp + 13] == 0x8e)) {
nvt_dbg(REORDER, "This is Rx EAPOL\n");
for (i = 0; i < pkt->len; i++) {
//printk("%02x:", sa[tmp + i]);
}
nvt_dbg(REORDER, "\n");
}
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
ba = ba_ctxt->ba_rx_ptr;
pn_start_ptr = nvt_adapter->nvt_priv.pn_unicast[idx];
nvt_dbg(REORDER, "ba_ctxt=%p, ba=%p\n", ba_ctxt, ba);
/* Check if block ACK is enabled for current TID */
if (ba == NULL) {
nvt_dbg(REORDER, "Block ACK is NOT enabled ba == NULL\n");
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return false;
}
pn_start_ptr = nvt_adapter->nvt_priv.pn_unicast[idx];
if (pn_start_ptr == NULL) {
pn_start_ptr = kzalloc(6, GFP_ATOMIC);
nvt_adapter->nvt_priv.pn_unicast[idx] =
pn_start_ptr;
}
nvt_dbg(REORDER, "ba_ctxt=%p, ba=%p\n", ba_ctxt, ba);
nvt_dbg(REORDER, "timeout=%d\n", ba->timeout);
nvt_dbg(REORDER, "seq_num=%d, win_start=%d, win_tail=%d\n",
seq_num, ba->win_start, ba->win_tail);
nvt_dbg(REORDER, "buff_cnt=%d\n", ba->buff_cnt);
nvt_dbg(REORDER, "Is_BC=%d, Is_MC=%d, cipher=%d\n", is_BC, is_MC, ct);
/*
* Only consider for Uni-cast Frame based on Address one
* is_BC & is_MC is based on Address three
*/
if (((is_BC == 1) || (is_MC == 1)) && (nvt_if->mode == NVT_FW_STA)) {
nvt_dbg(REORDER, "Not Unicast frame(According Address one)\n");
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return false;
}
if (true == nvt_seqno_geq(seq_num, ba->win_start)) {
/*
* The BA-Timer is restarted each time a frame under the
* BA-session is successfully received.
*/
if (ba->timeout > 0) {
nvt_dbg(REORDER,
"start ba timer = %d ms\n", ba->timeout);
mod_timer(&(ba->timer),
(jiffies + ((ba->timeout) * HZ) / 1000));
}
if (nvt_seqno_geq(seq_num, ba->win_tail) == true) {
ba->win_tail = seq_num;
}
/*
* If the sequence number of the frame is same as Win-Start,
* and the reorder queue is empty, then the new packet need
* not be buffered in the reordering queue.
* For this type of frames just update the window parameters
* and pass it up to the host.
*/
if ((seq_num == ba->win_start) && (ba->buff_cnt == 0)) {
pkt_cur.ct = ct;
pkt_cur.pkt = (struct sk_buff *)pkt;
pkt_cur.seq_num = seq_num;
nvt_ba_handle(ba, 2, &pkt_cur, nvt_if);
} else {
pkt_cur.pkt = pkt;
pkt_cur.ct = ct;
pkt_cur.seq_num = seq_num;
nvt_ba_handle(ba, 3, &pkt_cur, nvt_if);
/*
if (ba->timeout == 0) {
if (ba->buff_cnt > 0) {
nvt_dbg(REORDER, "start timer 1sec\n");
mod_timer(&(ba->timer),
(jiffies +
(BA_RECOVERY_TIMEOUT * HZ) /
1000));
} else {
nvt_dbg(REORDER, "stop timer\n");
if (timer_pending(&(ba->timer))) {
del_timer(&(ba->timer));
}
}
}
*/
}
} else {
dev_kfree_skb_any(pkt);
nvt_dbg(REORDER,
"seq_num is not within valid space, Discard it!!!\n");
}
/* Check Sync loss and flush the reorder queue when one is detected */
if ((ba->win_tail == SEQNO_SUB(ba->win_start, 1)) &&
(ba->buff_cnt > 0)) {
nvt_dbg(REORDER, "Sync loss happenned, check it out, ERROR!\n");
nvt_dbg(REORDER, "win_start=%d, buff_cnt=%d\n",
ba->win_start, ba->buff_cnt);
nvt_flush_reorder_q(ba);
ba->buff_cnt = 0;
}
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return true;
}
static s32 nvt_handle_addba_req_evt(struct _nvt_if *nvt_if,
const u16 msg_len, u8 *data)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _ba_struct_t *ba_ctxt = NULL;
struct _ba_rx_struct_t *ba = NULL;
u8 *pn_start_ptr;
gfp_t alloc_flag = GFP_ATOMIC;
ulong flags;
u16 buf_size;
u8 tid;
u8 sta_index;
u8 idx;
u8 err = 0;
nvt_dbg(REORDER, "%s\n", __func__);
nvt_dbg(REORDER, "is_in_interrupt=%lu\n", in_interrupt());
/* Get the station ID entry & TID & buff_size */
sta_index = data[2];
tid = data[3];
idx = (sta_index - 1) * NUM_TIDS + tid;
nvt_dbg(REORDER, "nvt_if=%p, nvt_adapter=%p\n", nvt_if, nvt_adapter);
nvt_dbg(REORDER, "sta_index=%d, tid=%d\n", sta_index, tid);
if ((sta_index == 0) || (sta_index > 8)) {
nvt_dbg(REORDER, "Error Sta_index not available, drop\n");
return -EINVAL;
}
if (tid > 7) {
nvt_dbg(REORDER, "Error tid not available, drop\n");
return -EINVAL;
}
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
ba = ba_ctxt->ba_rx_ptr;
pn_start_ptr = nvt_adapter->nvt_priv.pn_unicast[idx];
if (ba == NULL) {
nvt_dbg(REORDER, "ba == NULL, new BA is build\n");
ba = kzalloc(sizeof(struct _ba_rx_struct_t), alloc_flag);
/*Allocate 6 byte PN for WPA*/
if (ba == NULL) {
nvt_dbg(REORDER, "_ba_rx_struct_t allocate fail\n");
err = 1;
goto fail;
}
ba_ctxt->ba_rx_ptr = ba;
/*Allocate 6 byte PN for WPA*/
if (pn_start_ptr == NULL) {
pn_start_ptr = kzalloc(6, alloc_flag);
}
if (pn_start_ptr == NULL) {
nvt_dbg(ERROR, "PN allocate fail\n");
err = 2;
goto fail;
}
ba->rx_pn_val = &(nvt_adapter->nvt_priv.pn_unicast[idx]);
nvt_adapter->nvt_priv.pn_unicast[idx] = pn_start_ptr;
buf_size = data[14] | (data[15] << 8);
if ((buf_size > MAX_NUM_BA_BUFFERS) || (buf_size == 0)) {
nvt_dbg(REORDER, "buff_size is out of range, fail\n");
err = 2;
goto fail;
}
ba->buff_size = data[14] | (data[15] << 8);
g_num_ba_buffers = ba->buff_size;
ba->buff_list = kzalloc(BA_QUEUE_BUFF_SIZE, alloc_flag);
if ((ba->buff_list) == NULL) {
nvt_dbg(REORDER, "_bfd_rx_pkt_t allocate fail\n");
err = 2;
goto fail;
}
} else {
nvt_dbg(REORDER, "The TID already build, tid=%d\n", tid);
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
/*
* if same TID BA is re-build by peer device,
* we will clean old and build new one.
*/
nvt_reset_rx_ba_handle(ba_ctxt);
spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
ba = kzalloc(sizeof(struct _ba_rx_struct_t), alloc_flag);
if (ba == NULL) {
nvt_dbg(REORDER, "_ba_rx_struct_t allocate fail\n");
err = 1;
goto fail;
}
ba_ctxt->ba_rx_ptr = ba;
if (pn_start_ptr == NULL) {
nvt_dbg(ERROR, "PN should not be NULL\n");
pn_start_ptr = kzalloc(6, alloc_flag);
nvt_adapter->nvt_priv.pn_unicast[idx] = pn_start_ptr;
}
if (pn_start_ptr == NULL) {
nvt_dbg(ERROR, "PN allocate fail\n");
err = 1;
goto fail;
}
buf_size = data[14] | (data[15] << 8);
if ((buf_size > MAX_NUM_BA_BUFFERS) || (buf_size == 0)) {
nvt_dbg(REORDER, "buff_size is out of range, fail\n");
err = 2;
goto fail;
}
ba->buff_size = data[14] | (data[15] << 8);
g_num_ba_buffers = ba->buff_size;
ba->buff_list = kzalloc(BA_QUEUE_BUFF_SIZE, alloc_flag);
if ((ba->buff_list) == NULL) {
nvt_dbg(REORDER, "_bfd_rx_pkt_t allocate fail\n");
err = 2;
goto fail;
}
ba->rx_pn_val = &(nvt_adapter->nvt_priv.pn_unicast[idx]);
}
ba->sta_index = sta_index;
memcpy(ba_ctxt->dst_addr, &data[4], ETH_ALEN);
ba->ba_data.tid = tid;
ba->ba_data.entry = nvt_adapter;
ba_ctxt->entry = nvt_adapter;
/* in TUs = 1024us = 1.024ms */
ba->timeout = data[10] | (data[11] << 8);
ba->win_start = data[12] | (data[13] << 8);
ba->win_end = SEQNO_ADD(ba->win_start, (ba->buff_size - 1));
ba->win_tail = SEQNO_SUB(ba->win_start, 1);
nvt_dbg(REORDER, "ba->timeout=%d, ba->win_start=%d\n",
ba->timeout, ba->win_start);
nvt_dbg(REORDER, "ba->win_end=%d, ba->win_tail=%d\n",
ba->win_end, ba->win_tail);
/* Initial Session Timer */
init_timer(&(ba->timer));
ba->timer.function = nvt_ba_timeout_fn;
ba->timer.data = (unsigned long)ba_ctxt;
/* Initial Reorder Timer */
init_timer(&(ba->reorder_timer));
ba->reorder_timer.function = nvt_reorder_timeout_fn;
ba->reorder_timer.data = (unsigned long)ba_ctxt;
nvt_dbg(REORDER, "tid=%d, ba_ctxt=%p, ba=%p\n", tid, ba_ctxt, ba);
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return 0;
fail:
switch (err) {
case 2:
kfree(ba_ctxt->ba_rx_ptr);
ba_ctxt->ba_rx_ptr = NULL;
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
break;
default:
/* Do nothing */
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
break;
}
return -ENOMEM;
}
static s32 nvt_handle_delba_evt(struct _nvt_if *nvt_if,
const u16 msg_len, u8 *data)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _ba_struct_t *ba_ctxt = NULL;
u8 tid = 0;
u8 sta_index = 0;
u8 idx = 0;
nvt_dbg(REORDER, "%s\n", __func__);
/* Get the station ID entry & TID */
sta_index = data[2];
tid = data[3];
idx = (sta_index - 1) * NUM_TIDS + tid;
nvt_dbg(REORDER, "nvt_if=%p, nvt_adapter=%p\n", nvt_if, nvt_adapter);
nvt_dbg(REORDER, "sta_index=%d, tid=%d\n", sta_index, tid);
if ((sta_index == 0) || (sta_index > 8)) {
nvt_dbg(REORDER, "Error Sta_index not available, drop\n");
return -EINVAL;
}
if (tid > 7) {
nvt_dbg(REORDER, "Error tid not available, drop\n");
return -EINVAL;
}
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
nvt_reset_rx_ba_handle(ba_ctxt);
return 0;
}
static s32 nvt_handle_bar_evt(struct _nvt_if *nvt_if,
const u16 msg_len, u8 *data)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _ba_struct_t *ba_ctxt = NULL;
struct _ba_rx_struct_t *ba = NULL;
u16 seq_num;
ulong flags;
u8 tid;
u8 sta_index;
u8 idx;
nvt_dbg(REORDER, "%s\n", __func__);
/* Get the station ID entry & TID & SSN */
sta_index = data[2];
tid = data[3];
idx = (sta_index - 1) * NUM_TIDS + tid;
seq_num = data[4] | (data[5] << 8);
nvt_dbg(REORDER, "nvt_if=%p, nvt_adapter=%p\n", nvt_if, nvt_adapter);
nvt_dbg(REORDER, "sta_index=%d, tid=%d, seq_num=%d\n",
sta_index, tid, seq_num);
if ((sta_index == 0) || (sta_index > 8)) {
nvt_dbg(REORDER, "Error Sta_index not available, drop\n");
return -EINVAL;
}
if (tid > 7) {
nvt_dbg(REORDER, "Error tid not available, drop\n");
return -EINVAL;
}
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
spin_lock_irqsave(&ba_ctxt->ba_lock, flags);
ba = ba_ctxt->ba_rx_ptr;
if (ba == NULL) {
nvt_dbg(REORDER, "ba is NULL, return\n");
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return 0;
}
/* Reorder the BA-Rx Buffer */
nvt_reorder_ba_rx_buffer_bar(ba, tid, seq_num);
spin_unlock_irqrestore(&ba_ctxt->ba_lock, flags);
return 0;
}
#ifdef CONFIG_WIFI_TUNING_PHASE_II
static s32 nvt_notify_tp_monitor(struct _nvt_if *nvt_if,
const u16 msg_len, u8 *data)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _nvt_priv *nvt_priv = &nvt_adapter->nvt_priv;
struct _tx_info *tx_info = nvt_adapter->nvt_priv.tx_info;
u8 i, j, zero_mac[ETH_ALEN] = {0}, found = 0, phyrate_found;
u8 win_sz = 8; /* Note: Max. Sliding window size is 8 */
u8 *monitor_info_p = (((u8 *)data) + 2);
u8 *p;
u16 monitor_info_p_len = (msg_len-2); /* discard of WID */
/* // debug
nvt_dbg(CLEARVIEW, "%s: monitor_info_p_len:%d\n",
__func__, monitor_info_p_len);
for (i=0; i< monitor_info_p_len; i++) {
nvt_dbg(CLEARVIEW, " %x ", *(monitor_info_p+i));
//if(i%16==0) nvt_dbg(CLEARVIEW, "\n");
}
nvt_dbg(CLEARVIEW,"monitor_info_p:%p\n", monitor_info_p);
nvt_dbg(CLEARVIEW, "MAC_Sts_mac_off:%d\n", MAC_Sts_mac_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_phyr_off:%d\n", MAC_Sts_phyr_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_votxsz_off:%d\n", MAC_Sts_votxsz_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_betxsz_off:%d\n", MAC_Sts_betxsz_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_votxfrm_off:%d\n", MAC_Sts_votxfrm_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_betxfrm_off:%d\n", MAC_Sts_betxfrm_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_vodrpsz_off:%d\n", MAC_Sts_vodrpsz_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_bedrpsz_off:%d\n", MAC_Sts_bedrpsz_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_vodrpfrm_off:%d\n", MAC_Sts_vodrpfrm_off);
nvt_dbg(CLEARVIEW, "MAC_Sts_bedrpfrm_off:%d\n", MAC_Sts_bedrpfrm_off);
//*/
u16 wid = *((u8 *)data) | (*(((u8 *)data) + 1) << 8);
/* driver layer */
u32 outq_num_vo, outq_num_be, drop_num_be;
u32 outq_sz_vo, outq_sz_be;
/* MAC layer */
u16 mac_vodropframe, mac_bedropframe;
u32 phyrate = 0;
u32 mac_votxsz, mac_betxsz, mac_votxframe, mac_betxframe;
u32 mac_vodropsz, mac_bedropsz;
struct _sta_sts_in_ap *sta_sts_in_ap = NULL;
ulong flags = 0;
//WMM_EDCA_Q_T *hQue_be = (WMM_EDCA_Q_T *)pDrv->edca_q[EDCA_Q_BE];
//WMM_EDCA_Q_T *hQue_vo = (WMM_EDCA_Q_T *)pDrv->edca_q[EDCA_Q_VO];
if (wid != WID_EVENT_TP_MONITOR) {
// it will be freed by the caller
nvt_dbg(ERROR, "%s: Wrong WID: 0x%x isn't 0x000A ", __func__, wid);
} else {
if (monitor_info_p_len >= MAC_Status_Len) {
// Clear once MAC report becuase we don't want to handle
// the case of overflow in outq_num and outq_sz.
// Driver VO statistic
outq_num_vo = tx_info->stats[NV_TX_FIFO_AC_VO].out;
outq_sz_vo = tx_info->stats[NV_TX_FIFO_AC_VO].out_sz;
spin_lock_irqsave(&tx_info->lock, flags);
tx_info->stats[NV_TX_FIFO_AC_VO].out = 0;
tx_info->stats[NV_TX_FIFO_AC_VO].out_sz = 0;
spin_unlock_irqrestore(&tx_info->lock, flags);
// Driver BE statistic
outq_num_be = tx_info->stats[NV_TX_FIFO_AC_BE].out;
outq_sz_be = tx_info->stats[NV_TX_FIFO_AC_BE].out_sz;
spin_lock_irqsave(&tx_info->lock, flags);
tx_info->stats[NV_TX_FIFO_AC_BE].out = 0;
tx_info->stats[NV_TX_FIFO_AC_BE].out_sz = 0;
spin_unlock_irqrestore(&tx_info->lock, flags);
drop_num_be = tx_info->stats[NV_TX_FIFO_AC_BE].drop;
spin_lock_irqsave(&tx_info->lock, flags);
tx_info->stats[NV_TX_FIFO_AC_BE].drop = 0;
spin_unlock_irqrestore(&tx_info->lock, flags);
i = 0;
do {
found = 0;
p = (u8 *)(monitor_info_p + i);
//nvt_dbg(CLEARVIEW, "i:%d\n", i);
//nvt_dbg(CLEARVIEW, "p:%p\n", p);
// sta mac is not zero
if (memcmp((u8 *)(p + MAC_Sts_mac_off), zero_mac, ETH_ALEN)) {
list_for_each_entry(sta_sts_in_ap,
&nvt_priv->sta_list_in_ap, list) {
/* // debug
nvt_dbg(CLEARVIEW, "sta:" MACSTR "\n",
MAC2STR(sta_sts_in_ap->mac_addr));
nvt_dbg(CLEARVIEW, "MAC_Sts_mac_off:%d\n",
MAC_Sts_mac_off);
//*/
if (!memcmp((u8 *)(p + MAC_Sts_mac_off),
sta_sts_in_ap->mac_addr, ETH_ALEN)) {
found = 1;
phyrate = mac_votxsz = mac_betxsz = 0;
mac_votxframe = mac_betxframe = 0;
mac_vodropsz = mac_bedropsz = 0;
mac_vodropframe = mac_bedropsz = 0;
phyrate = *((p + MAC_Sts_phyr_off));
#if 1
mac_votxsz = (
(*((u8 *)(p + MAC_Sts_votxsz_off)) << 24) |
(*((u8 *)(p + MAC_Sts_votxsz_off + 1)) << 16) |
(*((u8 *)(p + MAC_Sts_votxsz_off + 2)) << 8) |
(*((u8 *)(p + MAC_Sts_votxsz_off + 3))));
mac_betxsz = (
(*((u8 *)(p + MAC_Sts_betxsz_off)) << 24) |
(*((u8 *)(p + MAC_Sts_betxsz_off + 1)) << 16) |
(*((u8 *)(p + MAC_Sts_betxsz_off + 2)) << 8) |
(*((u8 *)(p + MAC_Sts_betxsz_off + 3))));
mac_votxframe = (
(*((u8 *)(p + MAC_Sts_votxfrm_off)) << 24) |
(*((u8 *)(p + MAC_Sts_votxfrm_off + 1)) << 16) |
(*((u8 *)(p + MAC_Sts_votxfrm_off + 2)) << 8) |
(*((u8 *)(p + MAC_Sts_votxfrm_off + 3))));
mac_betxframe = (
(*((u8 *)(p + MAC_Sts_betxfrm_off)) << 24) |
(*((u8 *)(p + MAC_Sts_betxfrm_off + 1)) << 16) |
(*((u8 *)(p + MAC_Sts_betxfrm_off + 2)) << 8) |
(*((u8 *)(p + MAC_Sts_betxfrm_off + 3))));
mac_vodropsz = (
(*((u8 *)(p + MAC_Sts_vodrpsz_off)) << 24) |
(*((u8 *)(p + MAC_Sts_vodrpsz_off + 1)) << 16) |
(*((u8 *)(p + MAC_Sts_vodrpsz_off + 2)) << 8) |
(*((u8 *)(p + MAC_Sts_vodrpsz_off + 3))));
mac_bedropsz = (
(*((u8 *)(p + MAC_Sts_bedrpsz_off)) << 24) |
(*((u8 *)(p + MAC_Sts_bedrpsz_off + 1)) << 16) |
(*((u8 *)(p + MAC_Sts_bedrpsz_off + 2)) << 8) |
(*((u8 *)(p + MAC_Sts_bedrpsz_off + 3))));
mac_vodropframe = (
(*((u8 *)(p + MAC_Sts_vodrpfrm_off)) << 8) |
(*((u8 *)(p + MAC_Sts_vodrpfrm_off + 1))));
mac_bedropframe = (
(*((u8 *)(p + MAC_Sts_bedrpfrm_off)) << 8) |
(*((u8 *)(p + MAC_Sts_bedrpfrm_off + 1))));
#else
memcpy(&mac_votxsz,
(u8 *)(monitor_info_p+i+MAC_Sts_votxsz_off),
sizeof(u32));
mac_votxsz = endian_swap_u32(mac_votxsz);
memcpy(&mac_betxsz,
(u8 *)(monitor_info_p+i+MAC_Sts_betxsz_off),
sizeof(u32));
mac_betxsz = endian_swap_u32(mac_betxsz);
memcpy(&mac_votxframe,
(u8 *)(monitor_info_p+i+MAC_Sts_votxfrm_off),
sizeof(u32));
mac_votxframe = endian_swap_u32(mac_votxframe);
memcpy(&mac_betxframe,
(u8 *)(monitor_info_p+i+MAC_Sts_betxfrm_off),
sizeof(u32));
mac_betxframe = endian_swap_u32(mac_betxframe);
memcpy(&mac_vodropsz,
(u8 *)(monitor_info_p+i+MAC_Sts_vodrpsz_off),
sizeof(u32));
mac_vodropsz = endian_swap_u32(mac_vodropsz);
memcpy(&mac_bedropsz,
(u8 *)(monitor_info_p+i+MAC_Sts_bedrpsz_off),
sizeof(u32));
mac_bedropsz = endian_swap_u32(mac_bedropsz);
memcpy(&mac_vodropframe,
(u8 *)(monitor_info_p+i+MAC_Sts_vodrpfrm_off),
sizeof(u16));
mac_vodropframe = endian_swap_u16(mac_vodropframe);
memcpy(&mac_bedropframe,
(u8 *)(monitor_info_p+i+MAC_Sts_bedrpfrm_off),
sizeof(u16));
mac_bedropframe = endian_swap_u16(mac_bedropframe);
#endif
break;
}
}
if (found == 0) {
nvt_dbg(ERROR,
"%s: There's T-Put sts repored from MAC which ",
__func__);
nvt_dbg(ERROR, "doesn't join this AP ");
nvt_dbg(ERROR, "addr: %x:%x:%x:%x:%x:%x\n",
*((u8 *)(p + MAC_Sts_mac_off)),
*((u8 *)(p + MAC_Sts_mac_off + 1)),
*((u8 *)(p + MAC_Sts_mac_off + 2)),
*((u8 *)(p + MAC_Sts_mac_off + 3)),
*((u8 *)(p + MAC_Sts_mac_off + 4)),
*((u8 *)(p + MAC_Sts_mac_off + 5)));
} else {
phyrate_found = 0;
for (j = 0; j < TP_MONITOR_PHYRATE_TBL_SZ; j++) {
if (phyrate == tp_monitor_phyrate_map_tbl[j][0]) {
phyrate = tp_monitor_phyrate_map_tbl[j][1];
phyrate_found = 1;
break;
}
}
if (phyrate_found == 0) {
sta_sts_in_ap->phyrate = 0;
nvt_dbg(CLEARVIEW,
"%s: PhyRate:0x%x mapping failed, set to 0\n",
__func__, sta_sts_in_ap->phyrate);
} else {
// Info from MAC (TX size/frame cnt in VO/BE)
sta_sts_in_ap->mac_votxsz = mac_votxsz;
sta_sts_in_ap->mac_betxsz = mac_betxsz;
sta_sts_in_ap->mac_votxframe = mac_votxframe;
sta_sts_in_ap->mac_betxframe = mac_betxframe;
// Info from MAC (Drop size/frame cnt in VO/BE)
sta_sts_in_ap->mac_vodropsz = mac_vodropsz;
sta_sts_in_ap->mac_bedropsz = mac_bedropsz;
sta_sts_in_ap->mac_vodropframe = mac_vodropframe;
sta_sts_in_ap->mac_bedropframe = mac_bedropframe;
nvt_dbg(CLEARVIEW, "@ mac_votx:%04d,"
"mac_votxsz:%08d, mac_betx:%04d,"
"mac_betxsz:%08d\n",
mac_votxframe, mac_votxsz,
mac_betxframe, mac_betxsz);
nvt_dbg(CLEARVIEW, "mac_vodrop:%04d,"
"mac_vodropsz:%08d, mac_bedrop:%04d,"
"mac_bedropsz:%08d\n",
mac_vodropframe, mac_vodropsz,
mac_bedropframe, mac_bedropsz);
nvt_dbg(CLEARVIEW, "outq_num_vo:%04d,"
"outq_sz_vo:%08d, outq_num_be:%04d,"
"outq_sz_be:%08d\n",
outq_num_vo, outq_sz_vo,
outq_num_be, outq_sz_be);
nvt_dbg(CLEARVIEW, "drop_num_be: %04d\n",
drop_num_be);
nvt_dbg(CLEARVIEW, "phyrate: %02d.%d Mb/s\n",
phyrate/1000, (phyrate/100)%10);
#if 0 /* print format 1 */
printk("@ mac_votx:%04d, mac_votxsz:%08d,"
"mac_betx:%04d, mac_betxsz:%08d\n",
mac_votxframe, mac_votxsz,
mac_betxframe, mac_betxsz);
printk("mac_vodrop:%04d, mac_vodropsz:%08d,"
"mac_bedrop:%04d, mac_bedropsz:%08d\n",
mac_vodropframe, mac_vodropsz,
mac_bedropframe, mac_bedropsz);
printk("outq_num_vo:%04d, outq_sz_vo:%08d,"
"outq_num_be:%04d, outq_sz_be:%08d\n",
outq_num_vo, outq_sz_vo,
outq_num_be, outq_sz_be);
printk("drop_num_be: %04d\n", drop_num_be);
printk("phyrate: %02d.%d Mb/s\n",
phyrate/1000, (phyrate/100)%10);
#endif
#if 0 /* print format 2 */
printk(" VO BE\n");
printk("==============================================\n");
printk(" drv tx: ");
printk("%-4d(%-8d) ",
outq_num_vo, outq_sz_vo);
printk("%-4d(%-8d)\n", outq_num_be, outq_sz_be);
printk(" drv drop: ");
printk("%-4d ",
tx_info->stats[NV_TX_FIFO_AC_VO].drop);
printk("%-4d\n", drop_num_be);
printk(" mac tx: ");
printk("%-4d(%-8d) ",
mac_votxframe, mac_votxsz);
printk("%-4d(%-8d)\n",
mac_betxframe, mac_betxsz);
printk(" mac drop: ");
printk("%-4d(%-8d) ",
mac_vodropframe, mac_vodropsz);
printk("%-4d(%-8d)\n",
mac_bedropframe, mac_bedropsz);
printk("phyrate: %02d.%d Mb/s\n", phyrate/1000,
(phyrate/100)%10);
#endif
#if 1 /* print format 3 */
printk("@ %02d.%d ",
phyrate/1000, (phyrate/100)%10);
printk("%04d %08d %04d %08d ",
mac_votxframe, mac_votxsz,
mac_vodropframe, mac_vodropsz);
printk("%04d %08d %04d %08d\n",
mac_betxframe, mac_betxsz,
mac_bedropframe, mac_bedropsz);
printk("Drv %04d %08d %04d %08d %04d\n",
outq_num_vo, outq_sz_vo, outq_num_be,
outq_sz_be, drop_num_be);
#endif
// Record the last records
// Note: The latest one will at the beginning of array
for (j = (win_sz - 1); j > 0; j--) {
sta_sts_in_ap->phyrate_history[j] =
sta_sts_in_ap->phyrate_history[j-1];
sta_sts_in_ap->mac_vodropframe_history[j] =
sta_sts_in_ap->mac_vodropframe_history[j-1];
sta_sts_in_ap->mac_bedropframe_history[j] =
sta_sts_in_ap->mac_bedropframe_history[j-1];
sta_sts_in_ap->outq_vo_be_history[j] =
sta_sts_in_ap->outq_vo_be_history[j-1];
sta_sts_in_ap->drop_be_history[j] =
sta_sts_in_ap->drop_be_history[j-1];
}
sta_sts_in_ap->mac_vodropframe_history[0] =
sta_sts_in_ap->mac_vodropframe;
sta_sts_in_ap->mac_bedropframe_history[0] =
sta_sts_in_ap->mac_bedropframe;
sta_sts_in_ap->outq_vo_be_history[0] =
outq_num_be + outq_num_vo;
sta_sts_in_ap->drop_be_history[0] =
drop_num_be;
sta_sts_in_ap->phyrate_history[0] = phyrate;
// Average it
sta_sts_in_ap->enq_in_vo_be = 0;
sta_sts_in_ap->drop_in_be = 0;
for (j = 0; j < win_sz; j++) {
sta_sts_in_ap->enq_in_vo_be +=
sta_sts_in_ap->outq_vo_be_history[j];
sta_sts_in_ap->drop_in_be +=
sta_sts_in_ap->drop_be_history[j];
}
sta_sts_in_ap->enq_in_vo_be /= win_sz;
sta_sts_in_ap->drop_in_be /= win_sz;
#if 0 /* average the last 8 PhyRate */
for (j = 0; j < win_sz; j++) {
if (!sta_sts_in_ap->phyrate_history[j])
break;
}
if (j == win_sz) {
sta_sts_in_ap->phyrate = 0;
for (j = 0; j < win_sz; j++) {
sta_sts_in_ap->phyrate +=
sta_sts_in_ap->phyrate_history[j];
}
sta_sts_in_ap->phyrate /= win_sz;
} else {
sta_sts_in_ap->phyrate = phyrate;
}
#endif
#if 0 /* show the hisotry of phyrate */
for (j = 0; j < win_sz; j++) {
dbg_msg(DBG_FLAG_TP_MONITOR, ("%06d ",
sta_sts_in_ap->phyrate_history[j]));
}
dbg_msg(DBG_FLAG_TP_MONITOR, ("%06d\n",
sta_sts_in_ap->phyrate));
#endif
cview_predict_tp(sta_sts_in_ap, win_sz);
}
}
} else {
nvt_dbg(ERROR, "%s: MAC is zero\n", __func__);
}
i += MAC_Status_Len;
} while (i < monitor_info_p_len);
}
}
return 0;
}
s32 cview_predict_tp(struct _sta_sts_in_ap *sta_sts_in_ap, int win_sz)
{
int i, ret = 0;
unsigned int ori_t_put, first = 0;
for (i = 1; i < win_sz; i++) {
if (sta_sts_in_ap->phyrate_history[i])
break;
}
if (i == win_sz) {
// Initial value. Using PhyRate to estimate T-Put cause we don't have
// any information related to T-Put.
// phyrate*0.5 (bits)
sta_sts_in_ap->t_put =
(sta_sts_in_ap->phyrate_history[0] >> 1) * 1000;
nvt_dbg(CLEARVIEW,
"cview_predict_tp: phyrate_history[0]:%d, init t_put:%d\n",
sta_sts_in_ap->phyrate_history[0], sta_sts_in_ap->t_put);
ori_t_put = sta_sts_in_ap->phyrate_history[0];
first = 1;
sta_sts_in_ap->incr_counter = 0;
} else {
// Changing initial T-Put especial as it changed DRate as 1 or 2
// if(sta_sts_in_ap->phyrate_history[1] > 2000) {
// if(sta_sts_in_ap->phyrate_history[0] == 2000) {
// sta_sts_in_ap->t_put = TPUT_IN_PHY_2_0;
// }
// else if(sta_sts_in_ap->phyrate_history[0] == 1000) {
// sta_sts_in_ap->t_put = TPUT_IN_PHY_1_0;
// }
// }
ori_t_put = sta_sts_in_ap->t_put;
first = 0;
if (sta_sts_in_ap->mac_vodropframe_history[0]) {
//sta_sts_in_ap->t_put *= (0.5);
sta_sts_in_ap->t_put = ori_t_put / 2;
sta_sts_in_ap->incr_counter = 0;
} else if (sta_sts_in_ap->drop_be_history[0]) {
//sta_sts_in_ap->t_put *= (0.7);
sta_sts_in_ap->t_put = (ori_t_put*7)/10 + (ori_t_put*7)%10;
sta_sts_in_ap->incr_counter = 0;
} else if (sta_sts_in_ap->mac_bedropframe_history[0] > 0) {
#if 0
for (j = 1; j < VCLEAR_MACBEDROP_THRES; j++) {
if (sta_sts_in_ap->mac_bedropframe_history[j] < 10)
break;
}
if (j == VCLEAR_MACBEDROP_THRES) {
sta_sts_in_ap->t_put *= (0.9);
} else {
// hold the t-put
}
#else
//sta_sts_in_ap->t_put *= (0.9);
sta_sts_in_ap->t_put = (ori_t_put*9)/10 + (ori_t_put*9)%10;
if ((sta_sts_in_ap->mac_bedropframe_history[1] > 0) &&
(sta_sts_in_ap->mac_bedropframe_history[0] >=
sta_sts_in_ap->mac_bedropframe_history[1])) {
//sta_sts_in_ap->t_put *= (0.9);
sta_sts_in_ap->t_put = (ori_t_put*9)/10 + (ori_t_put*9)%10;
}
#endif
sta_sts_in_ap->incr_counter = 0;
} else {
#if 0
for (j = 0; j < VCLEAR_INCR_TP_THRES; j++) {
if (sta_sts_in_ap->mac_bedropframe_history[j] ||
sta_sts_in_ap->mac_bedropframe_history[j] ||
sta_sts_in_ap->drop_be_history[j]) {
break;
}
}
if (j == VCLEAR_INCR_TP_THRES) {
sta_sts_in_ap->t_put *= (1.1);
} else {
// hold this t-put
}
#else
if (!sta_sts_in_ap->mac_bedropframe_history[0]) {
sta_sts_in_ap->incr_counter++;
} else {
sta_sts_in_ap->incr_counter = 0;
}
#if 0
if (sta_sts_in_ap->incr_counter == 4) {
// increase more as its t-put less than 20KB or 30KB
if (sta_sts_in_ap->t_put < 20 * 1000 * 8) {
sta_sts_in_ap->t_put *= (1.3);
} else if (sta_sts_in_ap->t_put < 30 * 1000 * 8) {
sta_sts_in_ap->t_put *= (1.2);
} else {
sta_sts_in_ap->t_put *= (1.1);
}
sta_sts_in_ap->incr_counter = 0;
}
#else
if ((sta_sts_in_ap->t_put < QP_turning_point_3) &&
(sta_sts_in_ap->incr_counter == 4)) {
//sta_sts_in_ap->t_put *= (1.8);
sta_sts_in_ap->t_put = (ori_t_put*18)/10 + (ori_t_put*18)%10;
sta_sts_in_ap->incr_counter = 0;
} else if (sta_sts_in_ap->incr_counter == 8) {
//sta_sts_in_ap->t_put *= (1.2);
sta_sts_in_ap->t_put = (ori_t_put*12)/10 + (ori_t_put*12)%10;
sta_sts_in_ap->incr_counter = 0;
}
#endif
#endif
}
}
// Prevent T-put from exceeding its limitation
if (sta_sts_in_ap->t_put > VCLEAR_MAX_TP) {
sta_sts_in_ap->t_put = VCLEAR_MAX_TP;
} else if (sta_sts_in_ap->phyrate_history[0] == 1000) {
if (sta_sts_in_ap->t_put > TPUT_IN_PHY_1_0) {
sta_sts_in_ap->t_put = TPUT_IN_PHY_1_0;
}
} else if (sta_sts_in_ap->phyrate_history[0] == 2000) {
if (sta_sts_in_ap->t_put > TPUT_IN_PHY_2_0) {
sta_sts_in_ap->t_put = TPUT_IN_PHY_2_0;
}
}
// Set 10KB as its lowerbound
if (sta_sts_in_ap->t_put < VCLEAR_MIN_TP)
sta_sts_in_ap->t_put = VCLEAR_MIN_TP;
nvt_dbg(CLEARVIEW, "%s: [%02x:%02x:%02x:%02x:%02x:%02x] t_put: %d\n",
__func__,
sta_sts_in_ap->mac_addr[0], sta_sts_in_ap->mac_addr[1],
sta_sts_in_ap->mac_addr[2], sta_sts_in_ap->mac_addr[3],
sta_sts_in_ap->mac_addr[4], sta_sts_in_ap->mac_addr[5],
sta_sts_in_ap->t_put);
//*
printk("[%02x:%02x:%02x:%02x:%02x:%02x] first:%d t_put:%08d (ori:%08d)\n",
sta_sts_in_ap->mac_addr[0], sta_sts_in_ap->mac_addr[1],
sta_sts_in_ap->mac_addr[2], sta_sts_in_ap->mac_addr[3],
sta_sts_in_ap->mac_addr[4], sta_sts_in_ap->mac_addr[5],
first,
sta_sts_in_ap->t_put, ori_t_put);
//*/
return ret;
}
#endif
static void remote_fw_input(struct sk_buff *skb)
{
u8 const *delim = ",";
u8 *token;
int fuart_mode = 0;
int ret;
unsigned int src_pid, src_seq;
void *data;
struct nlmsghdr *nlh;
struct net_device *dev;
struct _nvt_if *nvt_if;
struct _nvt_bus *nvt_bus;
struct _nvt_priv *nvt_priv;
nlh = (struct nlmsghdr *)skb->data;
src_pid = nlh->nlmsg_pid;
src_seq = nlh->nlmsg_seq;
data = NLMSG_DATA(nlh);
//printk("string from user-space:%s\n", data);
token = strsep((char **)&data, delim);
dev = __dev_get_by_name(&init_net, token);
if (!dev) {
nvt_dbg(ERROR, "Incorrect Network Name: %s\n", token);
goto err;
}
nvt_if = netdev_priv(dev);
nvt_bus = nvt_if->nvt_adapter->nvt_bus;
nvt_priv = &nvt_if->nvt_adapter->nvt_priv;
token = strsep((char **)&data, delim);
if (!strncmp(token, "RemoteDebugStart", 16)) {
nvt_dbg(REMOTEDBG, "%s: RemoteDebug is starting...\n",
__func__);
nvt_priv->remote_debug_pid = src_pid;
nvt_priv->remote_debug_start = 1;
nvt_dbg(REMOTEDBG, "%s: utility_pid:%d\n", __func__,
nvt_priv->remote_debug_pid);
/* remote debug mode */
fuart_mode = 2;
ret = nvt_icfg_lock(nvt_bus);
if (ret < 0) {
goto icfg_send_err;
}
ret = nvt_icfg_reset(nvt_bus);
if (ret < 0) {
goto icfg_send_err;
}
ret = nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_UART_MODE,
(u8 *)&fuart_mode, sizeof(fuart_mode));
if (ret < 0) {
goto icfg_send_err;
}
ret = nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0);
nvt_icfg_unlock(nvt_bus);
} else if (!strncmp(token, "RemoteDebugStop", 15)) {
nvt_dbg(REMOTEDBG, "%s: RemoteDebug is closing...\n", __func__);
fuart_mode = 1;
ret = nvt_icfg_lock(nvt_bus);
if (ret < 0) {
goto icfg_send_err;
}
ret = nvt_icfg_reset(nvt_bus);
if (ret < 0) {
goto icfg_send_err;
}
ret = nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_UART_MODE,
(u8 *)&fuart_mode, sizeof(fuart_mode));
if (ret < 0) {
goto icfg_send_err;
}
ret = nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0);
nvt_icfg_unlock(nvt_bus);
nvt_priv->remote_debug_start = 0;
nvt_priv->remote_debug_pid = 0;
} else {
nvt_dbg(ERROR, "Not support string:%s\n", token);
}
//kfree_skb(skb);
return;
icfg_send_err:
nvt_icfg_unlock(nvt_bus);
err:
//kfree_skb(skb);
return;
}
static s32 nvt_remote_fwlog(struct _nvt_if *nvt_if, const u16 msg_len, u8 *data)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
struct _nvt_priv *nvt_priv = &nvt_if->nvt_adapter->nvt_priv;
struct sock *remote_fwlog_sock = nvt_priv->remote_fwlog_sock;
int ret = 0;
/* 1 more byte is null character */
int len = NLMSG_SPACE(msg_len + 1);
if (nvt_priv->remote_debug_start == 1) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
skb = alloc_skb(len, GFP_ATOMIC);
#else
skb = nlmsg_new(len, GFP_ATOMIC);
#endif
if (!skb) {
ret = 1;
goto skb_alloc_failed;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
nlh = NLMSG_PUT(skb, 0, 0, 0, msg_len + 1);
nlh->nlmsg_flags = 0;
#else
nlh = nlmsg_put(skb, 0, 0, 0, msg_len + 1, 0);
#endif
memcpy(NLMSG_DATA(nlh), data, msg_len);
*((char *)(NLMSG_DATA(nlh) + msg_len)) = '\0';
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
/* from Kernel */
NETLINK_CB(skb).pid = 0;
#else
/* from Kernel */
NETLINK_CB(skb).portid = 0;
#endif
/* unicast */
NETLINK_CB(skb).dst_group = 0;
ret = netlink_unicast(remote_fwlog_sock, skb,
nvt_priv->remote_debug_pid, MSG_DONTWAIT);
if (ret < 0) {
nvt_dbg(ERROR, "%s:send failed\n", __func__);
} else {
nvt_dbg(REMOTEDBG, "%s:Log has sent, pid:%d\n",
__func__, nvt_priv->remote_debug_pid);
}
} else {
nvt_dbg(REMOTEDBG, "%s:remote_debug_start:%d rather than 1\n",
__func__, nvt_priv->remote_debug_start);
}
return 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
/* used by NLMSG_PUT macro */
nlmsg_failure:
kfree_skb(skb);
#endif
skb_alloc_failed:
return ret;
}
static s32 nvt_fifo_credit_init(struct _nvt_if *nvt_if,
const u16 msg_len, u8 *data)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _nvt_priv *nvt_priv = &nvt_adapter->nvt_priv;
struct _tx_info *tx_info = nvt_priv->tx_info;
struct _nvt_bus *nvt_bus = nvt_adapter->nvt_bus;
u8 fc_enable_flag = *((u8 *)(data + 12));
u8 *credits = (u8 *)(data + 2);
s32 i = 0;
ulong flags;
nvt_dbg(TX, "%s\n", __func__);
spin_lock_irqsave(&tx_info->lock, flags);
if (fc_enable_flag & (1<<7)) {
tx_info->is_credit_borrow = 1;
} else {
tx_info->is_credit_borrow = 0;
}
spin_unlock_irqrestore(&tx_info->lock, flags);
/*initialize fc_borrow_credit info*/
if (fc_enable_flag & (1<<7)) {
if (nvt_icfg_lock(nvt_bus) < 0) {
goto next_check;
}
if (nvt_icfg_reset(nvt_bus) < 0) {
goto next_check;
}
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_FC_BORROW,
(u8 *)&(tx_info->is_credit_borrow),
sizeof(tx_info->is_credit_borrow)) < 0) {
goto next_check;
}
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
goto next_check;
}
next_check:
nvt_icfg_unlock(nvt_bus);
}
spin_lock_irqsave(&tx_info->lock, flags);
for (i = 0; i < NV_TX_FIFO_COUNT; i++) {
if (*credits) {
tx_info->fifo_credit_map |= 1 << i;
} else {
tx_info->fifo_credit_map &= ~(1 << i);
}
tx_info->fifo_max_credit[i] = *credits++;
}
for (i = 0; i < NV_TX_FIFO_COUNT; i++) {
if (*credits >= tx_info->fifo_max_credit[i]) {
tx_info->fifo_credit_map &= ~(1 << i);
tx_info->fifo_credit[i] = 0;
} else {
tx_info->fifo_credit[i] =
tx_info->fifo_max_credit[i] - *credits;
}
credits++;
}
tx_info->fcmode = *credits++;
tx_info->is_netif_stop = 0;
/* tx_info->fcmode == 0 means no flow control */
if (tx_info->fcmode == 0) {
for (i = 0; i < NV_TX_FIFO_COUNT; i++)
tx_info->fifo_credit_map &= ~(1 << i);
} else {
/* Do nothing */
}
for (i = 0; i < NV_TX_FIFO_COUNT; i++) {
tx_info->fifo_credit_update[i] = *credits++;
}
for (i = 0; i < NV_TX_FIFO_COUNT - 1; i++) {
tx_info->fifo_public_inuse[i] = *credits++;
}
for (i = 0; i < NV_TX_FIFO_COUNT - 1; i++) {
tx_info->fifo_public_update[i] = *credits++;
}
nvt_dbg(TX, "fifo_creditmap=%x, fcmode=%d\n",
tx_info->fifo_credit_map, tx_info->fcmode);
for (i = 0; i < NV_TX_FIFO_COUNT; i++) {
nvt_dbg(TX, "fifo[%d]=max:%d, credit:%d, update:%d\n",
i, tx_info->fifo_max_credit[i],
tx_info->fifo_credit[i],
tx_info->fifo_credit_update[i]);
}
for (i = 0; i < NV_TX_FIFO_COUNT - 1; i++) {
nvt_dbg(TX, "fifo[%d]=public_inuse:%d, public_update:%d\n",
i, tx_info->fifo_public_inuse[i],
tx_info->fifo_public_update[i]);
}
nvt_shedule_tx_dequeue(tx_info);
spin_unlock_irqrestore(&tx_info->lock, flags);
return 0;
}
s32 nvt_reset_fcmode(struct _nvt_if *nvt_if, u8 given_type)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _nvt_priv *nvt_priv = &nvt_adapter->nvt_priv;
struct _tx_info *tx_info = nvt_priv->tx_info;
if (given_type != nvt_if->mode) {
if (tx_info->fcmode) {
tx_info->fcmode = 0;
}
}
return 0;
}
static s32 nvt_fifocredit_indicate(struct _nvt_if *nvt_if,
const u16 msg_len, u8 *data)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _nvt_priv *nvt_priv = &nvt_adapter->nvt_priv;
struct _tx_info *tx_info = nvt_priv->tx_info;
u8 *credits = (u8 *)(data + 2);
u8 i = 0;
u8 inc = 0;
u32 ok = 0;
u32 fail = 0;
ulong flags;
u32 total_cnt = 0;
nvt_dbg(TX, "%s\n", __func__);
spin_lock_irqsave(&tx_info->lock, flags);
for (i = 0; i < NV_TX_FIFO_COUNT; i++) {
if (credits[i] >= tx_info->fifo_credit_update[i]) {
inc = credits[i] - tx_info->fifo_credit_update[i];
} else {
inc = 255 - (tx_info->fifo_credit_update[i] -
credits[i]) + 1;
}
tx_info->fifo_credit_update[i] = credits[i];
tx_info->fifo_credit[i] += inc;
if (tx_info->fifo_credit[i] > tx_info->fifo_max_credit[i]) {
nvt_dbg(TX, "fifo[%d],inc=%d,over!\n", i, inc);
tx_info->fifo_credit[i] = tx_info->fifo_max_credit[i];
}
if (tx_info->fifo_credit[i] > 0) {
tx_info->fifo_credit_map |= 1 << i;
} else {
tx_info->fifo_credit_map &= ~(1 << i);
}
nvt_dbg(TX, "update fifo_credit[%d] = %d\n",
i, tx_info->fifo_credit[i]);
}
ok = (credits[8] << 24) | (credits[7] << 16) |
(credits[6] << 8) | credits[5];
fail = (credits[12] << 24) | (credits[11] << 16) |
(credits[10] << 8) | credits[9];
nvt_dbg(TX, "tx ok=%d, fail=%d\n", ok, fail);
for (i = 0; i < NV_TX_FIFO_COUNT - 1; i++) {
if (credits[21 + i] >= tx_info->fifo_public_update[i]) {
inc = credits[21 + i] - tx_info->fifo_public_update[i];
} else {
inc = 255 - (tx_info->fifo_public_update[i] -
credits[21 + i]) + 1;
}
tx_info->fifo_public_update[i] = credits[21 + i];
tx_info->fifo_public_inuse[i] -= inc;
if (tx_info->fifo_public_inuse[i] >
tx_info->fifo_max_credit[NV_TX_FIFO_PUBLIC]) {
nvt_dbg(TX, "fifo[%d],inc=%d,in_use=%d,over!\n", i, inc,
tx_info->fifo_public_inuse[i]);
tx_info->fifo_public_inuse[i] = 0;
}
nvt_dbg(TX, "update fifo_public_inuse[%d] = %d\n",
i, tx_info->fifo_public_inuse[i]);
total_cnt += tx_info->fifo_public_inuse[i];
}
if (total_cnt + tx_info->fifo_credit[NV_TX_FIFO_PUBLIC] !=
tx_info->fifo_max_credit[NV_TX_FIFO_PUBLIC]) {
nvt_dbg(TX, "Public credit mismatch!!!\n");
}
nvt_shedule_tx_dequeue(tx_info);
spin_unlock_irqrestore(&tx_info->lock, flags);
return 0;
}
static void update_piggyback_fifocredit(struct _nvt_if *nvt_if,
struct _nvt_hwinfo_rx *info)
{
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
struct _nvt_priv *nvt_priv = &nvt_adapter->nvt_priv;
struct _tx_info *tx_info = nvt_priv->tx_info;
u8 data[30] = {0};
u16 msg_len = 30;
s32 err = 0;
static u8 old_pri;
static u8 new_pri;
static u8 old_public;
static u8 new_public;
/* tx_info->fcmode == 0 means no flow control */
if (tx_info->fcmode == 0) {
return;
}
nvt_dbg(TX, "%s\n", __func__);
data[2] = (ntohl(info->word2) & 0xFF000000) >> 24;
data[3] = (ntohl(info->word2) & 0x00FF0000) >> 16;
data[4] = (ntohl(info->word2) & 0x0000FF00) >> 8;
data[5] = (ntohl(info->word2) & 0x000000FF);
data[23] = info->word4.vo_piggy;
data[24] = info->word5.vi_piggy;
data[25] = info->word5.be_piggy;
data[26] = info->word5.bk_piggy;
data[6] = data[23] + data[24] + data[25] + data[26];
new_pri = data[2] + data[3] + data[4] + data[5];
new_public = data[6];
nvt_dbg(TX, "old_pri=%d,new_pri=%d,old_public=%d,new_public=%d\n",
old_pri, new_pri, old_public, new_public);
if ((new_pri == old_pri) && (new_public == old_public)) {
return;
}
old_pri = new_pri;
old_public = new_public;
nvt_dbg(TX, "VO=%d,VI=%d,BE=%d,BK=%d,pub=%d\n",
data[2], data[3], data[4], data[5], data[6]);
nvt_dbg(TX, "PUB: VO=%d,VI=%d,BE=%d,BK=%d\n",
data[23], data[24], data[25], data[26]);
err = nvt_fifocredit_indicate(nvt_if, msg_len, data);
}
/**
* nvt_is_priv_pkt - this function checks whether the received frame is
* event/iconfig or not
* @nvt_bus: bus structrue
* @skb: rx skb buffer
*
* Return: true - the received frame is event/iconfig.
* false - the received frame is not event/iconfig.
*/
s32 nvt_is_priv_pkt(struct _nvt_bus *nvt_bus, struct sk_buff *skb)
{
struct _nvt_hwinfo_rx *info;
nvt_dbg(REORDER, "%s\n", __func__);
info = (struct _nvt_hwinfo_rx *)skb->data;
if ((ntohl(info->word0) & 0x80000000) == 0x80000000) {
return 1;
} else {
return 0;
}
}
/**
* nvt_process_priv_pkt - this function process event/iconfig to do
* specific action.
* @nvt_bus: bus structrue
* @skb: rx skb buffer
*
* we add the event/iconfig handler here.
*
* Return: zero - success.
* negative - failure.
*/
s32 nvt_process_priv_pkt(struct _nvt_bus *nvt_bus, struct sk_buff *skb)
{
/* Event Message Format */
/* -------------------------------------------------------- */
/* | Message Type | ID | Length | WID Type | WID Data | */
/* -------------------------------------------------------- */
/* | 1 | 1 | 2 | 2 | event_len | */
/* -------------------------------------------------------- */
struct _nvt_adapter *nvt_adapter = nvt_bus->nvt_adapter;
struct _nvt_priv *nvt_priv;
struct _nvt_hwinfo_rx *info;
struct _nvt_if *nvt_if;
u8 *msg_type;
u8 *data;
u16 *wid;
u16 msg_len;
u8 ifidx = 0;
s32 err = 0;
info = (struct _nvt_hwinfo_rx *)skb->data;
msg_type = (u8 *)(skb->data + info->word1.offset + WID_EVENT_OFFSET);
data = (u8 *)(skb->data + info->word1.offset +
WID_EVENT_OFFSET + WID_VALUE_OFFSET);
wid = (u16 *)(skb->data + info->word1.offset +
WID_EVENT_OFFSET + WID_VALUE_OFFSET);
msg_len = *((u16 *)(skb->data + info->word1.offset +
WID_EVENT_OFFSET + WID_LENGTH_OFFSET)) - 4;
nvt_dbg(INFO, "%s\n", __func__);
nvt_dbg(INFO, "msg_type = %c, wid = 0x%04x\n", *msg_type, *wid);
//20150806 nash: handling firmware ready event
if (*wid == WID_EVENT_FW_READY) {
nvt_bus->fw_rdy_completed = 1;
if (waitqueue_active(&nvt_bus->fw_rdy_wait)) {
wake_up(&nvt_bus->fw_rdy_wait);
} else {
nvt_dbg(INFO, "%s: no active wait event\n", __func__);
}
return 0;
}
if (nvt_bus->nvt_adapter == NULL) {
nvt_dbg(INFO, "%s: nvt_adapter is NULL\n", __func__);
return -1;
}
nvt_priv = &nvt_adapter->nvt_priv;
/* Get nvt_if pointer */
nvt_if = nvt_get_if_by_index(nvt_adapter, ifidx);
if (!nvt_if) {
dev_kfree_skb_any(skb);
nvt_dbg(INFO, "ifidx = %d, nvt_if not match\n", ifidx);
return -1;
}
/* pull out Rx Host Header */
skb_pull(skb, info->word1.offset);
/* Process FW Message_Type == 'E' */
if (*wid == WID_EVENT_FC_CREDIT_INIT) {
nvt_dbg(INFO,
"FC: vo=%d, vi=%d, be=%d, bk=%d, pub=%d\n",
*(data + 2), *(data + 3), *(data + 4),
*(data + 5), *(data + 6));
err = nvt_fifo_credit_init(nvt_if, msg_len, data);
dev_kfree_skb_any(skb);
} else if (*wid == WID_EVENT_FC_INFO) {
nvt_dbg(TX,
"FC: seq=%d,vo=%d,vi=%d,be=%d,bk=%d,pub=%d\n",
*(msg_type + 1), *(data + 2), *(data + 3),
*(data + 4), *(data + 5), *(data + 6));
err = nvt_fifocredit_indicate(nvt_if, msg_len, data);
dev_kfree_skb_any(skb);
} else if (*wid == WID_EVENT_ADDBA_REQ) {
nvt_dbg(INFO, "ADDBA REQ EVENT\n");
err = nvt_handle_addba_req_evt(nvt_if, msg_len, data);
dev_kfree_skb_any(skb);
} else if (*wid == WID_EVENT_DELBA_REQ) {
nvt_dbg(INFO, "DELBA REQ EVENT\n");
err = nvt_handle_delba_evt(nvt_if, msg_len, data);
dev_kfree_skb_any(skb);
} else if (*wid == WID_EVENT_BAR) {
nvt_dbg(INFO, "BAR REQ EVENT\n");
err = nvt_handle_bar_evt(nvt_if, msg_len, data);
dev_kfree_skb_any(skb);
} else if (*wid == WID_EVENT_DHCP_RENEW_FIRST_TIME) {
if (*(data + 2) == 0) {
nvt_dbg(INFO, "Send the first DHCP REQ Success\n");
} else {
nvt_dbg(INFO, "Send the first DHCP REQ Fail\n");
}
dev_kfree_skb_any(skb);
} else if (*wid == WID_EVENT_TP_MONITOR) {
nvt_dbg(INFO, "Got TP_MONITOR EVENT\n");
#ifdef CONFIG_WIFI_TUNING_PHASE_II
err = nvt_notify_tp_monitor(nvt_if, msg_len, data);
#endif
dev_kfree_skb_any(skb);
} else if (*wid == WID_EVENT_DEBUG_MESSAGE_INFO) {
err = nvt_remote_fwlog(nvt_if, (msg_len - 2), (u8 *)(data + 2));
dev_kfree_skb_any(skb);
} else if (*wid == WID_EVENT_SETKEY_INFO) {
nvt_dbg(INFO, "Got WID_EVENT_SETKEY_INFO EVENT\n");
err = nvt_setkey_info(nvt_if, msg_len, data);
dev_kfree_skb_any(skb);
} else if (*wid == 0x0500) {
nvt_dbg(INFO, "Got ADDKEY_MSG4_INFO_ind\n");
atomic_dec(&nvt_if->eapol_cnt);
nvt_dbg(INFO, "pend_8021x_cnt = %d\n",
atomic_read(&nvt_if->eapol_cnt));
if (waitqueue_active(&nvt_if->addkey_wait)) {
wake_up(&nvt_if->addkey_wait);
nvt_dbg(INFO, "wake_up\n");
}
dev_kfree_skb_any(skb);
} else {
nvt_dbg(INFO, "put WID('E') in workqueue\n");
skb_queue_tail(&nvt_priv->fw_evt_list, skb);
schedule_work(&nvt_priv->fw_evt_work);
}
return err;
}
static s32 nvt_evt_handle(struct _nvt_adapter *nvt_adapter,
struct sk_buff *skb)
{
/* Event Message Format */
/* -------------------------------------------------------- */
/* | Message Type | ID | Length | WID Type | WID Data | */
/* -------------------------------------------------------- */
/* | 1 | 1 | 2 | 2 | event_len | */
/* -------------------------------------------------------- */
struct _nvt_if *nvt_if;
u8 *msg_type = (u8 *)(skb->data + WID_EVENT_OFFSET);
u8 *data = (u8 *)(skb->data + WID_EVENT_OFFSET + WID_VALUE_OFFSET);
u16 *wid = (u16 *)(skb->data + WID_EVENT_OFFSET + WID_VALUE_OFFSET);
u16 msg_len = *((u16 *)(skb->data +
WID_EVENT_OFFSET + WID_LENGTH_OFFSET)) - 4;
s32 err = 0;
u8 ifidx = 0;
u8 sta_type = 0;
nvt_dbg(INFO, "%s\n", __func__);
nvt_dbg(INFO, "msg_type = %c, wid = 0x%04x\n", *msg_type, *wid);
/* Get nvt_if pointer */
nvt_if = nvt_get_if_by_index(nvt_adapter, ifidx);
if (!nvt_if) {
nvt_dbg(ERROR, "ifidx = %d, nvt_if not match\n", ifidx);
return -1;
}
switch (*msg_type) {
case EVENT:
nvt_dbg(INFO, "WID(EVENT) is handle here\n");
if (*wid == WID_EVENT_BSS_INFO) {
nvt_dbg(INFO, "Scan Result\n");
err = nvt_scan_result(nvt_if, msg_len, data);
} else if (*wid == WID_EVENT_SCAN_DONE) {
nvt_dbg(INFO, "Scan Done\n");
err = nvt_scan_result(nvt_if, msg_len, data);
} else if ((*wid == WID_EVENT_ASSOC_REQ) &&
(nvt_if->mode == NVT_FW_AP)) {
nvt_dbg(INFO, "Assoc_req Info\n");
err = nvt_connect_status_ap(nvt_if, msg_len, data);
} else if (*wid == WID_EVENT_CONNECT_INFO) {
nvt_dbg(INFO, "Connect Info\n");
err = nvt_connect_result(nvt_if, msg_len, data);
} else if (*wid == WID_EVENT_RX_PROBEREQ_FRAME) {
nvt_dbg(INFO, "Receive RX_PROBEREQ_FRAME\n");
err = nvt_rx_mgmt_probereq(nvt_if, msg_len, data);
} else if (*wid == WID_EVENT_RX_ACTION_FRAME) {
nvt_dbg(INFO, "Receive RX_ACTION_FRAME\n");
err = nvt_rx_action_frame(nvt_if, msg_len, data);
} else if (*wid == WID_EVENT_LISTEN_DONE) {
nvt_dbg(INFO, "Listen Done\n");
err = nvt_p2p_roc_done(nvt_if, msg_len, data, false);
} else if ((*wid == WID_STA_JOIN_INFO) &&
(nvt_if->mode == NVT_FW_AP)) {
nvt_dbg(INFO, "Notify STA Disconnected\n");
sta_type = *(data + 9);
/* check if station leaving */
if (sta_type == 0) {
err = nvt_connect_status_ap(nvt_if,
msg_len, data);
}
} else if (*wid == WID_STATUS) {
nvt_dbg(INFO, "WID_STATUS\n");
if (nvt_if->mode == NVT_AP_MODE) {
err = nvt_enable_status_ap(nvt_if,
msg_len, data);
} else {
err = nvt_connect_status_station(nvt_if,
msg_len, data);
}
} else if (*wid == WID_MIC_FAILURE_INFO) {
nvt_dbg(INFO, "Got MIC_fail_ind\n");
err = nvt_micfail_status(nvt_if, msg_len, data);
} else if (*wid == WID_EVENT_RESUME) {
nvt_dbg(INFO, "Got Resume event, code=%d\n", data[2]);
err = nvt_resume_result(nvt_if, msg_len, data);
} else if (*wid == WID_EVENT_REKEY_OFFLOAD) {
nvt_dbg(INFO, "Got Rekey_Offload Info\n");
err = nvt_rekeyoffload_info(nvt_if, msg_len, data);
} else {
/* Do Nothing */
}
break;
default:
nvt_dbg(ERROR, "WID Coming here is weird!!!!!!\n");
/* Do nothing */
break;
}
return err;
}
static void nvt_fw_evt_worker(struct work_struct *work)
{
struct _nvt_priv *nvt_priv;
struct _nvt_adapter *nvt_adapter;
struct sk_buff *skb = NULL;
s32 err = 0;
nvt_priv = container_of(work, struct _nvt_priv, fw_evt_work);
nvt_adapter = container_of(nvt_priv, struct _nvt_adapter, nvt_priv);
nvt_dbg(INFO, "%s\n", __func__);
/* Dequeue Event is here */
while ((skb = skb_dequeue(&nvt_priv->fw_evt_list))) {
err = nvt_evt_handle(nvt_adapter, skb);
if (err) {
nvt_dbg(ERROR, "Event handled is fail!!\n");
err = 0;
}
/* Free SKB Buffer */
dev_kfree_skb_any(skb);
}
}
/**
* nvt_priv_init - init nvt priv struct parameters
*
* @nvt_adapter: struct _nvt_adapter
*
* Return: return 0 success
*/
s32 nvt_priv_init(struct _nvt_adapter *nvt_adapter)
{
struct _nvt_priv *nvt_priv = &nvt_adapter->nvt_priv;
struct _ba_struct_t *ba_ctxt = NULL;
struct _tx_info *tx_info;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct netlink_kernel_cfg netlink_cfg;
#endif
s32 i = 0;
s32 j = 0;
u8 idx;
s32 err = 0;
nvt_priv->pn_bcmc = 0;
/* RX_BA Initialization */
for (i = 0; i < ASSOC_NUM; i++) {
for (j = 0; j < NUM_TIDS; j++) {
idx = i * NUM_TIDS + j;
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
ba_ctxt->sta_index = i + 1;
ba_ctxt->tid = j;
spin_lock_init(&(ba_ctxt->ba_lock));
INIT_WORK(&(ba_ctxt->ba_timeout_work),
nvt_ba_timeout_worker);
nvt_priv->last_seq_ctl[idx] = 0xFFFF;
}
nvt_priv->last_seq_ctl[ASSOC_NUM * NUM_TIDS + i] = 0xFFFF;
}
/* Event/Iconfig Inititialization */
INIT_WORK(&nvt_priv->fw_evt_work, nvt_fw_evt_worker);
skb_queue_head_init(&nvt_priv->fw_evt_list);
/* Tx Inititialization */
tx_info = kzalloc(sizeof(struct _tx_info), GFP_KERNEL);
if (!tx_info) {
err = -ENOMEM;
goto fail;
}
nvt_priv->tx_info = tx_info;
INIT_WORK(&tx_info->tx_queue_work, nvt_tx_dequeue_worker);
skb_queue_head_init(&tx_info->be_queue_list);
skb_queue_head_init(&tx_info->bk_queue_list);
skb_queue_head_init(&tx_info->vi_queue_list);
skb_queue_head_init(&tx_info->vo_queue_list);
spin_lock_init(&tx_info->lock);
tx_info->nvt_adapter = nvt_adapter;
tx_info->fcmode = 0;
tx_info->is_netif_stop = 0;
/* watermark is used for multi-queue */
#ifndef CONFIG_WIFI_TUNING_PHASE_I
tx_info->fc_hi_watermark = 128;
tx_info->fc_low_watermark = 64;
#else
tx_info->fc_hi_watermark[NV_TX_FIFO_AC_VO] = 128;
tx_info->fc_hi_watermark[NV_TX_FIFO_AC_VI] = 64;
tx_info->fc_hi_watermark[NV_TX_FIFO_AC_BE] = 64;
tx_info->fc_hi_watermark[NV_TX_FIFO_AC_BK] = 64;
//tx_info->fc_low_watermark[NV_TX_FIFO_AC_VO] = 64; // no use
//tx_info->fc_low_watermark[NV_TX_FIFO_AC_VI] = 0; // no use
//tx_info->fc_low_watermark[NV_TX_FIFO_AC_BE] = 0; // no use
//tx_info->fc_low_watermark[NV_TX_FIFO_AC_BK] = 0; // no use
#endif
/* max_qcnt is used for single queue */
tx_info->max_qcnt = 256;
/* max_fqcnt is used for forward packet */
tx_info->max_fqcnt = 1000;
tx_info->is_credit_borrow = 0;
for (i = NV_TX_FIFO_AC_VO; i < NV_TX_FIFO_COUNT; i++) {
tx_info->fifo_credit_update[i] = 0;
}
for (i = NV_TX_FIFO_AC_VO; i < NV_TX_FIFO_COUNT - 1; i++) {
tx_info->fifo_public_update[i] = 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
netlink_cfg.input = remote_fw_input;
netlink_cfg.groups = 0;
nvt_priv->remote_fwlog_sock = netlink_kernel_create(&init_net,
NETLINK_USERSOCK, &netlink_cfg);
#else
/* initial netlink sock for remote fw log */
nvt_priv->remote_fwlog_sock = netlink_kernel_create(&init_net,
NETLINK_USERSOCK, 0, remote_fw_input, NULL, THIS_MODULE);
#endif
if (!nvt_priv->remote_fwlog_sock) {
nvt_dbg(ERROR, "%s: create netlink sock failed\n", __func__);
err = -ENOMEM;
goto fail;
}
//remote_debug_start = 0;
return 0;
fail:
return err;
}
/**
* nvt_priv_deinit - deinit nvt priv struct parameters
*
* @nvt_adapter: struct _nvt_adapter
*
* Return: return 0 success
*/
s32 nvt_priv_deinit(struct _nvt_adapter *nvt_adapter)
{
struct _nvt_priv *nvt_priv = &nvt_adapter->nvt_priv;
struct _tx_info *tx_info = nvt_priv->tx_info;
struct sk_buff *skb;
/* Event/Iconfig De-inititialization */
cancel_work_sync(&nvt_priv->fw_evt_work);
while ((skb = skb_dequeue(&nvt_priv->fw_evt_list))) {
dev_kfree_skb_any(skb);
}
/* Tx De-inititialization */
if (tx_info) {
nvt_priv->tx_info = NULL;
kfree(tx_info);
}
if (nvt_priv->remote_fwlog_sock) {
netlink_kernel_release(nvt_priv->remote_fwlog_sock);
//sock_release(nvt_priv->remote_fwlog_sock->sk_socket);
}
return 0;
}
void nvt_flush_txq(struct _nvt_adapter *nvt_adapter)
{
struct _nvt_priv *nvt_priv = &nvt_adapter->nvt_priv;
struct _tx_info *tx_info = nvt_priv->tx_info;
struct sk_buff *skb = NULL;
if (tx_info != NULL) {
while ((skb = skb_dequeue(&tx_info->bk_queue_list))) {
dev_kfree_skb_any(skb);
}
while ((skb = skb_dequeue(&tx_info->be_queue_list))) {
dev_kfree_skb_any(skb);
}
while ((skb = skb_dequeue(&tx_info->vi_queue_list))) {
dev_kfree_skb_any(skb);
}
while ((skb = skb_dequeue(&tx_info->vo_queue_list))) {
dev_kfree_skb_any(skb);
}
}
}