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