net: wireless: rockchip_wlan: add rtl8723cs support
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / rockchip_wlan / rtl8723cs / hal / rtl8703b / sdio / rtl8703bs_recv.c
diff --git a/drivers/net/wireless/rockchip_wlan/rtl8723cs/hal/rtl8703b/sdio/rtl8703bs_recv.c b/drivers/net/wireless/rockchip_wlan/rtl8723cs/hal/rtl8703b/sdio/rtl8703bs_recv.c
new file mode 100644 (file)
index 0000000..74895a3
--- /dev/null
@@ -0,0 +1,505 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTL8703BS_RECV_C_
+
+#include <rtl8703b_hal.h>
+
+
+static s32 initrecvbuf(struct recv_buf *precvbuf, PADAPTER padapter)
+{
+       _rtw_init_listhead(&precvbuf->list);
+       _rtw_spinlock_init(&precvbuf->recvbuf_lock);
+
+       precvbuf->adapter = padapter;
+
+       return _SUCCESS;
+}
+
+static void freerecvbuf(struct recv_buf *precvbuf)
+{
+       _rtw_spinlock_free(&precvbuf->recvbuf_lock);
+}
+
+#ifdef CONFIG_SDIO_RX_COPY
+static void rtl8703bs_recv_tasklet(void *priv)
+{
+       PADAPTER                        padapter;
+       PHAL_DATA_TYPE          pHalData;
+       struct recv_priv                *precvpriv;
+       struct recv_buf *precvbuf;
+       union recv_frame                *precvframe;
+       struct recv_frame_hdr   *phdr;
+       struct rx_pkt_attrib    *pattrib;
+       u8      *ptr;
+       u32     pkt_len, pkt_offset;
+       u8      rx_report_sz = 0;
+
+
+       padapter = (PADAPTER)priv;
+       pHalData = GET_HAL_DATA(padapter);
+       precvpriv = &padapter->recvpriv;
+
+       do {
+               precvbuf = rtw_dequeue_recvbuf(&precvpriv->recv_buf_pending_queue);
+               if (NULL == precvbuf)
+                       break;
+
+               ptr = precvbuf->pdata;
+
+               while (ptr < precvbuf->ptail) {
+                       precvframe = rtw_alloc_recvframe(&precvpriv->free_recv_queue);
+                       if (precvframe == NULL) {
+                               RTW_INFO("%s: no enough recv frame!\n", __FUNCTION__);
+                               rtw_enqueue_recvbuf_to_head(precvbuf, &precvpriv->recv_buf_pending_queue);
+
+                               /* The case of can't allocte recvframe should be temporary, */
+                               /* schedule again and hope recvframe is available next time. */
+#ifdef PLATFORM_LINUX
+                               tasklet_schedule(&precvpriv->recv_tasklet);
+#endif
+                               return;
+                       }
+
+                       /* rx desc parsing */
+                       rtl8703b_query_rx_desc_status(precvframe, ptr);
+
+                       pattrib = &precvframe->u.hdr.attrib;
+
+                       /* fix Hardware RX data error, drop whole recv_buffer */
+                       if ((!(pHalData->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err) {
+#if !(MP_DRIVER == 1)
+                               RTW_INFO("%s()-%d: RX Warning! rx CRC ERROR !!\n", __FUNCTION__, __LINE__);
+#endif
+                               rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                               break;
+                       }
+
+                       rx_report_sz = RXDESC_SIZE + pattrib->drvinfo_sz;
+                       pkt_offset = rx_report_sz + pattrib->shift_sz + pattrib->pkt_len;
+
+                       if ((ptr + pkt_offset) > precvbuf->ptail) {
+                               RTW_INFO("%s()-%d: : next pkt len(%p,%d) exceed ptail(%p)!\n", __FUNCTION__, __LINE__, ptr, pkt_offset, precvbuf->ptail);
+                               rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                               break;
+                       }
+
+                       if ((pattrib->crc_err) || (pattrib->icv_err)) {
+#ifdef CONFIG_MP_INCLUDED
+                               if (padapter->registrypriv.mp_mode == 1) {
+                                       if ((check_fwstate(&padapter->mlmepriv, WIFI_MP_STATE) == _TRUE)) { /* &&(padapter->mppriv.check_mp_pkt == 0)) */
+                                               if (pattrib->crc_err == 1)
+                                                       padapter->mppriv.rx_crcerrpktcount++;
+                                       }
+                               } else
+#endif
+                               {
+                                       RTW_INFO("%s: crc_err=%d icv_err=%d, skip!\n", __FUNCTION__, pattrib->crc_err, pattrib->icv_err);
+                               }
+                               rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                       } else {
+#ifdef CONFIG_RX_PACKET_APPEND_FCS
+                               if (check_fwstate(&padapter->mlmepriv, WIFI_MONITOR_STATE) == _FALSE)
+                                       if ((pattrib->pkt_rpt_type == NORMAL_RX) && (pHalData->ReceiveConfig & RCR_APPFCS))
+                                               pattrib->pkt_len -= IEEE80211_FCS_LEN;
+#endif
+
+                               if (rtw_os_alloc_recvframe(padapter, precvframe,
+                                       (ptr + rx_report_sz + pattrib->shift_sz), precvbuf->pskb) == _FAIL) {
+                                       rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                                       break;
+                               }
+                               recvframe_put(precvframe, pattrib->pkt_len);
+                               /* recvframe_pull(precvframe, drvinfo_sz + RXDESC_SIZE); */
+
+                               /* move to drv info position */
+                               ptr += RXDESC_SIZE;
+
+                               /* update drv info */
+                               if (pHalData->ReceiveConfig & RCR_APP_BA_SSN) {
+                                       /* rtl8703s_update_bassn(padapter, pdrvinfo); */
+                                       ptr += 4;
+                               }
+
+                               if (pattrib->pkt_rpt_type == NORMAL_RX) {
+                                       /* skip the rx packet with abnormal length */
+                                       if (pattrib->pkt_len < 14 || pattrib->pkt_len > 8192) {
+                                               RTW_INFO("skip abnormal rx packet(%d)\n", pattrib->pkt_len);
+                                               rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                                               break;
+                                       }
+
+#ifdef CONFIG_CONCURRENT_MODE
+                                       pre_recv_entry(precvframe, ptr);
+#endif /*CONFIG_CONCURRENT_MODE*/
+
+                                       if (pattrib->physt)
+                                               rx_query_phy_status(precvframe, ptr);
+
+                                       rtw_recv_entry(precvframe);
+                               } else {
+#ifdef CONFIG_FW_C2H_PKT
+                                       if (pattrib->pkt_rpt_type == C2H_PACKET)
+                                               rtw_hal_c2h_pkt_pre_hdl(padapter, precvframe->u.hdr.rx_data, pattrib->pkt_len);
+                                       else {
+                                               RTW_INFO("%s: [WARNNING] RX type(%d) not be handled!\n",
+                                                       __FUNCTION__, pattrib->pkt_rpt_type);
+                                       }
+#endif
+                                       rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                               }
+                       }
+
+                       pkt_offset = _RND8(pkt_offset);
+                       precvbuf->pdata += pkt_offset;
+                       ptr = precvbuf->pdata;
+                       precvframe = NULL;
+               }
+
+               rtw_enqueue_recvbuf(precvbuf, &precvpriv->free_recv_buf_queue);
+       } while (1);
+
+}
+#else
+static void rtl8703bs_recv_tasklet(void *priv)
+{
+       PADAPTER                                padapter;
+       PHAL_DATA_TYPE                  pHalData;
+       struct recv_priv                *precvpriv;
+       struct recv_buf         *precvbuf;
+       union recv_frame                *precvframe;
+       struct recv_frame_hdr   *phdr;
+       struct rx_pkt_attrib    *pattrib;
+       u8              *ptr;
+       _pkt            *ppkt;
+       u32             pkt_offset;
+
+       padapter = (PADAPTER)priv;
+       pHalData = GET_HAL_DATA(padapter);
+       precvpriv = &padapter->recvpriv;
+
+       do {
+               precvbuf = rtw_dequeue_recvbuf(&precvpriv->recv_buf_pending_queue);
+               if (NULL == precvbuf)
+                       break;
+
+               ptr = precvbuf->pdata;
+
+               while (ptr < precvbuf->ptail) {
+                       precvframe = rtw_alloc_recvframe(&precvpriv->free_recv_queue);
+                       if (precvframe == NULL) {
+                               rtw_enqueue_recvbuf_to_head(precvbuf, &precvpriv->recv_buf_pending_queue);
+
+                               /* The case of can't allocte recvframe should be temporary, */
+                               /* schedule again and hope recvframe is available next time. */
+#ifdef PLATFORM_LINUX
+                               tasklet_schedule(&precvpriv->recv_tasklet);
+#endif
+                               return;
+                       }
+
+                       phdr = &precvframe->u.hdr;
+                       pattrib = &phdr->attrib;
+
+                       rtl8703b_query_rx_desc_status(precvframe, ptr);
+
+#if 0
+                       {
+                               int i, len = 64;
+                               u8 *pptr = ptr;
+
+                               if ((*(pptr + RXDESC_SIZE + pattrib->drvinfo_sz) != 0x80) && (*(pptr + RXDESC_SIZE + pattrib->drvinfo_sz) != 0x40)) {
+                                       RTW_INFO("##############RxDESC###############\n");
+                                       for (i = 0; i < 32; i = i + 16)
+                                               RTW_INFO("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:\n", *(pptr + i),
+                                                       *(pptr + i + 1), *(pptr + i + 2) , *(pptr + i + 3) , *(pptr + i + 4), *(pptr + i + 5), *(pptr + i + 6), *(pptr + i + 7), *(pptr + i + 8),
+                                                       *(pptr + i + 9), *(pptr + i + 10),
+                                                       *(pptr + i + 11), *(pptr + i + 12), *(pptr + i + 13), *(pptr + i + 14), *(pptr + i + 15));
+
+                                       if (pattrib->pkt_len < 100)
+                                               len = pattrib->pkt_len;
+                                       pptr = ptr + RXDESC_SIZE + pattrib->drvinfo_sz;
+                                       RTW_INFO("##############Len=%d###############\n", pattrib->pkt_len);
+                                       for (i = 0; i < len; i = i + 16)
+                                               RTW_INFO("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:\n", *(pptr + i),
+                                                       *(pptr + i + 1), *(pptr + i + 2) , *(pptr + i + 3) , *(pptr + i + 4), *(pptr + i + 5), *(pptr + i + 6), *(pptr + i + 7), *(pptr + i + 8),
+                                                       *(pptr + i + 9), *(pptr + i + 10),
+                                                       *(pptr + i + 11), *(pptr + i + 12), *(pptr + i + 13), *(pptr + i + 14), *(pptr + i + 15));
+                                       RTW_INFO("#############################\n");
+                               }
+                       }
+#endif
+
+                       /* fix Hardware RX data error, drop whole recv_buffer */
+                       if ((!(pHalData->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err) {
+                               RTW_INFO("%s()-%d: RX Warning! rx CRC ERROR !!\n", __FUNCTION__, __LINE__);
+                               rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                               break;
+                       }
+
+                       pkt_offset = RXDESC_SIZE + pattrib->drvinfo_sz + pattrib->pkt_len;
+#if 0 /* reduce check to speed up */
+                       if ((ptr + pkt_offset) > precvbuf->ptail) {
+                               rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                               break;
+                       }
+#endif
+
+                       if ((pattrib->crc_err) || (pattrib->icv_err)) {
+#ifdef CONFIG_MP_INCLUDED
+                               if (padapter->registrypriv.mp_mode == 1) {
+                                       if ((check_fwstate(&padapter->mlmepriv, WIFI_MP_STATE) == _TRUE)) { /* &&(padapter->mppriv.check_mp_pkt == 0)) */
+                                               if (pattrib->crc_err == 1)
+                                                       padapter->mppriv.rx_crcerrpktcount++;
+                                       }
+                               } else
+#endif
+                               {
+                                       RTW_INFO("%s: crc_err=%d icv_err=%d, skip!\n", __FUNCTION__, pattrib->crc_err, pattrib->icv_err);
+                               }
+                               rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                       } else {
+                               ppkt = rtw_skb_clone(precvbuf->pskb);
+                               if (ppkt == NULL) {
+                                       RTW_INFO("%s: no enough memory to allocate SKB!\n", __FUNCTION__);
+                                       rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                                       rtw_enqueue_recvbuf_to_head(precvbuf, &precvpriv->recv_buf_pending_queue);
+
+                                       /* The case of can't allocte skb is serious and may never be recovered, */
+                                       /* once bDriverStopped is enable, this task should be stopped. */
+                                       if (!rtw_is_drv_stopped(padapter)) {
+#ifdef PLATFORM_LINUX
+                                               tasklet_schedule(&precvpriv->recv_tasklet);
+#endif
+                                       }
+
+                                       return;
+                               }
+
+                               phdr->pkt = ppkt;
+                               phdr->len = 0;
+                               phdr->rx_head = precvbuf->phead;
+                               phdr->rx_data = phdr->rx_tail = precvbuf->pdata;
+                               phdr->rx_end = precvbuf->pend;
+                               recvframe_put(precvframe, pkt_offset);
+                               recvframe_pull(precvframe, RXDESC_SIZE + pattrib->drvinfo_sz);
+                               skb_pull(ppkt, RXDESC_SIZE + pattrib->drvinfo_sz);
+
+#ifdef CONFIG_RX_PACKET_APPEND_FCS
+                               if (check_fwstate(&padapter->mlmepriv, WIFI_MONITOR_STATE) == _FALSE) {
+                                       if ((pattrib->pkt_rpt_type == NORMAL_RX) && (pHalData->ReceiveConfig & RCR_APPFCS)) {
+                                               recvframe_pull_tail(precvframe, IEEE80211_FCS_LEN);
+                                               pattrib->pkt_len -= IEEE80211_FCS_LEN;
+                                               ppkt->len = pattrib->pkt_len;
+                                       }
+                               }
+#endif
+
+                               /* move to drv info position */
+                               ptr += RXDESC_SIZE;
+
+                               /* update drv info */
+                               if (pHalData->ReceiveConfig & RCR_APP_BA_SSN) {
+                                       /* rtl8703s_update_bassn(padapter, pdrvinfo); */
+                                       ptr += 4;
+                               }
+
+                               if (pattrib->pkt_rpt_type == NORMAL_RX) {
+
+#ifdef CONFIG_CONCURRENT_MODE
+                                       pre_recv_entry(precvframe, ptr);
+#endif /*CONFIG_CONCURRENT_MODE*/
+
+
+                                       if (pattrib->physt)
+                                               rx_query_phy_status(precvframe, ptr);
+
+                                       rtw_recv_entry(precvframe);
+
+
+                               } else {
+#ifdef CONFIG_FW_C2H_PKT
+                                       if (pattrib->pkt_rpt_type == C2H_PACKET)
+                                               rtw_hal_c2h_pkt_pre_hdl(padapter, precvframe->u.hdr.rx_data, pattrib->pkt_len);
+                                       else {
+                                               RTW_INFO("%s: [WARNNING] RX type(%d) not be handled!\n",
+                                                       __FUNCTION__, pattrib->pkt_rpt_type);
+                                       }
+#endif
+                                       rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+                               }
+                       }
+
+                       pkt_offset = _RND8(pkt_offset);
+                       precvbuf->pdata += pkt_offset;
+                       ptr = precvbuf->pdata;
+               }
+
+               rtw_skb_free(precvbuf->pskb);
+               precvbuf->pskb = NULL;
+               rtw_enqueue_recvbuf(precvbuf, &precvpriv->free_recv_buf_queue);
+       } while (1);
+}
+#endif
+
+/*
+ * Initialize recv private variable for hardware dependent
+ * 1. recv buf
+ * 2. recv tasklet
+ *
+ */
+s32 rtl8703bs_init_recv_priv(PADAPTER padapter)
+{
+       s32                     res;
+       u32                     i, n;
+       struct recv_priv        *precvpriv;
+       struct recv_buf         *precvbuf;
+
+
+       res = _SUCCESS;
+       precvpriv = &padapter->recvpriv;
+
+       /* 3 1. init recv buffer */
+       _rtw_init_queue(&precvpriv->free_recv_buf_queue);
+       _rtw_init_queue(&precvpriv->recv_buf_pending_queue);
+
+       n = NR_RECVBUFF * sizeof(struct recv_buf) + 4;
+       precvpriv->pallocated_recv_buf = rtw_zmalloc(n);
+       if (precvpriv->pallocated_recv_buf == NULL) {
+               res = _FAIL;
+               goto exit;
+       }
+
+       precvpriv->precv_buf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(precvpriv->pallocated_recv_buf), 4);
+
+       /* init each recv buffer */
+       precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+       for (i = 0; i < NR_RECVBUFF; i++) {
+               res = initrecvbuf(precvbuf, padapter);
+               if (res == _FAIL)
+                       break;
+
+               res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf);
+               if (res == _FAIL) {
+                       freerecvbuf(precvbuf);
+                       break;
+               }
+
+#ifdef CONFIG_SDIO_RX_COPY
+               if (precvbuf->pskb == NULL) {
+                       SIZE_PTR tmpaddr = 0;
+                       SIZE_PTR alignment = 0;
+
+                       precvbuf->pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);
+
+                       if (precvbuf->pskb) {
+                               precvbuf->pskb->dev = padapter->pnetdev;
+
+                               tmpaddr = (SIZE_PTR)precvbuf->pskb->data;
+                               alignment = tmpaddr & (RECVBUFF_ALIGN_SZ - 1);
+                               skb_reserve(precvbuf->pskb, (RECVBUFF_ALIGN_SZ - alignment));
+                       }
+
+                       if (precvbuf->pskb == NULL)
+                               RTW_INFO("%s: alloc_skb fail!\n", __FUNCTION__);
+               }
+#endif
+
+               rtw_list_insert_tail(&precvbuf->list, &precvpriv->free_recv_buf_queue.queue);
+
+               precvbuf++;
+       }
+       precvpriv->free_recv_buf_queue_cnt = i;
+
+       if (res == _FAIL)
+               goto initbuferror;
+
+       /* 3 2. init tasklet */
+#ifdef PLATFORM_LINUX
+       tasklet_init(&precvpriv->recv_tasklet,
+                    (void(*)(unsigned long))rtl8703bs_recv_tasklet,
+                    (unsigned long)padapter);
+#endif
+
+       goto exit;
+
+initbuferror:
+       precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+       if (precvbuf) {
+               n = precvpriv->free_recv_buf_queue_cnt;
+               precvpriv->free_recv_buf_queue_cnt = 0;
+               for (i = 0; i < n ; i++) {
+                       rtw_list_delete(&precvbuf->list);
+                       rtw_os_recvbuf_resource_free(padapter, precvbuf);
+                       freerecvbuf(precvbuf);
+                       precvbuf++;
+               }
+               precvpriv->precv_buf = NULL;
+       }
+
+       if (precvpriv->pallocated_recv_buf) {
+               n = NR_RECVBUFF * sizeof(struct recv_buf) + 4;
+               rtw_mfree(precvpriv->pallocated_recv_buf, n);
+               precvpriv->pallocated_recv_buf = NULL;
+       }
+
+exit:
+       return res;
+}
+
+/*
+ * Free recv private variable of hardware dependent
+ * 1. recv buf
+ * 2. recv tasklet
+ *
+ */
+void rtl8703bs_free_recv_priv(PADAPTER padapter)
+{
+       u32                     i, n;
+       struct recv_priv        *precvpriv;
+       struct recv_buf         *precvbuf;
+
+
+       precvpriv = &padapter->recvpriv;
+
+       /* 3 1. kill tasklet */
+#ifdef PLATFORM_LINUX
+       tasklet_kill(&precvpriv->recv_tasklet);
+#endif
+
+       /* 3 2. free all recv buffers */
+       precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+       if (precvbuf) {
+               n = NR_RECVBUFF;
+               precvpriv->free_recv_buf_queue_cnt = 0;
+               for (i = 0; i < n ; i++) {
+                       rtw_list_delete(&precvbuf->list);
+                       rtw_os_recvbuf_resource_free(padapter, precvbuf);
+                       freerecvbuf(precvbuf);
+                       precvbuf++;
+               }
+               precvpriv->precv_buf = NULL;
+       }
+
+       if (precvpriv->pallocated_recv_buf) {
+               n = NR_RECVBUFF * sizeof(struct recv_buf) + 4;
+               rtw_mfree(precvpriv->pallocated_recv_buf, n);
+               precvpriv->pallocated_recv_buf = NULL;
+       }
+}