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