2699 lines
105 KiB
C
Executable File
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);
|
|
}
|
|
}
|
|
}
|