1*73b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
2*73b01e57SJeff Chen /*
3*73b01e57SJeff Chen * nxpwifi: 802.11n Aggregation
4*73b01e57SJeff Chen *
5*73b01e57SJeff Chen * Copyright 2011-2024 NXP
6*73b01e57SJeff Chen */
7*73b01e57SJeff Chen
8*73b01e57SJeff Chen #include "cfg.h"
9*73b01e57SJeff Chen #include "util.h"
10*73b01e57SJeff Chen #include "fw.h"
11*73b01e57SJeff Chen #include "main.h"
12*73b01e57SJeff Chen #include "wmm.h"
13*73b01e57SJeff Chen #include "11n.h"
14*73b01e57SJeff Chen #include "11n_aggr.h"
15*73b01e57SJeff Chen
16*73b01e57SJeff Chen /*
17*73b01e57SJeff Chen * Build an AMSDU subframe for aggregation, with fields
18*73b01e57SJeff Chen * (DA | SA | Length | SNAP header | MSDU), and compute padding
19*73b01e57SJeff Chen * to align the subframe to a 4-byte boundary.
20*73b01e57SJeff Chen */
nxpwifi_11n_form_amsdu_pkt(struct sk_buff * skb_aggr,struct sk_buff * skb_src,int * pad)21*73b01e57SJeff Chen static int nxpwifi_11n_form_amsdu_pkt(struct sk_buff *skb_aggr,
22*73b01e57SJeff Chen struct sk_buff *skb_src, int *pad)
23*73b01e57SJeff Chen
24*73b01e57SJeff Chen {
25*73b01e57SJeff Chen int dt_offset;
26*73b01e57SJeff Chen struct rfc_1042_hdr snap = {
27*73b01e57SJeff Chen 0xaa, /* LLC DSAP */
28*73b01e57SJeff Chen 0xaa, /* LLC SSAP */
29*73b01e57SJeff Chen 0x03, /* LLC CTRL */
30*73b01e57SJeff Chen {0x00, 0x00, 0x00}, /* SNAP OUI */
31*73b01e57SJeff Chen 0x0000 /* SNAP type */
32*73b01e57SJeff Chen /* This field will be overwritten later with ethertype */
33*73b01e57SJeff Chen };
34*73b01e57SJeff Chen struct tx_packet_hdr *tx_header;
35*73b01e57SJeff Chen
36*73b01e57SJeff Chen tx_header = skb_put(skb_aggr, sizeof(*tx_header));
37*73b01e57SJeff Chen
38*73b01e57SJeff Chen /* Copy DA and SA */
39*73b01e57SJeff Chen dt_offset = 2 * ETH_ALEN;
40*73b01e57SJeff Chen memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
41*73b01e57SJeff Chen
42*73b01e57SJeff Chen /* Copy SNAP header */
43*73b01e57SJeff Chen snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto;
44*73b01e57SJeff Chen
45*73b01e57SJeff Chen dt_offset += sizeof(__be16);
46*73b01e57SJeff Chen
47*73b01e57SJeff Chen memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
48*73b01e57SJeff Chen
49*73b01e57SJeff Chen skb_pull(skb_src, dt_offset);
50*73b01e57SJeff Chen
51*73b01e57SJeff Chen /* Update Length field */
52*73b01e57SJeff Chen tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN);
53*73b01e57SJeff Chen
54*73b01e57SJeff Chen /* Add payload */
55*73b01e57SJeff Chen skb_put_data(skb_aggr, skb_src->data, skb_src->len);
56*73b01e57SJeff Chen
57*73b01e57SJeff Chen /* Add padding for new MSDU to start from 4 byte boundary */
58*73b01e57SJeff Chen *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4;
59*73b01e57SJeff Chen
60*73b01e57SJeff Chen return skb_aggr->len + *pad;
61*73b01e57SJeff Chen }
62*73b01e57SJeff Chen
63*73b01e57SJeff Chen /*
64*73b01e57SJeff Chen * Adds TxPD to AMSDU header. Each AMSDU packet will contain one TxPD at the
65*73b01e57SJeff Chen * beginning, followed by multiple AMSDU subframes
66*73b01e57SJeff Chen */
67*73b01e57SJeff Chen static void
nxpwifi_11n_form_amsdu_txpd(struct nxpwifi_private * priv,struct sk_buff * skb)68*73b01e57SJeff Chen nxpwifi_11n_form_amsdu_txpd(struct nxpwifi_private *priv,
69*73b01e57SJeff Chen struct sk_buff *skb)
70*73b01e57SJeff Chen {
71*73b01e57SJeff Chen struct txpd *local_tx_pd;
72*73b01e57SJeff Chen
73*73b01e57SJeff Chen skb_push(skb, sizeof(*local_tx_pd));
74*73b01e57SJeff Chen
75*73b01e57SJeff Chen local_tx_pd = (struct txpd *)skb->data;
76*73b01e57SJeff Chen memset(local_tx_pd, 0, sizeof(struct txpd));
77*73b01e57SJeff Chen
78*73b01e57SJeff Chen /* Original priority has been overwritten */
79*73b01e57SJeff Chen local_tx_pd->priority = (u8)skb->priority;
80*73b01e57SJeff Chen local_tx_pd->pkt_delay_2ms =
81*73b01e57SJeff Chen nxpwifi_wmm_compute_drv_pkt_delay(priv, skb);
82*73b01e57SJeff Chen local_tx_pd->bss_num = priv->bss_num;
83*73b01e57SJeff Chen local_tx_pd->bss_type = priv->bss_type;
84*73b01e57SJeff Chen /* Always zero as the data is followed by struct txpd */
85*73b01e57SJeff Chen local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
86*73b01e57SJeff Chen local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU);
87*73b01e57SJeff Chen local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
88*73b01e57SJeff Chen sizeof(*local_tx_pd));
89*73b01e57SJeff Chen
90*73b01e57SJeff Chen if (local_tx_pd->tx_control == 0)
91*73b01e57SJeff Chen /* TxCtrl set by user or default */
92*73b01e57SJeff Chen local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
93*73b01e57SJeff Chen
94*73b01e57SJeff Chen if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA &&
95*73b01e57SJeff Chen priv->adapter->pps_uapsd_mode) {
96*73b01e57SJeff Chen if (nxpwifi_check_last_packet_indication(priv)) {
97*73b01e57SJeff Chen priv->adapter->tx_lock_flag = true;
98*73b01e57SJeff Chen local_tx_pd->flags =
99*73b01e57SJeff Chen NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET;
100*73b01e57SJeff Chen }
101*73b01e57SJeff Chen }
102*73b01e57SJeff Chen }
103*73b01e57SJeff Chen
104*73b01e57SJeff Chen /*
105*73b01e57SJeff Chen * Build an aggregated MSDU packet by encapsulating buffers from the RA
106*73b01e57SJeff Chen * list as AMSDU subframes and concatenating them. A TxPD is prepended
107*73b01e57SJeff Chen * before transmission to form the final AMSDU packet.
108*73b01e57SJeff Chen */
109*73b01e57SJeff Chen int
nxpwifi_11n_aggregate_pkt(struct nxpwifi_private * priv,struct nxpwifi_ra_list_tbl * pra_list,int ptrindex)110*73b01e57SJeff Chen nxpwifi_11n_aggregate_pkt(struct nxpwifi_private *priv,
111*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *pra_list,
112*73b01e57SJeff Chen int ptrindex)
113*73b01e57SJeff Chen __releases(&priv->wmm.ra_list_spinlock)
114*73b01e57SJeff Chen {
115*73b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
116*73b01e57SJeff Chen struct sk_buff *skb_aggr, *skb_src;
117*73b01e57SJeff Chen struct nxpwifi_txinfo *tx_info_aggr, *tx_info_src;
118*73b01e57SJeff Chen int pad = 0, aggr_num = 0, ret;
119*73b01e57SJeff Chen struct nxpwifi_tx_param tx_param;
120*73b01e57SJeff Chen struct txpd *ptx_pd = NULL;
121*73b01e57SJeff Chen int headroom = adapter->intf_hdr_len;
122*73b01e57SJeff Chen
123*73b01e57SJeff Chen skb_src = skb_peek(&pra_list->skb_head);
124*73b01e57SJeff Chen if (!skb_src) {
125*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
126*73b01e57SJeff Chen return 0;
127*73b01e57SJeff Chen }
128*73b01e57SJeff Chen
129*73b01e57SJeff Chen tx_info_src = NXPWIFI_SKB_TXCB(skb_src);
130*73b01e57SJeff Chen skb_aggr = nxpwifi_alloc_dma_align_buf(adapter->tx_buf_size,
131*73b01e57SJeff Chen GFP_ATOMIC);
132*73b01e57SJeff Chen if (!skb_aggr) {
133*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
134*73b01e57SJeff Chen return -ENOMEM;
135*73b01e57SJeff Chen }
136*73b01e57SJeff Chen
137*73b01e57SJeff Chen /*
138*73b01e57SJeff Chen * skb_aggr->data already 64 byte align, just reserve bus interface
139*73b01e57SJeff Chen * header and txpd.
140*73b01e57SJeff Chen */
141*73b01e57SJeff Chen skb_reserve(skb_aggr, headroom + sizeof(struct txpd));
142*73b01e57SJeff Chen tx_info_aggr = NXPWIFI_SKB_TXCB(skb_aggr);
143*73b01e57SJeff Chen
144*73b01e57SJeff Chen memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
145*73b01e57SJeff Chen tx_info_aggr->bss_type = tx_info_src->bss_type;
146*73b01e57SJeff Chen tx_info_aggr->bss_num = tx_info_src->bss_num;
147*73b01e57SJeff Chen
148*73b01e57SJeff Chen tx_info_aggr->flags |= NXPWIFI_BUF_FLAG_AGGR_PKT;
149*73b01e57SJeff Chen skb_aggr->priority = skb_src->priority;
150*73b01e57SJeff Chen skb_aggr->tstamp = skb_src->tstamp;
151*73b01e57SJeff Chen
152*73b01e57SJeff Chen do {
153*73b01e57SJeff Chen /* Check if AMSDU can accommodate this MSDU */
154*73b01e57SJeff Chen if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) >
155*73b01e57SJeff Chen adapter->tx_buf_size)
156*73b01e57SJeff Chen break;
157*73b01e57SJeff Chen
158*73b01e57SJeff Chen skb_src = skb_dequeue(&pra_list->skb_head);
159*73b01e57SJeff Chen pra_list->total_pkt_count--;
160*73b01e57SJeff Chen atomic_dec(&priv->wmm.tx_pkts_queued);
161*73b01e57SJeff Chen aggr_num++;
162*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
163*73b01e57SJeff Chen nxpwifi_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad);
164*73b01e57SJeff Chen
165*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb_src, 0, 0);
166*73b01e57SJeff Chen
167*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
168*73b01e57SJeff Chen
169*73b01e57SJeff Chen if (!nxpwifi_is_ralist_valid(priv, pra_list, ptrindex)) {
170*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
171*73b01e57SJeff Chen return -ENOENT;
172*73b01e57SJeff Chen }
173*73b01e57SJeff Chen
174*73b01e57SJeff Chen if (skb_tailroom(skb_aggr) < pad) {
175*73b01e57SJeff Chen pad = 0;
176*73b01e57SJeff Chen break;
177*73b01e57SJeff Chen }
178*73b01e57SJeff Chen skb_put(skb_aggr, pad);
179*73b01e57SJeff Chen
180*73b01e57SJeff Chen skb_src = skb_peek(&pra_list->skb_head);
181*73b01e57SJeff Chen
182*73b01e57SJeff Chen } while (skb_src);
183*73b01e57SJeff Chen
184*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
185*73b01e57SJeff Chen
186*73b01e57SJeff Chen /* Last AMSDU packet does not need padding */
187*73b01e57SJeff Chen skb_trim(skb_aggr, skb_aggr->len - pad);
188*73b01e57SJeff Chen
189*73b01e57SJeff Chen /* Form AMSDU */
190*73b01e57SJeff Chen nxpwifi_11n_form_amsdu_txpd(priv, skb_aggr);
191*73b01e57SJeff Chen if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
192*73b01e57SJeff Chen ptx_pd = (struct txpd *)skb_aggr->data;
193*73b01e57SJeff Chen
194*73b01e57SJeff Chen skb_push(skb_aggr, headroom);
195*73b01e57SJeff Chen tx_info_aggr->aggr_num = aggr_num * 2;
196*73b01e57SJeff Chen if (adapter->data_sent || adapter->tx_lock_flag) {
197*73b01e57SJeff Chen atomic_add(aggr_num * 2, &adapter->tx_queued);
198*73b01e57SJeff Chen skb_queue_tail(&adapter->tx_data_q, skb_aggr);
199*73b01e57SJeff Chen return 0;
200*73b01e57SJeff Chen }
201*73b01e57SJeff Chen
202*73b01e57SJeff Chen if (skb_src)
203*73b01e57SJeff Chen tx_param.next_pkt_len = skb_src->len + sizeof(struct txpd);
204*73b01e57SJeff Chen else
205*73b01e57SJeff Chen tx_param.next_pkt_len = 0;
206*73b01e57SJeff Chen
207*73b01e57SJeff Chen ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA,
208*73b01e57SJeff Chen skb_aggr, &tx_param);
209*73b01e57SJeff Chen
210*73b01e57SJeff Chen switch (ret) {
211*73b01e57SJeff Chen case -EBUSY:
212*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
213*73b01e57SJeff Chen if (!nxpwifi_is_ralist_valid(priv, pra_list, ptrindex)) {
214*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
215*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb_aggr, 1, -1);
216*73b01e57SJeff Chen return -EINVAL;
217*73b01e57SJeff Chen }
218*73b01e57SJeff Chen if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA &&
219*73b01e57SJeff Chen adapter->pps_uapsd_mode && adapter->tx_lock_flag) {
220*73b01e57SJeff Chen priv->adapter->tx_lock_flag = false;
221*73b01e57SJeff Chen if (ptx_pd)
222*73b01e57SJeff Chen ptx_pd->flags = 0;
223*73b01e57SJeff Chen }
224*73b01e57SJeff Chen
225*73b01e57SJeff Chen skb_queue_tail(&pra_list->skb_head, skb_aggr);
226*73b01e57SJeff Chen
227*73b01e57SJeff Chen pra_list->total_pkt_count++;
228*73b01e57SJeff Chen
229*73b01e57SJeff Chen atomic_inc(&priv->wmm.tx_pkts_queued);
230*73b01e57SJeff Chen
231*73b01e57SJeff Chen tx_info_aggr->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
232*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
233*73b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
234*73b01e57SJeff Chen break;
235*73b01e57SJeff Chen case -EINPROGRESS:
236*73b01e57SJeff Chen break;
237*73b01e57SJeff Chen case 0:
238*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb_aggr, 1, ret);
239*73b01e57SJeff Chen break;
240*73b01e57SJeff Chen default:
241*73b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n",
242*73b01e57SJeff Chen __func__, ret);
243*73b01e57SJeff Chen adapter->dbg.num_tx_host_to_card_failure++;
244*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb_aggr, 1, ret);
245*73b01e57SJeff Chen break;
246*73b01e57SJeff Chen }
247*73b01e57SJeff Chen if (ret != -EBUSY)
248*73b01e57SJeff Chen nxpwifi_rotate_priolists(priv, pra_list, ptrindex);
249*73b01e57SJeff Chen
250*73b01e57SJeff Chen return 0;
251*73b01e57SJeff Chen }
252