685 lines
24 KiB
C
Executable file
685 lines
24 KiB
C
Executable file
/****************************************************************************
|
|
*
|
|
* Copyright (c) 2012 - 2019 Samsung Electronics Co., Ltd. All rights reserved
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "debug.h"
|
|
#include "dev.h"
|
|
#include "ba.h"
|
|
#include "mgt.h"
|
|
#include "sap.h"
|
|
|
|
/* Timeout (in milliseconds) for frames in MPDU reorder buffer
|
|
*
|
|
* When a frame is out of order, the frame is stored in Reorder buffer.
|
|
* Frames can be released from the buffer, if subsequent frames arrive such that
|
|
* frames can be ordered or when this timeout occurs
|
|
*/
|
|
static uint ba_mpdu_reorder_age_timeout = 100; /* 100 milliseconds */
|
|
module_param(ba_mpdu_reorder_age_timeout, uint, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(ba_mpdu_reorder_age_timeout, "Timeout (in ms) before a BA frame in Reorder buffer is passed to upper layers");
|
|
|
|
static bool ba_out_of_range_delba_enable = 1;
|
|
module_param(ba_out_of_range_delba_enable, bool, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(ba_out_of_range_delba_enable, "Option to trigger DELBA on out of range frame (1: enable (default), 0: disable)");
|
|
|
|
#define ADVANCE_EXPECTED_SN(__ba_session_rx) \
|
|
{ \
|
|
__ba_session_rx->expected_sn++; \
|
|
__ba_session_rx->expected_sn &= 0xFFF; \
|
|
}
|
|
|
|
#define FREE_BUFFER_SLOT(__ba_session_rx, __index) \
|
|
{ \
|
|
__ba_session_rx->occupied_slots--; \
|
|
__ba_session_rx->buffer[__index].active = false; \
|
|
}
|
|
|
|
void slsi_rx_ba_init(struct slsi_dev *sdev)
|
|
{
|
|
slsi_spinlock_create(&sdev->rx_ba_buffer_pool_lock);
|
|
}
|
|
|
|
static struct slsi_ba_session_rx *slsi_rx_ba_alloc_buffer(struct net_device *dev)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct slsi_dev *sdev = ndev_vif->sdev;
|
|
struct slsi_ba_session_rx *buffer = NULL;
|
|
int i;
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "RX BA buffer pool status: %d,%d,%d,%d,%d,%d,%d,%d\n",
|
|
sdev->rx_ba_buffer_pool[0].used, sdev->rx_ba_buffer_pool[1].used, sdev->rx_ba_buffer_pool[2].used,
|
|
sdev->rx_ba_buffer_pool[3].used, sdev->rx_ba_buffer_pool[4].used, sdev->rx_ba_buffer_pool[5].used,
|
|
sdev->rx_ba_buffer_pool[6].used, sdev->rx_ba_buffer_pool[7].used);
|
|
|
|
slsi_spinlock_lock(&sdev->rx_ba_buffer_pool_lock);
|
|
for (i = 0; i < SLSI_MAX_RX_BA_SESSIONS; i++) {
|
|
if (!sdev->rx_ba_buffer_pool[i].used) {
|
|
sdev->rx_ba_buffer_pool[i].used = true;
|
|
buffer = &sdev->rx_ba_buffer_pool[i];
|
|
break;
|
|
}
|
|
}
|
|
slsi_spinlock_unlock(&sdev->rx_ba_buffer_pool_lock);
|
|
|
|
if (!buffer)
|
|
SLSI_NET_ERR(dev, "No free RX BA buffer\n");
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static void slsi_rx_ba_free_buffer(struct net_device *dev, struct slsi_peer *peer, int tid)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct slsi_dev *sdev = ndev_vif->sdev;
|
|
|
|
slsi_spinlock_lock(&sdev->rx_ba_buffer_pool_lock);
|
|
if (peer && peer->ba_session_rx[tid]) {
|
|
peer->ba_session_rx[tid]->used = false;
|
|
peer->ba_session_rx[tid] = NULL;
|
|
}
|
|
slsi_spinlock_unlock(&sdev->rx_ba_buffer_pool_lock);
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "RX BA buffer pool status: %d,%d,%d,%d,%d,%d,%d,%d\n",
|
|
sdev->rx_ba_buffer_pool[0].used, sdev->rx_ba_buffer_pool[1].used, sdev->rx_ba_buffer_pool[2].used,
|
|
sdev->rx_ba_buffer_pool[3].used, sdev->rx_ba_buffer_pool[4].used, sdev->rx_ba_buffer_pool[5].used,
|
|
sdev->rx_ba_buffer_pool[6].used, sdev->rx_ba_buffer_pool[7].used);
|
|
}
|
|
|
|
/* This code - slsi_ba_process_complete()
|
|
* must be called with the ba_lock spinlock held.
|
|
*/
|
|
void slsi_ba_process_complete(struct net_device *dev, bool ctx_napi)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct sk_buff *skb;
|
|
|
|
while ((skb = skb_dequeue(&ndev_vif->ba_complete)) != NULL) {
|
|
local_bh_disable();
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
slsi_rx_data_deliver_skb(ndev_vif->sdev, dev, skb, ctx_napi);
|
|
slsi_spinlock_lock(&ndev_vif->ba_lock);
|
|
local_bh_enable();
|
|
}
|
|
}
|
|
|
|
static void slsi_ba_signal_process_complete(struct net_device *dev)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
|
|
atomic_set(&ndev_vif->ba_flush, 1);
|
|
#ifndef CONFIG_SCSC_WLAN_RX_NAPI
|
|
slsi_skb_schedule_work(&ndev_vif->rx_data);
|
|
#endif
|
|
}
|
|
|
|
static void ba_add_frame_to_ba_complete(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx, struct slsi_ba_frame_desc *frame_desc)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
|
|
if (slsi_ba_replay_check_pn(dev, ba_session_rx, frame_desc)) {
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "drop: tid=%d sn=%d received PN %pm\n",
|
|
frame_desc->tid,
|
|
frame_desc->sn,
|
|
frame_desc->pn);
|
|
#ifdef CONFIG_SCSC_SMAPPER
|
|
hip4_smapper_free_mapped_skb(frame_desc->signal);
|
|
#endif
|
|
kfree_skb(frame_desc->signal);
|
|
return;
|
|
}
|
|
skb_queue_tail(&ndev_vif->ba_complete, frame_desc->signal);
|
|
}
|
|
|
|
static void ba_update_expected_sn(struct net_device *dev,
|
|
struct slsi_ba_session_rx *ba_session_rx, u16 sn)
|
|
{
|
|
u32 i, j;
|
|
u16 gap;
|
|
|
|
gap = (sn - ba_session_rx->expected_sn) & 0xFFF;
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Proccess the frames up to new expected_sn = %d gap = %d\n", sn, gap);
|
|
|
|
for (j = 0; j < gap && j < ba_session_rx->buffer_size; j++) {
|
|
i = SN_TO_INDEX(ba_session_rx, ba_session_rx->expected_sn);
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Proccess the slot index = %d\n", i);
|
|
if (ba_session_rx->buffer[i].active) {
|
|
ba_add_frame_to_ba_complete(dev, ba_session_rx, &ba_session_rx->buffer[i]);
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Proccess the frame at index = %d expected_sn = %d\n", i, ba_session_rx->expected_sn);
|
|
FREE_BUFFER_SLOT(ba_session_rx, i);
|
|
} else {
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Empty slot at index = %d\n", i);
|
|
}
|
|
ADVANCE_EXPECTED_SN(ba_session_rx);
|
|
}
|
|
ba_session_rx->expected_sn = sn;
|
|
}
|
|
|
|
static void ba_complete_ready_sequence(struct net_device *dev,
|
|
struct slsi_ba_session_rx *ba_session_rx)
|
|
{
|
|
int i;
|
|
|
|
i = SN_TO_INDEX(ba_session_rx, ba_session_rx->expected_sn);
|
|
while (ba_session_rx->buffer[i].active) {
|
|
ba_add_frame_to_ba_complete(dev, ba_session_rx, &ba_session_rx->buffer[i]);
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "Completed stored frame (expected_sn=%d) at i = %d\n",
|
|
ba_session_rx->expected_sn, i);
|
|
FREE_BUFFER_SLOT(ba_session_rx, i);
|
|
ADVANCE_EXPECTED_SN(ba_session_rx);
|
|
i = SN_TO_INDEX(ba_session_rx, ba_session_rx->expected_sn);
|
|
}
|
|
}
|
|
|
|
static void ba_scroll_window(struct net_device *dev,
|
|
struct slsi_ba_session_rx *ba_session_rx, u16 sn)
|
|
{
|
|
if (((sn - ba_session_rx->expected_sn) & 0xFFF) <= BA_WINDOW_BOUNDARY) {
|
|
ba_update_expected_sn(dev, ba_session_rx, sn);
|
|
ba_complete_ready_sequence(dev, ba_session_rx);
|
|
}
|
|
}
|
|
|
|
static void ba_delete_ba_on_old_frame(struct net_device *dev, struct slsi_peer *peer,
|
|
struct slsi_ba_session_rx *ba_session_rx, u16 sn)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
struct sap_drv_ma_to_mlme_delba_req *delba_req;
|
|
struct sk_buff *skb = NULL;
|
|
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "sn=%d\n", sn);
|
|
/* construct a message for MLME */
|
|
skb = alloc_skb(sizeof(struct sap_drv_ma_to_mlme_delba_req), GFP_ATOMIC);
|
|
|
|
if (WARN_ON(!skb))
|
|
return;
|
|
|
|
ba_session_rx->closing = true;
|
|
slsi_skb_cb_init(skb)->sig_length = sizeof(struct sap_drv_ma_to_mlme_delba_req);
|
|
slsi_skb_cb_get(skb)->data_length = sizeof(struct sap_drv_ma_to_mlme_delba_req);
|
|
|
|
delba_req = (struct sap_drv_ma_to_mlme_delba_req *)skb_put(skb, sizeof(struct sap_drv_ma_to_mlme_delba_req));
|
|
delba_req->header.id = cpu_to_le16(SAP_DRV_MA_TO_MLME_DELBA_REQ);
|
|
delba_req->header.receiver_pid = 0;
|
|
delba_req->header.sender_pid = 0;
|
|
delba_req->header.fw_reference = 0;
|
|
delba_req->vif = ndev_vif->ifnum;
|
|
|
|
memcpy(delba_req->peer_qsta_address, peer->address, ETH_ALEN);
|
|
delba_req->sequence_number = sn;
|
|
delba_req->user_priority = ba_session_rx->tid;
|
|
delba_req->reason = FAPI_REASONCODE_OUT_OF_RANGE_SEQUENCE_NUMBER;
|
|
delba_req->direction = FAPI_DIRECTION_RECEIVE;
|
|
|
|
/* queue the message for MLME SAP */
|
|
slsi_rx_enqueue_netdev_mlme(ndev_vif->sdev, skb, fapi_get_vif(skb));
|
|
}
|
|
|
|
static int ba_consume_frame_or_get_buffer_index(struct net_device *dev, struct slsi_peer *peer,
|
|
struct slsi_ba_session_rx *ba_session_rx, u16 sn, struct slsi_ba_frame_desc *frame_desc, bool *stop_timer)
|
|
{
|
|
int i;
|
|
u16 sn_temp;
|
|
#ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
#endif
|
|
|
|
*stop_timer = false;
|
|
|
|
if (((sn - ba_session_rx->expected_sn) & 0xFFF) <= BA_WINDOW_BOUNDARY) {
|
|
/* Once we are in BA window, set the flag for BA trigger */
|
|
if (!ba_session_rx->trigger_ba_after_ssn)
|
|
ba_session_rx->trigger_ba_after_ssn = true;
|
|
|
|
sn_temp = ba_session_rx->expected_sn + ba_session_rx->buffer_size;
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "New frame: sn=%d\n", sn);
|
|
|
|
if (!(((sn - sn_temp) & 0xFFF) > BA_WINDOW_BOUNDARY)) {
|
|
u16 new_expected_sn;
|
|
|
|
SLSI_NET_DBG2(dev, SLSI_RX_BA, "Frame is out of window\n");
|
|
sn_temp = (sn - ba_session_rx->buffer_size) & 0xFFF;
|
|
if (ba_session_rx->timer_on)
|
|
*stop_timer = true;
|
|
new_expected_sn = (sn_temp + 1) & 0xFFF;
|
|
ba_update_expected_sn(dev, ba_session_rx, new_expected_sn);
|
|
}
|
|
|
|
i = -1;
|
|
if (sn == ba_session_rx->expected_sn) {
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "sn = ba_session_rx->expected_sn = %d\n", sn);
|
|
if (ba_session_rx->timer_on)
|
|
*stop_timer = true;
|
|
ADVANCE_EXPECTED_SN(ba_session_rx);
|
|
ba_add_frame_to_ba_complete(dev, ba_session_rx, frame_desc);
|
|
} else {
|
|
i = SN_TO_INDEX(ba_session_rx, sn);
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "sn (%d) != ba_session_rx->expected_sn(%d), i = %d\n", sn, ba_session_rx->expected_sn, i);
|
|
if (ba_session_rx->buffer[i].active) {
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "free frame at i = %d\n", i);
|
|
i = -1;
|
|
#ifdef CONFIG_SCSC_SMAPPER
|
|
hip4_smapper_free_mapped_skb(frame_desc->signal);
|
|
#endif
|
|
kfree_skb(frame_desc->signal);
|
|
}
|
|
}
|
|
if (!IS_SN_LESS(sn, ba_session_rx->highest_received_sn))
|
|
ba_session_rx->highest_received_sn = sn;
|
|
} else {
|
|
i = -1;
|
|
if (!ba_session_rx->trigger_ba_after_ssn) {
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "frame before ssn, pass it up: sn=%d\n", sn);
|
|
ba_add_frame_to_ba_complete(dev, ba_session_rx, frame_desc);
|
|
} else {
|
|
frame_desc->flag_old_sn = true;
|
|
if (slsi_is_tdls_peer(dev, peer)) {
|
|
/* Don't drop old frames in TDLS AMPDU-reordering for interoperability with third party devices.
|
|
* When the TDLS link is established the peer sends few packets with AP's sequence number.
|
|
* BA reorder logic updates the expected sequence number. After that peer sends packets with
|
|
* starting sequence number negotiated in BA (0). But those frames are getting dropped here.
|
|
* Because of this TCP traffic does not work and TDLS link is getting disconnected.
|
|
*/
|
|
SLSI_NET_DBG1(dev, SLSI_RX_BA, "tdls: forward old frame: sn=%d, expected_sn=%d\n", sn, ba_session_rx->expected_sn);
|
|
frame_desc->flag_old_tdls = true;
|
|
ba_add_frame_to_ba_complete(dev, ba_session_rx, frame_desc);
|
|
} else {
|
|
/* this frame is deemed as old. But it may so happen that the reorder process did not wait long
|
|
* enough for this frame and moved to new window. So check here that the current frame still lies in
|
|
* originators transmit window by comparing it with highest sequence number received from originator.
|
|
*
|
|
* If it lies in the window pass the frame to next process else pass the frame and initiate DELBA procedure.
|
|
*/
|
|
if (IS_SN_LESS(ba_session_rx->highest_received_sn, ((sn + ba_session_rx->buffer_size) & 0xFFF))) {
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "old frame, but still in window: sn=%d, highest_received_sn=%d\n", sn, ba_session_rx->highest_received_sn);
|
|
ba_add_frame_to_ba_complete(dev, ba_session_rx, frame_desc);
|
|
} else {
|
|
SLSI_NET_WARN(dev, "old frame, drop: sn=%d, expected_sn=%d\n", sn, ba_session_rx->expected_sn);
|
|
#ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT
|
|
if (ndev_vif->enhanced_arp_detect_enabled)
|
|
slsi_fill_enhanced_arp_out_of_order_drop_counter(ndev_vif, frame_desc->signal);
|
|
#endif
|
|
#ifdef CONFIG_SCSC_SMAPPER
|
|
hip4_smapper_free_mapped_skb(frame_desc->signal);
|
|
#endif
|
|
kfree_skb(frame_desc->signal);
|
|
if (ba_out_of_range_delba_enable) {
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "as a countermeasure to re-sync, delete BA: sn=%d, expected_sn=%d\n", sn, ba_session_rx->expected_sn);
|
|
if (!ba_session_rx->closing)
|
|
ba_delete_ba_on_old_frame(dev, peer, ba_session_rx, sn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
|
|
static void slsi_ba_aging_timeout_handler(struct timer_list *t)
|
|
#else
|
|
static void slsi_ba_aging_timeout_handler(unsigned long data)
|
|
#endif
|
|
{
|
|
#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
|
|
struct slsi_ba_session_rx *ba_session_rx = from_timer(ba_session_rx, t, ba_age_timer);
|
|
#else
|
|
struct slsi_ba_session_rx *ba_session_rx = (struct slsi_ba_session_rx *)data;
|
|
#endif
|
|
u8 i, j;
|
|
u8 gap = 1;
|
|
u16 temp_sn;
|
|
struct net_device *dev = ba_session_rx->dev;
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "\n");
|
|
|
|
slsi_spinlock_lock(&ndev_vif->ba_lock);
|
|
|
|
ba_session_rx->timer_on = false;
|
|
|
|
if (ba_session_rx->active && ba_session_rx->occupied_slots) {
|
|
/* expected sequence has not arrived so start searching from next
|
|
* sequence number until a frame is available and determine the gap.
|
|
* Release all the frames upto next hole from the reorder buffer.
|
|
*/
|
|
temp_sn = (ba_session_rx->expected_sn + 1) & 0xFFF;
|
|
for (i = 0; i < MAX_BA_BUFFER_SIZE; i++) {
|
|
ba_session_rx->ba_window[i].sent = false;
|
|
ba_session_rx->ba_window[i].sn = 0;
|
|
}
|
|
|
|
for (j = 0; j < ba_session_rx->buffer_size; j++) {
|
|
i = SN_TO_INDEX(ba_session_rx, temp_sn);
|
|
|
|
if (ba_session_rx->buffer[i].active) {
|
|
while (gap--)
|
|
ADVANCE_EXPECTED_SN(ba_session_rx);
|
|
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Completed stored frame (expected_sn=%d) at i = %d\n", ba_session_rx->expected_sn, i);
|
|
ba_add_frame_to_ba_complete(dev, ba_session_rx, &ba_session_rx->buffer[i]);
|
|
FREE_BUFFER_SLOT(ba_session_rx, i);
|
|
ADVANCE_EXPECTED_SN(ba_session_rx);
|
|
ba_complete_ready_sequence(dev, ba_session_rx);
|
|
ba_session_rx->ba_timeouts++;
|
|
break;
|
|
}
|
|
/* advance temp sequence number and frame gap */
|
|
temp_sn = (temp_sn + 1) & 0xFFF;
|
|
gap++;
|
|
}
|
|
|
|
/* Check for next hole in the buffer, if hole exists create the timer for next missing frame */
|
|
/* do not rearm the timer if BA session is going down */
|
|
if (ba_session_rx->active && ba_session_rx->occupied_slots) {
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Timer start\n");
|
|
mod_timer(&ba_session_rx->ba_age_timer, jiffies + msecs_to_jiffies(ba_mpdu_reorder_age_timeout));
|
|
ba_session_rx->timer_on = true;
|
|
}
|
|
/* Process the data now marked as completed */
|
|
#ifdef CONFIG_SCSC_WLAN_RX_NAPI
|
|
slsi_ba_process_complete(dev, false);
|
|
#else
|
|
slsi_ba_signal_process_complete(dev);
|
|
#endif
|
|
}
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
}
|
|
|
|
int slsi_ba_process_frame(struct net_device *dev, struct slsi_peer *peer,
|
|
struct sk_buff *skb, u16 sequence_number, u16 tid)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
int i;
|
|
struct slsi_ba_session_rx *ba_session_rx = peer->ba_session_rx[tid];
|
|
struct slsi_ba_frame_desc frame_desc;
|
|
bool stop_timer = false;
|
|
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "Got frame(sn=%d)\n", sequence_number);
|
|
|
|
if (WARN_ON(tid > FAPI_PRIORITY_QOS_UP7)) {
|
|
SLSI_NET_ERR(dev, "tid=%d\n", tid);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ba_session_rx)
|
|
return -EINVAL;
|
|
|
|
slsi_spinlock_lock(&ndev_vif->ba_lock);
|
|
|
|
if (!ba_session_rx->active) {
|
|
SLSI_NET_ERR(dev, "No BA session exists\n");
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
frame_desc.signal = skb;
|
|
frame_desc.tid = tid;
|
|
frame_desc.sn = sequence_number;
|
|
frame_desc.active = true;
|
|
slsi_ba_replay_get_pn(dev, peer, skb, &frame_desc);
|
|
|
|
i = ba_consume_frame_or_get_buffer_index(dev, peer, ba_session_rx, sequence_number, &frame_desc, &stop_timer);
|
|
if (i >= 0) {
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "Store frame(sn=%d) at i = %d\n", sequence_number, i);
|
|
if (ba_session_rx->buffer[i].active) {
|
|
SLSI_NET_WARN(dev, "drop duplicate frame (sn=%d) at i = %d\n", sequence_number, i);
|
|
#ifdef CONFIG_SCSC_SMAPPER
|
|
hip4_smapper_free_mapped_skb(frame_desc.signal);
|
|
#endif
|
|
kfree_skb(frame_desc.signal);
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
return 0;
|
|
}
|
|
ba_session_rx->buffer[i] = frame_desc;
|
|
ba_session_rx->occupied_slots++;
|
|
} else {
|
|
SLSI_NET_DBG4(dev, SLSI_RX_BA, "Frame consumed - sn = %d\n", sequence_number);
|
|
}
|
|
|
|
ba_complete_ready_sequence(dev, ba_session_rx);
|
|
|
|
/* Timer decision:
|
|
*
|
|
* If the timer is not running (timer_on=false)
|
|
* Start the timer if there are holes (occupied_slots!=0)
|
|
*
|
|
* If the timer is running (timer_on=true)
|
|
* Stop the timer if
|
|
* There are no holes (occupied_slots=0)
|
|
* Restart the timer if
|
|
* stop_timer=true and there are holes (occupied_slots!=0)
|
|
* Leave the timer running (do nothing) if
|
|
* stop_timer=false and there are holes (occupied_slots!=0)
|
|
*/
|
|
|
|
if (!ba_session_rx->timer_on) {
|
|
if (ba_session_rx->occupied_slots) {
|
|
stop_timer = false;
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Timer start\n");
|
|
mod_timer(&ba_session_rx->ba_age_timer, jiffies + msecs_to_jiffies(ba_mpdu_reorder_age_timeout));
|
|
ba_session_rx->timer_on = true;
|
|
}
|
|
} else if (!ba_session_rx->occupied_slots) {
|
|
stop_timer = true;
|
|
} else if (stop_timer) {
|
|
stop_timer = false;
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Timer restart\n");
|
|
mod_timer(&ba_session_rx->ba_age_timer, jiffies + msecs_to_jiffies(ba_mpdu_reorder_age_timeout));
|
|
ba_session_rx->timer_on = true;
|
|
}
|
|
|
|
if (stop_timer) {
|
|
ba_session_rx->timer_on = false;
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Timer stop\n");
|
|
del_timer_sync(&ba_session_rx->ba_age_timer);
|
|
} else {
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
}
|
|
slsi_ba_signal_process_complete(dev);
|
|
return 0;
|
|
}
|
|
|
|
bool slsi_ba_check(struct slsi_peer *peer, u16 tid)
|
|
{
|
|
if (tid > FAPI_PRIORITY_QOS_UP7)
|
|
return false;
|
|
if (!peer->ba_session_rx[tid])
|
|
return false;
|
|
|
|
return peer->ba_session_rx[tid]->active;
|
|
}
|
|
|
|
static void __slsi_rx_ba_stop(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx)
|
|
{
|
|
u8 i, j;
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_RX_BA, "Stopping BA session: tid = %d\n", ba_session_rx->tid);
|
|
|
|
if (WARN_ON(!ba_session_rx->active)) {
|
|
SLSI_NET_ERR(dev, "No BA session exists\n");
|
|
return;
|
|
}
|
|
|
|
for (i = SN_TO_INDEX(ba_session_rx, ba_session_rx->expected_sn), j = 0;
|
|
j < ba_session_rx->buffer_size; i++, j++) {
|
|
i %= ba_session_rx->buffer_size;
|
|
if (ba_session_rx->buffer[i].active) {
|
|
ba_add_frame_to_ba_complete(dev, ba_session_rx, &ba_session_rx->buffer[i]);
|
|
SLSI_NET_DBG3(dev, SLSI_RX_BA, "Completed stored frame at i = %d\n", i);
|
|
FREE_BUFFER_SLOT(ba_session_rx, i);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_RX_NAPI
|
|
slsi_ba_process_complete(dev, false);
|
|
#else
|
|
slsi_ba_signal_process_complete(dev);
|
|
#endif
|
|
|
|
ba_session_rx->active = false;
|
|
}
|
|
|
|
static void slsi_rx_ba_stop_lock_held(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
|
|
__slsi_rx_ba_stop(dev, ba_session_rx);
|
|
ba_session_rx->timer_on = false;
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
del_timer_sync(&ba_session_rx->ba_age_timer);
|
|
slsi_spinlock_lock(&ndev_vif->ba_lock);
|
|
}
|
|
|
|
static void slsi_rx_ba_stop_lock_unheld(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
|
|
slsi_spinlock_lock(&ndev_vif->ba_lock);
|
|
__slsi_rx_ba_stop(dev, ba_session_rx);
|
|
ba_session_rx->timer_on = false;
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
del_timer_sync(&ba_session_rx->ba_age_timer);
|
|
}
|
|
|
|
void slsi_rx_ba_stop_all(struct net_device *dev, struct slsi_peer *peer)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_BA_SESSIONS_PER_PEER; i++)
|
|
if (peer->ba_session_rx[i] && peer->ba_session_rx[i]->active) {
|
|
slsi_rx_ba_stop_lock_unheld(dev, peer->ba_session_rx[i]);
|
|
slsi_rx_ba_free_buffer(dev, peer, i);
|
|
}
|
|
}
|
|
|
|
static int slsi_rx_ba_start(struct net_device *dev,
|
|
struct slsi_peer *peer,
|
|
struct slsi_ba_session_rx *ba_session_rx,
|
|
u16 tid, u16 buffer_size, u16 start_sn)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_RX_BA, "Request to start a new BA session tid=%d buffer_size=%d start_sn=%d\n",
|
|
tid, buffer_size, start_sn);
|
|
|
|
if (WARN_ON((!buffer_size) || (buffer_size > SLSI_BA_BUFFER_SIZE_MAX))) {
|
|
SLSI_NET_ERR(dev, "Invalid window size: buffer_size=%d\n", buffer_size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
slsi_spinlock_lock(&ndev_vif->ba_lock);
|
|
|
|
if (ba_session_rx->active) {
|
|
SLSI_NET_DBG1(dev, SLSI_RX_BA, "BA session already exists\n");
|
|
|
|
if ((ba_session_rx->buffer_size == buffer_size) &&
|
|
(ba_session_rx->expected_sn == start_sn)) {
|
|
SLSI_NET_DBG1(dev, SLSI_RX_BA,
|
|
"BA session tid=%d already exists. The parameters match so keep the existing session\n",
|
|
tid);
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
return 0;
|
|
}
|
|
SLSI_NET_DBG1(dev, SLSI_RX_BA, "Parameters don't match so stop the existing BA session: tid=%d\n", tid);
|
|
slsi_rx_ba_stop_lock_held(dev, ba_session_rx);
|
|
}
|
|
|
|
ba_session_rx->dev = dev;
|
|
ba_session_rx->peer = peer;
|
|
ba_session_rx->buffer_size = buffer_size;
|
|
ba_session_rx->start_sn = start_sn;
|
|
ba_session_rx->expected_sn = start_sn;
|
|
ba_session_rx->highest_received_sn = 0;
|
|
ba_session_rx->closing = false;
|
|
ba_session_rx->trigger_ba_after_ssn = false;
|
|
ba_session_rx->tid = tid;
|
|
ba_session_rx->timer_on = false;
|
|
#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
|
|
timer_setup(&ba_session_rx->ba_age_timer, slsi_ba_aging_timeout_handler, 0);
|
|
#else
|
|
ba_session_rx->ba_age_timer.function = slsi_ba_aging_timeout_handler;
|
|
ba_session_rx->ba_age_timer.data = (unsigned long)ba_session_rx;
|
|
init_timer(&ba_session_rx->ba_age_timer);
|
|
#endif
|
|
|
|
ba_session_rx->ba_timeouts = 0;
|
|
ba_session_rx->ba_drops_old = 0;
|
|
ba_session_rx->ba_drops_replay = 0;
|
|
|
|
ba_session_rx->active = true;
|
|
SLSI_NET_DBG1(dev, SLSI_RX_BA, "Started a new BA session tid=%d buffer_size=%d start_sn=%d\n",
|
|
tid, buffer_size, start_sn);
|
|
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
slsi_ba_signal_process_complete(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void slsi_ba_update_window(struct net_device *dev,
|
|
struct slsi_ba_session_rx *ba_session_rx, u16 sequence_number)
|
|
{
|
|
struct netdev_vif *ndev_vif = netdev_priv(dev);
|
|
|
|
slsi_spinlock_lock(&ndev_vif->ba_lock);
|
|
|
|
if (WARN_ON(!ba_session_rx->active)) {
|
|
SLSI_NET_ERR(dev, "No BA session exists\n");
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
return;
|
|
}
|
|
|
|
ba_scroll_window(dev, ba_session_rx, sequence_number);
|
|
#ifdef CONFIG_SCSC_WLAN_RX_NAPI
|
|
slsi_ba_process_complete(dev, false);
|
|
#else
|
|
slsi_ba_signal_process_complete(dev);
|
|
#endif
|
|
slsi_spinlock_unlock(&ndev_vif->ba_lock);
|
|
}
|
|
|
|
void slsi_handle_blockack(struct net_device *dev, struct slsi_peer *peer,
|
|
u16 reason_code, u16 user_priority, u16 buffer_size, u16 sequence_number)
|
|
{
|
|
struct slsi_ba_session_rx *ba_session_rx;
|
|
|
|
if (WARN_ON(user_priority > FAPI_PRIORITY_QOS_UP7)) {
|
|
SLSI_NET_ERR(dev, "Invalid user_priority=%d\n", user_priority);
|
|
return;
|
|
}
|
|
|
|
SLSI_NET_DBG1(dev, SLSI_MLME, "reason_code:%d, user_priority:%d, buffer_size:%d, sequence_num:%d)\n",
|
|
reason_code,
|
|
user_priority,
|
|
buffer_size,
|
|
sequence_number);
|
|
|
|
ba_session_rx = peer->ba_session_rx[user_priority];
|
|
|
|
switch (reason_code) {
|
|
case FAPI_REASONCODE_START:
|
|
if (!peer->ba_session_rx[user_priority])
|
|
peer->ba_session_rx[user_priority] = slsi_rx_ba_alloc_buffer(dev);
|
|
|
|
if (peer->ba_session_rx[user_priority])
|
|
if (slsi_rx_ba_start(dev, peer, peer->ba_session_rx[user_priority], user_priority, buffer_size, sequence_number) != 0)
|
|
slsi_rx_ba_free_buffer(dev, peer, user_priority);
|
|
break;
|
|
case FAPI_REASONCODE_END:
|
|
if (ba_session_rx) {
|
|
slsi_rx_ba_stop_lock_unheld(dev, ba_session_rx);
|
|
slsi_rx_ba_free_buffer(dev, peer, user_priority);
|
|
}
|
|
break;
|
|
case FAPI_REASONCODE_UNSPECIFIED_REASON:
|
|
if (ba_session_rx)
|
|
slsi_ba_update_window(dev, ba_session_rx, sequence_number);
|
|
break;
|
|
default:
|
|
SLSI_NET_ERR(dev, "Invalid value: reason_code=%d\n", reason_code);
|
|
break;
|
|
}
|
|
}
|