#include #include #include #include #include #include #include #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); } } }