162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NXP Wireless LAN device driver: 802.11n Aggregation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2011-2020 NXP 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "decl.h" 962306a36Sopenharmony_ci#include "ioctl.h" 1062306a36Sopenharmony_ci#include "util.h" 1162306a36Sopenharmony_ci#include "fw.h" 1262306a36Sopenharmony_ci#include "main.h" 1362306a36Sopenharmony_ci#include "wmm.h" 1462306a36Sopenharmony_ci#include "11n.h" 1562306a36Sopenharmony_ci#include "11n_aggr.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Creates an AMSDU subframe for aggregation into one AMSDU packet. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * The resultant AMSDU subframe format is - 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ 2362306a36Sopenharmony_ci * | DA | SA | Length | SNAP header | MSDU | 2462306a36Sopenharmony_ci * | data[0..5] | data[6..11] | | | data[14..] | 2562306a36Sopenharmony_ci * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ 2662306a36Sopenharmony_ci * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes--> 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * This function also computes the amount of padding required to make the 2962306a36Sopenharmony_ci * buffer length multiple of 4 bytes. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Data => |DA|SA|SNAP-TYPE|........ .| 3262306a36Sopenharmony_ci * MSDU => |DA|SA|Length|SNAP|...... ..| 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic int 3562306a36Sopenharmony_cimwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, 3662306a36Sopenharmony_ci struct sk_buff *skb_src, int *pad) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int dt_offset; 4062306a36Sopenharmony_ci struct rfc_1042_hdr snap = { 4162306a36Sopenharmony_ci 0xaa, /* LLC DSAP */ 4262306a36Sopenharmony_ci 0xaa, /* LLC SSAP */ 4362306a36Sopenharmony_ci 0x03, /* LLC CTRL */ 4462306a36Sopenharmony_ci {0x00, 0x00, 0x00}, /* SNAP OUI */ 4562306a36Sopenharmony_ci 0x0000 /* SNAP type */ 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * This field will be overwritten 4862306a36Sopenharmony_ci * later with ethertype 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci }; 5162306a36Sopenharmony_ci struct tx_packet_hdr *tx_header; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci tx_header = skb_put(skb_aggr, sizeof(*tx_header)); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Copy DA and SA */ 5662306a36Sopenharmony_ci dt_offset = 2 * ETH_ALEN; 5762306a36Sopenharmony_ci memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* Copy SNAP header */ 6062306a36Sopenharmony_ci snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci dt_offset += sizeof(__be16); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci skb_pull(skb_src, dt_offset); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Update Length field */ 6962306a36Sopenharmony_ci tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* Add payload */ 7262306a36Sopenharmony_ci skb_put_data(skb_aggr, skb_src->data, skb_src->len); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Add padding for new MSDU to start from 4 byte boundary */ 7562306a36Sopenharmony_ci *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return skb_aggr->len + *pad; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * Adds TxPD to AMSDU header. 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * Each AMSDU packet will contain one TxPD at the beginning, 8462306a36Sopenharmony_ci * followed by multiple AMSDU subframes. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistatic void 8762306a36Sopenharmony_cimwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, 8862306a36Sopenharmony_ci struct sk_buff *skb) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct txpd *local_tx_pd; 9162306a36Sopenharmony_ci struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci skb_push(skb, sizeof(*local_tx_pd)); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci local_tx_pd = (struct txpd *) skb->data; 9662306a36Sopenharmony_ci memset(local_tx_pd, 0, sizeof(struct txpd)); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Original priority has been overwritten */ 9962306a36Sopenharmony_ci local_tx_pd->priority = (u8) skb->priority; 10062306a36Sopenharmony_ci local_tx_pd->pkt_delay_2ms = 10162306a36Sopenharmony_ci mwifiex_wmm_compute_drv_pkt_delay(priv, skb); 10262306a36Sopenharmony_ci local_tx_pd->bss_num = priv->bss_num; 10362306a36Sopenharmony_ci local_tx_pd->bss_type = priv->bss_type; 10462306a36Sopenharmony_ci /* Always zero as the data is followed by struct txpd */ 10562306a36Sopenharmony_ci local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); 10662306a36Sopenharmony_ci local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); 10762306a36Sopenharmony_ci local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - 10862306a36Sopenharmony_ci sizeof(*local_tx_pd)); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) 11162306a36Sopenharmony_ci local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (local_tx_pd->tx_control == 0) 11462306a36Sopenharmony_ci /* TxCtrl set by user or default */ 11562306a36Sopenharmony_ci local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && 11862306a36Sopenharmony_ci priv->adapter->pps_uapsd_mode) { 11962306a36Sopenharmony_ci if (true == mwifiex_check_last_packet_indication(priv)) { 12062306a36Sopenharmony_ci priv->adapter->tx_lock_flag = true; 12162306a36Sopenharmony_ci local_tx_pd->flags = 12262306a36Sopenharmony_ci MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * Create aggregated packet. 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * This function creates an aggregated MSDU packet, by combining buffers 13162306a36Sopenharmony_ci * from the RA list. Each individual buffer is encapsulated as an AMSDU 13262306a36Sopenharmony_ci * subframe and all such subframes are concatenated together to form the 13362306a36Sopenharmony_ci * AMSDU packet. 13462306a36Sopenharmony_ci * 13562306a36Sopenharmony_ci * A TxPD is also added to the front of the resultant AMSDU packets for 13662306a36Sopenharmony_ci * transmission. The resultant packets format is - 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ 13962306a36Sopenharmony_ci * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| 14062306a36Sopenharmony_ci * | | 1 | 2 | .. | n | 14162306a36Sopenharmony_ci * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ciint 14462306a36Sopenharmony_cimwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, 14562306a36Sopenharmony_ci struct mwifiex_ra_list_tbl *pra_list, 14662306a36Sopenharmony_ci int ptrindex) 14762306a36Sopenharmony_ci __releases(&priv->wmm.ra_list_spinlock) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 15062306a36Sopenharmony_ci struct sk_buff *skb_aggr, *skb_src; 15162306a36Sopenharmony_ci struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; 15262306a36Sopenharmony_ci int pad = 0, aggr_num = 0, ret; 15362306a36Sopenharmony_ci struct mwifiex_tx_param tx_param; 15462306a36Sopenharmony_ci struct txpd *ptx_pd = NULL; 15562306a36Sopenharmony_ci int headroom = adapter->intf_hdr_len; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci skb_src = skb_peek(&pra_list->skb_head); 15862306a36Sopenharmony_ci if (!skb_src) { 15962306a36Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci tx_info_src = MWIFIEX_SKB_TXCB(skb_src); 16462306a36Sopenharmony_ci skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size, 16562306a36Sopenharmony_ci GFP_ATOMIC); 16662306a36Sopenharmony_ci if (!skb_aggr) { 16762306a36Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 16862306a36Sopenharmony_ci return -1; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* skb_aggr->data already 64 byte align, just reserve bus interface 17262306a36Sopenharmony_ci * header and txpd. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); 17562306a36Sopenharmony_ci tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); 17862306a36Sopenharmony_ci tx_info_aggr->bss_type = tx_info_src->bss_type; 17962306a36Sopenharmony_ci tx_info_aggr->bss_num = tx_info_src->bss_num; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) 18262306a36Sopenharmony_ci tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; 18362306a36Sopenharmony_ci tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; 18462306a36Sopenharmony_ci skb_aggr->priority = skb_src->priority; 18562306a36Sopenharmony_ci skb_aggr->tstamp = skb_src->tstamp; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci do { 18862306a36Sopenharmony_ci /* Check if AMSDU can accommodate this MSDU */ 18962306a36Sopenharmony_ci if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) > 19062306a36Sopenharmony_ci adapter->tx_buf_size) 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci skb_src = skb_dequeue(&pra_list->skb_head); 19462306a36Sopenharmony_ci pra_list->total_pkt_count--; 19562306a36Sopenharmony_ci atomic_dec(&priv->wmm.tx_pkts_queued); 19662306a36Sopenharmony_ci aggr_num++; 19762306a36Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 19862306a36Sopenharmony_ci mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_src, 0, 0); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { 20562306a36Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 20662306a36Sopenharmony_ci return -1; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (skb_tailroom(skb_aggr) < pad) { 21062306a36Sopenharmony_ci pad = 0; 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci skb_put(skb_aggr, pad); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci skb_src = skb_peek(&pra_list->skb_head); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci } while (skb_src); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Last AMSDU packet does not need padding */ 22262306a36Sopenharmony_ci skb_trim(skb_aggr, skb_aggr->len - pad); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Form AMSDU */ 22562306a36Sopenharmony_ci mwifiex_11n_form_amsdu_txpd(priv, skb_aggr); 22662306a36Sopenharmony_ci if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) 22762306a36Sopenharmony_ci ptx_pd = (struct txpd *)skb_aggr->data; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci skb_push(skb_aggr, headroom); 23062306a36Sopenharmony_ci tx_info_aggr->aggr_num = aggr_num * 2; 23162306a36Sopenharmony_ci if (adapter->data_sent || adapter->tx_lock_flag) { 23262306a36Sopenharmony_ci atomic_add(aggr_num * 2, &adapter->tx_queued); 23362306a36Sopenharmony_ci skb_queue_tail(&adapter->tx_data_q, skb_aggr); 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (skb_src) 23862306a36Sopenharmony_ci tx_param.next_pkt_len = skb_src->len + sizeof(struct txpd); 23962306a36Sopenharmony_ci else 24062306a36Sopenharmony_ci tx_param.next_pkt_len = 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (adapter->iface_type == MWIFIEX_USB) { 24362306a36Sopenharmony_ci ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, 24462306a36Sopenharmony_ci skb_aggr, &tx_param); 24562306a36Sopenharmony_ci } else { 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, 24862306a36Sopenharmony_ci skb_aggr, &tx_param); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci switch (ret) { 25162306a36Sopenharmony_ci case -EBUSY: 25262306a36Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 25362306a36Sopenharmony_ci if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { 25462306a36Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 25562306a36Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_aggr, 1, -1); 25662306a36Sopenharmony_ci return -1; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && 25962306a36Sopenharmony_ci adapter->pps_uapsd_mode && adapter->tx_lock_flag) { 26062306a36Sopenharmony_ci priv->adapter->tx_lock_flag = false; 26162306a36Sopenharmony_ci if (ptx_pd) 26262306a36Sopenharmony_ci ptx_pd->flags = 0; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci skb_queue_tail(&pra_list->skb_head, skb_aggr); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci pra_list->total_pkt_count++; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci atomic_inc(&priv->wmm.tx_pkts_queued); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; 27262306a36Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 27362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case -1: 27662306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", 27762306a36Sopenharmony_ci __func__, ret); 27862306a36Sopenharmony_ci adapter->dbg.num_tx_host_to_card_failure++; 27962306a36Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci case -EINPROGRESS: 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case 0: 28462306a36Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci default: 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci if (ret != -EBUSY) { 29062306a36Sopenharmony_ci mwifiex_rotate_priolists(priv, pra_list, ptrindex); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci} 295