1 /******************************************************************************
2 *
3 * Copyright(c) 2007 - 2017 Realtek Corporation.
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 _RTL8723DS_RECV_C_
16
17 #include <rtl8723d_hal.h>
18
19 #ifdef CONFIG_SDIO_RX_COPY
rtl8723ds_recv_hdl(_adapter * padapter)20 s32 rtl8723ds_recv_hdl(_adapter *padapter)
21 {
22 PHAL_DATA_TYPE pHalData;
23 struct recv_priv *precvpriv;
24 struct recv_buf *precvbuf;
25 union recv_frame *precvframe;
26 struct recv_frame_hdr *phdr;
27 struct rx_pkt_attrib *pattrib;
28 u8 *ptr;
29 u32 pkt_len, pkt_offset;
30 u8 rx_report_sz = 0;
31
32 pHalData = GET_HAL_DATA(padapter);
33 precvpriv = &padapter->recvpriv;
34
35 do {
36 precvbuf = rtw_dequeue_recvbuf(&precvpriv->recv_buf_pending_queue);
37 if (NULL == precvbuf)
38 break;
39
40 ptr = precvbuf->pdata;
41
42 while (ptr < precvbuf->ptail) {
43 precvframe = rtw_alloc_recvframe(&precvpriv->free_recv_queue);
44 if (precvframe == NULL) {
45 RTW_INFO("%s: no enough recv frame!\n", __FUNCTION__);
46 rtw_enqueue_recvbuf_to_head(precvbuf, &precvpriv->recv_buf_pending_queue);
47 return RTW_RFRAME_UNAVAIL;
48 }
49
50 /* rx desc parsing */
51 rtl8723d_query_rx_desc_status(precvframe, ptr);
52
53 pattrib = &precvframe->u.hdr.attrib;
54
55 /* fix Hardware RX data error, drop whole recv_buffer */
56 if (!rtw_hal_rcr_check(padapter, RCR_ACRC32) && pattrib->crc_err) {
57 #if !(MP_DRIVER == 1)
58 RTW_INFO("%s()-%d: RX Warning! rx CRC ERROR !!\n", __FUNCTION__, __LINE__);
59 #endif
60 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
61 break;
62 }
63
64 rx_report_sz = RXDESC_SIZE + pattrib->drvinfo_sz;
65 pkt_offset = rx_report_sz + pattrib->shift_sz + pattrib->pkt_len;
66
67 if ((ptr + pkt_offset) > precvbuf->ptail) {
68 RTW_INFO("%s()-%d: : next pkt len(%p,%d) exceed ptail(%p)!\n", __FUNCTION__, __LINE__, ptr, pkt_offset, precvbuf->ptail);
69 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
70 break;
71 }
72
73 if ((pattrib->crc_err) || (pattrib->icv_err)) {
74 #ifdef CONFIG_MP_INCLUDED
75 if (padapter->registrypriv.mp_mode == 1) {
76 if (check_fwstate(&padapter->mlmepriv, WIFI_MP_STATE) == _TRUE) { /* &&(padapter->mppriv.check_mp_pkt == 0)) */
77 if (pattrib->crc_err == 1)
78 padapter->mppriv.rx_crcerrpktcount++;
79 }
80 } else
81 #endif
82 {
83 RTW_INFO("%s: crc_err=%d icv_err=%d, skip!\n", __FUNCTION__, pattrib->crc_err, pattrib->icv_err);
84 }
85 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
86 } else {
87 #ifdef CONFIG_RX_PACKET_APPEND_FCS
88 if (check_fwstate(&padapter->mlmepriv, WIFI_MONITOR_STATE) == _FALSE)
89 if ((pattrib->pkt_rpt_type == NORMAL_RX) && rtw_hal_rcr_check(padapter, RCR_APPFCS))
90 pattrib->pkt_len -= IEEE80211_FCS_LEN;
91 #endif
92
93 if (rtw_os_alloc_recvframe(padapter, precvframe,
94 (ptr + rx_report_sz + pattrib->shift_sz), precvbuf->pskb) == _FAIL) {
95 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
96 break;
97 }
98
99 recvframe_put(precvframe, pattrib->pkt_len);
100 /* move to drv info position */
101 ptr += RXDESC_SIZE;
102
103 /* update drv info */
104 if (rtw_hal_rcr_check(padapter, RCR_APP_BA_SSN)) {
105 /* rtl8723s_update_bassn(padapter, pdrvinfo); */
106 ptr += 4;
107 }
108
109 if (pattrib->pkt_rpt_type == NORMAL_RX) {
110 /* skip the rx packet with abnormal length */
111 if (pattrib->pkt_len < 14 || pattrib->pkt_len > 8192) {
112 RTW_INFO("skip abnormal rx packet(%d)\n", pattrib->pkt_len);
113 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
114 break;
115 }
116
117 pre_recv_entry(precvframe, pattrib->physt ? ptr : NULL);
118
119 } else {
120 #ifdef CONFIG_FW_C2H_PKT
121 if (pattrib->pkt_rpt_type == C2H_PACKET)
122 rtw_hal_c2h_pkt_pre_hdl(padapter, precvframe->u.hdr.rx_data, pattrib->pkt_len);
123 else {
124 RTW_INFO("%s: [WARNNING] RX type(%d) not be handled!\n",
125 __FUNCTION__, pattrib->pkt_rpt_type);
126 }
127 #endif
128 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
129 }
130 }
131
132 pkt_offset = _RND8(pkt_offset);
133 precvbuf->pdata += pkt_offset;
134 ptr = precvbuf->pdata;
135 precvframe = NULL;
136 }
137
138 rtw_enqueue_recvbuf(precvbuf, &precvpriv->free_recv_buf_queue);
139 } while (1);
140
141 #ifdef CONFIG_RTW_NAPI
142 #ifdef CONFIG_RTW_NAPI_V2
143 if (padapter->registrypriv.en_napi) {
144 struct dvobj_priv *d;
145 struct _ADAPTER *a;
146 u8 i;
147
148 d = adapter_to_dvobj(padapter);
149 for (i = 0; i < d->iface_nums; i++) {
150 a = d->padapters[i];
151 precvpriv = &a->recvpriv;
152 if (rtw_if_up(a) == _TRUE
153 && skb_queue_len(&precvpriv->rx_napi_skb_queue))
154 napi_schedule(&a->napi);
155
156 }
157 }
158 #endif /* CONFIG_RTW_NAPI_V2 */
159 #endif /* CONFIG_RTW_NAPI */
160
161 return _SUCCESS;
162 }
163
rtl8723ds_recv_tasklet(void * priv)164 static void rtl8723ds_recv_tasklet(void *priv)
165 {
166 _adapter *adapter = (_adapter *)priv;
167 s32 ret;
168
169 ret = rtl8723ds_recv_hdl(adapter);
170 if (ret == RTW_RFRAME_UNAVAIL
171 || ret == RTW_RFRAME_PKT_UNAVAIL
172 ) {
173 /* schedule again and hope recvframe/packet is available next time. */
174 #ifdef PLATFORM_LINUX
175 tasklet_schedule(&adapter->recvpriv.recv_tasklet);
176 #endif
177 }
178 }
179 #else
rtl8723ds_recv_tasklet(void * priv)180 static void rtl8723ds_recv_tasklet(void *priv)
181 {
182 PADAPTER padapter;
183 PHAL_DATA_TYPE pHalData;
184 struct recv_priv *precvpriv;
185 struct recv_buf *precvbuf;
186 union recv_frame *precvframe;
187 struct recv_frame_hdr *phdr;
188 struct rx_pkt_attrib *pattrib;
189 u8 *ptr;
190 _pkt *ppkt;
191 u32 pkt_offset;
192
193 padapter = (PADAPTER)priv;
194 pHalData = GET_HAL_DATA(padapter);
195 precvpriv = &padapter->recvpriv;
196
197 do {
198 precvbuf = rtw_dequeue_recvbuf(&precvpriv->recv_buf_pending_queue);
199 if (NULL == precvbuf)
200 break;
201
202 ptr = precvbuf->pdata;
203
204 while (ptr < precvbuf->ptail) {
205 precvframe = rtw_alloc_recvframe(&precvpriv->free_recv_queue);
206 if (precvframe == NULL) {
207 rtw_enqueue_recvbuf_to_head(precvbuf, &precvpriv->recv_buf_pending_queue);
208
209 /* The case of can't allocate recvframe should be temporary, */
210 /* schedule again and hope recvframe is available next time. */
211 #ifdef PLATFORM_LINUX
212 tasklet_schedule(&precvpriv->recv_tasklet);
213 #endif
214 return;
215 }
216
217 phdr = &precvframe->u.hdr;
218 pattrib = &phdr->attrib;
219
220 rtl8723d_query_rx_desc_status(precvframe, ptr);
221
222 #if 0
223 {
224 int i, len = 64;
225 u8 *pptr = ptr;
226
227 if ((*(pptr + RXDESC_SIZE + pattrib->drvinfo_sz) != 0x80) && (*(pptr + RXDESC_SIZE + pattrib->drvinfo_sz) != 0x40)) {
228 RTW_INFO("##############RxDESC###############\n");
229 for (i = 0; i < 32; i = i + 16)
230 RTW_INFO("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:\n", *(pptr + i),
231 *(pptr + i + 1), *(pptr + i + 2) , *(pptr + i + 3) , *(pptr + i + 4), *(pptr + i + 5), *(pptr + i + 6), *(pptr + i + 7), *(pptr + i + 8),
232 *(pptr + i + 9), *(pptr + i + 10),
233 *(pptr + i + 11), *(pptr + i + 12), *(pptr + i + 13), *(pptr + i + 14), *(pptr + i + 15));
234
235 if (pattrib->pkt_len < 100)
236 len = pattrib->pkt_len;
237 pptr = ptr + RXDESC_SIZE + pattrib->drvinfo_sz;
238 RTW_INFO("##############Len=%d###############\n", pattrib->pkt_len);
239 for (i = 0; i < len; i = i + 16)
240 RTW_INFO("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:\n", *(pptr + i),
241 *(pptr + i + 1), *(pptr + i + 2) , *(pptr + i + 3) , *(pptr + i + 4), *(pptr + i + 5), *(pptr + i + 6), *(pptr + i + 7), *(pptr + i + 8),
242 *(pptr + i + 9), *(pptr + i + 10),
243 *(pptr + i + 11), *(pptr + i + 12), *(pptr + i + 13), *(pptr + i + 14), *(pptr + i + 15));
244 RTW_INFO("#############################\n");
245 }
246 }
247 #endif
248
249 /* fix Hardware RX data error, drop whole recv_buffer */
250 if (!rtw_hal_rcr_check(padapter, RCR_ACRC32) && pattrib->crc_err) {
251 RTW_INFO("%s()-%d: RX Warning! rx CRC ERROR !!\n", __FUNCTION__, __LINE__);
252 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
253 break;
254 }
255
256 pkt_offset = RXDESC_SIZE + pattrib->drvinfo_sz + pattrib->pkt_len;
257 #if 0 /* reduce check to speed up */
258 if ((ptr + pkt_offset) > precvbuf->ptail) {
259 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
260 break;
261 }
262 #endif
263
264 if ((pattrib->crc_err) || (pattrib->icv_err)) {
265 #ifdef CONFIG_MP_INCLUDED
266 if (padapter->registrypriv.mp_mode == 1) {
267 if (check_fwstate(&padapter->mlmepriv, WIFI_MP_STATE) == _TRUE) { /* &&(padapter->mppriv.check_mp_pkt == 0)) */
268 if (pattrib->crc_err == 1)
269 padapter->mppriv.rx_crcerrpktcount++;
270 }
271 } else
272 #endif
273 {
274 RTW_INFO("%s: crc_err=%d icv_err=%d, skip!\n", __FUNCTION__, pattrib->crc_err, pattrib->icv_err);
275 }
276 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
277 } else {
278 ppkt = rtw_skb_clone(precvbuf->pskb);
279 if (ppkt == NULL) {
280 RTW_INFO("%s: no enough memory to allocate SKB!\n", __FUNCTION__);
281 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
282 rtw_enqueue_recvbuf_to_head(precvbuf, &precvpriv->recv_buf_pending_queue);
283
284 /* The case of can't allocate skb is serious and may never be recovered, */
285 /* once bDriverStopped is enable, this task should be stopped. */
286 if (!rtw_is_drv_stopped(padapter)) {
287 #ifdef PLATFORM_LINUX
288 tasklet_schedule(&precvpriv->recv_tasklet);
289 #endif
290 }
291
292 return;
293 }
294
295 phdr->pkt = ppkt;
296 phdr->len = 0;
297 phdr->rx_head = precvbuf->phead;
298 phdr->rx_data = phdr->rx_tail = precvbuf->pdata;
299 phdr->rx_end = precvbuf->pend;
300 recvframe_put(precvframe, pkt_offset);
301 recvframe_pull(precvframe, RXDESC_SIZE + pattrib->drvinfo_sz);
302 skb_pull(ppkt, RXDESC_SIZE + pattrib->drvinfo_sz);
303
304 #ifdef CONFIG_RX_PACKET_APPEND_FCS
305 if (check_fwstate(&padapter->mlmepriv, WIFI_MONITOR_STATE) == _FALSE) {
306 if ((pattrib->pkt_rpt_type == NORMAL_RX) && rtw_hal_rcr_check(padapter, RCR_APPFCS)) {
307 recvframe_pull_tail(precvframe, IEEE80211_FCS_LEN);
308 pattrib->pkt_len -= IEEE80211_FCS_LEN;
309 ppkt->len = pattrib->pkt_len;
310 }
311 }
312 #endif
313
314 /* move to drv info position */
315 ptr += RXDESC_SIZE;
316
317 /* update drv info */
318 if (rtw_hal_rcr_check(padapter, RCR_APP_BA_SSN)) {
319 /* rtl8723s_update_bassn(padapter, pdrvinfo); */
320 ptr += 4;
321 }
322
323 if (pattrib->pkt_rpt_type == NORMAL_RX)
324 pre_recv_entry(precvframe, pattrib->physt ? ptr : NULL);
325 else {
326 #ifdef CONFIG_FW_C2H_PKT
327 if (pattrib->pkt_rpt_type == C2H_PACKET)
328 rtw_hal_c2h_pkt_pre_hdl(padapter, precvframe->u.hdr.rx_data, pattrib->pkt_len);
329 else {
330 RTW_INFO("%s: [WARNNING] RX type(%d) not be handled!\n",
331 __FUNCTION__, pattrib->pkt_rpt_type);
332 }
333 #endif
334 rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
335 }
336 }
337
338 pkt_offset = _RND8(pkt_offset);
339 precvbuf->pdata += pkt_offset;
340 ptr = precvbuf->pdata;
341 }
342
343 rtw_skb_free(precvbuf->pskb);
344 precvbuf->pskb = NULL;
345 rtw_enqueue_recvbuf(precvbuf, &precvpriv->free_recv_buf_queue);
346 } while (1);
347 }
348 #endif
349
350 /*
351 * Initialize recv private variable for hardware dependent
352 * 1. recv buf
353 * 2. recv tasklet
354 *
355 */
rtl8723ds_init_recv_priv(PADAPTER padapter)356 s32 rtl8723ds_init_recv_priv(PADAPTER padapter)
357 {
358 struct registry_priv *regsty = adapter_to_regsty(padapter);
359 s32 res;
360 u32 i, n;
361 struct recv_priv *precvpriv;
362 struct recv_buf *precvbuf;
363
364
365 res = _SUCCESS;
366 precvpriv = &padapter->recvpriv;
367
368 /* 3 1. init recv buffer */
369 _rtw_init_queue(&precvpriv->free_recv_buf_queue);
370 _rtw_init_queue(&precvpriv->recv_buf_pending_queue);
371
372 n = regsty->recvbuf_nr * sizeof(struct recv_buf) + 4;
373 precvpriv->pallocated_recv_buf = rtw_zmalloc(n);
374 if (precvpriv->pallocated_recv_buf == NULL) {
375 res = _FAIL;
376 goto exit;
377 }
378
379 precvpriv->precv_buf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(precvpriv->pallocated_recv_buf), 4);
380
381 /* init each recv buffer */
382 precvbuf = (struct recv_buf *)precvpriv->precv_buf;
383 for (i = 0; i < regsty->recvbuf_nr; i++) {
384 res = sdio_initrecvbuf(precvbuf, padapter);
385 if (res == _FAIL)
386 break;
387
388 res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf, MAX_RECVBUF_SZ);
389 if (res == _FAIL) {
390 sdio_freerecvbuf(precvbuf);
391 break;
392 }
393
394 rtw_list_insert_tail(&precvbuf->list, &precvpriv->free_recv_buf_queue.queue);
395
396 precvbuf++;
397 }
398 precvpriv->free_recv_buf_queue_cnt = i;
399
400 if (res == _FAIL)
401 goto initbuferror;
402
403 /* 3 2. init tasklet */
404 #ifdef PLATFORM_LINUX
405 tasklet_init(&precvpriv->recv_tasklet,
406 (void(*)(unsigned long))rtl8723ds_recv_tasklet,
407 (unsigned long)padapter);
408 #endif
409
410 goto exit;
411
412 initbuferror:
413 precvbuf = (struct recv_buf *)precvpriv->precv_buf;
414 if (precvbuf) {
415 n = precvpriv->free_recv_buf_queue_cnt;
416 precvpriv->free_recv_buf_queue_cnt = 0;
417 for (i = 0; i < n ; i++) {
418 rtw_list_delete(&precvbuf->list);
419 rtw_os_recvbuf_resource_free(padapter, precvbuf);
420 sdio_freerecvbuf(precvbuf);
421 precvbuf++;
422 }
423 precvpriv->precv_buf = NULL;
424 }
425
426 if (precvpriv->pallocated_recv_buf) {
427 n = regsty->recvbuf_nr * sizeof(struct recv_buf) + 4;
428 rtw_mfree(precvpriv->pallocated_recv_buf, n);
429 precvpriv->pallocated_recv_buf = NULL;
430 }
431
432 exit:
433 return res;
434 }
435
436 /*
437 * Free recv private variable of hardware dependent
438 * 1. recv buf
439 * 2. recv tasklet
440 *
441 */
rtl8723ds_free_recv_priv(PADAPTER padapter)442 void rtl8723ds_free_recv_priv(PADAPTER padapter)
443 {
444 struct registry_priv *regsty = &padapter->registrypriv;
445 u32 i, n;
446 struct recv_priv *precvpriv;
447 struct recv_buf *precvbuf;
448
449
450 precvpriv = &padapter->recvpriv;
451
452 /* 3 1. kill tasklet */
453 #ifdef PLATFORM_LINUX
454 tasklet_kill(&precvpriv->recv_tasklet);
455 #endif
456
457 /* 3 2. free all recv buffers */
458 precvbuf = (struct recv_buf *)precvpriv->precv_buf;
459 if (precvbuf) {
460 n = regsty->recvbuf_nr;
461 precvpriv->free_recv_buf_queue_cnt = 0;
462 for (i = 0; i < n ; i++) {
463 rtw_list_delete(&precvbuf->list);
464 rtw_os_recvbuf_resource_free(padapter, precvbuf);
465 sdio_freerecvbuf(precvbuf);
466 precvbuf++;
467 }
468 precvpriv->precv_buf = NULL;
469 }
470
471 if (precvpriv->pallocated_recv_buf) {
472 n = regsty->recvbuf_nr * sizeof(struct recv_buf) + 4;
473 rtw_mfree(precvpriv->pallocated_recv_buf, n);
474 precvpriv->pallocated_recv_buf = NULL;
475 }
476 }
477