• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  ******************************************************************************/
15 #define _RTL8192C_XMIT_C_
16 #include <osdep_service.h>
17 #include <drv_types.h>
18 #include <wifi.h>
19 #include <osdep_intf.h>
20 #include <usb_ops.h>
21 /* include <rtl8192c_hal.h> */
22 #include <rtl8723a_hal.h>
23 
urb_zero_packet_chk(struct rtw_adapter * padapter,int sz)24 static int urb_zero_packet_chk(struct rtw_adapter *padapter, int sz)
25 {
26 	int blnSetTxDescOffset;
27 	struct dvobj_priv	*pdvobj = adapter_to_dvobj(padapter);
28 
29 	if (pdvobj->ishighspeed) {
30 		if (((sz + TXDESC_SIZE) % 512) == 0)
31 			blnSetTxDescOffset = 1;
32 		else
33 			blnSetTxDescOffset = 0;
34 	} else {
35 		if (((sz + TXDESC_SIZE) % 64) == 0)
36 			blnSetTxDescOffset = 1;
37 		else
38 			blnSetTxDescOffset = 0;
39 	}
40 	return blnSetTxDescOffset;
41 }
42 
rtl8192cu_cal_txdesc_chksum(struct tx_desc * ptxdesc)43 static void rtl8192cu_cal_txdesc_chksum(struct tx_desc	*ptxdesc)
44 {
45 		__le16	*usPtr = (__le16 *)ptxdesc;
46 		u32 count = 16;		/*  (32 bytes / 2 bytes per XOR) => 16 times */
47 		u32 index;
48 		u16 checksum = 0;
49 
50 		/* Clear first */
51 		ptxdesc->txdw7 &= cpu_to_le32(0xffff0000);
52 
53 		for (index = 0 ; index < count ; index++)
54 			checksum = checksum ^ le16_to_cpu(*(usPtr + index));
55 
56 		ptxdesc->txdw7 |= cpu_to_le32(0x0000ffff&checksum);
57 }
58 
fill_txdesc_sectype(struct pkt_attrib * pattrib,struct tx_desc * ptxdesc)59 static void fill_txdesc_sectype(struct pkt_attrib *pattrib, struct tx_desc *ptxdesc)
60 {
61 	if ((pattrib->encrypt > 0) && !pattrib->bswenc) {
62 		switch (pattrib->encrypt) {
63 		/* SEC_TYPE */
64 		case WLAN_CIPHER_SUITE_WEP40:
65 		case WLAN_CIPHER_SUITE_WEP104:
66 			ptxdesc->txdw1 |= cpu_to_le32((0x01<<22)&0x00c00000);
67 			break;
68 		case WLAN_CIPHER_SUITE_TKIP:
69 			/* ptxdesc->txdw1 |= cpu_to_le32((0x02<<22)&0x00c00000); */
70 			ptxdesc->txdw1 |= cpu_to_le32((0x01<<22)&0x00c00000);
71 			break;
72 		case WLAN_CIPHER_SUITE_CCMP:
73 			ptxdesc->txdw1 |= cpu_to_le32((0x03<<22)&0x00c00000);
74 			break;
75 		case 0:
76 		default:
77 			break;
78 		}
79 	}
80 }
81 
fill_txdesc_vcs(struct pkt_attrib * pattrib,__le32 * pdw)82 static void fill_txdesc_vcs(struct pkt_attrib *pattrib, __le32 *pdw)
83 {
84 	/* DBG_8723A("cvs_mode =%d\n", pattrib->vcs_mode); */
85 
86 	switch (pattrib->vcs_mode) {
87 	case RTS_CTS:
88 		*pdw |= cpu_to_le32(BIT(12));
89 		break;
90 	case CTS_TO_SELF:
91 		*pdw |= cpu_to_le32(BIT(11));
92 		break;
93 	case NONE_VCS:
94 	default:
95 		break;
96 	}
97 
98 	if (pattrib->vcs_mode) {
99 		*pdw |= cpu_to_le32(BIT(13));
100 
101 		/*  Set RTS BW */
102 		if (pattrib->ht_en) {
103 			*pdw |= (pattrib->bwmode&HT_CHANNEL_WIDTH_40) ?	cpu_to_le32(BIT(27)) : 0;
104 
105 			if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
106 				*pdw |= cpu_to_le32((0x01<<28)&0x30000000);
107 			else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
108 				*pdw |= cpu_to_le32((0x02<<28)&0x30000000);
109 			else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
110 				*pdw |= 0;
111 			else
112 				*pdw |= cpu_to_le32((0x03<<28)&0x30000000);
113 		}
114 	}
115 }
116 
fill_txdesc_phy(struct pkt_attrib * pattrib,__le32 * pdw)117 static void fill_txdesc_phy(struct pkt_attrib *pattrib, __le32 *pdw)
118 {
119 	if (pattrib->ht_en) {
120 		*pdw |= (pattrib->bwmode&HT_CHANNEL_WIDTH_40) ? cpu_to_le32(BIT(25)) : 0;
121 
122 		if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
123 			*pdw |= cpu_to_le32((0x01<<20)&0x003f0000);
124 		else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
125 			*pdw |= cpu_to_le32((0x02<<20)&0x003f0000);
126 		else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
127 			*pdw |= 0;
128 		else
129 			*pdw |= cpu_to_le32((0x03<<20)&0x003f0000);
130 	}
131 }
132 
update_txdesc(struct xmit_frame * pxmitframe,u8 * pmem,s32 sz)133 static s32 update_txdesc(struct xmit_frame *pxmitframe, u8 *pmem, s32 sz)
134 {
135 	int	pull = 0;
136 	uint	qsel;
137 	struct rtw_adapter	*padapter = pxmitframe->padapter;
138 	struct pkt_attrib	*pattrib = &pxmitframe->attrib;
139 	struct hal_data_8723a	*pHalData = GET_HAL_DATA(padapter);
140 	struct dm_priv	*pdmpriv = &pHalData->dmpriv;
141 	struct tx_desc	*ptxdesc = (struct tx_desc *)pmem;
142 	struct mlme_ext_priv	*pmlmeext = &padapter->mlmeextpriv;
143 	struct mlme_ext_info	*pmlmeinfo = &pmlmeext->mlmext_info;
144 	int	bmcst = is_multicast_ether_addr(pattrib->ra);
145 
146 	if (urb_zero_packet_chk(padapter, sz) == 0) {
147 		ptxdesc = (struct tx_desc *)(pmem+PACKET_OFFSET_SZ);
148 		pull = 1;
149 		pxmitframe->pkt_offset--;
150 	}
151 
152 	memset(ptxdesc, 0, sizeof(struct tx_desc));
153 
154 	if (pxmitframe->frame_tag == DATA_FRAMETAG) {
155 		/* offset 4 */
156 		ptxdesc->txdw1 |= cpu_to_le32(pattrib->mac_id&0x1f);
157 
158 		qsel = (uint)(pattrib->qsel & 0x0000001f);
159 		ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
160 
161 		ptxdesc->txdw1 |= cpu_to_le32((pattrib->raid<<16) & 0x000f0000);
162 
163 		fill_txdesc_sectype(pattrib, ptxdesc);
164 
165 		if (pattrib->ampdu_en)
166 			ptxdesc->txdw1 |= cpu_to_le32(BIT(5));/* AGG EN */
167 		else
168 			ptxdesc->txdw1 |= cpu_to_le32(BIT(6));/* AGG BK */
169 
170 		/* offset 8 */
171 
172 		/* offset 12 */
173 		ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
174 
175 		/* offset 16 , offset 20 */
176 		if (pattrib->qos_en)
177 			ptxdesc->txdw4 |= cpu_to_le32(BIT(6));/* QoS */
178 
179 		if ((pattrib->ether_type != 0x888e) &&
180 		    (pattrib->ether_type != 0x0806) &&
181 		    (pattrib->dhcp_pkt != 1)) {
182 			/* Non EAP & ARP & DHCP type data packet */
183 
184 			fill_txdesc_vcs(pattrib, &ptxdesc->txdw4);
185 			fill_txdesc_phy(pattrib, &ptxdesc->txdw4);
186 
187 			ptxdesc->txdw4 |= cpu_to_le32(0x00000008);/* RTS Rate = 24M */
188 			ptxdesc->txdw5 |= cpu_to_le32(0x0001ff00);/*  */
189 
190 			/* use REG_INIDATA_RATE_SEL value */
191 			ptxdesc->txdw5 |= cpu_to_le32(pdmpriv->INIDATA_RATE[pattrib->mac_id]);
192 		} else {
193 			/*  EAP data packet and ARP packet. */
194 			/*  Use the 1M data rate to send the EAP/ARP packet. */
195 			/*  This will maybe make the handshake smooth. */
196 
197 			ptxdesc->txdw1 |= cpu_to_le32(BIT(6));/* AGG BK */
198 
199 			ptxdesc->txdw4 |= cpu_to_le32(BIT(8));/* driver uses rate */
200 
201 			if (pmlmeinfo->preamble_mode == PREAMBLE_SHORT)
202 				ptxdesc->txdw4 |= cpu_to_le32(BIT(24));/*  DATA_SHORT */
203 
204 			ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate23a(pmlmeext->tx_rate));
205 		}
206 	} else if (pxmitframe->frame_tag == MGNT_FRAMETAG) {
207 		/* offset 4 */
208 		ptxdesc->txdw1 |= cpu_to_le32(pattrib->mac_id&0x1f);
209 
210 		qsel = (uint)(pattrib->qsel&0x0000001f);
211 		ptxdesc->txdw1 |= cpu_to_le32((qsel<<QSEL_SHT)&0x00001f00);
212 
213 		ptxdesc->txdw1 |= cpu_to_le32((pattrib->raid<<16) & 0x000f0000);
214 
215 		/* offset 8 */
216 		/* CCX-TXRPT ack for xmit mgmt frames. */
217 		if (pxmitframe->ack_report)
218 			ptxdesc->txdw2 |= cpu_to_le32(BIT(19));
219 
220 		/* offset 12 */
221 		ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
222 
223 		/* offset 16 */
224 		ptxdesc->txdw4 |= cpu_to_le32(BIT(8));/* driver uses rate */
225 
226 		/* offset 20 */
227 		ptxdesc->txdw5 |= cpu_to_le32(BIT(17));/* retry limit enable */
228 		ptxdesc->txdw5 |= cpu_to_le32(0x00180000);/* retry limit = 6 */
229 
230 		ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate23a(pmlmeext->tx_rate));
231 	} else if (pxmitframe->frame_tag == TXAGG_FRAMETAG) {
232 		DBG_8723A("pxmitframe->frame_tag == TXAGG_FRAMETAG\n");
233 	} else {
234 		DBG_8723A("pxmitframe->frame_tag = %d\n",
235 			  pxmitframe->frame_tag);
236 
237 		/* offset 4 */
238 		ptxdesc->txdw1 |= cpu_to_le32((4)&0x1f);/* CAM_ID(MAC_ID) */
239 
240 		ptxdesc->txdw1 |= cpu_to_le32((6<<16) & 0x000f0000);/* raid */
241 
242 		/* offset 8 */
243 
244 		/* offset 12 */
245 		ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
246 
247 		/* offset 16 */
248 		ptxdesc->txdw4 |= cpu_to_le32(BIT(8));/* driver uses rate */
249 
250 		/* offset 20 */
251 		ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate23a(pmlmeext->tx_rate));
252 	}
253 
254 	/*  (1) The sequence number of each non-Qos frame / broadcast / multicast / */
255 	/*  mgnt frame should be controlled by Hw because Fw will also send null data */
256 	/*  which we cannot control when Fw LPS enable. */
257 	/*  --> default enable non-Qos data sequense number. 2010.06.23. by tynli. */
258 	/*  (2) Enable HW SEQ control for beacon packet, because we use Hw beacon. */
259 	/*  (3) Use HW Qos SEQ to control the seq num of Ext port non-Qos packets. */
260 	if (!pattrib->qos_en) {
261 		/*  Hw set sequence number */
262 		ptxdesc->txdw4 |= cpu_to_le32(BIT(7));
263 		/* set bit3 to 1. */
264 		ptxdesc->txdw3 |= cpu_to_le32((8 << 28));
265 	}
266 
267 	/* offset 0 */
268 	ptxdesc->txdw0 |= cpu_to_le32(sz&0x0000ffff);
269 	ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG);
270 	ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ)<<OFFSET_SHT)&0x00ff0000);/* 32 bytes for TX Desc */
271 
272 	if (bmcst)
273 		ptxdesc->txdw0 |= cpu_to_le32(BIT(24));
274 
275 	RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
276 		 "offset0-txdesc = 0x%x\n", ptxdesc->txdw0);
277 
278 	/* offset 4 */
279 	/*  pkt_offset, unit:8 bytes padding */
280 	if (pxmitframe->pkt_offset > 0)
281 		ptxdesc->txdw1 |= cpu_to_le32((pxmitframe->pkt_offset << 26) & 0x7c000000);
282 
283 	rtl8192cu_cal_txdesc_chksum(ptxdesc);
284 	return pull;
285 }
286 
rtw_dump_xframe(struct rtw_adapter * padapter,struct xmit_frame * pxmitframe)287 static int rtw_dump_xframe(struct rtw_adapter *padapter,
288 			   struct xmit_frame *pxmitframe)
289 {
290 	int ret = _SUCCESS;
291 	int inner_ret = _SUCCESS;
292 	int t, sz, w_sz, pull = 0;
293 	u8 *mem_addr;
294 	u32 ff_hwaddr;
295 	struct xmit_buf *pxmitbuf = pxmitframe->pxmitbuf;
296 	struct pkt_attrib *pattrib = &pxmitframe->attrib;
297 	struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
298 
299 	if (pxmitframe->frame_tag == DATA_FRAMETAG &&
300 	    pxmitframe->attrib.ether_type != ETH_P_ARP &&
301 	    pxmitframe->attrib.ether_type != ETH_P_PAE &&
302 	    pxmitframe->attrib.dhcp_pkt != 1)
303 		rtw_issue_addbareq_cmd23a(padapter, pxmitframe);
304 
305 	mem_addr = pxmitframe->buf_addr;
306 
307 	RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, "rtw_dump_xframe()\n");
308 
309 	for (t = 0; t < pattrib->nr_frags; t++) {
310 		if (inner_ret != _SUCCESS && ret == _SUCCESS)
311 			ret = _FAIL;
312 
313 		if (t != (pattrib->nr_frags - 1)) {
314 			RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
315 				 "pattrib->nr_frags =%d\n", pattrib->nr_frags);
316 
317 			sz = pxmitpriv->frag_len;
318 			sz = sz - 4 - pattrib->icv_len;
319 		} else {
320 			/* no frag */
321 			sz = pattrib->last_txcmdsz;
322 		}
323 
324 		pull = update_txdesc(pxmitframe, mem_addr, sz);
325 
326 		if (pull) {
327 			mem_addr += PACKET_OFFSET_SZ; /* pull txdesc head */
328 
329 			pxmitframe->buf_addr = mem_addr;
330 
331 			w_sz = sz + TXDESC_SIZE;
332 		} else {
333 			w_sz = sz + TXDESC_SIZE + PACKET_OFFSET_SZ;
334 		}
335 
336 		ff_hwaddr = rtw_get_ff_hwaddr23a(pxmitframe);
337 		inner_ret = rtl8723au_write_port(padapter, ff_hwaddr,
338 						 w_sz, pxmitbuf);
339 		rtw_count_tx_stats23a(padapter, pxmitframe, sz);
340 
341 		RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
342 			 "rtw_write_port, w_sz =%d\n", w_sz);
343 
344 		mem_addr += w_sz;
345 
346 		mem_addr = PTR_ALIGN(mem_addr, 4);
347 	}
348 
349 	rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
350 
351 	if  (ret != _SUCCESS)
352 		rtw23a_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_UNKNOWN);
353 
354 	return ret;
355 }
356 
rtl8723au_xmitframe_complete(struct rtw_adapter * padapter,struct xmit_priv * pxmitpriv,struct xmit_buf * pxmitbuf)357 bool rtl8723au_xmitframe_complete(struct rtw_adapter *padapter,
358 				  struct xmit_priv *pxmitpriv,
359 				  struct xmit_buf *pxmitbuf)
360 {
361 	struct hw_xmit *phwxmits;
362 	struct xmit_frame *pxmitframe;
363 	int hwentry;
364 	int res = _SUCCESS, xcnt = 0;
365 
366 	phwxmits = pxmitpriv->hwxmits;
367 	hwentry = pxmitpriv->hwxmit_entry;
368 
369 	RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, "xmitframe_complete()\n");
370 
371 	if (pxmitbuf == NULL) {
372 		pxmitbuf = rtw_alloc_xmitbuf23a(pxmitpriv);
373 		if (!pxmitbuf)
374 			return false;
375 	}
376 	pxmitframe =  rtw_dequeue_xframe23a(pxmitpriv, phwxmits, hwentry);
377 
378 	if (pxmitframe) {
379 		pxmitframe->pxmitbuf = pxmitbuf;
380 
381 		pxmitframe->buf_addr = pxmitbuf->pbuf;
382 
383 		pxmitbuf->priv_data = pxmitframe;
384 
385 		if (pxmitframe->frame_tag == DATA_FRAMETAG) {
386 			if (pxmitframe->attrib.priority <= 15)/* TID0~15 */
387 				res = rtw_xmitframe_coalesce23a(padapter, pxmitframe->pkt, pxmitframe);
388 
389 			rtw_os_xmit_complete23a(padapter, pxmitframe);/* always return ndis_packet after rtw_xmitframe_coalesce23a */
390 		}
391 
392 		RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
393 			 "xmitframe_complete(): rtw_dump_xframe\n");
394 
395 		if (res == _SUCCESS) {
396 			rtw_dump_xframe(padapter, pxmitframe);
397 		} else {
398 			rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf);
399 			rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
400 		}
401 		xcnt++;
402 	} else {
403 		rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf);
404 		return false;
405 	}
406 	return true;
407 }
408 
xmitframe_direct(struct rtw_adapter * padapter,struct xmit_frame * pxmitframe)409 static int xmitframe_direct(struct rtw_adapter *padapter,
410 			    struct xmit_frame *pxmitframe)
411 {
412 	int res;
413 
414 	res = rtw_xmitframe_coalesce23a(padapter, pxmitframe->pkt, pxmitframe);
415 	if (res == _SUCCESS)
416 		rtw_dump_xframe(padapter, pxmitframe);
417 	return res;
418 }
419 
420 /*
421  * Return
422  *	true	dump packet directly
423  *	false	enqueue packet
424  */
rtl8723au_hal_xmit(struct rtw_adapter * padapter,struct xmit_frame * pxmitframe)425 bool rtl8723au_hal_xmit(struct rtw_adapter *padapter,
426 			struct xmit_frame *pxmitframe)
427 {
428 	int res;
429 	struct xmit_buf *pxmitbuf = NULL;
430 	struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
431 	struct pkt_attrib *pattrib = &pxmitframe->attrib;
432 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
433 
434 	pattrib->qsel = pattrib->priority;
435 	spin_lock_bh(&pxmitpriv->lock);
436 
437 #ifdef CONFIG_8723AU_AP_MODE
438 	if (xmitframe_enqueue_for_sleeping_sta23a(padapter, pxmitframe)) {
439 		struct sta_info *psta;
440 		struct sta_priv *pstapriv = &padapter->stapriv;
441 
442 		spin_unlock_bh(&pxmitpriv->lock);
443 
444 		if (pattrib->psta)
445 			psta = pattrib->psta;
446 		else
447 			psta = rtw_get_stainfo23a(pstapriv, pattrib->ra);
448 
449 		if (psta) {
450 			if (psta->sleepq_len > (NR_XMITFRAME>>3))
451 				wakeup_sta_to_xmit23a(padapter, psta);
452 		}
453 
454 		return false;
455 	}
456 #endif
457 
458 	if (rtw_txframes_sta_ac_pending23a(padapter, pattrib) > 0)
459 		goto enqueue;
460 
461 	if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true)
462 		goto enqueue;
463 
464 	pxmitbuf = rtw_alloc_xmitbuf23a(pxmitpriv);
465 	if (pxmitbuf == NULL)
466 		goto enqueue;
467 
468 	spin_unlock_bh(&pxmitpriv->lock);
469 
470 	pxmitframe->pxmitbuf = pxmitbuf;
471 	pxmitframe->buf_addr = pxmitbuf->pbuf;
472 	pxmitbuf->priv_data = pxmitframe;
473 
474 	if (xmitframe_direct(padapter, pxmitframe) != _SUCCESS) {
475 		rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf);
476 		rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
477 	}
478 	return true;
479 
480 enqueue:
481 	res = rtw_xmitframe_enqueue23a(padapter, pxmitframe);
482 	spin_unlock_bh(&pxmitpriv->lock);
483 
484 	if (res != _SUCCESS) {
485 		RT_TRACE(_module_xmit_osdep_c_, _drv_err_,
486 			 "pre_xmitframe: enqueue xmitframe fail\n");
487 		rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
488 
489 		/*  Trick, make the statistics correct */
490 		pxmitpriv->tx_pkts--;
491 		pxmitpriv->tx_drop++;
492 		return true;
493 	}
494 	return false;
495 }
496 
rtl8723au_mgnt_xmit(struct rtw_adapter * padapter,struct xmit_frame * pmgntframe)497 int rtl8723au_mgnt_xmit(struct rtw_adapter *padapter,
498 			struct xmit_frame *pmgntframe)
499 {
500 	return rtw_dump_xframe(padapter, pmgntframe);
501 }
502 
rtl8723au_hal_xmitframe_enqueue(struct rtw_adapter * padapter,struct xmit_frame * pxmitframe)503 int rtl8723au_hal_xmitframe_enqueue(struct rtw_adapter *padapter,
504 				    struct xmit_frame *pxmitframe)
505 {
506 	struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
507 	int err;
508 
509 	err = rtw_xmitframe_enqueue23a(padapter, pxmitframe);
510 	if (err != _SUCCESS) {
511 		rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
512 
513 		/*  Trick, make the statistics correct */
514 		pxmitpriv->tx_pkts--;
515 		pxmitpriv->tx_drop++;
516 	} else {
517 		tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
518 	}
519 	return err;
520 }
521