1277b024eSKalle Valo /* 2932183aaSGanapathi Bhat * NXP Wireless LAN device driver: 802.11n RX Re-ordering 3277b024eSKalle Valo * 4932183aaSGanapathi Bhat * Copyright 2011-2020 NXP 5277b024eSKalle Valo * 6932183aaSGanapathi Bhat * This software file (the "File") is distributed by NXP 7932183aaSGanapathi Bhat * under the terms of the GNU General Public License Version 2, June 1991 8277b024eSKalle Valo * (the "License"). You may use, redistribute and/or modify this File in 9277b024eSKalle Valo * accordance with the terms and conditions of the License, a copy of which 10277b024eSKalle Valo * is available by writing to the Free Software Foundation, Inc., 11277b024eSKalle Valo * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 12277b024eSKalle Valo * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 13277b024eSKalle Valo * 14277b024eSKalle Valo * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 15277b024eSKalle Valo * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 16277b024eSKalle Valo * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 17277b024eSKalle Valo * this warranty disclaimer. 18277b024eSKalle Valo */ 19277b024eSKalle Valo 20277b024eSKalle Valo #include "decl.h" 21277b024eSKalle Valo #include "ioctl.h" 22277b024eSKalle Valo #include "util.h" 23277b024eSKalle Valo #include "fw.h" 24277b024eSKalle Valo #include "main.h" 25277b024eSKalle Valo #include "wmm.h" 26277b024eSKalle Valo #include "11n.h" 27277b024eSKalle Valo #include "11n_rxreorder.h" 28277b024eSKalle Valo 29277b024eSKalle Valo /* This function will dispatch amsdu packet and forward it to kernel/upper 30277b024eSKalle Valo * layer. 31277b024eSKalle Valo */ 32277b024eSKalle Valo static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, 33277b024eSKalle Valo struct sk_buff *skb) 34277b024eSKalle Valo { 35277b024eSKalle Valo struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); 36277b024eSKalle Valo int ret; 37277b024eSKalle Valo 38277b024eSKalle Valo if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { 39277b024eSKalle Valo struct sk_buff_head list; 40277b024eSKalle Valo struct sk_buff *rx_skb; 41277b024eSKalle Valo 42277b024eSKalle Valo __skb_queue_head_init(&list); 43277b024eSKalle Valo 44277b024eSKalle Valo skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); 45277b024eSKalle Valo skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); 46277b024eSKalle Valo 47277b024eSKalle Valo ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, 488b935ee2SJohannes Berg priv->wdev.iftype, 0, NULL, NULL); 49277b024eSKalle Valo 50277b024eSKalle Valo while (!skb_queue_empty(&list)) { 51776f7420SAmitkumar Karwar struct rx_packet_hdr *rx_hdr; 52776f7420SAmitkumar Karwar 53277b024eSKalle Valo rx_skb = __skb_dequeue(&list); 54776f7420SAmitkumar Karwar rx_hdr = (struct rx_packet_hdr *)rx_skb->data; 55776f7420SAmitkumar Karwar if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && 56776f7420SAmitkumar Karwar ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { 57776f7420SAmitkumar Karwar mwifiex_process_tdls_action_frame(priv, 58776f7420SAmitkumar Karwar (u8 *)rx_hdr, 59776f7420SAmitkumar Karwar skb->len); 60776f7420SAmitkumar Karwar } 61776f7420SAmitkumar Karwar 62bf00dc22SXinming Hu if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) 63bf00dc22SXinming Hu ret = mwifiex_uap_recv_packet(priv, rx_skb); 64bf00dc22SXinming Hu else 65277b024eSKalle Valo ret = mwifiex_recv_packet(priv, rx_skb); 66277b024eSKalle Valo if (ret == -1) 67277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR, 68277b024eSKalle Valo "Rx of A-MSDU failed"); 69277b024eSKalle Valo } 70277b024eSKalle Valo return 0; 71277b024eSKalle Valo } 72277b024eSKalle Valo 73277b024eSKalle Valo return -1; 74277b024eSKalle Valo } 75277b024eSKalle Valo 76277b024eSKalle Valo /* This function will process the rx packet and forward it to kernel/upper 77277b024eSKalle Valo * layer. 78277b024eSKalle Valo */ 79ce2e942eSBrian Norris static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, 80ce2e942eSBrian Norris struct sk_buff *payload) 81277b024eSKalle Valo { 82277b024eSKalle Valo 8399ffe72cSXinming Hu int ret; 8499ffe72cSXinming Hu 8599ffe72cSXinming Hu if (!payload) { 8699ffe72cSXinming Hu mwifiex_dbg(priv->adapter, INFO, "info: fw drop data\n"); 8799ffe72cSXinming Hu return 0; 8899ffe72cSXinming Hu } 8999ffe72cSXinming Hu 9099ffe72cSXinming Hu ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload); 91277b024eSKalle Valo if (!ret) 92277b024eSKalle Valo return 0; 93277b024eSKalle Valo 94277b024eSKalle Valo if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) 95277b024eSKalle Valo return mwifiex_handle_uap_rx_forward(priv, payload); 96277b024eSKalle Valo 97277b024eSKalle Valo return mwifiex_process_rx_packet(priv, payload); 98277b024eSKalle Valo } 99277b024eSKalle Valo 100277b024eSKalle Valo /* 101277b024eSKalle Valo * This function dispatches all packets in the Rx reorder table until the 102277b024eSKalle Valo * start window. 103277b024eSKalle Valo * 104277b024eSKalle Valo * There could be holes in the buffer, which are skipped by the function. 105277b024eSKalle Valo * Since the buffer is linear, the function uses rotation to simulate 106277b024eSKalle Valo * circular buffer. 107277b024eSKalle Valo */ 108277b024eSKalle Valo static void 109277b024eSKalle Valo mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, 110277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl, 111277b024eSKalle Valo int start_win) 112277b024eSKalle Valo { 113ce2e942eSBrian Norris struct sk_buff_head list; 114ce2e942eSBrian Norris struct sk_buff *skb; 115277b024eSKalle Valo int pkt_to_send, i; 116277b024eSKalle Valo 117ce2e942eSBrian Norris __skb_queue_head_init(&list); 1188a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 119ce2e942eSBrian Norris 120277b024eSKalle Valo pkt_to_send = (start_win > tbl->start_win) ? 121277b024eSKalle Valo min((start_win - tbl->start_win), tbl->win_size) : 122277b024eSKalle Valo tbl->win_size; 123277b024eSKalle Valo 124277b024eSKalle Valo for (i = 0; i < pkt_to_send; ++i) { 125277b024eSKalle Valo if (tbl->rx_reorder_ptr[i]) { 126ce2e942eSBrian Norris skb = tbl->rx_reorder_ptr[i]; 127ce2e942eSBrian Norris __skb_queue_tail(&list, skb); 128277b024eSKalle Valo tbl->rx_reorder_ptr[i] = NULL; 129277b024eSKalle Valo } 130277b024eSKalle Valo } 131277b024eSKalle Valo 132277b024eSKalle Valo /* 133277b024eSKalle Valo * We don't have a circular buffer, hence use rotation to simulate 134277b024eSKalle Valo * circular buffer 135277b024eSKalle Valo */ 136277b024eSKalle Valo for (i = 0; i < tbl->win_size - pkt_to_send; ++i) { 137277b024eSKalle Valo tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i]; 138277b024eSKalle Valo tbl->rx_reorder_ptr[pkt_to_send + i] = NULL; 139277b024eSKalle Valo } 140277b024eSKalle Valo 141277b024eSKalle Valo tbl->start_win = start_win; 1428a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 143ce2e942eSBrian Norris 144ce2e942eSBrian Norris while ((skb = __skb_dequeue(&list))) 145ce2e942eSBrian Norris mwifiex_11n_dispatch_pkt(priv, skb); 146277b024eSKalle Valo } 147277b024eSKalle Valo 148277b024eSKalle Valo /* 149277b024eSKalle Valo * This function dispatches all packets in the Rx reorder table until 150277b024eSKalle Valo * a hole is found. 151277b024eSKalle Valo * 152277b024eSKalle Valo * The start window is adjusted automatically when a hole is located. 153277b024eSKalle Valo * Since the buffer is linear, the function uses rotation to simulate 154277b024eSKalle Valo * circular buffer. 155277b024eSKalle Valo */ 156277b024eSKalle Valo static void 157277b024eSKalle Valo mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, 158277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl) 159277b024eSKalle Valo { 160ce2e942eSBrian Norris struct sk_buff_head list; 161ce2e942eSBrian Norris struct sk_buff *skb; 162277b024eSKalle Valo int i, j, xchg; 163277b024eSKalle Valo 164ce2e942eSBrian Norris __skb_queue_head_init(&list); 1658a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 166ce2e942eSBrian Norris 167ce2e942eSBrian Norris for (i = 0; i < tbl->win_size; ++i) { 168ce2e942eSBrian Norris if (!tbl->rx_reorder_ptr[i]) 169277b024eSKalle Valo break; 170ce2e942eSBrian Norris skb = tbl->rx_reorder_ptr[i]; 171ce2e942eSBrian Norris __skb_queue_tail(&list, skb); 172277b024eSKalle Valo tbl->rx_reorder_ptr[i] = NULL; 173277b024eSKalle Valo } 174277b024eSKalle Valo 175277b024eSKalle Valo /* 176277b024eSKalle Valo * We don't have a circular buffer, hence use rotation to simulate 177277b024eSKalle Valo * circular buffer 178277b024eSKalle Valo */ 179277b024eSKalle Valo if (i > 0) { 180277b024eSKalle Valo xchg = tbl->win_size - i; 181277b024eSKalle Valo for (j = 0; j < xchg; ++j) { 182277b024eSKalle Valo tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j]; 183277b024eSKalle Valo tbl->rx_reorder_ptr[i + j] = NULL; 184277b024eSKalle Valo } 185277b024eSKalle Valo } 186277b024eSKalle Valo tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); 187ce2e942eSBrian Norris 1888a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 189ce2e942eSBrian Norris 190ce2e942eSBrian Norris while ((skb = __skb_dequeue(&list))) 191ce2e942eSBrian Norris mwifiex_11n_dispatch_pkt(priv, skb); 192277b024eSKalle Valo } 193277b024eSKalle Valo 194277b024eSKalle Valo /* 195277b024eSKalle Valo * This function deletes the Rx reorder table and frees the memory. 196277b024eSKalle Valo * 197277b024eSKalle Valo * The function stops the associated timer and dispatches all the 198277b024eSKalle Valo * pending packets in the Rx reorder table before deletion. 199277b024eSKalle Valo */ 200277b024eSKalle Valo static void 201277b024eSKalle Valo mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, 202277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl) 203277b024eSKalle Valo { 204277b024eSKalle Valo int start_win; 205277b024eSKalle Valo 206277b024eSKalle Valo if (!tbl) 207277b024eSKalle Valo return; 208277b024eSKalle Valo 2098a7f9fd8SBrian Norris spin_lock_bh(&priv->adapter->rx_proc_lock); 210277b024eSKalle Valo priv->adapter->rx_locked = true; 211277b024eSKalle Valo if (priv->adapter->rx_processing) { 2128a7f9fd8SBrian Norris spin_unlock_bh(&priv->adapter->rx_proc_lock); 213277b024eSKalle Valo flush_workqueue(priv->adapter->rx_workqueue); 214277b024eSKalle Valo } else { 2158a7f9fd8SBrian Norris spin_unlock_bh(&priv->adapter->rx_proc_lock); 216277b024eSKalle Valo } 217277b024eSKalle Valo 218277b024eSKalle Valo start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); 219277b024eSKalle Valo mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); 220277b024eSKalle Valo 221277b024eSKalle Valo del_timer_sync(&tbl->timer_context.timer); 222277b024eSKalle Valo tbl->timer_context.timer_is_set = false; 2231aa48f08SBrian Norris 2248a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 225277b024eSKalle Valo list_del(&tbl->list); 2268a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 2271aa48f08SBrian Norris 228277b024eSKalle Valo kfree(tbl->rx_reorder_ptr); 229277b024eSKalle Valo kfree(tbl); 230277b024eSKalle Valo 2318a7f9fd8SBrian Norris spin_lock_bh(&priv->adapter->rx_proc_lock); 232277b024eSKalle Valo priv->adapter->rx_locked = false; 2338a7f9fd8SBrian Norris spin_unlock_bh(&priv->adapter->rx_proc_lock); 234277b024eSKalle Valo 235277b024eSKalle Valo } 236277b024eSKalle Valo 237277b024eSKalle Valo /* 238277b024eSKalle Valo * This function returns the pointer to an entry in Rx reordering 239277b024eSKalle Valo * table which matches the given TA/TID pair. 240277b024eSKalle Valo */ 241277b024eSKalle Valo struct mwifiex_rx_reorder_tbl * 242277b024eSKalle Valo mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) 243277b024eSKalle Valo { 244277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl; 245277b024eSKalle Valo 2468a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 2471aa48f08SBrian Norris list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { 2481aa48f08SBrian Norris if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { 2498a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 250277b024eSKalle Valo return tbl; 2511aa48f08SBrian Norris } 2521aa48f08SBrian Norris } 2538a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 254277b024eSKalle Valo 255277b024eSKalle Valo return NULL; 256277b024eSKalle Valo } 257277b024eSKalle Valo 258277b024eSKalle Valo /* This function retrieves the pointer to an entry in Rx reordering 259277b024eSKalle Valo * table which matches the given TA and deletes it. 260277b024eSKalle Valo */ 261277b024eSKalle Valo void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) 262277b024eSKalle Valo { 263277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl, *tmp; 264277b024eSKalle Valo 265277b024eSKalle Valo if (!ta) 266277b024eSKalle Valo return; 267277b024eSKalle Valo 2688a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 2691aa48f08SBrian Norris list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { 2701aa48f08SBrian Norris if (!memcmp(tbl->ta, ta, ETH_ALEN)) { 2718a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 272277b024eSKalle Valo mwifiex_del_rx_reorder_entry(priv, tbl); 2738a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 2741aa48f08SBrian Norris } 2751aa48f08SBrian Norris } 2768a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 277277b024eSKalle Valo 278277b024eSKalle Valo return; 279277b024eSKalle Valo } 280277b024eSKalle Valo 281277b024eSKalle Valo /* 282277b024eSKalle Valo * This function finds the last sequence number used in the packets 283277b024eSKalle Valo * buffered in Rx reordering table. 284277b024eSKalle Valo */ 285277b024eSKalle Valo static int 286277b024eSKalle Valo mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) 287277b024eSKalle Valo { 288277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; 2891aa48f08SBrian Norris struct mwifiex_private *priv = ctx->priv; 290277b024eSKalle Valo int i; 291277b024eSKalle Valo 2928a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 2931aa48f08SBrian Norris for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { 2941aa48f08SBrian Norris if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { 2958a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 296277b024eSKalle Valo return i; 2971aa48f08SBrian Norris } 2981aa48f08SBrian Norris } 2998a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 300277b024eSKalle Valo 301277b024eSKalle Valo return -1; 302277b024eSKalle Valo } 303277b024eSKalle Valo 304277b024eSKalle Valo /* 305277b024eSKalle Valo * This function flushes all the packets in Rx reordering table. 306277b024eSKalle Valo * 307277b024eSKalle Valo * The function checks if any packets are currently buffered in the 308277b024eSKalle Valo * table or not. In case there are packets available, it dispatches 309277b024eSKalle Valo * them and then dumps the Rx reordering table. 310277b024eSKalle Valo */ 311277b024eSKalle Valo static void 31208c2eb8eSKees Cook mwifiex_flush_data(struct timer_list *t) 313277b024eSKalle Valo { 314277b024eSKalle Valo struct reorder_tmr_cnxt *ctx = 31508c2eb8eSKees Cook from_timer(ctx, t, timer); 316277b024eSKalle Valo int start_win, seq_num; 317277b024eSKalle Valo 318277b024eSKalle Valo ctx->timer_is_set = false; 319277b024eSKalle Valo seq_num = mwifiex_11n_find_last_seq_num(ctx); 320277b024eSKalle Valo 3211aa48f08SBrian Norris if (seq_num < 0) 322277b024eSKalle Valo return; 323277b024eSKalle Valo 324277b024eSKalle Valo mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num); 325277b024eSKalle Valo start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); 326277b024eSKalle Valo mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, 327277b024eSKalle Valo start_win); 328277b024eSKalle Valo } 329277b024eSKalle Valo 330277b024eSKalle Valo /* 331277b024eSKalle Valo * This function creates an entry in Rx reordering table for the 332277b024eSKalle Valo * given TA/TID. 333277b024eSKalle Valo * 334277b024eSKalle Valo * The function also initializes the entry with sequence number, window 335277b024eSKalle Valo * size as well as initializes the timer. 336277b024eSKalle Valo * 337277b024eSKalle Valo * If the received TA/TID pair is already present, all the packets are 338277b024eSKalle Valo * dispatched and the window size is moved until the SSN. 339277b024eSKalle Valo */ 340277b024eSKalle Valo static void 341277b024eSKalle Valo mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, 342277b024eSKalle Valo int tid, int win_size, int seq_num) 343277b024eSKalle Valo { 344277b024eSKalle Valo int i; 345277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl, *new_node; 346277b024eSKalle Valo u16 last_seq = 0; 347277b024eSKalle Valo struct mwifiex_sta_node *node; 348277b024eSKalle Valo 349277b024eSKalle Valo /* 350277b024eSKalle Valo * If we get a TID, ta pair which is already present dispatch all the 351277b024eSKalle Valo * the packets and move the window size until the ssn 352277b024eSKalle Valo */ 353277b024eSKalle Valo tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); 354277b024eSKalle Valo if (tbl) { 355277b024eSKalle Valo mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); 356277b024eSKalle Valo return; 357277b024eSKalle Valo } 358277b024eSKalle Valo /* if !tbl then create one */ 359277b024eSKalle Valo new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); 360277b024eSKalle Valo if (!new_node) 361277b024eSKalle Valo return; 362277b024eSKalle Valo 363277b024eSKalle Valo INIT_LIST_HEAD(&new_node->list); 364277b024eSKalle Valo new_node->tid = tid; 365277b024eSKalle Valo memcpy(new_node->ta, ta, ETH_ALEN); 366277b024eSKalle Valo new_node->start_win = seq_num; 367277b024eSKalle Valo new_node->init_win = seq_num; 368277b024eSKalle Valo new_node->flags = 0; 369277b024eSKalle Valo 3708a7f9fd8SBrian Norris spin_lock_bh(&priv->sta_list_spinlock); 371277b024eSKalle Valo if (mwifiex_queuing_ra_based(priv)) { 372277b024eSKalle Valo if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { 373277b024eSKalle Valo node = mwifiex_get_sta_entry(priv, ta); 374277b024eSKalle Valo if (node) 375277b024eSKalle Valo last_seq = node->rx_seq[tid]; 376277b024eSKalle Valo } 377277b024eSKalle Valo } else { 378277b024eSKalle Valo node = mwifiex_get_sta_entry(priv, ta); 379277b024eSKalle Valo if (node) 380277b024eSKalle Valo last_seq = node->rx_seq[tid]; 381277b024eSKalle Valo else 382277b024eSKalle Valo last_seq = priv->rx_seq[tid]; 383277b024eSKalle Valo } 3848a7f9fd8SBrian Norris spin_unlock_bh(&priv->sta_list_spinlock); 385277b024eSKalle Valo 386277b024eSKalle Valo mwifiex_dbg(priv->adapter, INFO, 387277b024eSKalle Valo "info: last_seq=%d start_win=%d\n", 388277b024eSKalle Valo last_seq, new_node->start_win); 389277b024eSKalle Valo 390277b024eSKalle Valo if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && 391277b024eSKalle Valo last_seq >= new_node->start_win) { 392277b024eSKalle Valo new_node->start_win = last_seq + 1; 393277b024eSKalle Valo new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; 394277b024eSKalle Valo } 395277b024eSKalle Valo 396277b024eSKalle Valo new_node->win_size = win_size; 397277b024eSKalle Valo 3986396bb22SKees Cook new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *), 399277b024eSKalle Valo GFP_KERNEL); 400277b024eSKalle Valo if (!new_node->rx_reorder_ptr) { 401277b024eSKalle Valo kfree((u8 *) new_node); 402277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR, 403277b024eSKalle Valo "%s: failed to alloc reorder_ptr\n", __func__); 404277b024eSKalle Valo return; 405277b024eSKalle Valo } 406277b024eSKalle Valo 407277b024eSKalle Valo new_node->timer_context.ptr = new_node; 408277b024eSKalle Valo new_node->timer_context.priv = priv; 409277b024eSKalle Valo new_node->timer_context.timer_is_set = false; 410277b024eSKalle Valo 41108c2eb8eSKees Cook timer_setup(&new_node->timer_context.timer, mwifiex_flush_data, 0); 412277b024eSKalle Valo 413277b024eSKalle Valo for (i = 0; i < win_size; ++i) 414277b024eSKalle Valo new_node->rx_reorder_ptr[i] = NULL; 415277b024eSKalle Valo 4168a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 417277b024eSKalle Valo list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); 4188a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 419277b024eSKalle Valo } 420277b024eSKalle Valo 421277b024eSKalle Valo static void 422277b024eSKalle Valo mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl) 423277b024eSKalle Valo { 424277b024eSKalle Valo u32 min_flush_time; 425277b024eSKalle Valo 426277b024eSKalle Valo if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32) 427277b024eSKalle Valo min_flush_time = MIN_FLUSH_TIMER_15_MS; 428277b024eSKalle Valo else 429277b024eSKalle Valo min_flush_time = MIN_FLUSH_TIMER_MS; 430277b024eSKalle Valo 431277b024eSKalle Valo mod_timer(&tbl->timer_context.timer, 432277b024eSKalle Valo jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size)); 433277b024eSKalle Valo 434277b024eSKalle Valo tbl->timer_context.timer_is_set = true; 435277b024eSKalle Valo } 436277b024eSKalle Valo 437277b024eSKalle Valo /* 438277b024eSKalle Valo * This function prepares command for adding a BA request. 439277b024eSKalle Valo * 440277b024eSKalle Valo * Preparation includes - 441277b024eSKalle Valo * - Setting command ID and proper size 442277b024eSKalle Valo * - Setting add BA request buffer 443277b024eSKalle Valo * - Ensuring correct endian-ness 444277b024eSKalle Valo */ 445277b024eSKalle Valo int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) 446277b024eSKalle Valo { 447277b024eSKalle Valo struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; 448277b024eSKalle Valo 449277b024eSKalle Valo cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); 450277b024eSKalle Valo cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); 451277b024eSKalle Valo memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); 452277b024eSKalle Valo 453277b024eSKalle Valo return 0; 454277b024eSKalle Valo } 455277b024eSKalle Valo 456277b024eSKalle Valo /* 457277b024eSKalle Valo * This function prepares command for adding a BA response. 458277b024eSKalle Valo * 459277b024eSKalle Valo * Preparation includes - 460277b024eSKalle Valo * - Setting command ID and proper size 461277b024eSKalle Valo * - Setting add BA response buffer 462277b024eSKalle Valo * - Ensuring correct endian-ness 463277b024eSKalle Valo */ 464277b024eSKalle Valo int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, 465277b024eSKalle Valo struct host_cmd_ds_command *cmd, 466277b024eSKalle Valo struct host_cmd_ds_11n_addba_req 467277b024eSKalle Valo *cmd_addba_req) 468277b024eSKalle Valo { 469277b024eSKalle Valo struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; 470277b024eSKalle Valo struct mwifiex_sta_node *sta_ptr; 471277b024eSKalle Valo u32 rx_win_size = priv->add_ba_param.rx_win_size; 472277b024eSKalle Valo u8 tid; 473277b024eSKalle Valo int win_size; 474277b024eSKalle Valo uint16_t block_ack_param_set; 475277b024eSKalle Valo 476277b024eSKalle Valo if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && 477277b024eSKalle Valo ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && 478277b024eSKalle Valo priv->adapter->is_hw_11ac_capable && 479277b024eSKalle Valo memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { 4808a7f9fd8SBrian Norris spin_lock_bh(&priv->sta_list_spinlock); 481277b024eSKalle Valo sta_ptr = mwifiex_get_sta_entry(priv, 482277b024eSKalle Valo cmd_addba_req->peer_mac_addr); 483277b024eSKalle Valo if (!sta_ptr) { 4848a7f9fd8SBrian Norris spin_unlock_bh(&priv->sta_list_spinlock); 485277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR, 486277b024eSKalle Valo "BA setup with unknown TDLS peer %pM!\n", 487277b024eSKalle Valo cmd_addba_req->peer_mac_addr); 488277b024eSKalle Valo return -1; 489277b024eSKalle Valo } 490277b024eSKalle Valo if (sta_ptr->is_11ac_enabled) 491277b024eSKalle Valo rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; 4928a7f9fd8SBrian Norris spin_unlock_bh(&priv->sta_list_spinlock); 493277b024eSKalle Valo } 494277b024eSKalle Valo 495277b024eSKalle Valo cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); 496277b024eSKalle Valo cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); 497277b024eSKalle Valo 498277b024eSKalle Valo memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, 499277b024eSKalle Valo ETH_ALEN); 500277b024eSKalle Valo add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; 501277b024eSKalle Valo add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; 502277b024eSKalle Valo add_ba_rsp->ssn = cmd_addba_req->ssn; 503277b024eSKalle Valo 504277b024eSKalle Valo block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); 505277b024eSKalle Valo tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) 506277b024eSKalle Valo >> BLOCKACKPARAM_TID_POS; 507277b024eSKalle Valo add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); 508277b024eSKalle Valo block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; 509277b024eSKalle Valo 510277b024eSKalle Valo /* If we don't support AMSDU inside AMPDU, reset the bit */ 511277b024eSKalle Valo if (!priv->add_ba_param.rx_amsdu || 512277b024eSKalle Valo (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) 513277b024eSKalle Valo block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; 514277b024eSKalle Valo block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; 515277b024eSKalle Valo add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); 516277b024eSKalle Valo win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) 517277b024eSKalle Valo & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) 518277b024eSKalle Valo >> BLOCKACKPARAM_WINSIZE_POS; 519277b024eSKalle Valo cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); 520277b024eSKalle Valo 521277b024eSKalle Valo mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, 522277b024eSKalle Valo tid, win_size, 523277b024eSKalle Valo le16_to_cpu(cmd_addba_req->ssn)); 524277b024eSKalle Valo return 0; 525277b024eSKalle Valo } 526277b024eSKalle Valo 527277b024eSKalle Valo /* 528277b024eSKalle Valo * This function prepares command for deleting a BA request. 529277b024eSKalle Valo * 530277b024eSKalle Valo * Preparation includes - 531277b024eSKalle Valo * - Setting command ID and proper size 532277b024eSKalle Valo * - Setting del BA request buffer 533277b024eSKalle Valo * - Ensuring correct endian-ness 534277b024eSKalle Valo */ 535277b024eSKalle Valo int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) 536277b024eSKalle Valo { 537277b024eSKalle Valo struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; 538277b024eSKalle Valo 539277b024eSKalle Valo cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); 540277b024eSKalle Valo cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); 541277b024eSKalle Valo memcpy(del_ba, data_buf, sizeof(*del_ba)); 542277b024eSKalle Valo 543277b024eSKalle Valo return 0; 544277b024eSKalle Valo } 545277b024eSKalle Valo 546277b024eSKalle Valo /* 547277b024eSKalle Valo * This function identifies if Rx reordering is needed for a received packet. 548277b024eSKalle Valo * 549277b024eSKalle Valo * In case reordering is required, the function will do the reordering 550277b024eSKalle Valo * before sending it to kernel. 551277b024eSKalle Valo * 552277b024eSKalle Valo * The Rx reorder table is checked first with the received TID/TA pair. If 553277b024eSKalle Valo * not found, the received packet is dispatched immediately. But if found, 554277b024eSKalle Valo * the packet is reordered and all the packets in the updated Rx reordering 555277b024eSKalle Valo * table is dispatched until a hole is found. 556277b024eSKalle Valo * 557277b024eSKalle Valo * For sequence number less than the starting window, the packet is dropped. 558277b024eSKalle Valo */ 559277b024eSKalle Valo int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, 560277b024eSKalle Valo u16 seq_num, u16 tid, 561277b024eSKalle Valo u8 *ta, u8 pkt_type, void *payload) 562277b024eSKalle Valo { 563277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl; 564277b024eSKalle Valo int prev_start_win, start_win, end_win, win_size; 565277b024eSKalle Valo u16 pkt_index; 566277b024eSKalle Valo bool init_window_shift = false; 567277b024eSKalle Valo int ret = 0; 568277b024eSKalle Valo 569277b024eSKalle Valo tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); 570277b024eSKalle Valo if (!tbl) { 571277b024eSKalle Valo if (pkt_type != PKT_TYPE_BAR) 572277b024eSKalle Valo mwifiex_11n_dispatch_pkt(priv, payload); 573277b024eSKalle Valo return ret; 574277b024eSKalle Valo } 575277b024eSKalle Valo 576277b024eSKalle Valo if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) { 577277b024eSKalle Valo mwifiex_11n_dispatch_pkt(priv, payload); 578277b024eSKalle Valo return ret; 579277b024eSKalle Valo } 580277b024eSKalle Valo 581277b024eSKalle Valo start_win = tbl->start_win; 582277b024eSKalle Valo prev_start_win = start_win; 583277b024eSKalle Valo win_size = tbl->win_size; 584277b024eSKalle Valo end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); 585277b024eSKalle Valo if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { 586277b024eSKalle Valo init_window_shift = true; 587277b024eSKalle Valo tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; 588277b024eSKalle Valo } 589277b024eSKalle Valo 590277b024eSKalle Valo if (tbl->flags & RXREOR_FORCE_NO_DROP) { 591277b024eSKalle Valo mwifiex_dbg(priv->adapter, INFO, 592277b024eSKalle Valo "RXREOR_FORCE_NO_DROP when HS is activated\n"); 593277b024eSKalle Valo tbl->flags &= ~RXREOR_FORCE_NO_DROP; 594277b024eSKalle Valo } else if (init_window_shift && seq_num < start_win && 595277b024eSKalle Valo seq_num >= tbl->init_win) { 596277b024eSKalle Valo mwifiex_dbg(priv->adapter, INFO, 597277b024eSKalle Valo "Sender TID sequence number reset %d->%d for SSN %d\n", 598277b024eSKalle Valo start_win, seq_num, tbl->init_win); 599277b024eSKalle Valo tbl->start_win = start_win = seq_num; 600277b024eSKalle Valo end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); 601277b024eSKalle Valo } else { 602277b024eSKalle Valo /* 603277b024eSKalle Valo * If seq_num is less then starting win then ignore and drop 604277b024eSKalle Valo * the packet 605277b024eSKalle Valo */ 606277b024eSKalle Valo if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { 607277b024eSKalle Valo if (seq_num >= ((start_win + TWOPOW11) & 608277b024eSKalle Valo (MAX_TID_VALUE - 1)) && 609277b024eSKalle Valo seq_num < start_win) { 610277b024eSKalle Valo ret = -1; 611277b024eSKalle Valo goto done; 612277b024eSKalle Valo } 613277b024eSKalle Valo } else if ((seq_num < start_win) || 614277b024eSKalle Valo (seq_num >= (start_win + TWOPOW11))) { 615277b024eSKalle Valo ret = -1; 616277b024eSKalle Valo goto done; 617277b024eSKalle Valo } 618277b024eSKalle Valo } 619277b024eSKalle Valo 620277b024eSKalle Valo /* 621277b024eSKalle Valo * If this packet is a BAR we adjust seq_num as 622277b024eSKalle Valo * WinStart = seq_num 623277b024eSKalle Valo */ 624277b024eSKalle Valo if (pkt_type == PKT_TYPE_BAR) 625277b024eSKalle Valo seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); 626277b024eSKalle Valo 627277b024eSKalle Valo if (((end_win < start_win) && 628277b024eSKalle Valo (seq_num < start_win) && (seq_num > end_win)) || 629277b024eSKalle Valo ((end_win > start_win) && ((seq_num > end_win) || 630277b024eSKalle Valo (seq_num < start_win)))) { 631277b024eSKalle Valo end_win = seq_num; 632277b024eSKalle Valo if (((end_win - win_size) + 1) >= 0) 633277b024eSKalle Valo start_win = (end_win - win_size) + 1; 634277b024eSKalle Valo else 635277b024eSKalle Valo start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1; 636277b024eSKalle Valo mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); 637277b024eSKalle Valo } 638277b024eSKalle Valo 639277b024eSKalle Valo if (pkt_type != PKT_TYPE_BAR) { 640277b024eSKalle Valo if (seq_num >= start_win) 641277b024eSKalle Valo pkt_index = seq_num - start_win; 642277b024eSKalle Valo else 643277b024eSKalle Valo pkt_index = (seq_num+MAX_TID_VALUE) - start_win; 644277b024eSKalle Valo 645277b024eSKalle Valo if (tbl->rx_reorder_ptr[pkt_index]) { 646277b024eSKalle Valo ret = -1; 647277b024eSKalle Valo goto done; 648277b024eSKalle Valo } 649277b024eSKalle Valo 650277b024eSKalle Valo tbl->rx_reorder_ptr[pkt_index] = payload; 651277b024eSKalle Valo } 652277b024eSKalle Valo 653277b024eSKalle Valo /* 654277b024eSKalle Valo * Dispatch all packets sequentially from start_win until a 655277b024eSKalle Valo * hole is found and adjust the start_win appropriately 656277b024eSKalle Valo */ 657277b024eSKalle Valo mwifiex_11n_scan_and_dispatch(priv, tbl); 658277b024eSKalle Valo 659277b024eSKalle Valo done: 660277b024eSKalle Valo if (!tbl->timer_context.timer_is_set || 661277b024eSKalle Valo prev_start_win != tbl->start_win) 662277b024eSKalle Valo mwifiex_11n_rxreorder_timer_restart(tbl); 663277b024eSKalle Valo return ret; 664277b024eSKalle Valo } 665277b024eSKalle Valo 666277b024eSKalle Valo /* 667277b024eSKalle Valo * This function deletes an entry for a given TID/TA pair. 668277b024eSKalle Valo * 669277b024eSKalle Valo * The TID/TA are taken from del BA event body. 670277b024eSKalle Valo */ 671277b024eSKalle Valo void 672277b024eSKalle Valo mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, 673277b024eSKalle Valo u8 type, int initiator) 674277b024eSKalle Valo { 675277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl; 676277b024eSKalle Valo struct mwifiex_tx_ba_stream_tbl *ptx_tbl; 677277b024eSKalle Valo struct mwifiex_ra_list_tbl *ra_list; 678277b024eSKalle Valo u8 cleanup_rx_reorder_tbl; 679277b024eSKalle Valo int tid_down; 680277b024eSKalle Valo 681277b024eSKalle Valo if (type == TYPE_DELBA_RECEIVE) 682277b024eSKalle Valo cleanup_rx_reorder_tbl = (initiator) ? true : false; 683277b024eSKalle Valo else 684277b024eSKalle Valo cleanup_rx_reorder_tbl = (initiator) ? false : true; 685277b024eSKalle Valo 686277b024eSKalle Valo mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n", 687277b024eSKalle Valo peer_mac, tid, initiator); 688277b024eSKalle Valo 689277b024eSKalle Valo if (cleanup_rx_reorder_tbl) { 690277b024eSKalle Valo tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, 691277b024eSKalle Valo peer_mac); 692277b024eSKalle Valo if (!tbl) { 693277b024eSKalle Valo mwifiex_dbg(priv->adapter, EVENT, 694277b024eSKalle Valo "event: TID, TA not found in table\n"); 695277b024eSKalle Valo return; 696277b024eSKalle Valo } 697277b024eSKalle Valo mwifiex_del_rx_reorder_entry(priv, tbl); 698277b024eSKalle Valo } else { 699277b024eSKalle Valo ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac); 700277b024eSKalle Valo if (!ptx_tbl) { 701277b024eSKalle Valo mwifiex_dbg(priv->adapter, EVENT, 702277b024eSKalle Valo "event: TID, RA not found in table\n"); 703277b024eSKalle Valo return; 704277b024eSKalle Valo } 705277b024eSKalle Valo 706277b024eSKalle Valo tid_down = mwifiex_wmm_downgrade_tid(priv, tid); 707277b024eSKalle Valo ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac); 708277b024eSKalle Valo if (ra_list) { 709277b024eSKalle Valo ra_list->amsdu_in_ampdu = false; 710277b024eSKalle Valo ra_list->ba_status = BA_SETUP_NONE; 711277b024eSKalle Valo } 7128a7f9fd8SBrian Norris spin_lock_bh(&priv->tx_ba_stream_tbl_lock); 713277b024eSKalle Valo mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); 7148a7f9fd8SBrian Norris spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); 715277b024eSKalle Valo } 716277b024eSKalle Valo } 717277b024eSKalle Valo 718277b024eSKalle Valo /* 719277b024eSKalle Valo * This function handles the command response of an add BA response. 720277b024eSKalle Valo * 721277b024eSKalle Valo * Handling includes changing the header fields into CPU format and 722277b024eSKalle Valo * creating the stream, provided the add BA is accepted. 723277b024eSKalle Valo */ 724277b024eSKalle Valo int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, 725277b024eSKalle Valo struct host_cmd_ds_command *resp) 726277b024eSKalle Valo { 727277b024eSKalle Valo struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; 728277b024eSKalle Valo int tid, win_size; 729277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl; 730277b024eSKalle Valo uint16_t block_ack_param_set; 731277b024eSKalle Valo 732277b024eSKalle Valo block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); 733277b024eSKalle Valo 734277b024eSKalle Valo tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) 735277b024eSKalle Valo >> BLOCKACKPARAM_TID_POS; 736277b024eSKalle Valo /* 737277b024eSKalle Valo * Check if we had rejected the ADDBA, if yes then do not create 738277b024eSKalle Valo * the stream 739277b024eSKalle Valo */ 740277b024eSKalle Valo if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { 741277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n", 742277b024eSKalle Valo add_ba_rsp->peer_mac_addr, tid); 743277b024eSKalle Valo 744277b024eSKalle Valo tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, 745277b024eSKalle Valo add_ba_rsp->peer_mac_addr); 746277b024eSKalle Valo if (tbl) 747277b024eSKalle Valo mwifiex_del_rx_reorder_entry(priv, tbl); 748277b024eSKalle Valo 749277b024eSKalle Valo return 0; 750277b024eSKalle Valo } 751277b024eSKalle Valo 752277b024eSKalle Valo win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) 753277b024eSKalle Valo >> BLOCKACKPARAM_WINSIZE_POS; 754277b024eSKalle Valo 755277b024eSKalle Valo tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, 756277b024eSKalle Valo add_ba_rsp->peer_mac_addr); 757277b024eSKalle Valo if (tbl) { 758277b024eSKalle Valo if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && 759277b024eSKalle Valo priv->add_ba_param.rx_amsdu && 760277b024eSKalle Valo (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) 761277b024eSKalle Valo tbl->amsdu = true; 762277b024eSKalle Valo else 763277b024eSKalle Valo tbl->amsdu = false; 764277b024eSKalle Valo } 765277b024eSKalle Valo 766277b024eSKalle Valo mwifiex_dbg(priv->adapter, CMD, 767277b024eSKalle Valo "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", 768277b024eSKalle Valo add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); 769277b024eSKalle Valo 770277b024eSKalle Valo return 0; 771277b024eSKalle Valo } 772277b024eSKalle Valo 773277b024eSKalle Valo /* 774277b024eSKalle Valo * This function handles BA stream timeout event by preparing and sending 775277b024eSKalle Valo * a command to the firmware. 776277b024eSKalle Valo */ 777277b024eSKalle Valo void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, 778277b024eSKalle Valo struct host_cmd_ds_11n_batimeout *event) 779277b024eSKalle Valo { 780277b024eSKalle Valo struct host_cmd_ds_11n_delba delba; 781277b024eSKalle Valo 782277b024eSKalle Valo memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); 783277b024eSKalle Valo memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); 784277b024eSKalle Valo 785277b024eSKalle Valo delba.del_ba_param_set |= 786277b024eSKalle Valo cpu_to_le16((u16) event->tid << DELBA_TID_POS); 787277b024eSKalle Valo delba.del_ba_param_set |= cpu_to_le16( 788277b024eSKalle Valo (u16) event->origninator << DELBA_INITIATOR_POS); 789277b024eSKalle Valo delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); 790277b024eSKalle Valo mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false); 791277b024eSKalle Valo } 792277b024eSKalle Valo 793277b024eSKalle Valo /* 794277b024eSKalle Valo * This function cleans up the Rx reorder table by deleting all the entries 795277b024eSKalle Valo * and re-initializing. 796277b024eSKalle Valo */ 797277b024eSKalle Valo void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) 798277b024eSKalle Valo { 799277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; 800277b024eSKalle Valo 8018a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 802277b024eSKalle Valo list_for_each_entry_safe(del_tbl_ptr, tmp_node, 8031aa48f08SBrian Norris &priv->rx_reorder_tbl_ptr, list) { 8048a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 805277b024eSKalle Valo mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); 8068a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 8071aa48f08SBrian Norris } 808277b024eSKalle Valo INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); 8098a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 810277b024eSKalle Valo 811277b024eSKalle Valo mwifiex_reset_11n_rx_seq_num(priv); 812277b024eSKalle Valo } 813277b024eSKalle Valo 814277b024eSKalle Valo /* 815277b024eSKalle Valo * This function updates all rx_reorder_tbl's flags. 816277b024eSKalle Valo */ 817277b024eSKalle Valo void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) 818277b024eSKalle Valo { 819277b024eSKalle Valo struct mwifiex_private *priv; 820277b024eSKalle Valo struct mwifiex_rx_reorder_tbl *tbl; 821277b024eSKalle Valo int i; 822277b024eSKalle Valo 823277b024eSKalle Valo for (i = 0; i < adapter->priv_num; i++) { 824277b024eSKalle Valo priv = adapter->priv[i]; 825277b024eSKalle Valo if (!priv) 826277b024eSKalle Valo continue; 827277b024eSKalle Valo 8288a7f9fd8SBrian Norris spin_lock_bh(&priv->rx_reorder_tbl_lock); 829277b024eSKalle Valo list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) 830277b024eSKalle Valo tbl->flags = flags; 8318a7f9fd8SBrian Norris spin_unlock_bh(&priv->rx_reorder_tbl_lock); 832277b024eSKalle Valo } 833277b024eSKalle Valo 834277b024eSKalle Valo return; 835277b024eSKalle Valo } 836277b024eSKalle Valo 837277b024eSKalle Valo /* This function update all the rx_win_size based on coex flag 838277b024eSKalle Valo */ 839277b024eSKalle Valo static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter, 840277b024eSKalle Valo bool coex_flag) 841277b024eSKalle Valo { 842277b024eSKalle Valo u8 i; 843277b024eSKalle Valo u32 rx_win_size; 844277b024eSKalle Valo struct mwifiex_private *priv; 845277b024eSKalle Valo 846277b024eSKalle Valo dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag); 847277b024eSKalle Valo 848277b024eSKalle Valo for (i = 0; i < adapter->priv_num; i++) { 849277b024eSKalle Valo if (!adapter->priv[i]) 850277b024eSKalle Valo continue; 851277b024eSKalle Valo priv = adapter->priv[i]; 852277b024eSKalle Valo rx_win_size = priv->add_ba_param.rx_win_size; 853277b024eSKalle Valo if (coex_flag) { 854277b024eSKalle Valo if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) 855277b024eSKalle Valo priv->add_ba_param.rx_win_size = 856277b024eSKalle Valo MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; 857277b024eSKalle Valo if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) 858277b024eSKalle Valo priv->add_ba_param.rx_win_size = 859277b024eSKalle Valo MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; 860277b024eSKalle Valo if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) 861277b024eSKalle Valo priv->add_ba_param.rx_win_size = 862277b024eSKalle Valo MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE; 863277b024eSKalle Valo } else { 864277b024eSKalle Valo if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) 865277b024eSKalle Valo priv->add_ba_param.rx_win_size = 866277b024eSKalle Valo MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; 867277b024eSKalle Valo if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) 868277b024eSKalle Valo priv->add_ba_param.rx_win_size = 869277b024eSKalle Valo MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; 870277b024eSKalle Valo if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) 871277b024eSKalle Valo priv->add_ba_param.rx_win_size = 872277b024eSKalle Valo MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; 873277b024eSKalle Valo } 874277b024eSKalle Valo 875277b024eSKalle Valo if (adapter->coex_win_size && adapter->coex_rx_win_size) 876277b024eSKalle Valo priv->add_ba_param.rx_win_size = 877277b024eSKalle Valo adapter->coex_rx_win_size; 878277b024eSKalle Valo 879277b024eSKalle Valo if (rx_win_size != priv->add_ba_param.rx_win_size) { 880277b024eSKalle Valo if (!priv->media_connected) 881277b024eSKalle Valo continue; 882277b024eSKalle Valo for (i = 0; i < MAX_NUM_TID; i++) 883277b024eSKalle Valo mwifiex_11n_delba(priv, i); 884277b024eSKalle Valo } 885277b024eSKalle Valo } 886277b024eSKalle Valo } 887277b024eSKalle Valo 888277b024eSKalle Valo /* This function check coex for RX BA 889277b024eSKalle Valo */ 890277b024eSKalle Valo void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter) 891277b024eSKalle Valo { 892277b024eSKalle Valo u8 i; 893277b024eSKalle Valo struct mwifiex_private *priv; 894277b024eSKalle Valo u8 count = 0; 895277b024eSKalle Valo 896277b024eSKalle Valo for (i = 0; i < adapter->priv_num; i++) { 897277b024eSKalle Valo if (adapter->priv[i]) { 898277b024eSKalle Valo priv = adapter->priv[i]; 899277b024eSKalle Valo if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { 900277b024eSKalle Valo if (priv->media_connected) 901277b024eSKalle Valo count++; 902277b024eSKalle Valo } 903277b024eSKalle Valo if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { 904277b024eSKalle Valo if (priv->bss_started) 905277b024eSKalle Valo count++; 906277b024eSKalle Valo } 907277b024eSKalle Valo } 908277b024eSKalle Valo if (count >= MWIFIEX_BSS_COEX_COUNT) 909277b024eSKalle Valo break; 910277b024eSKalle Valo } 911277b024eSKalle Valo if (count >= MWIFIEX_BSS_COEX_COUNT) 912277b024eSKalle Valo mwifiex_update_ampdu_rxwinsize(adapter, true); 913277b024eSKalle Valo else 914277b024eSKalle Valo mwifiex_update_ampdu_rxwinsize(adapter, false); 915277b024eSKalle Valo } 91699ffe72cSXinming Hu 91799ffe72cSXinming Hu /* This function handles rxba_sync event 91899ffe72cSXinming Hu */ 91999ffe72cSXinming Hu void mwifiex_11n_rxba_sync_event(struct mwifiex_private *priv, 92099ffe72cSXinming Hu u8 *event_buf, u16 len) 92199ffe72cSXinming Hu { 92299ffe72cSXinming Hu struct mwifiex_ie_types_rxba_sync *tlv_rxba = (void *)event_buf; 92399ffe72cSXinming Hu u16 tlv_type, tlv_len; 92499ffe72cSXinming Hu struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; 92599ffe72cSXinming Hu u8 i, j; 92699ffe72cSXinming Hu u16 seq_num, tlv_seq_num, tlv_bitmap_len; 92799ffe72cSXinming Hu int tlv_buf_left = len; 92899ffe72cSXinming Hu int ret; 92999ffe72cSXinming Hu u8 *tmp; 93099ffe72cSXinming Hu 93199ffe72cSXinming Hu mwifiex_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:", 93299ffe72cSXinming Hu event_buf, len); 93399ffe72cSXinming Hu while (tlv_buf_left >= sizeof(*tlv_rxba)) { 93499ffe72cSXinming Hu tlv_type = le16_to_cpu(tlv_rxba->header.type); 93599ffe72cSXinming Hu tlv_len = le16_to_cpu(tlv_rxba->header.len); 93699ffe72cSXinming Hu if (tlv_type != TLV_TYPE_RXBA_SYNC) { 93799ffe72cSXinming Hu mwifiex_dbg(priv->adapter, ERROR, 93899ffe72cSXinming Hu "Wrong TLV id=0x%x\n", tlv_type); 93999ffe72cSXinming Hu return; 94099ffe72cSXinming Hu } 94199ffe72cSXinming Hu 94299ffe72cSXinming Hu tlv_seq_num = le16_to_cpu(tlv_rxba->seq_num); 94399ffe72cSXinming Hu tlv_bitmap_len = le16_to_cpu(tlv_rxba->bitmap_len); 94499ffe72cSXinming Hu mwifiex_dbg(priv->adapter, INFO, 94599ffe72cSXinming Hu "%pM tid=%d seq_num=%d bitmap_len=%d\n", 94699ffe72cSXinming Hu tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num, 94799ffe72cSXinming Hu tlv_bitmap_len); 94899ffe72cSXinming Hu 94999ffe72cSXinming Hu rx_reor_tbl_ptr = 95099ffe72cSXinming Hu mwifiex_11n_get_rx_reorder_tbl(priv, tlv_rxba->tid, 95199ffe72cSXinming Hu tlv_rxba->mac); 95299ffe72cSXinming Hu if (!rx_reor_tbl_ptr) { 95399ffe72cSXinming Hu mwifiex_dbg(priv->adapter, ERROR, 95499ffe72cSXinming Hu "Can not find rx_reorder_tbl!"); 95599ffe72cSXinming Hu return; 95699ffe72cSXinming Hu } 95799ffe72cSXinming Hu 95899ffe72cSXinming Hu for (i = 0; i < tlv_bitmap_len; i++) { 95999ffe72cSXinming Hu for (j = 0 ; j < 8; j++) { 96099ffe72cSXinming Hu if (tlv_rxba->bitmap[i] & (1 << j)) { 96199ffe72cSXinming Hu seq_num = (MAX_TID_VALUE - 1) & 96299ffe72cSXinming Hu (tlv_seq_num + i * 8 + j); 96399ffe72cSXinming Hu 96499ffe72cSXinming Hu mwifiex_dbg(priv->adapter, ERROR, 96599ffe72cSXinming Hu "drop packet,seq=%d\n", 96699ffe72cSXinming Hu seq_num); 96799ffe72cSXinming Hu 96899ffe72cSXinming Hu ret = mwifiex_11n_rx_reorder_pkt 96999ffe72cSXinming Hu (priv, seq_num, tlv_rxba->tid, 97099ffe72cSXinming Hu tlv_rxba->mac, 0, NULL); 97199ffe72cSXinming Hu 97299ffe72cSXinming Hu if (ret) 97399ffe72cSXinming Hu mwifiex_dbg(priv->adapter, 97499ffe72cSXinming Hu ERROR, 97599ffe72cSXinming Hu "Fail to drop packet"); 97699ffe72cSXinming Hu } 97799ffe72cSXinming Hu } 97899ffe72cSXinming Hu } 97999ffe72cSXinming Hu 98099ffe72cSXinming Hu tlv_buf_left -= (sizeof(*tlv_rxba) + tlv_len); 98199ffe72cSXinming Hu tmp = (u8 *)tlv_rxba + tlv_len + sizeof(*tlv_rxba); 98299ffe72cSXinming Hu tlv_rxba = (struct mwifiex_ie_types_rxba_sync *)tmp; 98399ffe72cSXinming Hu } 98499ffe72cSXinming Hu } 985