diff options
Diffstat (limited to 'drivers/net/wireless/mwifiex/11n_rxreorder.c')
| -rw-r--r-- | drivers/net/wireless/mwifiex/11n_rxreorder.c | 301 |
1 files changed, 231 insertions, 70 deletions
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index 900ee129e82..0c3571f830b 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -26,6 +26,56 @@ #include "11n.h" #include "11n_rxreorder.h" +/* This function will dispatch amsdu packet and forward it to kernel/upper + * layer. + */ +static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); + int ret; + + if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { + struct sk_buff_head list; + struct sk_buff *rx_skb; + + __skb_queue_head_init(&list); + + skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); + skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); + + ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, + priv->wdev->iftype, 0, false); + + while (!skb_queue_empty(&list)) { + rx_skb = __skb_dequeue(&list); + ret = mwifiex_recv_packet(priv, rx_skb); + if (ret == -1) + dev_err(priv->adapter->dev, + "Rx of A-MSDU failed"); + } + return 0; + } + + return -1; +} + +/* This function will process the rx packet and forward it to kernel/upper + * layer. + */ +static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) +{ + int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload); + + if (!ret) + return 0; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + return mwifiex_handle_uap_rx_forward(priv, payload); + + return mwifiex_process_rx_packet(priv, payload); +} + /* * This function dispatches all packets in the Rx reorder table until the * start window. @@ -35,8 +85,9 @@ * circular buffer. */ static void -mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, - struct mwifiex_rx_reorder_tbl *tbl, int start_win) +mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, + struct mwifiex_rx_reorder_tbl *tbl, + int start_win) { int pkt_to_send, i; void *rx_tmp_ptr; @@ -55,7 +106,7 @@ mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, } spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); if (rx_tmp_ptr) - mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr); + mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); } spin_lock_irqsave(&priv->rx_pkt_lock, flags); @@ -97,7 +148,7 @@ mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, rx_tmp_ptr = tbl->rx_reorder_ptr[i]; tbl->rx_reorder_ptr[i] = NULL; spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr); + mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); } spin_lock_irqsave(&priv->rx_pkt_lock, flags); @@ -127,14 +178,15 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl) { unsigned long flags; + int start_win; if (!tbl) return; - mwifiex_11n_dispatch_pkt(priv, tbl, (tbl->start_win + tbl->win_size) & - (MAX_TID_VALUE - 1)); + start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); - del_timer(&tbl->timer_context.timer); + del_timer_sync(&tbl->timer_context.timer); spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); list_del(&tbl->list); @@ -148,7 +200,7 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, * This function returns the pointer to an entry in Rx reordering * table which matches the given TA/TID pair. */ -static struct mwifiex_rx_reorder_tbl * +struct mwifiex_rx_reorder_tbl * mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) { struct mwifiex_rx_reorder_tbl *tbl; @@ -167,6 +219,31 @@ mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) return NULL; } +/* This function retrieves the pointer to an entry in Rx reordering + * table which matches the given TA and deletes it. + */ +void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) +{ + struct mwifiex_rx_reorder_tbl *tbl, *tmp; + unsigned long flags; + + if (!ta) + return; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN)) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + flags); + mwifiex_del_rx_reorder_entry(priv, tbl); + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + } + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return; +} + /* * This function finds the last sequence number used in the packets * buffered in Rx reordering table. @@ -195,17 +272,17 @@ mwifiex_flush_data(unsigned long context) { struct reorder_tmr_cnxt *ctx = (struct reorder_tmr_cnxt *) context; - int start_win; + int start_win, seq_num; - start_win = mwifiex_11n_find_last_seq_num(ctx->ptr); + seq_num = mwifiex_11n_find_last_seq_num(ctx->ptr); - if (start_win < 0) + if (seq_num < 0) return; - dev_dbg(ctx->priv->adapter->dev, "info: flush data %d\n", start_win); - mwifiex_11n_dispatch_pkt(ctx->priv, ctx->ptr, - (ctx->ptr->start_win + start_win + 1) & - (MAX_TID_VALUE - 1)); + dev_dbg(ctx->priv->adapter->dev, "info: flush data %d\n", seq_num); + start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); + mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, + start_win); } /* @@ -226,6 +303,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, struct mwifiex_rx_reorder_tbl *tbl, *new_node; u16 last_seq = 0; unsigned long flags; + struct mwifiex_sta_node *node; /* * If we get a TID, ta pair which is already present dispatch all the @@ -233,32 +311,43 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, */ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); if (tbl) { - mwifiex_11n_dispatch_pkt(priv, tbl, seq_num); + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); return; } /* if !tbl then create one */ new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); - if (!new_node) { - dev_err(priv->adapter->dev, "%s: failed to alloc new_node\n", - __func__); + if (!new_node) return; - } INIT_LIST_HEAD(&new_node->list); new_node->tid = tid; memcpy(new_node->ta, ta, ETH_ALEN); new_node->start_win = seq_num; - if (mwifiex_queuing_ra_based(priv)) - /* TODO for adhoc */ + new_node->init_win = seq_num; + new_node->flags = 0; + + if (mwifiex_queuing_ra_based(priv)) { dev_dbg(priv->adapter->dev, - "info: ADHOC:last_seq=%d start_win=%d\n", + "info: AP/ADHOC:last_seq=%d start_win=%d\n", last_seq, new_node->start_win); - else - last_seq = priv->rx_seq[tid]; + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { + node = mwifiex_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + } + } else { + node = mwifiex_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + else + last_seq = priv->rx_seq[tid]; + } if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && - last_seq >= new_node->start_win) + last_seq >= new_node->start_win) { new_node->start_win = last_seq + 1; + new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; + } new_node->win_size = win_size; @@ -297,9 +386,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, */ int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) { - struct host_cmd_ds_11n_addba_req *add_ba_req = - (struct host_cmd_ds_11n_addba_req *) - &cmd->params.add_ba_req; + struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); @@ -321,13 +408,29 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, struct host_cmd_ds_11n_addba_req *cmd_addba_req) { - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = - (struct host_cmd_ds_11n_addba_rsp *) - &cmd->params.add_ba_rsp; + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; + struct mwifiex_sta_node *sta_ptr; + u32 rx_win_size = priv->add_ba_param.rx_win_size; u8 tid; int win_size; uint16_t block_ack_param_set; + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { + sta_ptr = mwifiex_get_sta_entry(priv, + cmd_addba_req->peer_mac_addr); + if (!sta_ptr) { + dev_warn(priv->adapter->dev, + "BA setup with unknown TDLS peer %pM!\n", + cmd_addba_req->peer_mac_addr); + return -1; + } + if (sta_ptr->is_11ac_enabled) + rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; + } + cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); @@ -342,10 +445,12 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, >> BLOCKACKPARAM_TID_POS; add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; - /* We donot support AMSDU inside AMPDU, hence reset the bit */ - block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; - block_ack_param_set |= (priv->add_ba_param.rx_win_size << - BLOCKACKPARAM_WINSIZE_POS); + + /* If we don't support AMSDU inside AMPDU, reset the bit */ + if (!priv->add_ba_param.rx_amsdu || + (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) + block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; + block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) @@ -368,8 +473,7 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, */ int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) { - struct host_cmd_ds_11n_delba *del_ba = (struct host_cmd_ds_11n_delba *) - &cmd->params.del_ba; + struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); @@ -398,32 +502,55 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl; int start_win, end_win, win_size; u16 pkt_index; + bool init_window_shift = false; - tbl = mwifiex_11n_get_rx_reorder_tbl((struct mwifiex_private *) priv, - tid, ta); + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); if (!tbl) { if (pkt_type != PKT_TYPE_BAR) - mwifiex_process_rx_packet(priv->adapter, payload); + mwifiex_11n_dispatch_pkt(priv, payload); return 0; } + + if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) { + mwifiex_11n_dispatch_pkt(priv, payload); + return 0; + } + start_win = tbl->start_win; win_size = tbl->win_size; end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); - del_timer(&tbl->timer_context.timer); + if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { + init_window_shift = true; + tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; + } mod_timer(&tbl->timer_context.timer, - jiffies + (MIN_FLUSH_TIMER_MS * win_size * HZ) / 1000); + jiffies + msecs_to_jiffies(MIN_FLUSH_TIMER_MS * win_size)); - /* - * If seq_num is less then starting win then ignore and drop the - * packet - */ - if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {/* Wrap */ - if (seq_num >= ((start_win + TWOPOW11) & - (MAX_TID_VALUE - 1)) && (seq_num < start_win)) + if (tbl->flags & RXREOR_FORCE_NO_DROP) { + dev_dbg(priv->adapter->dev, + "RXREOR_FORCE_NO_DROP when HS is activated\n"); + tbl->flags &= ~RXREOR_FORCE_NO_DROP; + } else if (init_window_shift && seq_num < start_win && + seq_num >= tbl->init_win) { + dev_dbg(priv->adapter->dev, + "Sender TID sequence number reset %d->%d for SSN %d\n", + start_win, seq_num, tbl->init_win); + tbl->start_win = start_win = seq_num; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + } else { + /* + * If seq_num is less then starting win then ignore and drop + * the packet + */ + if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { + if (seq_num >= ((start_win + TWOPOW11) & + (MAX_TID_VALUE - 1)) && + seq_num < start_win) + return -1; + } else if ((seq_num < start_win) || + (seq_num > (start_win + TWOPOW11))) { return -1; - } else if ((seq_num < start_win) || - (seq_num > (start_win + TWOPOW11))) { - return -1; + } } /* @@ -434,8 +561,7 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); if (((end_win < start_win) && - (seq_num < (TWOPOW11 - (MAX_TID_VALUE - start_win))) && - (seq_num > end_win)) || + (seq_num < start_win) && (seq_num > end_win)) || ((end_win > start_win) && ((seq_num > end_win) || (seq_num < start_win)))) { end_win = seq_num; @@ -443,7 +569,7 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, start_win = (end_win - win_size) + 1; else start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1; - mwifiex_11n_dispatch_pkt(priv, tbl, start_win); + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); } if (pkt_type != PKT_TYPE_BAR) { @@ -521,9 +647,7 @@ mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = - (struct host_cmd_ds_11n_addba_rsp *) - &resp->params.add_ba_rsp; + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; int tid, win_size; struct mwifiex_rx_reorder_tbl *tbl; uint16_t block_ack_param_set; @@ -536,16 +660,7 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, * Check if we had rejected the ADDBA, if yes then do not create * the stream */ - if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { - win_size = (block_ack_param_set & - IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) - >> BLOCKACKPARAM_WINSIZE_POS; - - dev_dbg(priv->adapter->dev, - "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", - add_ba_rsp->peer_mac_addr, tid, - add_ba_rsp->ssn, win_size); - } else { + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { dev_err(priv->adapter->dev, "ADDBA RSP: failed %pM tid=%d)\n", add_ba_rsp->peer_mac_addr, tid); @@ -553,8 +668,28 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, add_ba_rsp->peer_mac_addr); if (tbl) mwifiex_del_rx_reorder_entry(priv, tbl); + + return 0; } + win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) { + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.rx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + tbl->amsdu = true; + else + tbl->amsdu = false; + } + + dev_dbg(priv->adapter->dev, + "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", + add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); + return 0; } @@ -575,7 +710,7 @@ void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, delba.del_ba_param_set |= cpu_to_le16( (u16) event->origninator << DELBA_INITIATOR_POS); delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); - mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba); + mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false); } /* @@ -599,3 +734,29 @@ void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); mwifiex_reset_11n_rx_seq_num(priv); } + +/* + * This function updates all rx_reorder_tbl's flags. + */ +void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) +{ + struct mwifiex_private *priv; + struct mwifiex_rx_reorder_tbl *tbl; + unsigned long lock_flags; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (list_empty(&priv->rx_reorder_tbl_ptr)) + continue; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) + tbl->flags = flags; + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); + } + + return; +} |
