/* * Copyright (C) 2016 Spreadtrum Communications Inc. * * Authors : * star.liu * yifei.li * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "reorder.h" #include "rx_msg.h" #include "work.h" #include "cmdevt.h" #include "wl_intf.h" #include "qos.h" #include "debug.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) static void ba_reorder_timeout(struct timer_list *t); #else static void ba_reorder_timeout(unsigned long data); #endif static inline unsigned int get_index_size(unsigned int size) { unsigned int index_size = MIN_INDEX_SIZE; while (size > index_size) index_size = (index_size << 1); wl_info("%s: rx ba index size: %d\n", __func__, index_size); return index_size; } static inline void set_ba_node_desc(struct rx_ba_node_desc *ba_node_desc, unsigned short win_start, unsigned short win_size, unsigned int index_mask) { ba_node_desc->win_size = win_size; ba_node_desc->win_start = win_start; ba_node_desc->win_start_init = win_start; ba_node_desc->win_limit = SEQNO_ADD(ba_node_desc->win_start, (ba_node_desc->win_size - 1)); ba_node_desc->win_tail = SEQNO_SUB(ba_node_desc->win_start, 1); ba_node_desc->index_mask = index_mask; ba_node_desc->buff_cnt = 0; wl_info("%s:(win_start:%d, win_size:%d, win_tail:%d, index_mask:%d)\n", __func__, ba_node_desc->win_start, ba_node_desc->win_size, ba_node_desc->win_tail, ba_node_desc->index_mask); } static inline void set_ba_pkt_desc(struct rx_ba_pkt_desc *ba_pkt_desc, struct rx_msdu_desc *msdu_desc) { ba_pkt_desc->seq = msdu_desc->seq_num; ba_pkt_desc->pn_l = msdu_desc->pn_l; ba_pkt_desc->pn_h = msdu_desc->pn_h; ba_pkt_desc->cipher_type = msdu_desc->cipher_type; } static inline void reorder_set_skb_list(struct sprdwl_rx_ba_entry *ba_entry, struct sk_buff *skb_head, struct sk_buff *skb_last) { spin_lock_bh(&ba_entry->skb_list_lock); if (!ba_entry->skb_head) { ba_entry->skb_head = skb_head; ba_entry->skb_last = skb_last; } else { ba_entry->skb_last->next = skb_head; ba_entry->skb_last = skb_last; } ba_entry->skb_last->next = NULL; spin_unlock_bh(&ba_entry->skb_list_lock); } struct sk_buff *reorder_get_skb_list(struct sprdwl_rx_ba_entry *ba_entry) { struct sk_buff *skb = NULL; spin_lock_bh(&ba_entry->skb_list_lock); skb = ba_entry->skb_head; ba_entry->skb_head = NULL; ba_entry->skb_last = NULL; spin_unlock_bh(&ba_entry->skb_list_lock); return skb; } static inline void mod_reorder_timer(struct rx_ba_node *ba_node) { if (ba_node->rx_ba->buff_cnt) { mod_timer(&ba_node->reorder_timer, jiffies + RX_BA_LOSS_RECOVERY_TIMEOUT); } else { del_timer(&ba_node->reorder_timer); ba_node->timeout_cnt = 0; } } static inline bool is_same_pn(struct rx_ba_pkt_desc *ba_pkt_desc, struct rx_msdu_desc *msdu_desc) { bool ret = true; unsigned char cipher_type = 0; cipher_type = ba_pkt_desc->cipher_type; if ((cipher_type == SPRDWL_HW_TKIP) || (cipher_type == SPRDWL_HW_CCMP)) { if ((ba_pkt_desc->pn_l != msdu_desc->pn_l) || (ba_pkt_desc->pn_h != msdu_desc->pn_h)) ret = false; } return ret; } static inline bool replay_detection(struct rx_ba_pkt_desc *ba_pkt_desc, struct rx_ba_node_desc *ba_node_desc) { bool ret = true; unsigned int old_val_low = 0; unsigned int old_val_high = 0; unsigned int rx_val_low = 0; unsigned int rx_val_high = 0; unsigned char cipher_type = 0; /* FIXME: Need to check sta entry instead of check cipher_type param */ cipher_type = ba_pkt_desc->cipher_type; /* FIXME: Maybe other cipher type need to do replay detection * HW do not support other cipher type now */ if ((cipher_type == SPRDWL_HW_TKIP) || (cipher_type == SPRDWL_HW_CCMP)) { old_val_low = ba_node_desc->pn_l; old_val_high = ba_node_desc->pn_h; rx_val_low = ba_pkt_desc->pn_l; rx_val_high = ba_pkt_desc->pn_h; if ((1 == ba_node_desc->reset_pn) && (old_val_low >= rx_val_low) && (old_val_high >= rx_val_high)) { wl_err("%s: clear reset_pn,old_val_low: %d, old_val_high: %d, rx_val_low: %d, rx_val_high: %d\n", __func__, old_val_low, old_val_high, rx_val_low, rx_val_high); ba_node_desc->reset_pn = 0; ba_node_desc->pn_l = rx_val_low; ba_node_desc->pn_h = rx_val_high; } else if (((old_val_high == rx_val_high) && (old_val_low < rx_val_low)) || (old_val_high < rx_val_high)) { ba_node_desc->pn_l = rx_val_low; ba_node_desc->pn_h = rx_val_high; } else { ret = false; wl_err("%s: old_val_low: %d, old_val_high: %d\n", __func__, old_val_low, old_val_high); wl_err("%s: rx_val_low: %d, rx_val_high: %d\n", __func__, rx_val_low, rx_val_high); } } return ret; } static inline void send_order_msdu(struct sprdwl_rx_ba_entry *ba_entry, struct rx_msdu_desc *msdu_desc, struct sk_buff *skb, struct rx_ba_node_desc *ba_node_desc) { struct rx_ba_pkt_desc ba_pkt_desc; set_ba_pkt_desc(&ba_pkt_desc, msdu_desc); ba_node_desc->win_start = SEQNO_ADD(ba_node_desc->win_start, 1); ba_node_desc->win_limit = SEQNO_ADD(ba_node_desc->win_limit, 1); ba_node_desc->win_tail = SEQNO_SUB(ba_node_desc->win_start, 1); wl_debug("%s: seq: %d\n", __func__, ba_pkt_desc.seq); wl_debug("%s: win_start: %d, win_tail: %d, buff_cnt: %d\n", __func__, ba_node_desc->win_start, ba_node_desc->win_tail, ba_node_desc->buff_cnt); if (skb) { if (replay_detection(&ba_pkt_desc, ba_node_desc)) reorder_set_skb_list(ba_entry, skb, skb); else dev_kfree_skb(skb); } } static inline void send_reorder_skb(struct sprdwl_rx_ba_entry *ba_entry, struct rx_ba_node_desc *ba_node_desc, struct rx_ba_pkt *pkt) { wl_debug("%s: seq: %d, msdu num: %d\n", __func__, pkt->desc.seq, pkt->desc.msdu_num); if (pkt->skb && pkt->skb_last) { if (replay_detection(&pkt->desc, ba_node_desc)) reorder_set_skb_list(ba_entry, pkt->skb, pkt->skb_last); else kfree_skb_list(pkt->skb); } memset(pkt, 0, sizeof(struct rx_ba_pkt)); } static inline void flush_reorder_buffer(struct rx_ba_node_desc *ba_node_desc) { int i = 0; struct rx_ba_pkt *pkt = NULL; for (i = 0; i < (ba_node_desc->index_mask + 1); i++) { pkt = &ba_node_desc->reorder_buffer[i]; kfree_skb_list(pkt->skb); memset(pkt, 0, sizeof(struct rx_ba_pkt)); } ba_node_desc->buff_cnt = 0; } static inline void joint_msdu(struct rx_ba_pkt *pkt, struct sk_buff *newsk) { if (newsk) { if (pkt->skb_last) { pkt->skb_last->next = newsk; pkt->skb_last = pkt->skb_last->next; } else { pkt->skb = newsk; pkt->skb_last = pkt->skb; } pkt->skb_last->next = NULL; } } static unsigned short send_msdu_in_order(struct sprdwl_rx_ba_entry *ba_entry, struct rx_ba_node_desc *ba_node_desc) { unsigned short seq_num = ba_node_desc->win_start; struct rx_ba_pkt *pkt = NULL; unsigned int index = 0; while (1) { index = seq_num & ba_node_desc->index_mask; pkt = &ba_node_desc->reorder_buffer[index]; if (!ba_node_desc->buff_cnt || !pkt->desc.last) break; send_reorder_skb(ba_entry, ba_node_desc, pkt); ba_node_desc->buff_cnt--; seq_num++; } seq_num &= SEQNO_MASK; return seq_num; } static unsigned short send_msdu_with_gap(struct sprdwl_rx_ba_entry *ba_entry, struct rx_ba_node_desc *ba_node_desc, unsigned short last_seqno) { unsigned short seq_num = ba_node_desc->win_start; struct rx_ba_pkt *pkt = NULL; unsigned short num_frms = 0; unsigned short num = SEQNO_SUB(last_seqno, seq_num); unsigned int index = 0; while (num--) { index = seq_num & ba_node_desc->index_mask; pkt = &ba_node_desc->reorder_buffer[index]; if (ba_node_desc->buff_cnt && pkt->desc.msdu_num) { send_reorder_skb(ba_entry, ba_node_desc, pkt); num_frms++; ba_node_desc->buff_cnt--; } seq_num++; } return num_frms; } static inline void between_seqlo_seqhi(struct sprdwl_rx_ba_entry *ba_entry, struct rx_ba_node_desc *ba_node_desc) { ba_node_desc->win_start = send_msdu_in_order(ba_entry, ba_node_desc); ba_node_desc->win_limit = SEQNO_ADD(ba_node_desc->win_start, (ba_node_desc->win_size - 1)); } static inline void greater_than_seqhi(struct sprdwl_rx_ba_entry *ba_entry, struct rx_ba_node_desc *ba_node_desc, unsigned short seq_num) { unsigned short pos_win_end; unsigned short pos_win_start; pos_win_end = seq_num; pos_win_start = SEQNO_SUB(pos_win_end, (ba_node_desc->win_size - 1)); send_msdu_with_gap(ba_entry, ba_node_desc, pos_win_start); ba_node_desc->win_start = pos_win_start; ba_node_desc->win_start = send_msdu_in_order(ba_entry, ba_node_desc); ba_node_desc->win_limit = SEQNO_ADD(ba_node_desc->win_start, (ba_node_desc->win_size - 1)); } static inline void bar_send_ba_buffer(struct sprdwl_rx_ba_entry *ba_entry, struct rx_ba_node_desc *ba_node_desc, unsigned short seq_num) { if (!seqno_leq(seq_num, ba_node_desc->win_start)) { send_msdu_with_gap(ba_entry, ba_node_desc, seq_num); ba_node_desc->win_start = seq_num; ba_node_desc->win_start = send_msdu_in_order(ba_entry, ba_node_desc); ba_node_desc->win_limit = SEQNO_ADD(ba_node_desc->win_start, (ba_node_desc->win_size - 1)); } } static inline int insert_msdu(struct rx_msdu_desc *msdu_desc, struct sk_buff *skb, struct rx_ba_node_desc *ba_node_desc) { int ret = 0; unsigned short seq_num = msdu_desc->seq_num; unsigned short index = seq_num & ba_node_desc->index_mask; struct rx_ba_pkt *insert = &ba_node_desc->reorder_buffer[index]; bool last_msdu_flag = msdu_desc->last_msdu_of_mpdu; wl_debug("%s: index: %d, seq: %d\n", __func__, index, insert->desc.seq); if (insert->desc.msdu_num != 0) { if ((insert->desc.seq == seq_num) && (insert->desc.last != 1) && is_same_pn(&insert->desc, msdu_desc)) { joint_msdu(insert, skb); insert->desc.msdu_num++; insert->desc.last = last_msdu_flag; } else { wl_err("%s: in_use: %d\n", __func__, insert->desc.seq); ret = -EINVAL; } } else { joint_msdu(insert, skb); set_ba_pkt_desc(&insert->desc, msdu_desc); insert->desc.last = last_msdu_flag; insert->desc.msdu_num = 1; ba_node_desc->buff_cnt++; } return ret; } static int reorder_msdu(struct sprdwl_rx_ba_entry *ba_entry, struct rx_msdu_desc *msdu_desc, struct sk_buff *skb, struct rx_ba_node *ba_node) { int ret = -EINVAL; unsigned short seq_num = msdu_desc->seq_num; struct rx_ba_node_desc *ba_node_desc = ba_node->rx_ba; if (seqno_geq(seq_num, ba_node_desc->win_start)) { ba_node_desc->win_start_init = 0; if (!seqno_leq(seq_num, ba_node_desc->win_limit)) { /* Buffer is full, send data now */ greater_than_seqhi(ba_entry, ba_node_desc, seq_num); } ret = insert_msdu(msdu_desc, skb, ba_node_desc); if (!ret && seqno_geq(seq_num, ba_node_desc->win_tail)) ba_node_desc->win_tail = seq_num; } else if (!seqno_geq(seq_num, ba_node_desc->win_start_init)) { wl_info("%s: seq_num: %d is less than win_start_init: %d\n", __func__, seq_num, ba_node_desc->win_start_init); reorder_set_skb_list(ba_entry, skb, skb); ret = 0; } else { wl_debug("%s: seq_num: %d is less than win_start: %d\n", __func__, seq_num, ba_node_desc->win_start); } if (ret && skb) { wl_debug("%s: kfree skb %d", __func__, ret); dev_kfree_skb(skb); } return ret; } static void reorder_msdu_process(struct sprdwl_rx_ba_entry *ba_entry, struct rx_msdu_desc *msdu_desc, struct sk_buff *skb, struct rx_ba_node *ba_node) { struct rx_ba_node_desc *ba_node_desc = ba_node->rx_ba; int ret = 0; int seq_num = msdu_desc->seq_num; bool last_msdu_flag = msdu_desc->last_msdu_of_mpdu; unsigned short old_win_start = 0; spin_lock_bh(&ba_node->ba_node_lock); if (likely(ba_node->active)) { wl_debug("%s: seq: %d, last_msdu_of_mpdu: %d\n", __func__, seq_num, last_msdu_flag); wl_debug("%s: win_start: %d, win_tail: %d, buff_cnt: %d\n", __func__, ba_node_desc->win_start, ba_node_desc->win_tail, ba_node_desc->buff_cnt); /* FIXME: Data come in sequence in default */ if ((seq_num == ba_node_desc->win_start) && !ba_node_desc->buff_cnt && last_msdu_flag) { ba_node_desc->win_start_init = 0; send_order_msdu(ba_entry, msdu_desc, skb, ba_node_desc); goto out; } old_win_start = ba_node_desc->win_start; ret = reorder_msdu(ba_entry, msdu_desc, skb, ba_node); if (!ret) { if (last_msdu_flag && (seq_num == ba_node_desc->win_start)) { between_seqlo_seqhi(ba_entry, ba_node_desc); mod_reorder_timer(ba_node); } else if (!timer_pending(&ba_node->reorder_timer) || (old_win_start != ba_node_desc->win_start)) { wl_debug("%s: start timer\n", __func__); mod_reorder_timer(ba_node); } } else if (unlikely(!ba_node_desc->buff_cnt)) { /* Should never happen */ del_timer(&ba_node->reorder_timer); ba_node->timeout_cnt = 0; } } else { wl_err("%s: BA SESSION IS NO ACTIVE sta_lut_index: %d, tid: %d\n", __func__, msdu_desc->sta_lut_index, msdu_desc->tid); reorder_set_skb_list(ba_entry, skb, skb); } out: spin_unlock_bh(&ba_node->ba_node_lock); } static inline void init_ba_node(struct sprdwl_rx_ba_entry *ba_entry, struct rx_ba_node *ba_node, unsigned char sta_lut_index, unsigned char tid) { /* Init reorder spinlock */ spin_lock_init(&ba_node->ba_node_lock); ba_node->active = 0; ba_node->sta_lut_index = sta_lut_index; ba_node->tid = tid; ba_node->ba_entry = ba_entry; /* Init reorder timer */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) timer_setup(&ba_node->reorder_timer, ba_reorder_timeout, 0); #else init_timer(&ba_node->reorder_timer); ba_node->reorder_timer.data = (unsigned long)ba_node; ba_node->reorder_timer.function = ba_reorder_timeout; #endif } static inline bool is_same_ba(struct rx_ba_node *ba_node, unsigned char sta_lut_index, unsigned char tid) { bool ret = false; if (ba_node) { if ((ba_node->sta_lut_index == sta_lut_index) && (ba_node->tid == tid)) ret = true; } return ret; } static struct rx_ba_node *find_ba_node(struct sprdwl_rx_ba_entry *ba_entry, unsigned char sta_lut_index, unsigned char tid) { struct rx_ba_node *ba_node = NULL; if (tid < NUM_TIDS) { if (is_same_ba(ba_entry->current_ba_node, sta_lut_index, tid)) { ba_node = ba_entry->current_ba_node; } else { struct hlist_head *head = &ba_entry->hlist[tid]; if (!hlist_empty(head)) { hlist_for_each_entry(ba_node, head, hlist) { if (sta_lut_index == ba_node->sta_lut_index) { ba_entry->current_ba_node = ba_node; break; } } } } } else { wl_err("%s: TID is too large sta_lut_index: %d, tid: %d\n", __func__, sta_lut_index, tid); } return ba_node; } static struct rx_ba_node *create_ba_node(struct sprdwl_rx_ba_entry *ba_entry, unsigned char sta_lut_index, unsigned char tid, unsigned int size) { struct rx_ba_node *ba_node = NULL; struct hlist_head *head = &ba_entry->hlist[tid]; unsigned int rx_ba_size = sizeof(struct rx_ba_node_desc) + (size * sizeof(struct rx_ba_pkt)); ba_node = kzalloc(sizeof(*ba_node), GFP_ATOMIC); if (ba_node) { ba_node->rx_ba = kzalloc(rx_ba_size, GFP_ATOMIC); if (ba_node->rx_ba) { init_ba_node(ba_entry, ba_node, sta_lut_index, tid); INIT_HLIST_NODE(&ba_node->hlist); hlist_add_head(&ba_node->hlist, head); ba_entry->current_ba_node = ba_node; } else { kfree(ba_node); ba_node = NULL; } } return ba_node; } void reset_pn(struct sprdwl_priv *priv, const u8 *mac_addr) { struct sprdwl_intf *intf = NULL; struct sprdwl_rx_if *rx_if = NULL; struct sprdwl_rx_ba_entry *ba_entry = NULL; unsigned char i, tid, lut_id = 0xff; struct rx_ba_node *ba_node = NULL; if (!mac_addr) return; if (priv) { intf = (struct sprdwl_intf *)(priv->hw_priv); rx_if = (struct sprdwl_rx_if *)intf->sprdwl_rx; ba_entry = &rx_if->ba_entry; } else { wl_info("%s: parameter priv is NULL\n", __func__); return; } for (i = 0; i < MAX_LUT_NUM; i++) { if (ether_addr_equal(intf->peer_entry[i].tx.da, mac_addr)) { lut_id = intf->peer_entry[i].lut_index; break; } } if (lut_id == 0xff) return; for (tid = 0; tid < NUM_TIDS; tid++) { ba_node = find_ba_node(ba_entry, lut_id, tid); if (ba_node) { spin_lock_bh(&ba_node->ba_node_lock); ba_node->rx_ba->reset_pn = 1; wl_info("%s: set,lut=%d,tid=%d,pn_l=%d,pn_h=%d\n", __func__, lut_id, tid, ba_node->rx_ba->pn_l, ba_node->rx_ba->pn_h); spin_unlock_bh(&ba_node->ba_node_lock); } } } void reorder_data_process(struct sprdwl_rx_ba_entry *ba_entry, struct sk_buff *pskb) { struct rx_ba_node *ba_node = NULL; struct rx_msdu_desc *msdu_desc = NULL; if (pskb) { msdu_desc = (struct rx_msdu_desc *)pskb->data; wl_debug("%s: qos_flag: %d, ampdu_flag: %d, bc_mc_flag: %d\n", __func__, msdu_desc->qos_flag, msdu_desc->ampdu_flag, msdu_desc->bc_mc_flag); if (!msdu_desc->bc_mc_flag && msdu_desc->qos_flag) { ba_node = find_ba_node(ba_entry, msdu_desc->sta_lut_index, msdu_desc->tid); if (ba_node) reorder_msdu_process(ba_entry, msdu_desc, pskb, ba_node); else reorder_set_skb_list(ba_entry, pskb, pskb); } else { reorder_set_skb_list(ba_entry, pskb, pskb); } } } static void wlan_filter_event(struct sprdwl_rx_ba_entry *ba_entry, struct sprdwl_event_ba *ba_event) { struct rx_ba_node *ba_node = NULL; struct rx_msdu_desc msdu_desc; ba_node = find_ba_node(ba_entry, ba_event->sta_lut_index, ba_event->tid); if (ba_node) { msdu_desc.last_msdu_of_mpdu = 1; msdu_desc.seq_num = ba_event->msdu_param.seq_num; msdu_desc.msdu_index_of_mpdu = 0; msdu_desc.pn_l = 0; msdu_desc.pn_h = 0; msdu_desc.cipher_type = 0; msdu_desc.tid = ba_event->tid; msdu_desc.sta_lut_index = ba_event->sta_lut_index; reorder_msdu_process(ba_entry, &msdu_desc, NULL, ba_node); } } static void wlan_delba_event(struct sprdwl_rx_ba_entry *ba_entry, struct sprdwl_event_ba *ba_event) { struct rx_ba_node *ba_node = NULL; struct rx_ba_node_desc *ba_node_desc = NULL; wl_info("enter %s\n", __func__); ba_node = find_ba_node(ba_entry, ba_event->sta_lut_index, ba_event->tid); if (!ba_node) { wl_err("%s: NOT FOUND sta_lut_index: %d, tid: %d\n", __func__, ba_event->sta_lut_index, ba_event->tid); return; } del_timer_sync(&ba_node->reorder_timer); spin_lock_bh(&ba_node->ba_node_lock); if (ba_node->active) { ba_node_desc = ba_node->rx_ba; ba_node->active = 0; ba_node->timeout_cnt = 0; between_seqlo_seqhi(ba_entry, ba_node_desc); flush_reorder_buffer(ba_node_desc); } hlist_del(&ba_node->hlist); spin_unlock_bh(&ba_node->ba_node_lock); kfree(ba_node->rx_ba); kfree(ba_node); ba_node = NULL; ba_entry->current_ba_node = NULL; } #if 0 static void wlan_bar_event(struct sprdwl_rx_ba_entry *ba_entry, struct sprdwl_event_ba *ba_event) { struct rx_ba_node *ba_node = NULL; struct rx_ba_node_desc *ba_node_desc = NULL; wl_info("enter %s\n", __func__); ba_node = find_ba_node(ba_entry, ba_event->sta_lut_index, ba_event->tid); if (!ba_node) { wl_err("%s: NOT FOUND sta_lut_index: %d, tid: %d\n", __func__, ba_event->sta_lut_index, ba_event->tid); return; } spin_lock_bh(&ba_node->ba_node_lock); if (ba_node->active) { ba_node_desc = ba_node->rx_ba; if (!seqno_leq(ba_event->win_param.win_start, ba_node_desc->win_start)) { bar_send_ba_buffer(ba_entry, ba_node_desc, ba_event->win_param.win_start); mod_reorder_timer(ba_node); } wl_info("%s:(active:%d, tid:%d)\n", __func__, ba_node->active, ba_node->tid); wl_info("%s:(win_size:%d, win_start:%d, win_tail:%d)\n", __func__, ba_node_desc->win_size, ba_node_desc->win_start, ba_node_desc->win_tail); } else { wl_err("%s: BA SESSION IS NO ACTIVE sta_lut_index: %d, tid: %d\n", __func__, ba_event->sta_lut_index, ba_event->tid); } spin_unlock_bh(&ba_node->ba_node_lock); } #endif static void send_addba_rsp(struct sprdwl_rx_ba_entry *ba_entry, unsigned char tid, unsigned char sta_lut_index, int status) { struct sprdwl_ba_event_data ba_data; struct sprdwl_intf *intf = NULL; struct sprdwl_peer_entry *peer_entry = NULL; struct sprdwl_rx_if *rx_if = container_of(ba_entry, struct sprdwl_rx_if, ba_entry); intf = rx_if->intf; peer_entry = sprdwl_find_peer_entry_using_lut_index(intf, sta_lut_index); if (peer_entry == NULL) { wl_err("%s, peer not found\n", __func__); return; } ba_data.addba_rsp.type = SPRDWL_ADDBA_RSP_CMD; ba_data.addba_rsp.tid = tid; ether_addr_copy(ba_data.addba_rsp.da, peer_entry->tx.da); ba_data.addba_rsp.success = (status) ? 0 : 1; ba_data.ba_entry = ba_entry; ba_data.sta_lut_index = sta_lut_index; sprdwl_rx_send_cmd(intf, (void *)(&ba_data), sizeof(ba_data), SPRDWL_WORK_BA_MGMT, peer_entry->ctx_id); } static void send_delba(struct sprdwl_rx_ba_entry *ba_entry, unsigned short tid, unsigned char sta_lut_index) { struct sprdwl_cmd_ba delba; struct sprdwl_intf *intf = NULL; struct sprdwl_peer_entry *peer_entry = NULL; struct sprdwl_rx_if *rx_if = container_of(ba_entry, struct sprdwl_rx_if, ba_entry); intf = rx_if->intf; peer_entry = sprdwl_find_peer_entry_using_lut_index(intf, sta_lut_index); if (peer_entry == NULL) { wl_err("%s, peer not found\n", __func__); return; } delba.type = SPRDWL_DELBA_CMD; delba.tid = tid; ether_addr_copy(delba.da, peer_entry->tx.da); delba.success = 1; sprdwl_rx_send_cmd(intf, (void *)(&delba), sizeof(delba), SPRDWL_WORK_BA_MGMT_DELBA, peer_entry->ctx_id); } static int wlan_addba_event(struct sprdwl_rx_ba_entry *ba_entry, struct sprdwl_event_ba *ba_event) { struct rx_ba_node *ba_node = NULL; int ret = 0; unsigned char sta_lut_index = ba_event->sta_lut_index; unsigned char tid = ba_event->tid; unsigned short win_start = ba_event->win_param.win_start; unsigned short win_size = ba_event->win_param.win_size; unsigned int index_size = get_index_size(2 * win_size); wl_info("enter %s\n", __func__); ba_node = find_ba_node(ba_entry, sta_lut_index, tid); if (!ba_node) { ba_node = create_ba_node(ba_entry, sta_lut_index, tid, index_size); if (!ba_node) { wl_err("%s: Create ba_entry fail\n", __func__); ret = -ENOMEM; goto out; } } spin_lock_bh(&ba_node->ba_node_lock); #ifdef CP2_RESET_SUPPORT ba_node->active = 0; #endif /*CP2_RESET_SUPPORT*/ if (likely(!ba_node->active)) { set_ba_node_desc(ba_node->rx_ba, win_start, win_size, INDEX_SIZE_MASK(index_size)); #if 0 ba_node->active = 1; wl_debug("%s:(active:%d, tid:%d)\n", __func__, ba_node->active, ba_node->tid); #endif } else { /* Should never happen */ wl_err("%s: BA SESSION IS ACTIVE sta_lut_index: %d, tid: %d\n", __func__, sta_lut_index, tid); ret = -EINVAL; } spin_unlock_bh(&ba_node->ba_node_lock); out: return ret; } void wlan_ba_session_event(void *hw_intf, unsigned char *data, unsigned short len) { struct sprdwl_intf *intf = (struct sprdwl_intf *)hw_intf; struct sprdwl_rx_if *rx_if = (struct sprdwl_rx_if *)intf->sprdwl_rx; struct sprdwl_rx_ba_entry *ba_entry = &rx_if->ba_entry; struct sprdwl_event_ba *ba_event = (struct sprdwl_event_ba *)data; unsigned char type = ba_event->type; int ret = 0; struct sprdwl_peer_entry *peer_entry = NULL; u8 qos_index; switch (type) { case SPRDWL_ADDBA_REQ_EVENT: ret = wlan_addba_event(ba_entry, ba_event); send_addba_rsp(ba_entry, ba_event->tid, ba_event->sta_lut_index, ret); break; case SPRDWL_DELBA_EVENT: wlan_delba_event(ba_entry, ba_event); break; case SPRDWL_BAR_EVENT: /*wlan_bar_event(ba_entry, ba_event);*/ break; case SPRDWL_FILTER_EVENT: wlan_filter_event(ba_entry, ba_event); break; case SPRDWL_DELTXBA_EVENT: peer_entry = &intf->peer_entry[ba_event->sta_lut_index]; qos_index = tid_map_to_qosindex(ba_event->tid); peer_entry = &intf->peer_entry[ba_event->sta_lut_index]; if (test_and_clear_bit(ba_event->tid, &peer_entry->ba_tx_done_map)) wl_info("%s, %d, deltxba, lut=%d, tid=%d, map=%lu\n", __func__, __LINE__, ba_event->sta_lut_index, ba_event->tid, peer_entry->ba_tx_done_map); break; default: wl_err("%s: Error type: %d\n", __func__, type); break; } /* TODO:Should we handle skb list here? */ } void sprdwl_reorder_init(struct sprdwl_rx_ba_entry *ba_entry) { int i = 0; for (i = 0; i < NUM_TIDS; i++) INIT_HLIST_HEAD(&ba_entry->hlist[i]); spin_lock_init(&ba_entry->skb_list_lock); } void sprdwl_reorder_deinit(struct sprdwl_rx_ba_entry *ba_entry) { int i = 0; struct rx_ba_node *ba_node = NULL; for (i = 0; i < NUM_TIDS; i++) { struct hlist_head *head = &ba_entry->hlist[i]; struct hlist_node *node = NULL; if (hlist_empty(head)) continue; hlist_for_each_entry_safe(ba_node, node, head, hlist) { del_timer_sync(&ba_node->reorder_timer); spin_lock_bh(&ba_node->ba_node_lock); ba_node->active = 0; flush_reorder_buffer(ba_node->rx_ba); hlist_del(&ba_node->hlist); spin_unlock_bh(&ba_node->ba_node_lock); kfree(ba_node->rx_ba); kfree(ba_node); ba_node = NULL; } } } static unsigned short get_first_seqno_in_buff(struct rx_ba_node_desc *ba_node_desc) { unsigned short seqno = ba_node_desc->win_start; unsigned short index = 0; while (seqno_leq(seqno, ba_node_desc->win_tail)) { index = seqno & ba_node_desc->index_mask; if (ba_node_desc->reorder_buffer[index].desc.last) break; seqno = SEQNO_ADD(seqno, 1); } wl_info("%s: first seqno: %d\n", __func__, seqno); return seqno; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) static void ba_reorder_timeout(struct timer_list *t) { struct rx_ba_node *ba_node = from_timer(ba_node, t, reorder_timer); #else static void ba_reorder_timeout(unsigned long data) { struct rx_ba_node *ba_node = (struct rx_ba_node *)data; #endif struct sprdwl_rx_ba_entry *ba_entry = ba_node->ba_entry; struct rx_ba_node_desc *ba_node_desc = ba_node->rx_ba; struct sprdwl_rx_if *rx_if = container_of(ba_entry, struct sprdwl_rx_if, ba_entry); unsigned short pos_seqno = 0; wl_info("enter %s\n", __func__); debug_cnt_inc(REORDER_TIMEOUT_CNT); spin_lock_bh(&ba_node->ba_node_lock); if (ba_node->active && ba_node_desc->buff_cnt && !timer_pending(&ba_node->reorder_timer)) { ba_node_desc->win_start_init = 0; pos_seqno = get_first_seqno_in_buff(ba_node_desc); send_msdu_with_gap(ba_entry, ba_node_desc, pos_seqno); ba_node_desc->win_start = pos_seqno; ba_node_desc->win_start = send_msdu_in_order(ba_entry, ba_node_desc); ba_node_desc->win_limit = SEQNO_ADD(ba_node_desc->win_start, (ba_node_desc->win_size - 1)); ba_node->timeout_cnt++; if (ba_node->timeout_cnt > MAX_TIMEOUT_CNT) { ba_node->active = 0; ba_node->timeout_cnt = 0; wl_info("%s, %d, send_delba\n", __func__, __LINE__); send_delba(ba_entry, ba_node->tid, ba_node->sta_lut_index); } mod_reorder_timer(ba_node); } spin_unlock_bh(&ba_node->ba_node_lock); spin_lock_bh(&ba_entry->skb_list_lock); if (ba_entry->skb_head) { spin_unlock_bh(&ba_entry->skb_list_lock); #ifdef SPRD_RX_THREAD rx_up(rx_if); #else if (!work_pending(&rx_if->rx_work)) { wl_info("%s: queue rx workqueue\n", __func__); queue_work(rx_if->rx_queue, &rx_if->rx_work); } #endif } else { spin_unlock_bh(&ba_entry->skb_list_lock); } wl_info("leave %s\n", __func__); } void peer_entry_delba(void *hw_intf, unsigned char lut_index) { int tid = 0; struct rx_ba_node *ba_node = NULL; struct rx_ba_node_desc *ba_node_desc = NULL; struct sprdwl_intf *intf = (struct sprdwl_intf *)hw_intf; struct sprdwl_rx_if *rx_if = (struct sprdwl_rx_if *)intf->sprdwl_rx; struct sprdwl_rx_ba_entry *ba_entry = &rx_if->ba_entry; wl_info("enter %s\n", __func__); for (tid = 0; tid < NUM_TIDS; tid++) { ba_node = find_ba_node(ba_entry, lut_index, tid); if (ba_node) { wl_info("%s: del ba lut_index: %d, tid %d\n", __func__, lut_index, tid); del_timer_sync(&ba_node->reorder_timer); spin_lock_bh(&ba_node->ba_node_lock); if (ba_node->active) { ba_node_desc = ba_node->rx_ba; ba_node->active = 0; ba_node->timeout_cnt = 0; flush_reorder_buffer(ba_node_desc); } hlist_del(&ba_node->hlist); spin_unlock_bh(&ba_node->ba_node_lock); kfree(ba_node->rx_ba); kfree(ba_node); ba_node = NULL; ba_entry->current_ba_node = NULL; } } } void sprdwl_active_ba_node(struct sprdwl_rx_ba_entry *ba_entry, u8 sta_lut_index, u8 tid) { struct rx_ba_node *ba_node = NULL; ba_node = find_ba_node(ba_entry, sta_lut_index, tid); if (ba_node == NULL) { wl_err("BA node not found, tid = %d\n", tid); return; } spin_lock_bh(&ba_node->ba_node_lock); ba_node->active = 1; spin_unlock_bh(&ba_node->ba_node_lock); wl_info("%s BA active tid = %d\n", __func__, tid); }