15e6e3a92SBing Zhao /* 25e6e3a92SBing Zhao * Marvell Wireless LAN device driver: 802.11n RX Re-ordering 35e6e3a92SBing Zhao * 465da33f5SXinming Hu * Copyright (C) 2011-2014, Marvell International Ltd. 55e6e3a92SBing Zhao * 65e6e3a92SBing Zhao * This software file (the "File") is distributed by Marvell International 75e6e3a92SBing Zhao * Ltd. under the terms of the GNU General Public License Version 2, June 1991 85e6e3a92SBing Zhao * (the "License"). You may use, redistribute and/or modify this File in 95e6e3a92SBing Zhao * accordance with the terms and conditions of the License, a copy of which 105e6e3a92SBing Zhao * is available by writing to the Free Software Foundation, Inc., 115e6e3a92SBing Zhao * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 125e6e3a92SBing Zhao * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 135e6e3a92SBing Zhao * 145e6e3a92SBing Zhao * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 155e6e3a92SBing Zhao * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 165e6e3a92SBing Zhao * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 175e6e3a92SBing Zhao * this warranty disclaimer. 185e6e3a92SBing Zhao */ 195e6e3a92SBing Zhao 205e6e3a92SBing Zhao #include "decl.h" 215e6e3a92SBing Zhao #include "ioctl.h" 225e6e3a92SBing Zhao #include "util.h" 235e6e3a92SBing Zhao #include "fw.h" 245e6e3a92SBing Zhao #include "main.h" 255e6e3a92SBing Zhao #include "wmm.h" 265e6e3a92SBing Zhao #include "11n.h" 275e6e3a92SBing Zhao #include "11n_rxreorder.h" 285e6e3a92SBing Zhao 294c9f9fb2SAmitkumar Karwar /* This function will dispatch amsdu packet and forward it to kernel/upper 304c9f9fb2SAmitkumar Karwar * layer. 314c9f9fb2SAmitkumar Karwar */ 324c9f9fb2SAmitkumar Karwar static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, 334c9f9fb2SAmitkumar Karwar struct sk_buff *skb) 344c9f9fb2SAmitkumar Karwar { 354c9f9fb2SAmitkumar Karwar struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); 364c9f9fb2SAmitkumar Karwar int ret; 374c9f9fb2SAmitkumar Karwar 384c9f9fb2SAmitkumar Karwar if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { 394c9f9fb2SAmitkumar Karwar struct sk_buff_head list; 404c9f9fb2SAmitkumar Karwar struct sk_buff *rx_skb; 414c9f9fb2SAmitkumar Karwar 424c9f9fb2SAmitkumar Karwar __skb_queue_head_init(&list); 434c9f9fb2SAmitkumar Karwar 444c9f9fb2SAmitkumar Karwar skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); 454c9f9fb2SAmitkumar Karwar skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); 464c9f9fb2SAmitkumar Karwar 474c9f9fb2SAmitkumar Karwar ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, 488b935ee2SJohannes Berg priv->wdev.iftype, 0, NULL, NULL); 494c9f9fb2SAmitkumar Karwar 504c9f9fb2SAmitkumar Karwar while (!skb_queue_empty(&list)) { 51776f7420SAmitkumar Karwar struct rx_packet_hdr *rx_hdr; 52776f7420SAmitkumar Karwar 534c9f9fb2SAmitkumar Karwar 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 654c9f9fb2SAmitkumar Karwar ret = mwifiex_recv_packet(priv, rx_skb); 664c9f9fb2SAmitkumar Karwar if (ret == -1) 67acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, ERROR, 684c9f9fb2SAmitkumar Karwar "Rx of A-MSDU failed"); 694c9f9fb2SAmitkumar Karwar } 704c9f9fb2SAmitkumar Karwar return 0; 714c9f9fb2SAmitkumar Karwar } 724c9f9fb2SAmitkumar Karwar 734c9f9fb2SAmitkumar Karwar return -1; 744c9f9fb2SAmitkumar Karwar } 754c9f9fb2SAmitkumar Karwar 765e6e43ebSAmitkumar Karwar /* This function will process the rx packet and forward it to kernel/upper 775e6e43ebSAmitkumar Karwar * layer. 785e6e43ebSAmitkumar Karwar */ 795e6e43ebSAmitkumar Karwar static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) 805e6e43ebSAmitkumar Karwar { 814c9f9fb2SAmitkumar Karwar 8299ffe72cSXinming Hu int ret; 8399ffe72cSXinming Hu 8499ffe72cSXinming Hu if (!payload) { 8599ffe72cSXinming Hu mwifiex_dbg(priv->adapter, INFO, "info: fw drop data\n"); 8699ffe72cSXinming Hu return 0; 8799ffe72cSXinming Hu } 8899ffe72cSXinming Hu 8999ffe72cSXinming Hu ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload); 904c9f9fb2SAmitkumar Karwar if (!ret) 914c9f9fb2SAmitkumar Karwar return 0; 924c9f9fb2SAmitkumar Karwar 935e6e43ebSAmitkumar Karwar if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) 945e6e43ebSAmitkumar Karwar return mwifiex_handle_uap_rx_forward(priv, payload); 955e6e43ebSAmitkumar Karwar 965e6e43ebSAmitkumar Karwar return mwifiex_process_rx_packet(priv, payload); 975e6e43ebSAmitkumar Karwar } 985e6e43ebSAmitkumar Karwar 995e6e3a92SBing Zhao /* 1008c00228eSYogesh Ashok Powar * This function dispatches all packets in the Rx reorder table until the 1018c00228eSYogesh Ashok Powar * start window. 1025e6e3a92SBing Zhao * 1035e6e3a92SBing Zhao * There could be holes in the buffer, which are skipped by the function. 1045e6e3a92SBing Zhao * Since the buffer is linear, the function uses rotation to simulate 1055e6e3a92SBing Zhao * circular buffer. 1065e6e3a92SBing Zhao */ 107ab581472SYogesh Ashok Powar static void 1085e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, 1095e6e43ebSAmitkumar Karwar struct mwifiex_rx_reorder_tbl *tbl, 1105e6e43ebSAmitkumar Karwar int start_win) 1115e6e3a92SBing Zhao { 1128c00228eSYogesh Ashok Powar int pkt_to_send, i; 113270e58e8SYogesh Ashok Powar void *rx_tmp_ptr; 1145e6e3a92SBing Zhao unsigned long flags; 1155e6e3a92SBing Zhao 1168c00228eSYogesh Ashok Powar pkt_to_send = (start_win > tbl->start_win) ? 1178c00228eSYogesh Ashok Powar min((start_win - tbl->start_win), tbl->win_size) : 1188c00228eSYogesh Ashok Powar tbl->win_size; 1195e6e3a92SBing Zhao 1208c00228eSYogesh Ashok Powar for (i = 0; i < pkt_to_send; ++i) { 1215e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_pkt_lock, flags); 1225e6e3a92SBing Zhao rx_tmp_ptr = NULL; 1238c00228eSYogesh Ashok Powar if (tbl->rx_reorder_ptr[i]) { 1248c00228eSYogesh Ashok Powar rx_tmp_ptr = tbl->rx_reorder_ptr[i]; 1258c00228eSYogesh Ashok Powar tbl->rx_reorder_ptr[i] = NULL; 1265e6e3a92SBing Zhao } 1275e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); 1285e6e43ebSAmitkumar Karwar if (rx_tmp_ptr) 1295e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); 1305e6e3a92SBing Zhao } 1315e6e3a92SBing Zhao 1325e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_pkt_lock, flags); 1335e6e3a92SBing Zhao /* 1345e6e3a92SBing Zhao * We don't have a circular buffer, hence use rotation to simulate 1355e6e3a92SBing Zhao * circular buffer 1365e6e3a92SBing Zhao */ 1378c00228eSYogesh Ashok Powar for (i = 0; i < tbl->win_size - pkt_to_send; ++i) { 1388c00228eSYogesh Ashok Powar tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i]; 1398c00228eSYogesh Ashok Powar tbl->rx_reorder_ptr[pkt_to_send + i] = NULL; 1405e6e3a92SBing Zhao } 1415e6e3a92SBing Zhao 1428c00228eSYogesh Ashok Powar tbl->start_win = start_win; 1435e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); 1445e6e3a92SBing Zhao } 1455e6e3a92SBing Zhao 1465e6e3a92SBing Zhao /* 1475e6e3a92SBing Zhao * This function dispatches all packets in the Rx reorder table until 1485e6e3a92SBing Zhao * a hole is found. 1495e6e3a92SBing Zhao * 1505e6e3a92SBing Zhao * The start window is adjusted automatically when a hole is located. 1515e6e3a92SBing Zhao * Since the buffer is linear, the function uses rotation to simulate 1525e6e3a92SBing Zhao * circular buffer. 1535e6e3a92SBing Zhao */ 154ab581472SYogesh Ashok Powar static void 1555e6e3a92SBing Zhao mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, 1568c00228eSYogesh Ashok Powar struct mwifiex_rx_reorder_tbl *tbl) 1575e6e3a92SBing Zhao { 1585e6e3a92SBing Zhao int i, j, xchg; 159270e58e8SYogesh Ashok Powar void *rx_tmp_ptr; 1605e6e3a92SBing Zhao unsigned long flags; 1615e6e3a92SBing Zhao 1628c00228eSYogesh Ashok Powar for (i = 0; i < tbl->win_size; ++i) { 1635e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_pkt_lock, flags); 1648c00228eSYogesh Ashok Powar if (!tbl->rx_reorder_ptr[i]) { 1655e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); 1665e6e3a92SBing Zhao break; 1675e6e3a92SBing Zhao } 1688c00228eSYogesh Ashok Powar rx_tmp_ptr = tbl->rx_reorder_ptr[i]; 1698c00228eSYogesh Ashok Powar tbl->rx_reorder_ptr[i] = NULL; 1705e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); 1715e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); 1725e6e3a92SBing Zhao } 1735e6e3a92SBing Zhao 1745e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_pkt_lock, flags); 1755e6e3a92SBing Zhao /* 1765e6e3a92SBing Zhao * We don't have a circular buffer, hence use rotation to simulate 1775e6e3a92SBing Zhao * circular buffer 1785e6e3a92SBing Zhao */ 1795e6e3a92SBing Zhao if (i > 0) { 1808c00228eSYogesh Ashok Powar xchg = tbl->win_size - i; 1815e6e3a92SBing Zhao for (j = 0; j < xchg; ++j) { 1828c00228eSYogesh Ashok Powar tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j]; 1838c00228eSYogesh Ashok Powar tbl->rx_reorder_ptr[i + j] = NULL; 1845e6e3a92SBing Zhao } 1855e6e3a92SBing Zhao } 1868c00228eSYogesh Ashok Powar tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); 1875e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); 1885e6e3a92SBing Zhao } 1895e6e3a92SBing Zhao 1905e6e3a92SBing Zhao /* 1915e6e3a92SBing Zhao * This function deletes the Rx reorder table and frees the memory. 1925e6e3a92SBing Zhao * 1935e6e3a92SBing Zhao * The function stops the associated timer and dispatches all the 1945e6e3a92SBing Zhao * pending packets in the Rx reorder table before deletion. 1955e6e3a92SBing Zhao */ 1965e6e3a92SBing Zhao static void 1978c00228eSYogesh Ashok Powar mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, 1988c00228eSYogesh Ashok Powar struct mwifiex_rx_reorder_tbl *tbl) 1995e6e3a92SBing Zhao { 2005e6e3a92SBing Zhao unsigned long flags; 20163410c37SAmitkumar Karwar int start_win; 2025e6e3a92SBing Zhao 2038c00228eSYogesh Ashok Powar if (!tbl) 2045e6e3a92SBing Zhao return; 2055e6e3a92SBing Zhao 2066e251174SAvinash Patil spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); 2076e251174SAvinash Patil priv->adapter->rx_locked = true; 2086e251174SAvinash Patil if (priv->adapter->rx_processing) { 2096e251174SAvinash Patil spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); 2106e251174SAvinash Patil flush_workqueue(priv->adapter->rx_workqueue); 2116e251174SAvinash Patil } else { 2126e251174SAvinash Patil spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); 2136e251174SAvinash Patil } 2146e251174SAvinash Patil 21563410c37SAmitkumar Karwar start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); 2165e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); 2175e6e3a92SBing Zhao 218629873f2SAvinash Patil del_timer_sync(&tbl->timer_context.timer); 2193a8fede1SMarc Yang tbl->timer_context.timer_is_set = false; 2205e6e3a92SBing Zhao 2215e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); 2228c00228eSYogesh Ashok Powar list_del(&tbl->list); 2235e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); 2245e6e3a92SBing Zhao 2258c00228eSYogesh Ashok Powar kfree(tbl->rx_reorder_ptr); 2268c00228eSYogesh Ashok Powar kfree(tbl); 2276e251174SAvinash Patil 2286e251174SAvinash Patil spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); 2296e251174SAvinash Patil priv->adapter->rx_locked = false; 2306e251174SAvinash Patil spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); 2316e251174SAvinash Patil 2325e6e3a92SBing Zhao } 2335e6e3a92SBing Zhao 2345e6e3a92SBing Zhao /* 2355e6e3a92SBing Zhao * This function returns the pointer to an entry in Rx reordering 2365e6e3a92SBing Zhao * table which matches the given TA/TID pair. 2375e6e3a92SBing Zhao */ 238d1cf3b95SAvinash Patil struct mwifiex_rx_reorder_tbl * 2395e6e3a92SBing Zhao mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) 2405e6e3a92SBing Zhao { 2418c00228eSYogesh Ashok Powar struct mwifiex_rx_reorder_tbl *tbl; 2425e6e3a92SBing Zhao unsigned long flags; 2435e6e3a92SBing Zhao 2445e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); 2458c00228eSYogesh Ashok Powar list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { 2468c00228eSYogesh Ashok Powar if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { 2475e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, 2485e6e3a92SBing Zhao flags); 2498c00228eSYogesh Ashok Powar return tbl; 2505e6e3a92SBing Zhao } 2515e6e3a92SBing Zhao } 2525e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); 2535e6e3a92SBing Zhao 2545e6e3a92SBing Zhao return NULL; 2555e6e3a92SBing Zhao } 2565e6e3a92SBing Zhao 2573e238a11SAvinash Patil /* This function retrieves the pointer to an entry in Rx reordering 2583e238a11SAvinash Patil * table which matches the given TA and deletes it. 2593e238a11SAvinash Patil */ 2603e238a11SAvinash Patil void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) 2613e238a11SAvinash Patil { 2623e238a11SAvinash Patil struct mwifiex_rx_reorder_tbl *tbl, *tmp; 2633e238a11SAvinash Patil unsigned long flags; 2643e238a11SAvinash Patil 2653e238a11SAvinash Patil if (!ta) 2663e238a11SAvinash Patil return; 2673e238a11SAvinash Patil 2683e238a11SAvinash Patil spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); 2693e238a11SAvinash Patil list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { 2703e238a11SAvinash Patil if (!memcmp(tbl->ta, ta, ETH_ALEN)) { 2713e238a11SAvinash Patil spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, 2723e238a11SAvinash Patil flags); 2733e238a11SAvinash Patil mwifiex_del_rx_reorder_entry(priv, tbl); 2743e238a11SAvinash Patil spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); 2753e238a11SAvinash Patil } 2763e238a11SAvinash Patil } 2773e238a11SAvinash Patil spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); 2783e238a11SAvinash Patil 2793e238a11SAvinash Patil return; 2803e238a11SAvinash Patil } 2813e238a11SAvinash Patil 2825e6e3a92SBing Zhao /* 2835e6e3a92SBing Zhao * This function finds the last sequence number used in the packets 2845e6e3a92SBing Zhao * buffered in Rx reordering table. 2855e6e3a92SBing Zhao */ 2865e6e3a92SBing Zhao static int 2872d702830SAmitkumar Karwar mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) 2885e6e3a92SBing Zhao { 2892d702830SAmitkumar Karwar struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; 2902d702830SAmitkumar Karwar struct mwifiex_private *priv = ctx->priv; 2912d702830SAmitkumar Karwar unsigned long flags; 2925e6e3a92SBing Zhao int i; 2935e6e3a92SBing Zhao 2942d702830SAmitkumar Karwar spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); 2952d702830SAmitkumar Karwar for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { 2962d702830SAmitkumar Karwar if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { 2972d702830SAmitkumar Karwar spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, 2982d702830SAmitkumar Karwar flags); 2995e6e3a92SBing Zhao return i; 3002d702830SAmitkumar Karwar } 3012d702830SAmitkumar Karwar } 3022d702830SAmitkumar Karwar spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); 3035e6e3a92SBing Zhao 3045e6e3a92SBing Zhao return -1; 3055e6e3a92SBing Zhao } 3065e6e3a92SBing Zhao 3075e6e3a92SBing Zhao /* 3085e6e3a92SBing Zhao * This function flushes all the packets in Rx reordering table. 3095e6e3a92SBing Zhao * 3105e6e3a92SBing Zhao * The function checks if any packets are currently buffered in the 3115e6e3a92SBing Zhao * table or not. In case there are packets available, it dispatches 3125e6e3a92SBing Zhao * them and then dumps the Rx reordering table. 3135e6e3a92SBing Zhao */ 3145e6e3a92SBing Zhao static void 31508c2eb8eSKees Cook mwifiex_flush_data(struct timer_list *t) 3165e6e3a92SBing Zhao { 3178c00228eSYogesh Ashok Powar struct reorder_tmr_cnxt *ctx = 31808c2eb8eSKees Cook from_timer(ctx, t, timer); 31963410c37SAmitkumar Karwar int start_win, seq_num; 3205e6e3a92SBing Zhao 3213a8fede1SMarc Yang ctx->timer_is_set = false; 3222d702830SAmitkumar Karwar seq_num = mwifiex_11n_find_last_seq_num(ctx); 323bb7de2baSYogesh Ashok Powar 32463410c37SAmitkumar Karwar if (seq_num < 0) 325bb7de2baSYogesh Ashok Powar return; 326bb7de2baSYogesh Ashok Powar 327acebe8c1SZhaoyang Liu mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num); 32863410c37SAmitkumar Karwar start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); 3295e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, 3305e6e43ebSAmitkumar Karwar start_win); 3315e6e3a92SBing Zhao } 3325e6e3a92SBing Zhao 3335e6e3a92SBing Zhao /* 3345e6e3a92SBing Zhao * This function creates an entry in Rx reordering table for the 3355e6e3a92SBing Zhao * given TA/TID. 3365e6e3a92SBing Zhao * 3375e6e3a92SBing Zhao * The function also initializes the entry with sequence number, window 3385e6e3a92SBing Zhao * size as well as initializes the timer. 3395e6e3a92SBing Zhao * 3405e6e3a92SBing Zhao * If the received TA/TID pair is already present, all the packets are 3415e6e3a92SBing Zhao * dispatched and the window size is moved until the SSN. 3425e6e3a92SBing Zhao */ 3435e6e3a92SBing Zhao static void 3445e6e3a92SBing Zhao mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, 3455e6e3a92SBing Zhao int tid, int win_size, int seq_num) 3465e6e3a92SBing Zhao { 3475e6e3a92SBing Zhao int i; 3488c00228eSYogesh Ashok Powar struct mwifiex_rx_reorder_tbl *tbl, *new_node; 3495e6e3a92SBing Zhao u16 last_seq = 0; 3505e6e3a92SBing Zhao unsigned long flags; 3515a009adfSAvinash Patil struct mwifiex_sta_node *node; 3525e6e3a92SBing Zhao 3535e6e3a92SBing Zhao /* 3545e6e3a92SBing Zhao * If we get a TID, ta pair which is already present dispatch all the 3555e6e3a92SBing Zhao * the packets and move the window size until the ssn 3565e6e3a92SBing Zhao */ 3578c00228eSYogesh Ashok Powar tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); 3588c00228eSYogesh Ashok Powar if (tbl) { 3595e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); 3605e6e3a92SBing Zhao return; 3615e6e3a92SBing Zhao } 3628c00228eSYogesh Ashok Powar /* if !tbl then create one */ 3635e6e3a92SBing Zhao new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); 3640d2e7a5cSJoe Perches if (!new_node) 3655e6e3a92SBing Zhao return; 3665e6e3a92SBing Zhao 3675e6e3a92SBing Zhao INIT_LIST_HEAD(&new_node->list); 3685e6e3a92SBing Zhao new_node->tid = tid; 3695e6e3a92SBing Zhao memcpy(new_node->ta, ta, ETH_ALEN); 3705e6e3a92SBing Zhao new_node->start_win = seq_num; 3718acbea61SPaul Stewart new_node->init_win = seq_num; 3728acbea61SPaul Stewart new_node->flags = 0; 3735a009adfSAvinash Patil 374c11fb985SAvinash Patil spin_lock_irqsave(&priv->sta_list_spinlock, flags); 3755a009adfSAvinash Patil if (mwifiex_queuing_ra_based(priv)) { 3765a009adfSAvinash Patil if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { 3775a009adfSAvinash Patil node = mwifiex_get_sta_entry(priv, ta); 3785a009adfSAvinash Patil if (node) 3795a009adfSAvinash Patil last_seq = node->rx_seq[tid]; 3805a009adfSAvinash Patil } 3815a009adfSAvinash Patil } else { 382daeb5bb4SAvinash Patil node = mwifiex_get_sta_entry(priv, ta); 383daeb5bb4SAvinash Patil if (node) 384daeb5bb4SAvinash Patil last_seq = node->rx_seq[tid]; 385daeb5bb4SAvinash Patil else 3865e6e3a92SBing Zhao last_seq = priv->rx_seq[tid]; 3875a009adfSAvinash Patil } 388c11fb985SAvinash Patil spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); 3895e6e3a92SBing Zhao 390acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, INFO, 391acebe8c1SZhaoyang Liu "info: last_seq=%d start_win=%d\n", 39209bd1976SAvinash Patil last_seq, new_node->start_win); 39309bd1976SAvinash Patil 39492583924SStone Piao if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && 3958acbea61SPaul Stewart last_seq >= new_node->start_win) { 3965e6e3a92SBing Zhao new_node->start_win = last_seq + 1; 3978acbea61SPaul Stewart new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; 3988acbea61SPaul Stewart } 3995e6e3a92SBing Zhao 4005e6e3a92SBing Zhao new_node->win_size = win_size; 4015e6e3a92SBing Zhao 402*6396bb22SKees Cook new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *), 4035e6e3a92SBing Zhao GFP_KERNEL); 4045e6e3a92SBing Zhao if (!new_node->rx_reorder_ptr) { 4055e6e3a92SBing Zhao kfree((u8 *) new_node); 406acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, ERROR, 4075e6e3a92SBing Zhao "%s: failed to alloc reorder_ptr\n", __func__); 4085e6e3a92SBing Zhao return; 4095e6e3a92SBing Zhao } 4105e6e3a92SBing Zhao 4115e6e3a92SBing Zhao new_node->timer_context.ptr = new_node; 4125e6e3a92SBing Zhao new_node->timer_context.priv = priv; 4133a8fede1SMarc Yang new_node->timer_context.timer_is_set = false; 4145e6e3a92SBing Zhao 41508c2eb8eSKees Cook timer_setup(&new_node->timer_context.timer, mwifiex_flush_data, 0); 4165e6e3a92SBing Zhao 4175e6e3a92SBing Zhao for (i = 0; i < win_size; ++i) 4185e6e3a92SBing Zhao new_node->rx_reorder_ptr[i] = NULL; 4195e6e3a92SBing Zhao 4205e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); 4215e6e3a92SBing Zhao list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); 4225e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); 4235e6e3a92SBing Zhao } 4245e6e3a92SBing Zhao 4253a8fede1SMarc Yang static void 4263a8fede1SMarc Yang mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl) 4273a8fede1SMarc Yang { 4283a8fede1SMarc Yang u32 min_flush_time; 4293a8fede1SMarc Yang 4303a8fede1SMarc Yang if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32) 4313a8fede1SMarc Yang min_flush_time = MIN_FLUSH_TIMER_15_MS; 4323a8fede1SMarc Yang else 4333a8fede1SMarc Yang min_flush_time = MIN_FLUSH_TIMER_MS; 4343a8fede1SMarc Yang 4353a8fede1SMarc Yang mod_timer(&tbl->timer_context.timer, 4363a8fede1SMarc Yang jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size)); 4373a8fede1SMarc Yang 4383a8fede1SMarc Yang tbl->timer_context.timer_is_set = true; 4393a8fede1SMarc Yang } 4403a8fede1SMarc Yang 4415e6e3a92SBing Zhao /* 4425e6e3a92SBing Zhao * This function prepares command for adding a BA request. 4435e6e3a92SBing Zhao * 4445e6e3a92SBing Zhao * Preparation includes - 4455e6e3a92SBing Zhao * - Setting command ID and proper size 4465e6e3a92SBing Zhao * - Setting add BA request buffer 4475e6e3a92SBing Zhao * - Ensuring correct endian-ness 4485e6e3a92SBing Zhao */ 449572e8f3eSAmitkumar Karwar int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) 4505e6e3a92SBing Zhao { 4512c208890SJoe Perches struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; 4525e6e3a92SBing Zhao 4535e6e3a92SBing Zhao cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); 4545e6e3a92SBing Zhao cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); 4555e6e3a92SBing Zhao memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); 4565e6e3a92SBing Zhao 4575e6e3a92SBing Zhao return 0; 4585e6e3a92SBing Zhao } 4595e6e3a92SBing Zhao 4605e6e3a92SBing Zhao /* 4615e6e3a92SBing Zhao * This function prepares command for adding a BA response. 4625e6e3a92SBing Zhao * 4635e6e3a92SBing Zhao * Preparation includes - 4645e6e3a92SBing Zhao * - Setting command ID and proper size 4655e6e3a92SBing Zhao * - Setting add BA response buffer 4665e6e3a92SBing Zhao * - Ensuring correct endian-ness 4675e6e3a92SBing Zhao */ 4685e6e3a92SBing Zhao int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, 4695e6e3a92SBing Zhao struct host_cmd_ds_command *cmd, 470a5ffddb7SAmitkumar Karwar struct host_cmd_ds_11n_addba_req 471a5ffddb7SAmitkumar Karwar *cmd_addba_req) 4725e6e3a92SBing Zhao { 4732c208890SJoe Perches struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; 474b06c5321SAvinash Patil struct mwifiex_sta_node *sta_ptr; 475b06c5321SAvinash Patil u32 rx_win_size = priv->add_ba_param.rx_win_size; 476270e58e8SYogesh Ashok Powar u8 tid; 477270e58e8SYogesh Ashok Powar int win_size; 478c11fb985SAvinash Patil unsigned long flags; 4795e6e3a92SBing Zhao uint16_t block_ack_param_set; 4805e6e3a92SBing Zhao 481b06c5321SAvinash Patil if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && 482b06c5321SAvinash Patil ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && 483b06c5321SAvinash Patil priv->adapter->is_hw_11ac_capable && 484b06c5321SAvinash Patil memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { 485c11fb985SAvinash Patil spin_lock_irqsave(&priv->sta_list_spinlock, flags); 486b06c5321SAvinash Patil sta_ptr = mwifiex_get_sta_entry(priv, 487b06c5321SAvinash Patil cmd_addba_req->peer_mac_addr); 488b06c5321SAvinash Patil if (!sta_ptr) { 48909bd1976SAvinash Patil spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); 490acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, ERROR, 491b06c5321SAvinash Patil "BA setup with unknown TDLS peer %pM!\n", 492b06c5321SAvinash Patil cmd_addba_req->peer_mac_addr); 493b06c5321SAvinash Patil return -1; 494b06c5321SAvinash Patil } 495b06c5321SAvinash Patil if (sta_ptr->is_11ac_enabled) 496b06c5321SAvinash Patil rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; 497c11fb985SAvinash Patil spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); 498b06c5321SAvinash Patil } 499b06c5321SAvinash Patil 5005e6e3a92SBing Zhao cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); 5015e6e3a92SBing Zhao cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); 5025e6e3a92SBing Zhao 5035e6e3a92SBing Zhao memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, 5045e6e3a92SBing Zhao ETH_ALEN); 5055e6e3a92SBing Zhao add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; 5065e6e3a92SBing Zhao add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; 5075e6e3a92SBing Zhao add_ba_rsp->ssn = cmd_addba_req->ssn; 5085e6e3a92SBing Zhao 5095e6e3a92SBing Zhao block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); 5105e6e3a92SBing Zhao tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) 5115e6e3a92SBing Zhao >> BLOCKACKPARAM_TID_POS; 5125e6e3a92SBing Zhao add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); 5135e6e3a92SBing Zhao block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; 5144c9f9fb2SAmitkumar Karwar 5154c9f9fb2SAmitkumar Karwar /* If we don't support AMSDU inside AMPDU, reset the bit */ 5164c9f9fb2SAmitkumar Karwar if (!priv->add_ba_param.rx_amsdu || 5174c9f9fb2SAmitkumar Karwar (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) 5185e6e3a92SBing Zhao block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; 519b06c5321SAvinash Patil block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; 5205e6e3a92SBing Zhao add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); 5215e6e3a92SBing Zhao win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) 5225e6e3a92SBing Zhao & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) 5235e6e3a92SBing Zhao >> BLOCKACKPARAM_WINSIZE_POS; 5245e6e3a92SBing Zhao cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); 5255e6e3a92SBing Zhao 5265e6e3a92SBing Zhao mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, 52784266841SYogesh Ashok Powar tid, win_size, 52884266841SYogesh Ashok Powar le16_to_cpu(cmd_addba_req->ssn)); 5295e6e3a92SBing Zhao return 0; 5305e6e3a92SBing Zhao } 5315e6e3a92SBing Zhao 5325e6e3a92SBing Zhao /* 5335e6e3a92SBing Zhao * This function prepares command for deleting a BA request. 5345e6e3a92SBing Zhao * 5355e6e3a92SBing Zhao * Preparation includes - 5365e6e3a92SBing Zhao * - Setting command ID and proper size 5375e6e3a92SBing Zhao * - Setting del BA request buffer 5385e6e3a92SBing Zhao * - Ensuring correct endian-ness 5395e6e3a92SBing Zhao */ 540572e8f3eSAmitkumar Karwar int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) 5415e6e3a92SBing Zhao { 5422c208890SJoe Perches struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; 5435e6e3a92SBing Zhao 5445e6e3a92SBing Zhao cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); 5455e6e3a92SBing Zhao cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); 5465e6e3a92SBing Zhao memcpy(del_ba, data_buf, sizeof(*del_ba)); 5475e6e3a92SBing Zhao 5485e6e3a92SBing Zhao return 0; 5495e6e3a92SBing Zhao } 5505e6e3a92SBing Zhao 5515e6e3a92SBing Zhao /* 5525e6e3a92SBing Zhao * This function identifies if Rx reordering is needed for a received packet. 5535e6e3a92SBing Zhao * 5545e6e3a92SBing Zhao * In case reordering is required, the function will do the reordering 5555e6e3a92SBing Zhao * before sending it to kernel. 5565e6e3a92SBing Zhao * 5575e6e3a92SBing Zhao * The Rx reorder table is checked first with the received TID/TA pair. If 5585e6e3a92SBing Zhao * not found, the received packet is dispatched immediately. But if found, 5595e6e3a92SBing Zhao * the packet is reordered and all the packets in the updated Rx reordering 5605e6e3a92SBing Zhao * table is dispatched until a hole is found. 5615e6e3a92SBing Zhao * 5625e6e3a92SBing Zhao * For sequence number less than the starting window, the packet is dropped. 5635e6e3a92SBing Zhao */ 5645e6e3a92SBing Zhao int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, 5655e6e3a92SBing Zhao u16 seq_num, u16 tid, 5665e6e3a92SBing Zhao u8 *ta, u8 pkt_type, void *payload) 5675e6e3a92SBing Zhao { 5688c00228eSYogesh Ashok Powar struct mwifiex_rx_reorder_tbl *tbl; 5693a8fede1SMarc Yang int prev_start_win, start_win, end_win, win_size; 570270e58e8SYogesh Ashok Powar u16 pkt_index; 5718acbea61SPaul Stewart bool init_window_shift = false; 5723a8fede1SMarc Yang int ret = 0; 5735e6e3a92SBing Zhao 5742c208890SJoe Perches tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); 5758c00228eSYogesh Ashok Powar if (!tbl) { 5765e6e43ebSAmitkumar Karwar if (pkt_type != PKT_TYPE_BAR) 5775e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt(priv, payload); 5783a8fede1SMarc Yang return ret; 5795e6e3a92SBing Zhao } 5804c9f9fb2SAmitkumar Karwar 5814c9f9fb2SAmitkumar Karwar if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) { 5824c9f9fb2SAmitkumar Karwar mwifiex_11n_dispatch_pkt(priv, payload); 5833a8fede1SMarc Yang return ret; 5844c9f9fb2SAmitkumar Karwar } 5854c9f9fb2SAmitkumar Karwar 5868c00228eSYogesh Ashok Powar start_win = tbl->start_win; 5873a8fede1SMarc Yang prev_start_win = start_win; 5888c00228eSYogesh Ashok Powar win_size = tbl->win_size; 5895e6e3a92SBing Zhao end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); 5908acbea61SPaul Stewart if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { 5918acbea61SPaul Stewart init_window_shift = true; 5928acbea61SPaul Stewart tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; 5938acbea61SPaul Stewart } 5945e6e3a92SBing Zhao 5952db96c3dSAvinash Patil if (tbl->flags & RXREOR_FORCE_NO_DROP) { 596acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, INFO, 5972db96c3dSAvinash Patil "RXREOR_FORCE_NO_DROP when HS is activated\n"); 5982db96c3dSAvinash Patil tbl->flags &= ~RXREOR_FORCE_NO_DROP; 5998acbea61SPaul Stewart } else if (init_window_shift && seq_num < start_win && 6008acbea61SPaul Stewart seq_num >= tbl->init_win) { 601acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, INFO, 6028acbea61SPaul Stewart "Sender TID sequence number reset %d->%d for SSN %d\n", 6038acbea61SPaul Stewart start_win, seq_num, tbl->init_win); 6048acbea61SPaul Stewart tbl->start_win = start_win = seq_num; 6058acbea61SPaul Stewart end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); 6062db96c3dSAvinash Patil } else { 6078acbea61SPaul Stewart /* 6088acbea61SPaul Stewart * If seq_num is less then starting win then ignore and drop 6098acbea61SPaul Stewart * the packet 6108acbea61SPaul Stewart */ 6112db96c3dSAvinash Patil if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { 61284266841SYogesh Ashok Powar if (seq_num >= ((start_win + TWOPOW11) & 6132db96c3dSAvinash Patil (MAX_TID_VALUE - 1)) && 6143a8fede1SMarc Yang seq_num < start_win) { 6153a8fede1SMarc Yang ret = -1; 6163a8fede1SMarc Yang goto done; 6173a8fede1SMarc Yang } 61884266841SYogesh Ashok Powar } else if ((seq_num < start_win) || 6193a8fede1SMarc Yang (seq_num >= (start_win + TWOPOW11))) { 6203a8fede1SMarc Yang ret = -1; 6213a8fede1SMarc Yang goto done; 6225e6e3a92SBing Zhao } 6232db96c3dSAvinash Patil } 6245e6e3a92SBing Zhao 6255e6e3a92SBing Zhao /* 6265e6e3a92SBing Zhao * If this packet is a BAR we adjust seq_num as 6275e6e3a92SBing Zhao * WinStart = seq_num 6285e6e3a92SBing Zhao */ 6295e6e3a92SBing Zhao if (pkt_type == PKT_TYPE_BAR) 6305e6e3a92SBing Zhao seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); 6315e6e3a92SBing Zhao 63284266841SYogesh Ashok Powar if (((end_win < start_win) && 6332db96c3dSAvinash Patil (seq_num < start_win) && (seq_num > end_win)) || 63484266841SYogesh Ashok Powar ((end_win > start_win) && ((seq_num > end_win) || 63584266841SYogesh Ashok Powar (seq_num < start_win)))) { 6365e6e3a92SBing Zhao end_win = seq_num; 637cc751498SAndrzej Hajda if (((end_win - win_size) + 1) >= 0) 6385e6e3a92SBing Zhao start_win = (end_win - win_size) + 1; 6395e6e3a92SBing Zhao else 640cc751498SAndrzej Hajda start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1; 6415e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); 6425e6e3a92SBing Zhao } 6435e6e3a92SBing Zhao 6445e6e3a92SBing Zhao if (pkt_type != PKT_TYPE_BAR) { 6455e6e3a92SBing Zhao if (seq_num >= start_win) 6465e6e3a92SBing Zhao pkt_index = seq_num - start_win; 6475e6e3a92SBing Zhao else 6485e6e3a92SBing Zhao pkt_index = (seq_num+MAX_TID_VALUE) - start_win; 6495e6e3a92SBing Zhao 6503a8fede1SMarc Yang if (tbl->rx_reorder_ptr[pkt_index]) { 6513a8fede1SMarc Yang ret = -1; 6523a8fede1SMarc Yang goto done; 6533a8fede1SMarc Yang } 6545e6e3a92SBing Zhao 6558c00228eSYogesh Ashok Powar tbl->rx_reorder_ptr[pkt_index] = payload; 6565e6e3a92SBing Zhao } 6575e6e3a92SBing Zhao 6585e6e3a92SBing Zhao /* 6595e6e3a92SBing Zhao * Dispatch all packets sequentially from start_win until a 6605e6e3a92SBing Zhao * hole is found and adjust the start_win appropriately 6615e6e3a92SBing Zhao */ 6628c00228eSYogesh Ashok Powar mwifiex_11n_scan_and_dispatch(priv, tbl); 6635e6e3a92SBing Zhao 6643a8fede1SMarc Yang done: 6653a8fede1SMarc Yang if (!tbl->timer_context.timer_is_set || 6663a8fede1SMarc Yang prev_start_win != tbl->start_win) 6673a8fede1SMarc Yang mwifiex_11n_rxreorder_timer_restart(tbl); 6683a8fede1SMarc Yang return ret; 6695e6e3a92SBing Zhao } 6705e6e3a92SBing Zhao 6715e6e3a92SBing Zhao /* 6725e6e3a92SBing Zhao * This function deletes an entry for a given TID/TA pair. 6735e6e3a92SBing Zhao * 6745e6e3a92SBing Zhao * The TID/TA are taken from del BA event body. 6755e6e3a92SBing Zhao */ 6765e6e3a92SBing Zhao void 6773e822635SYogesh Ashok Powar mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, 6783e822635SYogesh Ashok Powar u8 type, int initiator) 6795e6e3a92SBing Zhao { 6808c00228eSYogesh Ashok Powar struct mwifiex_rx_reorder_tbl *tbl; 6815e6e3a92SBing Zhao struct mwifiex_tx_ba_stream_tbl *ptx_tbl; 68239df5e82SZhaoyang Liu struct mwifiex_ra_list_tbl *ra_list; 6835e6e3a92SBing Zhao u8 cleanup_rx_reorder_tbl; 6845e6e3a92SBing Zhao unsigned long flags; 685719a25e3SXinming Hu int tid_down; 6865e6e3a92SBing Zhao 6875e6e3a92SBing Zhao if (type == TYPE_DELBA_RECEIVE) 6885e6e3a92SBing Zhao cleanup_rx_reorder_tbl = (initiator) ? true : false; 6895e6e3a92SBing Zhao else 6905e6e3a92SBing Zhao cleanup_rx_reorder_tbl = (initiator) ? false : true; 6915e6e3a92SBing Zhao 692acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n", 69384266841SYogesh Ashok Powar peer_mac, tid, initiator); 6945e6e3a92SBing Zhao 6955e6e3a92SBing Zhao if (cleanup_rx_reorder_tbl) { 6968c00228eSYogesh Ashok Powar tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, 6975e6e3a92SBing Zhao peer_mac); 6988c00228eSYogesh Ashok Powar if (!tbl) { 699acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, EVENT, 7005e6e3a92SBing Zhao "event: TID, TA not found in table\n"); 7015e6e3a92SBing Zhao return; 7025e6e3a92SBing Zhao } 7038c00228eSYogesh Ashok Powar mwifiex_del_rx_reorder_entry(priv, tbl); 7045e6e3a92SBing Zhao } else { 7053e822635SYogesh Ashok Powar ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac); 7065e6e3a92SBing Zhao if (!ptx_tbl) { 707acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, EVENT, 7085e6e3a92SBing Zhao "event: TID, RA not found in table\n"); 7095e6e3a92SBing Zhao return; 7105e6e3a92SBing Zhao } 711719a25e3SXinming Hu 712719a25e3SXinming Hu tid_down = mwifiex_wmm_downgrade_tid(priv, tid); 713719a25e3SXinming Hu ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac); 71439df5e82SZhaoyang Liu if (ra_list) { 71539df5e82SZhaoyang Liu ra_list->amsdu_in_ampdu = false; 71639df5e82SZhaoyang Liu ra_list->ba_status = BA_SETUP_NONE; 71739df5e82SZhaoyang Liu } 7185e6e3a92SBing Zhao spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); 7195e6e3a92SBing Zhao mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); 7205e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); 7215e6e3a92SBing Zhao } 7225e6e3a92SBing Zhao } 7235e6e3a92SBing Zhao 7245e6e3a92SBing Zhao /* 7255e6e3a92SBing Zhao * This function handles the command response of an add BA response. 7265e6e3a92SBing Zhao * 7275e6e3a92SBing Zhao * Handling includes changing the header fields into CPU format and 7285e6e3a92SBing Zhao * creating the stream, provided the add BA is accepted. 7295e6e3a92SBing Zhao */ 7305e6e3a92SBing Zhao int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, 7315e6e3a92SBing Zhao struct host_cmd_ds_command *resp) 7325e6e3a92SBing Zhao { 7332c208890SJoe Perches struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; 7345e6e3a92SBing Zhao int tid, win_size; 7358c00228eSYogesh Ashok Powar struct mwifiex_rx_reorder_tbl *tbl; 7365e6e3a92SBing Zhao uint16_t block_ack_param_set; 7375e6e3a92SBing Zhao 7385e6e3a92SBing Zhao block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); 7395e6e3a92SBing Zhao 7405e6e3a92SBing Zhao tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) 7415e6e3a92SBing Zhao >> BLOCKACKPARAM_TID_POS; 7425e6e3a92SBing Zhao /* 7435e6e3a92SBing Zhao * Check if we had rejected the ADDBA, if yes then do not create 7445e6e3a92SBing Zhao * the stream 7455e6e3a92SBing Zhao */ 74663410c37SAmitkumar Karwar if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { 747acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n", 7485e6e3a92SBing Zhao add_ba_rsp->peer_mac_addr, tid); 7495e6e3a92SBing Zhao 7508c00228eSYogesh Ashok Powar tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, 7518c00228eSYogesh Ashok Powar add_ba_rsp->peer_mac_addr); 7528c00228eSYogesh Ashok Powar if (tbl) 7538c00228eSYogesh Ashok Powar mwifiex_del_rx_reorder_entry(priv, tbl); 75463410c37SAmitkumar Karwar 75563410c37SAmitkumar Karwar return 0; 7565e6e3a92SBing Zhao } 7575e6e3a92SBing Zhao 75863410c37SAmitkumar Karwar win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) 75963410c37SAmitkumar Karwar >> BLOCKACKPARAM_WINSIZE_POS; 76063410c37SAmitkumar Karwar 7614c9f9fb2SAmitkumar Karwar tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, 7624c9f9fb2SAmitkumar Karwar add_ba_rsp->peer_mac_addr); 7634c9f9fb2SAmitkumar Karwar if (tbl) { 7644c9f9fb2SAmitkumar Karwar if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && 7654c9f9fb2SAmitkumar Karwar priv->add_ba_param.rx_amsdu && 7664c9f9fb2SAmitkumar Karwar (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) 7674c9f9fb2SAmitkumar Karwar tbl->amsdu = true; 7684c9f9fb2SAmitkumar Karwar else 7694c9f9fb2SAmitkumar Karwar tbl->amsdu = false; 7704c9f9fb2SAmitkumar Karwar } 7714c9f9fb2SAmitkumar Karwar 772acebe8c1SZhaoyang Liu mwifiex_dbg(priv->adapter, CMD, 77363410c37SAmitkumar Karwar "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", 77463410c37SAmitkumar Karwar add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); 77563410c37SAmitkumar Karwar 7765e6e3a92SBing Zhao return 0; 7775e6e3a92SBing Zhao } 7785e6e3a92SBing Zhao 7795e6e3a92SBing Zhao /* 7805e6e3a92SBing Zhao * This function handles BA stream timeout event by preparing and sending 7815e6e3a92SBing Zhao * a command to the firmware. 7825e6e3a92SBing Zhao */ 7835e6e3a92SBing Zhao void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, 7845e6e3a92SBing Zhao struct host_cmd_ds_11n_batimeout *event) 7855e6e3a92SBing Zhao { 7865e6e3a92SBing Zhao struct host_cmd_ds_11n_delba delba; 7875e6e3a92SBing Zhao 7885e6e3a92SBing Zhao memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); 7895e6e3a92SBing Zhao memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); 7905e6e3a92SBing Zhao 7915e6e3a92SBing Zhao delba.del_ba_param_set |= 7925e6e3a92SBing Zhao cpu_to_le16((u16) event->tid << DELBA_TID_POS); 7935e6e3a92SBing Zhao delba.del_ba_param_set |= cpu_to_le16( 7945e6e3a92SBing Zhao (u16) event->origninator << DELBA_INITIATOR_POS); 7955e6e3a92SBing Zhao delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); 796fa0ecbb9SBing Zhao mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false); 7975e6e3a92SBing Zhao } 7985e6e3a92SBing Zhao 7995e6e3a92SBing Zhao /* 8005e6e3a92SBing Zhao * This function cleans up the Rx reorder table by deleting all the entries 8015e6e3a92SBing Zhao * and re-initializing. 8025e6e3a92SBing Zhao */ 8035e6e3a92SBing Zhao void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) 8045e6e3a92SBing Zhao { 8055e6e3a92SBing Zhao struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; 8065e6e3a92SBing Zhao unsigned long flags; 8075e6e3a92SBing Zhao 8085e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); 8095e6e3a92SBing Zhao list_for_each_entry_safe(del_tbl_ptr, tmp_node, 8105e6e3a92SBing Zhao &priv->rx_reorder_tbl_ptr, list) { 8115e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); 8128c00228eSYogesh Ashok Powar mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); 8135e6e3a92SBing Zhao spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); 8145e6e3a92SBing Zhao } 8152d702830SAmitkumar Karwar INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); 8165e6e3a92SBing Zhao spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); 8175e6e3a92SBing Zhao 81892583924SStone Piao mwifiex_reset_11n_rx_seq_num(priv); 8195e6e3a92SBing Zhao } 8202db96c3dSAvinash Patil 8212db96c3dSAvinash Patil /* 8222db96c3dSAvinash Patil * This function updates all rx_reorder_tbl's flags. 8232db96c3dSAvinash Patil */ 8242db96c3dSAvinash Patil void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) 8252db96c3dSAvinash Patil { 8262db96c3dSAvinash Patil struct mwifiex_private *priv; 8272db96c3dSAvinash Patil struct mwifiex_rx_reorder_tbl *tbl; 8282db96c3dSAvinash Patil unsigned long lock_flags; 8292db96c3dSAvinash Patil int i; 8302db96c3dSAvinash Patil 8312db96c3dSAvinash Patil for (i = 0; i < adapter->priv_num; i++) { 8322db96c3dSAvinash Patil priv = adapter->priv[i]; 8332db96c3dSAvinash Patil if (!priv) 8342db96c3dSAvinash Patil continue; 8352db96c3dSAvinash Patil 8362db96c3dSAvinash Patil spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); 8372db96c3dSAvinash Patil list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) 8382db96c3dSAvinash Patil tbl->flags = flags; 8392db96c3dSAvinash Patil spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); 8402db96c3dSAvinash Patil } 8412db96c3dSAvinash Patil 8422db96c3dSAvinash Patil return; 8432db96c3dSAvinash Patil } 844d219b7ebSChunfan Chen 845d219b7ebSChunfan Chen /* This function update all the rx_win_size based on coex flag 846d219b7ebSChunfan Chen */ 847d219b7ebSChunfan Chen static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter, 848d219b7ebSChunfan Chen bool coex_flag) 849d219b7ebSChunfan Chen { 850d219b7ebSChunfan Chen u8 i; 851d219b7ebSChunfan Chen u32 rx_win_size; 852d219b7ebSChunfan Chen struct mwifiex_private *priv; 853d219b7ebSChunfan Chen 854d219b7ebSChunfan Chen dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag); 855d219b7ebSChunfan Chen 856d219b7ebSChunfan Chen for (i = 0; i < adapter->priv_num; i++) { 857d219b7ebSChunfan Chen if (!adapter->priv[i]) 858d219b7ebSChunfan Chen continue; 859d219b7ebSChunfan Chen priv = adapter->priv[i]; 860d219b7ebSChunfan Chen rx_win_size = priv->add_ba_param.rx_win_size; 861d219b7ebSChunfan Chen if (coex_flag) { 862d219b7ebSChunfan Chen if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) 863d219b7ebSChunfan Chen priv->add_ba_param.rx_win_size = 864d219b7ebSChunfan Chen MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; 865d219b7ebSChunfan Chen if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) 866d219b7ebSChunfan Chen priv->add_ba_param.rx_win_size = 867d219b7ebSChunfan Chen MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; 868d219b7ebSChunfan Chen if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) 869d219b7ebSChunfan Chen priv->add_ba_param.rx_win_size = 870d219b7ebSChunfan Chen MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE; 871d219b7ebSChunfan Chen } else { 872d219b7ebSChunfan Chen if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) 873d219b7ebSChunfan Chen priv->add_ba_param.rx_win_size = 874d219b7ebSChunfan Chen MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; 875d219b7ebSChunfan Chen if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) 876d219b7ebSChunfan Chen priv->add_ba_param.rx_win_size = 877d219b7ebSChunfan Chen MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; 878d219b7ebSChunfan Chen if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) 879d219b7ebSChunfan Chen priv->add_ba_param.rx_win_size = 880d219b7ebSChunfan Chen MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; 881d219b7ebSChunfan Chen } 882d219b7ebSChunfan Chen 883d219b7ebSChunfan Chen if (adapter->coex_win_size && adapter->coex_rx_win_size) 884d219b7ebSChunfan Chen priv->add_ba_param.rx_win_size = 885d219b7ebSChunfan Chen adapter->coex_rx_win_size; 886d219b7ebSChunfan Chen 887d219b7ebSChunfan Chen if (rx_win_size != priv->add_ba_param.rx_win_size) { 888d219b7ebSChunfan Chen if (!priv->media_connected) 889d219b7ebSChunfan Chen continue; 890d219b7ebSChunfan Chen for (i = 0; i < MAX_NUM_TID; i++) 891d219b7ebSChunfan Chen mwifiex_11n_delba(priv, i); 892d219b7ebSChunfan Chen } 893d219b7ebSChunfan Chen } 894d219b7ebSChunfan Chen } 895d219b7ebSChunfan Chen 896d219b7ebSChunfan Chen /* This function check coex for RX BA 897d219b7ebSChunfan Chen */ 898d219b7ebSChunfan Chen void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter) 899d219b7ebSChunfan Chen { 900d219b7ebSChunfan Chen u8 i; 901d219b7ebSChunfan Chen struct mwifiex_private *priv; 902d219b7ebSChunfan Chen u8 count = 0; 903d219b7ebSChunfan Chen 904d219b7ebSChunfan Chen for (i = 0; i < adapter->priv_num; i++) { 905d219b7ebSChunfan Chen if (adapter->priv[i]) { 906d219b7ebSChunfan Chen priv = adapter->priv[i]; 907d219b7ebSChunfan Chen if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { 908d219b7ebSChunfan Chen if (priv->media_connected) 909d219b7ebSChunfan Chen count++; 910d219b7ebSChunfan Chen } 911d219b7ebSChunfan Chen if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { 912d219b7ebSChunfan Chen if (priv->bss_started) 913d219b7ebSChunfan Chen count++; 914d219b7ebSChunfan Chen } 915d219b7ebSChunfan Chen } 916d219b7ebSChunfan Chen if (count >= MWIFIEX_BSS_COEX_COUNT) 917d219b7ebSChunfan Chen break; 918d219b7ebSChunfan Chen } 919d219b7ebSChunfan Chen if (count >= MWIFIEX_BSS_COEX_COUNT) 920d219b7ebSChunfan Chen mwifiex_update_ampdu_rxwinsize(adapter, true); 921d219b7ebSChunfan Chen else 922d219b7ebSChunfan Chen mwifiex_update_ampdu_rxwinsize(adapter, false); 923d219b7ebSChunfan Chen } 92499ffe72cSXinming Hu 92599ffe72cSXinming Hu /* This function handles rxba_sync event 92699ffe72cSXinming Hu */ 92799ffe72cSXinming Hu void mwifiex_11n_rxba_sync_event(struct mwifiex_private *priv, 92899ffe72cSXinming Hu u8 *event_buf, u16 len) 92999ffe72cSXinming Hu { 93099ffe72cSXinming Hu struct mwifiex_ie_types_rxba_sync *tlv_rxba = (void *)event_buf; 93199ffe72cSXinming Hu u16 tlv_type, tlv_len; 93299ffe72cSXinming Hu struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; 93399ffe72cSXinming Hu u8 i, j; 93499ffe72cSXinming Hu u16 seq_num, tlv_seq_num, tlv_bitmap_len; 93599ffe72cSXinming Hu int tlv_buf_left = len; 93699ffe72cSXinming Hu int ret; 93799ffe72cSXinming Hu u8 *tmp; 93899ffe72cSXinming Hu 93999ffe72cSXinming Hu mwifiex_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:", 94099ffe72cSXinming Hu event_buf, len); 94199ffe72cSXinming Hu while (tlv_buf_left >= sizeof(*tlv_rxba)) { 94299ffe72cSXinming Hu tlv_type = le16_to_cpu(tlv_rxba->header.type); 94399ffe72cSXinming Hu tlv_len = le16_to_cpu(tlv_rxba->header.len); 94499ffe72cSXinming Hu if (tlv_type != TLV_TYPE_RXBA_SYNC) { 94599ffe72cSXinming Hu mwifiex_dbg(priv->adapter, ERROR, 94699ffe72cSXinming Hu "Wrong TLV id=0x%x\n", tlv_type); 94799ffe72cSXinming Hu return; 94899ffe72cSXinming Hu } 94999ffe72cSXinming Hu 95099ffe72cSXinming Hu tlv_seq_num = le16_to_cpu(tlv_rxba->seq_num); 95199ffe72cSXinming Hu tlv_bitmap_len = le16_to_cpu(tlv_rxba->bitmap_len); 95299ffe72cSXinming Hu mwifiex_dbg(priv->adapter, INFO, 95399ffe72cSXinming Hu "%pM tid=%d seq_num=%d bitmap_len=%d\n", 95499ffe72cSXinming Hu tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num, 95599ffe72cSXinming Hu tlv_bitmap_len); 95699ffe72cSXinming Hu 95799ffe72cSXinming Hu rx_reor_tbl_ptr = 95899ffe72cSXinming Hu mwifiex_11n_get_rx_reorder_tbl(priv, tlv_rxba->tid, 95999ffe72cSXinming Hu tlv_rxba->mac); 96099ffe72cSXinming Hu if (!rx_reor_tbl_ptr) { 96199ffe72cSXinming Hu mwifiex_dbg(priv->adapter, ERROR, 96299ffe72cSXinming Hu "Can not find rx_reorder_tbl!"); 96399ffe72cSXinming Hu return; 96499ffe72cSXinming Hu } 96599ffe72cSXinming Hu 96699ffe72cSXinming Hu for (i = 0; i < tlv_bitmap_len; i++) { 96799ffe72cSXinming Hu for (j = 0 ; j < 8; j++) { 96899ffe72cSXinming Hu if (tlv_rxba->bitmap[i] & (1 << j)) { 96999ffe72cSXinming Hu seq_num = (MAX_TID_VALUE - 1) & 97099ffe72cSXinming Hu (tlv_seq_num + i * 8 + j); 97199ffe72cSXinming Hu 97299ffe72cSXinming Hu mwifiex_dbg(priv->adapter, ERROR, 97399ffe72cSXinming Hu "drop packet,seq=%d\n", 97499ffe72cSXinming Hu seq_num); 97599ffe72cSXinming Hu 97699ffe72cSXinming Hu ret = mwifiex_11n_rx_reorder_pkt 97799ffe72cSXinming Hu (priv, seq_num, tlv_rxba->tid, 97899ffe72cSXinming Hu tlv_rxba->mac, 0, NULL); 97999ffe72cSXinming Hu 98099ffe72cSXinming Hu if (ret) 98199ffe72cSXinming Hu mwifiex_dbg(priv->adapter, 98299ffe72cSXinming Hu ERROR, 98399ffe72cSXinming Hu "Fail to drop packet"); 98499ffe72cSXinming Hu } 98599ffe72cSXinming Hu } 98699ffe72cSXinming Hu } 98799ffe72cSXinming Hu 98899ffe72cSXinming Hu tlv_buf_left -= (sizeof(*tlv_rxba) + tlv_len); 98999ffe72cSXinming Hu tmp = (u8 *)tlv_rxba + tlv_len + sizeof(*tlv_rxba); 99099ffe72cSXinming Hu tlv_rxba = (struct mwifiex_ie_types_rxba_sync *)tmp; 99199ffe72cSXinming Hu } 99299ffe72cSXinming Hu } 993