xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/nxp/mlan/mlan_11n_aggr.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /** @file mlan_11n_aggr.c
2  *
3  *  @brief This file contains functions for 11n Aggregation.
4  *
5  *
6  *  Copyright 2008-2021 NXP
7  *
8  *  This software file (the File) is distributed by NXP
9  *  under the terms of the GNU General Public License Version 2, June 1991
10  *  (the License).  You may use, redistribute and/or modify the File in
11  *  accordance with the terms and conditions of the License, a copy of which
12  *  is available by writing to the Free Software Foundation, Inc.,
13  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
14  *  worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
15  *
16  *  THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
17  *  IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
18  *  ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
19  *  this warranty disclaimer.
20  *
21  */
22 
23 /********************************************************
24 Change log:
25     11/10/2008: initial version
26 ********************************************************/
27 
28 #include "mlan.h"
29 #include "mlan_join.h"
30 #include "mlan_util.h"
31 #include "mlan_fw.h"
32 #include "mlan_main.h"
33 #include "mlan_wmm.h"
34 #include "mlan_11n.h"
35 #include "mlan_11n_aggr.h"
36 
37 /********************************************************
38 			Local Variables
39 ********************************************************/
40 
41 /********************************************************
42 			Global Variables
43 ********************************************************/
44 
45 /********************************************************
46 			Local Functions
47 ********************************************************/
48 /**
49  *  @brief Aggregate individual packets into one AMSDU packet
50  *
51  *  @param pmadapter A pointer to mlan_adapter structure
52  *  @param amsdu_buf A pointer to packet buffer
53  *  @param data      A pointer to aggregated data packet being formed
54  *  @param pkt_len   Length of current packet to aggregate
55  *  @param pad       Pad
56  *
57  *  @return         Final packet size
58  */
wlan_11n_form_amsdu_pkt(pmlan_adapter pmadapter,t_u8 * amsdu_buf,t_u8 * data,int pkt_len,int * pad)59 static int wlan_11n_form_amsdu_pkt(pmlan_adapter pmadapter, t_u8 *amsdu_buf,
60 				   t_u8 *data, int pkt_len, int *pad)
61 {
62 	int dt_offset, amsdu_buf_offset;
63 	Rfc1042Hdr_t snap = {
64 		0xaa, /* LLC DSAP */
65 		0xaa, /* LLC SSAP */
66 		0x03, /* LLC CTRL */
67 		{0x00, 0x00, 0x00}, /* SNAP OUI */
68 		0x0000 /* SNAP type */
69 		/*
70 		 * This field will be overwritten
71 		 * later with ethertype
72 		 */
73 	};
74 
75 	ENTER();
76 
77 	memcpy_ext(pmadapter, amsdu_buf, data, (MLAN_MAC_ADDR_LENGTH)*2,
78 		   (MLAN_MAC_ADDR_LENGTH)*2);
79 	dt_offset = amsdu_buf_offset = (MLAN_MAC_ADDR_LENGTH)*2;
80 
81 	snap.snap_type = *(t_u16 *)(data + dt_offset);
82 	dt_offset += sizeof(t_u16);
83 	*(t_u16 *)(amsdu_buf + amsdu_buf_offset) =
84 		mlan_htons(pkt_len + LLC_SNAP_LEN -
85 			   ((2 * MLAN_MAC_ADDR_LENGTH) + sizeof(t_u16)));
86 	amsdu_buf_offset += sizeof(t_u16);
87 	memcpy_ext(pmadapter, amsdu_buf + amsdu_buf_offset, &snap, LLC_SNAP_LEN,
88 		   LLC_SNAP_LEN);
89 	amsdu_buf_offset += LLC_SNAP_LEN;
90 
91 	memcpy_ext(pmadapter, amsdu_buf + amsdu_buf_offset, data + dt_offset,
92 		   pkt_len - dt_offset, pkt_len - dt_offset);
93 	*pad = (((pkt_len + LLC_SNAP_LEN) & 3)) ?
94 		       (4 - (((pkt_len + LLC_SNAP_LEN)) & 3)) :
95 		       0;
96 
97 	LEAVE();
98 	return pkt_len + LLC_SNAP_LEN + *pad;
99 }
100 
101 /**
102  *  @brief Add TxPD to AMSDU header
103  *
104  *  @param priv     A pointer to mlan_private structure
105  *  @param mbuf		Pointer to buffer where the TxPD will be formed
106  *
107  *  @return		N/A
108  */
wlan_11n_form_amsdu_txpd(mlan_private * priv,mlan_buffer * mbuf)109 static void wlan_11n_form_amsdu_txpd(mlan_private *priv, mlan_buffer *mbuf)
110 {
111 	TxPD *ptx_pd;
112 	mlan_adapter *pmadapter = priv->adapter;
113 
114 	ENTER();
115 
116 	ptx_pd = (TxPD *)mbuf->pbuf;
117 	memset(pmadapter, ptx_pd, 0, sizeof(TxPD));
118 
119 	/*
120 	 * Original priority has been overwritten
121 	 */
122 	ptx_pd->priority = (t_u8)mbuf->priority;
123 	ptx_pd->pkt_delay_2ms =
124 		wlan_wmm_compute_driver_packet_delay(priv, mbuf);
125 	ptx_pd->bss_num = GET_BSS_NUM(priv);
126 	ptx_pd->bss_type = priv->bss_type;
127 	/* Always zero as the data is followed by TxPD */
128 	ptx_pd->tx_pkt_offset = sizeof(TxPD);
129 	ptx_pd->tx_pkt_type = PKT_TYPE_AMSDU;
130 	if (mbuf->flags & MLAN_BUF_FLAG_TDLS)
131 		ptx_pd->flags = MRVDRV_TxPD_FLAGS_TDLS_PACKET;
132 	if (ptx_pd->tx_control == 0)
133 		/* TxCtrl set by user or default */
134 		ptx_pd->tx_control = priv->pkt_tx_ctrl;
135 
136 	endian_convert_TxPD(ptx_pd);
137 
138 	LEAVE();
139 }
140 
141 /**
142  *  @brief Update the TxPktLength field in TxPD after the complete AMSDU
143  *  packet is formed
144  *
145  *  @param priv     A pointer to mlan_private structure
146  *  @param mbuf     TxPD buffer
147  *
148  *  @return         N/A
149  */
wlan_11n_update_pktlen_amsdu_txpd(mlan_private * priv,pmlan_buffer mbuf)150 static INLINE void wlan_11n_update_pktlen_amsdu_txpd(mlan_private *priv,
151 						     pmlan_buffer mbuf)
152 {
153 	TxPD *ptx_pd;
154 	ENTER();
155 
156 	ptx_pd = (TxPD *)mbuf->pbuf;
157 	ptx_pd->tx_pkt_length =
158 		(t_u16)wlan_cpu_to_le16(mbuf->data_len - sizeof(TxPD));
159 #ifdef STA_SUPPORT
160 	if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
161 	    (priv->adapter->pps_uapsd_mode)) {
162 		if (MTRUE == wlan_check_last_packet_indication(priv)) {
163 			priv->adapter->tx_lock_flag = MTRUE;
164 			ptx_pd->flags |= MRVDRV_TxPD_POWER_MGMT_LAST_PACKET;
165 		}
166 	}
167 #endif /* STA_SUPPORT */
168 	LEAVE();
169 }
170 
171 /**
172  *  @brief Get number of aggregated packets
173  *
174  *  @param data			A pointer to packet data
175  *  @param total_pkt_len	Total packet length
176  *
177  *  @return			Number of packets
178  */
wlan_11n_get_num_aggrpkts(t_u8 * data,int total_pkt_len)179 static int wlan_11n_get_num_aggrpkts(t_u8 *data, int total_pkt_len)
180 {
181 	int pkt_count = 0, pkt_len, pad;
182 	t_u8 hdr_len = sizeof(Eth803Hdr_t);
183 
184 	ENTER();
185 	while (total_pkt_len >= hdr_len) {
186 		/* Length will be in network format, change it to host */
187 		pkt_len = mlan_ntohs(
188 			(*(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH))));
189 		if (pkt_len > total_pkt_len) {
190 			PRINTM(MERROR, "Error in packet length.\n");
191 			break;
192 		}
193 
194 		pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ?
195 			      (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) :
196 			      0;
197 		data += pkt_len + pad + sizeof(Eth803Hdr_t);
198 		total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t);
199 		++pkt_count;
200 	}
201 	LEAVE();
202 	return pkt_count;
203 }
204 
205 /********************************************************
206 			Global Functions
207 ********************************************************/
208 
209 /**
210  *  @brief Deaggregate the received AMSDU packet
211  *
212  *  @param priv		A pointer to mlan_private structure
213  *  @param pmbuf	A pointer to aggregated data packet
214  *
215  *  @return		MLAN_STATUS_SUCCESS --success, otherwise fail
216  */
wlan_11n_deaggregate_pkt(mlan_private * priv,pmlan_buffer pmbuf)217 mlan_status wlan_11n_deaggregate_pkt(mlan_private *priv, pmlan_buffer pmbuf)
218 {
219 	t_u16 pkt_len;
220 	int total_pkt_len;
221 	t_u8 *data;
222 	mlan_adapter *pmadapter = priv->adapter;
223 	t_u32 max_rx_data_size = MLAN_RX_DATA_BUF_SIZE;
224 	int pad;
225 	mlan_status ret = MLAN_STATUS_FAILURE;
226 	RxPacketHdr_t *prx_pkt;
227 	mlan_buffer *daggr_mbuf = MNULL;
228 	t_u8 rfc1042_eth_hdr[MLAN_MAC_ADDR_LENGTH] = {0xaa, 0xaa, 0x03,
229 						      0x00, 0x00, 0x00};
230 	t_u8 hdr_len = sizeof(Eth803Hdr_t);
231 	t_u8 eapol_type[2] = {0x88, 0x8e};
232 	t_u8 tdls_action_type[2] = {0x89, 0x0d};
233 	t_u32 in_ts_sec, in_ts_usec;
234 	t_u32 out_ts_sec, out_ts_usec;
235 	t_u32 in_copy_ts_sec, in_copy_ts_usec;
236 	t_u32 out_copy_ts_sec, out_copy_ts_usec;
237 	t_u32 copy_delay = 0;
238 	t_u32 delay = 0;
239 
240 	ENTER();
241 
242 	data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset);
243 	total_pkt_len = pmbuf->data_len;
244 
245 	/* Sanity test */
246 #if defined(USB)
247 	if (IS_USB(pmadapter->card_type) &&
248 	    pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable) {
249 		max_rx_data_size =
250 			pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_max;
251 		if (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_mode ==
252 		    MLAN_USB_AGGR_MODE_NUM) {
253 			max_rx_data_size *=
254 				MAX(MLAN_USB_MAX_PKT_SIZE,
255 				    pmadapter->pcard_usb->usb_rx_deaggr
256 					    .aggr_ctrl.aggr_align);
257 			max_rx_data_size =
258 				MAX(max_rx_data_size, MLAN_RX_DATA_BUF_SIZE);
259 		}
260 	}
261 #endif
262 	if (total_pkt_len > (int)max_rx_data_size) {
263 		PRINTM(MERROR,
264 		       "Total packet length greater than tx buffer"
265 		       " size %d\n",
266 		       total_pkt_len);
267 		goto done;
268 	}
269 	if (pmadapter->tp_state_on)
270 		pmadapter->callbacks.moal_get_system_time(
271 			pmadapter->pmoal_handle, &in_ts_sec, &in_ts_usec);
272 	pmbuf->use_count = wlan_11n_get_num_aggrpkts(data, total_pkt_len);
273 
274 	// rx_trace 7
275 	if (pmadapter->tp_state_on) {
276 		pmadapter->callbacks.moal_tp_accounting(
277 			pmadapter->pmoal_handle, pmbuf, 7 /*RX_DROP_P3*/);
278 		pmadapter->callbacks.moal_tp_accounting_rx_param(
279 			pmadapter->pmoal_handle, 4, pmbuf->use_count);
280 	}
281 	if (pmadapter->tp_state_drop_point == 7 /*RX_DROP_P3*/)
282 		goto done;
283 	prx_pkt = (RxPacketHdr_t *)data;
284 	if (pmbuf->pdesc && !memcmp(pmadapter, prx_pkt->eth803_hdr.dest_addr,
285 				    priv->curr_addr, MLAN_MAC_ADDR_LENGTH)) {
286 		if (pmadapter->callbacks.moal_recv_amsdu_packet) {
287 			ret = pmadapter->callbacks.moal_recv_amsdu_packet(
288 				pmadapter->pmoal_handle, pmbuf);
289 			if (ret == MLAN_STATUS_PENDING) {
290 				priv->msdu_in_rx_amsdu_cnt += pmbuf->use_count;
291 				priv->amsdu_rx_cnt++;
292 				return ret;
293 			}
294 			goto done;
295 		}
296 	}
297 	while (total_pkt_len >= hdr_len) {
298 		prx_pkt = (RxPacketHdr_t *)data;
299 		/* Length will be in network format, change it to host */
300 		pkt_len = mlan_ntohs(
301 			(*(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH))));
302 		if (pkt_len > total_pkt_len) {
303 			PRINTM(MERROR,
304 			       "Error in packet length: total_pkt_len = %d, pkt_len = %d\n",
305 			       total_pkt_len, pkt_len);
306 			ret = MLAN_STATUS_FAILURE;
307 			break;
308 		}
309 
310 		pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ?
311 			      (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) :
312 			      0;
313 
314 		total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t);
315 
316 		if (memcmp(pmadapter, &prx_pkt->rfc1042_hdr, rfc1042_eth_hdr,
317 			   sizeof(rfc1042_eth_hdr)) == 0) {
318 			memmove(pmadapter, data + LLC_SNAP_LEN, data,
319 				(2 * MLAN_MAC_ADDR_LENGTH));
320 			data += LLC_SNAP_LEN;
321 			pkt_len += sizeof(Eth803Hdr_t) - LLC_SNAP_LEN;
322 		} else {
323 			*(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH)) =
324 				(t_u16)0;
325 			pkt_len += sizeof(Eth803Hdr_t);
326 		}
327 		daggr_mbuf = wlan_alloc_mlan_buffer(pmadapter,
328 						    pkt_len + MLAN_NET_IP_ALIGN,
329 						    0, MOAL_ALLOC_MLAN_BUFFER);
330 		if (daggr_mbuf == MNULL) {
331 			PRINTM(MERROR, "Error allocating daggr mlan_buffer\n");
332 			ret = MLAN_STATUS_FAILURE;
333 			break;
334 		}
335 		daggr_mbuf->data_offset += MLAN_NET_IP_ALIGN;
336 		daggr_mbuf->bss_index = pmbuf->bss_index;
337 		daggr_mbuf->buf_type = pmbuf->buf_type;
338 		daggr_mbuf->data_len = pkt_len;
339 		daggr_mbuf->in_ts_sec = pmbuf->in_ts_sec;
340 		daggr_mbuf->in_ts_usec = pmbuf->in_ts_usec;
341 		daggr_mbuf->extra_ts_sec = pmbuf->extra_ts_sec;
342 		daggr_mbuf->extra_ts_usec = pmbuf->extra_ts_usec;
343 		daggr_mbuf->pparent = pmbuf;
344 		daggr_mbuf->priority = pmbuf->priority;
345 		if (pmadapter->tp_state_on)
346 			pmadapter->callbacks.moal_get_system_time(
347 				pmadapter->pmoal_handle, &in_copy_ts_sec,
348 				&in_copy_ts_usec);
349 		memcpy_ext(pmadapter,
350 			   daggr_mbuf->pbuf + daggr_mbuf->data_offset, data,
351 			   pkt_len, daggr_mbuf->data_len);
352 		if (pmadapter->tp_state_on) {
353 			pmadapter->callbacks.moal_get_system_time(
354 				pmadapter->pmoal_handle, &out_copy_ts_sec,
355 				&out_copy_ts_usec);
356 			copy_delay +=
357 				(t_s32)(out_copy_ts_sec - in_copy_ts_sec) *
358 				1000000;
359 			copy_delay +=
360 				(t_s32)(out_copy_ts_usec - in_copy_ts_usec);
361 		}
362 #ifdef UAP_SUPPORT
363 		if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
364 			ret = wlan_uap_recv_packet(priv, daggr_mbuf);
365 		} else {
366 #endif /* UAP_SUPPORT */
367 			/** send EAPOL from AMSDU pkt to firmware */
368 			if (priv->sec_info.ewpa_enabled &&
369 			    (!memcmp(pmadapter,
370 				     daggr_mbuf->pbuf +
371 					     daggr_mbuf->data_offset +
372 					     MLAN_ETHER_PKT_TYPE_OFFSET,
373 				     eapol_type, sizeof(eapol_type)))) {
374 				ret = wlan_prepare_cmd(
375 					priv, HostCmd_CMD_802_11_EAPOL_PKT, 0,
376 					0, MNULL, daggr_mbuf);
377 				if (ret == MLAN_STATUS_SUCCESS)
378 					wlan_recv_event(
379 						priv,
380 						MLAN_EVENT_ID_DRV_DEFER_HANDLING,
381 						MNULL);
382 				wlan_free_mlan_buffer(pmadapter, daggr_mbuf);
383 				data += pkt_len + pad;
384 				continue;
385 			}
386 			/**process tdls packet*/
387 			if (!memcmp(pmadapter,
388 				    daggr_mbuf->pbuf + daggr_mbuf->data_offset +
389 					    MLAN_ETHER_PKT_TYPE_OFFSET,
390 				    tdls_action_type,
391 				    sizeof(tdls_action_type))) {
392 				PRINTM(MEVENT,
393 				       "Recevie AMSDU TDLS action frame\n");
394 				wlan_process_tdls_action_frame(
395 					priv,
396 					daggr_mbuf->pbuf +
397 						daggr_mbuf->data_offset,
398 					daggr_mbuf->data_len);
399 			}
400 
401 			ret = pmadapter->callbacks.moal_recv_packet(
402 				pmadapter->pmoal_handle, daggr_mbuf);
403 #ifdef UAP_SUPPORT
404 		}
405 #endif /* UAP_SUPPORT */
406 		switch (ret) {
407 		case MLAN_STATUS_PENDING:
408 			break;
409 		case MLAN_STATUS_FAILURE:
410 			PRINTM(MERROR, "Deaggr, send to moal failed\n");
411 			daggr_mbuf->status_code = MLAN_ERROR_PKT_INVALID;
412 			/* fall through */
413 		case MLAN_STATUS_SUCCESS:
414 			wlan_recv_packet_complete(pmadapter, daggr_mbuf, ret);
415 			break;
416 		default:
417 			break;
418 		}
419 
420 		data += pkt_len + pad;
421 	}
422 	if (pmadapter->tp_state_on) {
423 		pmadapter->callbacks.moal_get_system_time(
424 			pmadapter->pmoal_handle, &out_ts_sec, &out_ts_usec);
425 		delay += (t_s32)(out_ts_sec - in_ts_sec) * 1000000;
426 		delay += (t_s32)(out_ts_usec - in_ts_usec);
427 		pmadapter->callbacks.moal_amsdu_tp_accounting(
428 			pmadapter->pmoal_handle, delay, copy_delay);
429 	}
430 
431 done:
432 	priv->msdu_in_rx_amsdu_cnt += pmbuf->use_count;
433 	priv->amsdu_rx_cnt++;
434 	/** we should free the aggr buffer after deaggr */
435 	pmadapter->ops.data_complete(pmadapter, pmbuf, ret);
436 	LEAVE();
437 	return ret;
438 }
439 
440 /**
441  *  @brief Aggregate multiple packets into one single AMSDU packet
442  *
443  *  @param priv     A pointer to mlan_private structure
444  *  @param pra_list Pointer to the RA List table containing the pointers
445  *                  to packets.
446  *  @param headroom Any interface specific headroom that may be need. TxPD
447  *                  will be formed leaving this headroom.
448  *  @param ptrindex Pointer index
449  *
450  *  @return     Final packet size or MLAN_STATUS_FAILURE
451  */
wlan_11n_aggregate_pkt(mlan_private * priv,raListTbl * pra_list,int headroom,int ptrindex)452 int wlan_11n_aggregate_pkt(mlan_private *priv, raListTbl *pra_list,
453 			   int headroom, int ptrindex)
454 {
455 	int pkt_size = 0;
456 	pmlan_adapter pmadapter = priv->adapter;
457 	mlan_buffer *pmbuf_aggr, *pmbuf_src;
458 	t_u8 *data;
459 	int pad = 0;
460 	mlan_status ret = MLAN_STATUS_SUCCESS;
461 #ifdef DEBUG_LEVEL1
462 	t_u32 sec = 0, usec = 0;
463 #endif
464 	mlan_tx_param tx_param;
465 #ifdef STA_SUPPORT
466 	TxPD *ptx_pd = MNULL;
467 #endif
468 	t_u32 max_amsdu_size = MIN(pra_list->max_amsdu, pmadapter->tx_buf_size);
469 	t_u32 msdu_in_tx_amsdu_cnt = 0;
470 	ENTER();
471 
472 	PRINTM(MDAT_D, "Handling Aggr packet\n");
473 
474 	pmbuf_src = (pmlan_buffer)util_peek_list(
475 		pmadapter->pmoal_handle, &pra_list->buf_head, MNULL, MNULL);
476 	if (pmbuf_src) {
477 		pmbuf_aggr = wlan_alloc_mlan_buffer(
478 			pmadapter, pmadapter->tx_buf_size, 0,
479 			MOAL_MALLOC_BUFFER | MOAL_MEM_FLAG_ATOMIC);
480 		if (!pmbuf_aggr) {
481 			PRINTM(MERROR, "Error allocating mlan_buffer\n");
482 			pmadapter->callbacks.moal_spin_unlock(
483 				pmadapter->pmoal_handle,
484 				priv->wmm.ra_list_spinlock);
485 			LEAVE();
486 			return MLAN_STATUS_FAILURE;
487 		}
488 
489 		data = pmbuf_aggr->pbuf + headroom;
490 		pmbuf_aggr->bss_index = pmbuf_src->bss_index;
491 		pmbuf_aggr->buf_type = pmbuf_src->buf_type;
492 		pmbuf_aggr->priority = pmbuf_src->priority;
493 		pmbuf_aggr->pbuf = data;
494 		pmbuf_aggr->data_offset = 0;
495 		pmbuf_aggr->in_ts_sec = pmbuf_src->in_ts_sec;
496 		pmbuf_aggr->in_ts_usec = pmbuf_src->in_ts_usec;
497 		if (pmbuf_src->flags & MLAN_BUF_FLAG_TDLS)
498 			pmbuf_aggr->flags |= MLAN_BUF_FLAG_TDLS;
499 		if (pmbuf_src->flags & MLAN_BUF_FLAG_TCP_ACK)
500 			pmbuf_aggr->flags |= MLAN_BUF_FLAG_TCP_ACK;
501 
502 		/* Form AMSDU */
503 		wlan_11n_form_amsdu_txpd(priv, pmbuf_aggr);
504 		pkt_size = sizeof(TxPD);
505 #ifdef STA_SUPPORT
506 		if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA)
507 			ptx_pd = (TxPD *)pmbuf_aggr->pbuf;
508 #endif
509 		priv->msdu_in_tx_amsdu_cnt++;
510 	} else {
511 		pmadapter->callbacks.moal_spin_unlock(
512 			pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
513 		goto exit;
514 	}
515 
516 	while (pmbuf_src && ((pkt_size + (pmbuf_src->data_len + LLC_SNAP_LEN) +
517 			      headroom) <= max_amsdu_size)) {
518 		pmbuf_src =
519 			(pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle,
520 							&pra_list->buf_head,
521 							MNULL, MNULL);
522 		/* Collects TP statistics */
523 		if (pmadapter->tp_state_on && (pkt_size > sizeof(TxPD)))
524 			pmadapter->callbacks.moal_tp_accounting(
525 				pmadapter->pmoal_handle, pmbuf_src->pdesc, 3);
526 		pra_list->total_pkts--;
527 
528 		/* decrement for every PDU taken from the list */
529 		priv->wmm.pkts_queued[ptrindex]--;
530 		util_scalar_decrement(pmadapter->pmoal_handle,
531 				      &priv->wmm.tx_pkts_queued, MNULL, MNULL);
532 
533 		pmadapter->callbacks.moal_spin_unlock(
534 			pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
535 
536 		if (pmbuf_src) {
537 			pkt_size += wlan_11n_form_amsdu_pkt(
538 				pmadapter, (data + pkt_size),
539 				pmbuf_src->pbuf + pmbuf_src->data_offset,
540 				pmbuf_src->data_len, &pad);
541 
542 			DBG_HEXDUMP(MDAT_D, "pmbuf_src", pmbuf_src,
543 				    sizeof(mlan_buffer));
544 			wlan_write_data_complete(pmadapter, pmbuf_src,
545 						 MLAN_STATUS_SUCCESS);
546 		}
547 
548 		pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
549 						    priv->wmm.ra_list_spinlock);
550 
551 		if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) {
552 			pmadapter->callbacks.moal_spin_unlock(
553 				pmadapter->pmoal_handle,
554 				priv->wmm.ra_list_spinlock);
555 			LEAVE();
556 			return MLAN_STATUS_FAILURE;
557 		}
558 
559 		pmbuf_src =
560 			(pmlan_buffer)util_peek_list(pmadapter->pmoal_handle,
561 						     &pra_list->buf_head, MNULL,
562 						     MNULL);
563 		priv->msdu_in_tx_amsdu_cnt++;
564 		msdu_in_tx_amsdu_cnt++;
565 	}
566 
567 	pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
568 					      priv->wmm.ra_list_spinlock);
569 
570 	/* Last AMSDU packet does not need padding */
571 	pkt_size -= pad;
572 	pmbuf_aggr->data_len = pkt_size;
573 	wlan_11n_update_pktlen_amsdu_txpd(priv, pmbuf_aggr);
574 	pmbuf_aggr->data_len += headroom;
575 	pmbuf_aggr->pbuf = data - headroom;
576 	tx_param.next_pkt_len =
577 		((pmbuf_src) ? pmbuf_src->data_len + sizeof(TxPD) : 0);
578 	/* Collects TP statistics */
579 	if (pmadapter->tp_state_on) {
580 		pmadapter->callbacks.moal_tp_accounting(pmadapter->pmoal_handle,
581 							pmbuf_aggr, 4);
582 		pmadapter->callbacks.moal_tp_accounting_rx_param(
583 			pmadapter->pmoal_handle, 5, msdu_in_tx_amsdu_cnt);
584 	}
585 
586 	/* Drop Tx packets at drop point 4 */
587 	if (pmadapter->tp_state_drop_point == 4) {
588 		wlan_write_data_complete(pmadapter, pmbuf_aggr, ret);
589 		goto exit;
590 	} else
591 		ret = pmadapter->ops.host_to_card(priv, MLAN_TYPE_DATA,
592 						  pmbuf_aggr, &tx_param);
593 	switch (ret) {
594 #ifdef USB
595 	case MLAN_STATUS_PRESOURCE:
596 		PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n");
597 		break;
598 #endif
599 	case MLAN_STATUS_RESOURCE:
600 		pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
601 						    priv->wmm.ra_list_spinlock);
602 
603 		if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) {
604 			pmadapter->callbacks.moal_spin_unlock(
605 				pmadapter->pmoal_handle,
606 				priv->wmm.ra_list_spinlock);
607 			pmbuf_aggr->status_code = MLAN_ERROR_PKT_INVALID;
608 			wlan_write_data_complete(pmadapter, pmbuf_aggr,
609 						 MLAN_STATUS_FAILURE);
610 			LEAVE();
611 			return MLAN_STATUS_FAILURE;
612 		}
613 #ifdef STA_SUPPORT
614 		/* reset tx_lock_flag */
615 		if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
616 		    pmadapter->pps_uapsd_mode &&
617 		    (pmadapter->tx_lock_flag == MTRUE)) {
618 			pmadapter->tx_lock_flag = MFALSE;
619 			if (ptx_pd != MNULL)
620 				ptx_pd->flags = 0;
621 		}
622 #endif
623 		util_enqueue_list_head(pmadapter->pmoal_handle,
624 				       &pra_list->buf_head,
625 				       (pmlan_linked_list)pmbuf_aggr, MNULL,
626 				       MNULL);
627 
628 		pra_list->total_pkts++;
629 
630 		/* add back only one: aggregated packet is requeued as one */
631 		priv->wmm.pkts_queued[ptrindex]++;
632 		util_scalar_increment(pmadapter->pmoal_handle,
633 				      &priv->wmm.tx_pkts_queued, MNULL, MNULL);
634 		pmbuf_aggr->flags |= MLAN_BUF_FLAG_REQUEUED_PKT;
635 		pmadapter->callbacks.moal_spin_unlock(
636 			pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
637 		PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n");
638 		pmbuf_aggr->status_code = MLAN_ERROR_PKT_INVALID;
639 		break;
640 	case MLAN_STATUS_FAILURE:
641 		pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL;
642 		pmadapter->dbg.num_tx_host_to_card_failure++;
643 		wlan_write_data_complete(pmadapter, pmbuf_aggr, ret);
644 		goto exit;
645 	case MLAN_STATUS_PENDING:
646 		break;
647 	case MLAN_STATUS_SUCCESS:
648 		wlan_write_data_complete(pmadapter, pmbuf_aggr, ret);
649 		break;
650 	default:
651 		break;
652 	}
653 	if (ret != MLAN_STATUS_RESOURCE) {
654 		pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
655 						    priv->wmm.ra_list_spinlock);
656 		if (wlan_is_ralist_valid(priv, pra_list, ptrindex)) {
657 			priv->wmm.packets_out[ptrindex]++;
658 			priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list;
659 		}
660 		pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur =
661 			pmadapter->bssprio_tbl[priv->bss_priority]
662 				.bssprio_cur->pnext;
663 		pmadapter->callbacks.moal_spin_unlock(
664 			pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
665 	}
666 	PRINTM_GET_SYS_TIME(MDATA, &sec, &usec);
667 	PRINTM_NETINTF(MDATA, priv);
668 	PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec);
669 	priv->amsdu_tx_cnt++;
670 
671 exit:
672 	LEAVE();
673 	return pkt_size + headroom;
674 }
675