summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/rtlwifi/rtl8723as/hal/rtl8723a/sdio/rtl8723as_recv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rtlwifi/rtl8723as/hal/rtl8723a/sdio/rtl8723as_recv.c')
-rwxr-xr-xdrivers/net/wireless/rtlwifi/rtl8723as/hal/rtl8723a/sdio/rtl8723as_recv.c997
1 files changed, 997 insertions, 0 deletions
diff --git a/drivers/net/wireless/rtlwifi/rtl8723as/hal/rtl8723a/sdio/rtl8723as_recv.c b/drivers/net/wireless/rtlwifi/rtl8723as/hal/rtl8723a/sdio/rtl8723as_recv.c
new file mode 100755
index 000000000000..d9923679de72
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723as/hal/rtl8723a/sdio/rtl8723as_recv.c
@@ -0,0 +1,997 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 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 _RTL8723AS_RECV_C_
+
+#include <drv_conf.h>
+
+#if defined (PLATFORM_LINUX) && defined (PLATFORM_WINDOWS)
+#error "Shall be Linux or Windows, but not both!\n"
+#endif
+
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <rtl8723a_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);
+}
+
+static void update_recvframe_attrib(
+ union recv_frame *precvframe,
+ struct recv_stat *prxstat)
+{
+ struct rx_pkt_attrib *pattrib;
+ struct recv_stat report;
+ PRXREPORT prxreport;
+
+
+ report.rxdw0 = le32_to_cpu(prxstat->rxdw0);
+ report.rxdw1 = le32_to_cpu(prxstat->rxdw1);
+ report.rxdw2 = le32_to_cpu(prxstat->rxdw2);
+ report.rxdw3 = le32_to_cpu(prxstat->rxdw3);
+ report.rxdw4 = le32_to_cpu(prxstat->rxdw4);
+ report.rxdw5 = le32_to_cpu(prxstat->rxdw5);
+
+ prxreport = (PRXREPORT)&report;
+
+ pattrib = &precvframe->u.hdr.attrib;
+ _rtw_memset(pattrib, 0, sizeof(struct rx_pkt_attrib));
+
+ // update rx report to recv_frame attribute
+ pattrib->pkt_len = (u16)prxreport->pktlen;
+ pattrib->drvinfo_sz = (u8)(prxreport->drvinfosize << 3);
+ pattrib->physt = (u8)prxreport->physt;
+
+ pattrib->crc_err = (u8)prxreport->crc32;
+ pattrib->icv_err = (u8)prxreport->icverr;
+
+ pattrib->bdecrypted = (u8)(prxreport->swdec ? 0 : 1);
+ pattrib->encrypt = (u8)prxreport->security;
+
+ pattrib->qos = (u8)prxreport->qos;
+ pattrib->priority = (u8)prxreport->tid;
+
+ pattrib->amsdu = (u8)prxreport->amsdu;
+
+ pattrib->seq_num = (u16)prxreport->seq;
+ pattrib->frag_num = (u8)prxreport->frag;
+ pattrib->mfrag = (u8)prxreport->mf;
+ pattrib->mdata = (u8)prxreport->md;
+
+ pattrib->mcs_rate = (u8)prxreport->rxmcs;
+ pattrib->rxht = (u8)prxreport->rxht;
+}
+
+/*
+ * Notice:
+ * Before calling this function,
+ * precvframe->u.hdr.rx_data should be ready!
+ */
+void update_recvframe_phyinfo(
+ union recv_frame *precvframe,
+ struct phy_stat *pphy_status)
+{
+ PADAPTER padapter= precvframe->u.hdr.adapter;
+ struct rx_pkt_attrib *pattrib = &precvframe->u.hdr.attrib;
+ HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter);
+ PODM_PHY_INFO_T pPHYInfo = (PODM_PHY_INFO_T)(&pattrib->phy_info);
+
+ u8 *wlanhdr;
+ ODM_PACKET_INFO_T pkt_info;
+ u8 *sa;
+ //_irqL irqL;
+ struct sta_priv *pstapriv;
+ struct sta_info *psta;
+
+ pkt_info.bPacketMatchBSSID =_FALSE;
+ pkt_info.bPacketToSelf = _FALSE;
+ pkt_info.bPacketBeacon = _FALSE;
+
+
+ wlanhdr = get_recvframe_data(precvframe);
+
+ pkt_info.bPacketMatchBSSID = ((!IsFrameTypeCtrl(wlanhdr)) &&
+ !pattrib->icv_err && !pattrib->crc_err &&
+ _rtw_memcmp(get_hdr_bssid(wlanhdr), get_bssid(&padapter->mlmepriv), ETH_ALEN));
+
+ pkt_info.bPacketToSelf = pkt_info.bPacketMatchBSSID && (_rtw_memcmp(get_da(wlanhdr), myid(&padapter->eeprompriv), ETH_ALEN));
+
+ pkt_info.bPacketBeacon = pkt_info.bPacketMatchBSSID && (GetFrameSubType(wlanhdr) == WIFI_BEACON);
+
+ if(pkt_info.bPacketBeacon){
+ if(check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE) == _TRUE){
+ sa = padapter->mlmepriv.cur_network.network.MacAddress;
+ #if 0
+ {
+ DBG_8192C("==> rx beacon from AP[%02x:%02x:%02x:%02x:%02x:%02x]\n",
+ sa[0],sa[1],sa[2],sa[3],sa[4],sa[5]);
+ }
+ #endif
+ }
+ //to do Ad-hoc
+ }
+ else{
+ sa = get_sa(wlanhdr);
+ }
+
+ pkt_info.StationID = 0xFF;
+
+ pstapriv = &padapter->stapriv;
+ psta = rtw_get_stainfo(pstapriv, sa);
+ if (psta)
+ {
+ pkt_info.StationID = psta->mac_id;
+ //DBG_8192C("%s ==> StationID(%d)\n",__FUNCTION__,pkt_info.StationID);
+ }
+ pkt_info.Rate = pattrib->mcs_rate;
+
+
+ //rtl8192c_query_rx_phy_status(precvframe, pphy_status);
+ //_enter_critical_bh(&pHalData->odm_stainfo_lock, &irqL);
+ ODM_PhyStatusQuery(&pHalData->odmpriv,pPHYInfo,(u8 *)pphy_status,&(pkt_info));
+ //_exit_critical_bh(&pHalData->odm_stainfo_lock, &irqL);
+ precvframe->u.hdr.psta = NULL;
+ if (pkt_info.bPacketMatchBSSID &&
+ (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == _TRUE))
+ {
+ if (psta)
+ {
+ precvframe->u.hdr.psta = psta;
+ rtl8192c_process_phy_info(padapter, precvframe);
+ }
+ }
+ else if (pkt_info.bPacketToSelf || pkt_info.bPacketBeacon)
+ {
+ if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE) == _TRUE)
+ {
+ if (psta)
+ {
+ precvframe->u.hdr.psta = psta;
+ }
+ }
+ rtl8192c_process_phy_info(padapter, precvframe);
+ }
+}
+
+static s32 pre_recv_entry(union recv_frame *precvframe, struct recv_buf *precvbuf, struct phy_stat *pphy_status)
+{
+ s32 ret=_SUCCESS;
+#ifdef CONFIG_CONCURRENT_MODE
+ u8 *primary_myid, *secondary_myid, *paddr1;
+ union recv_frame *precvframe_if2 = NULL;
+ _adapter *primary_padapter = precvframe->u.hdr.adapter;
+ _adapter *secondary_padapter = primary_padapter->pbuddy_adapter;
+ struct recv_priv *precvpriv = &primary_padapter->recvpriv;
+ _queue *pfree_recv_queue = &precvpriv->free_recv_queue;
+ HAL_DATA_TYPE *pHalData = GET_HAL_DATA(primary_padapter);
+
+ if(!secondary_padapter)
+ return ret;
+
+ paddr1 = GetAddr1Ptr(precvframe->u.hdr.rx_data);
+
+ if(IS_MCAST(paddr1) == _FALSE)//unicast packets
+ {
+ //primary_myid = myid(&primary_padapter->eeprompriv);
+ secondary_myid = myid(&secondary_padapter->eeprompriv);
+
+ if(_rtw_memcmp(paddr1, secondary_myid, ETH_ALEN))
+ {
+ //change to secondary interface
+ precvframe->u.hdr.adapter = secondary_padapter;
+ }
+
+ //ret = recv_entry(precvframe);
+
+ }
+ else // Handle BC/MC Packets
+ {
+ //clone/copy to if2
+ _pkt *pkt_copy = NULL;
+ struct rx_pkt_attrib *pattrib = NULL;
+
+ precvframe_if2 = rtw_alloc_recvframe(pfree_recv_queue);
+
+ if(!precvframe_if2)
+ return _FAIL;
+
+ precvframe_if2->u.hdr.adapter = secondary_padapter;
+ _rtw_memcpy(&precvframe_if2->u.hdr.attrib, &precvframe->u.hdr.attrib, sizeof(struct rx_pkt_attrib));
+ pattrib = &precvframe_if2->u.hdr.attrib;
+
+ //driver need to set skb len for rtw_skb_copy().
+ //If skb->len is zero, rtw_skb_copy() will not copy data from original skb.
+ skb_put(precvframe->u.hdr.pkt, pattrib->pkt_len);
+
+ pkt_copy = rtw_skb_copy(precvframe->u.hdr.pkt);
+ if (pkt_copy == NULL)
+ {
+ if((pattrib->mfrag == 1)&&(pattrib->frag_num == 0))
+ {
+ DBG_8192C("pre_recv_entry(): rtw_skb_copy fail , drop frag frame \n");
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ return ret;
+ }
+
+ pkt_copy = rtw_skb_clone(precvframe->u.hdr.pkt);
+ if(pkt_copy == NULL)
+ {
+ DBG_8192C("pre_recv_entry(): rtw_skb_clone fail , drop frame\n");
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ return ret;
+ }
+ }
+
+ pkt_copy->dev = secondary_padapter->pnetdev;
+
+ precvframe_if2->u.hdr.pkt = pkt_copy;
+ precvframe_if2->u.hdr.rx_head = pkt_copy->head;
+ precvframe_if2->u.hdr.rx_data = pkt_copy->data;
+ precvframe_if2->u.hdr.rx_tail = skb_tail_pointer(pkt_copy);
+ precvframe_if2->u.hdr.rx_end = skb_end_pointer(pkt_copy);
+ precvframe_if2->u.hdr.len = pkt_copy->len;
+
+ //recvframe_put(precvframe_if2, pattrib->pkt_len);
+
+ if ( pHalData->ReceiveConfig & RCR_APPFCS)
+ recvframe_pull_tail(precvframe_if2, IEEE80211_FCS_LEN);
+
+ if (pattrib->physt)
+ update_recvframe_phyinfo(precvframe_if2, pphy_status);
+
+ if(rtw_recv_entry(precvframe_if2) != _SUCCESS)
+ {
+ RT_TRACE(_module_rtl871x_recv_c_,_drv_err_,
+ ("recvbuf2recvframe: rtw_recv_entry(precvframe) != _SUCCESS\n"));
+ }
+ }
+
+ if (precvframe->u.hdr.attrib.physt)
+ update_recvframe_phyinfo(precvframe, pphy_status);
+
+ ret = rtw_recv_entry(precvframe);
+#endif
+
+ return ret;
+
+}
+
+
+#ifdef CONFIG_SDIO_RX_COPY
+#ifdef CONFIG_DIRECT_RECV
+void rtl8723as_recv(PADAPTER padapter, struct recv_buf *precvbuf)
+{
+ //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;
+ _irqL irql;
+ u8 *ptr;
+ u32 pkt_len, pkt_offset, skb_len, alloc_sz;
+ _pkt *pkt_copy = NULL;
+ u8 shift_sz = 0, rx_report_sz = 0;
+
+ pHalData = GET_HAL_DATA(padapter);
+ precvpriv = &padapter->recvpriv;
+
+ ptr = precvbuf->pdata;
+
+ while (ptr < precvbuf->ptail)
+ {
+ precvframe = rtw_alloc_recvframe(&precvpriv->free_recv_queue);
+ if (precvframe == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("%s: no enough recv frame!\n",__FUNCTION__));
+ break;
+ }
+
+ //rx desc parsing
+ update_recvframe_attrib(precvframe, (struct recv_stat*)ptr);
+
+ pattrib = &precvframe->u.hdr.attrib;
+
+ // fix Hardware RX data error, drop whole recv_buffer
+ if ((!(pHalData->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err)
+ {
+ DBG_8192C("%s()-%d: RX Warning! rx CRC ERROR !!\n", __FUNCTION__, __LINE__);
+
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ break;
+ }
+
+ if (pHalData->ReceiveConfig & RCR_APP_BA_SSN)
+ rx_report_sz = RXDESC_SIZE + 4 + pattrib->drvinfo_sz;
+ else
+ rx_report_sz = RXDESC_SIZE + pattrib->drvinfo_sz;
+
+ pkt_offset = rx_report_sz + pattrib->pkt_len;
+
+ if ((ptr + pkt_offset) > precvbuf->ptail) {
+ DBG_8192C("%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++;
+ }
+ }
+ #endif
+ DBG_8192C("%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
+ {
+ // Modified by Albert 20101213
+ // For 8 bytes IP header alignment.
+ if (pattrib->qos) // Qos data, wireless lan header length is 26
+ {
+ shift_sz = 6;
+ }
+ else
+ {
+ shift_sz = 0;
+ }
+
+ skb_len = pattrib->pkt_len;
+
+ // for first fragment packet, driver need allocate 1536+drvinfo_sz+RXDESC_SIZE to defrag packet.
+ // modify alloc_sz for recvive crc error packet by thomas 2011-06-02
+ if((pattrib->mfrag == 1)&&(pattrib->frag_num == 0)){
+ //alloc_sz = 1664; //1664 is 128 alignment.
+ if(skb_len <= 1650)
+ alloc_sz = 1664;
+ else
+ alloc_sz = skb_len + 14;
+ }
+ else {
+ alloc_sz = skb_len;
+ // 6 is for IP header 8 bytes alignment in QoS packet case.
+ // 8 is for skb->data 4 bytes alignment.
+ alloc_sz += 14;
+ }
+
+ pkt_copy = rtw_skb_alloc(alloc_sz);
+
+ if(pkt_copy)
+ {
+ pkt_copy->dev = padapter->pnetdev;
+ precvframe->u.hdr.pkt = pkt_copy;
+ skb_reserve( pkt_copy, 8 - ((SIZE_PTR)( pkt_copy->data ) & 7 ));//force pkt_copy->data at 8-byte alignment address
+ skb_reserve( pkt_copy, shift_sz );//force ip_hdr at 8-byte alignment address according to shift_sz.
+ _rtw_memcpy(pkt_copy->data, (ptr + rx_report_sz), skb_len);
+ precvframe->u.hdr.rx_head = pkt_copy->head;
+ precvframe->u.hdr.rx_data = precvframe->u.hdr.rx_tail = pkt_copy->data;
+ precvframe->u.hdr.rx_end = skb_end_pointer(pkt_copy);
+ }
+ else
+ {
+ if((pattrib->mfrag == 1)&&(pattrib->frag_num == 0))
+ {
+ DBG_8192C("rtl8723as_recv_tasklet: alloc_skb fail , drop frag frame \n");
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ break;
+ }
+
+ precvframe->u.hdr.pkt = rtw_skb_clone(precvbuf->pskb);
+ if(precvframe->u.hdr.pkt)
+ {
+ _pkt *pkt_clone = precvframe->u.hdr.pkt;
+
+ pkt_clone->data = ptr + rx_report_sz;
+ skb_reset_tail_pointer(pkt_clone);
+ precvframe->u.hdr.rx_head = precvframe->u.hdr.rx_data = precvframe->u.hdr.rx_tail
+ = pkt_clone->data;
+ precvframe->u.hdr.rx_end = pkt_clone->data + skb_len;
+ }
+ else
+ {
+ DBG_8192C("rtl8723as_recv_tasklet: rtw_skb_clone fail\n");
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ break;
+ }
+ }
+
+ recvframe_put(precvframe, skb_len);
+ //recvframe_pull(precvframe, drvinfo_sz + RXDESC_SIZE);
+
+ if (pHalData->ReceiveConfig & RCR_APPFCS)
+ recvframe_pull_tail(precvframe, IEEE80211_FCS_LEN);
+
+ // move to drv info position
+ ptr += RXDESC_SIZE;
+
+ // update drv info
+ if (pHalData->ReceiveConfig & RCR_APP_BA_SSN) {
+ //rtl8723s_update_bassn(padapter, pdrvinfo);
+ ptr += 4;
+ }
+
+#ifdef CONFIG_CONCURRENT_MODE
+ if(rtw_buddy_adapter_up(padapter))
+ {
+ if(pre_recv_entry(precvframe, precvbuf, (struct phy_stat*)ptr) != _SUCCESS)
+ {
+ RT_TRACE(_module_rtl871x_recv_c_,_drv_err_,
+ ("recvbuf2recvframe: recv_entry(precvframe) != _SUCCESS\n"));
+ }
+ }
+ else
+#endif
+ {
+ if (pattrib->physt)
+ update_recvframe_phyinfo(precvframe, (struct phy_stat*)ptr);
+
+ if (rtw_recv_entry(precvframe) != _SUCCESS)
+ {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("%s: rtw_recv_entry(precvframe) != _SUCCESS\n",__FUNCTION__));
+ }
+ }
+ }
+
+ // Page size of receive package is 128 bytes alignment =>DMA AGG
+ // refer to _InitTransferPageSize()
+ pkt_offset = _RND128(pkt_offset);
+ precvbuf->pdata += pkt_offset;
+ ptr = precvbuf->pdata;
+ precvframe = NULL;
+ pkt_copy = NULL;
+
+ }
+
+ rtw_enqueue_recvbuf(precvbuf, &precvpriv->free_recv_buf_queue);
+
+}
+#endif //CONFIG_DIRECT_RECV
+
+static void rtl8723as_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;
+ _irqL irql;
+ u8 *ptr;
+ u32 pkt_len, pkt_offset, skb_len, alloc_sz;
+ _pkt *pkt_copy = NULL;
+ u8 shift_sz = 0, 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) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("%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
+ update_recvframe_attrib(precvframe, (struct recv_stat*)ptr);
+
+ pattrib = &precvframe->u.hdr.attrib;
+
+ // fix Hardware RX data error, drop whole recv_buffer
+ if ((!(pHalData->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err)
+ {
+ DBG_8192C("%s()-%d: RX Warning! rx CRC ERROR !!\n", __FUNCTION__, __LINE__);
+
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ break;
+ }
+
+ if (pHalData->ReceiveConfig & RCR_APP_BA_SSN)
+ rx_report_sz = RXDESC_SIZE + 4 + pattrib->drvinfo_sz;
+ else
+ rx_report_sz = RXDESC_SIZE + pattrib->drvinfo_sz;
+
+ pkt_offset = rx_report_sz + pattrib->pkt_len;
+
+ if ((ptr + pkt_offset) > precvbuf->ptail) {
+ DBG_8192C("%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++;
+ }
+ }
+ #endif
+ DBG_8192C("%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
+ {
+ // Modified by Albert 20101213
+ // For 8 bytes IP header alignment.
+ if (pattrib->qos) // Qos data, wireless lan header length is 26
+ {
+ shift_sz = 6;
+ }
+ else
+ {
+ shift_sz = 0;
+ }
+
+ skb_len = pattrib->pkt_len;
+
+ // for first fragment packet, driver need allocate 1536+drvinfo_sz+RXDESC_SIZE to defrag packet.
+ // modify alloc_sz for recvive crc error packet by thomas 2011-06-02
+ if((pattrib->mfrag == 1)&&(pattrib->frag_num == 0)){
+ //alloc_sz = 1664; //1664 is 128 alignment.
+ if(skb_len <= 1650)
+ alloc_sz = 1664;
+ else
+ alloc_sz = skb_len + 14;
+ }
+ else {
+ alloc_sz = skb_len;
+ // 6 is for IP header 8 bytes alignment in QoS packet case.
+ // 8 is for skb->data 4 bytes alignment.
+ alloc_sz += 14;
+ }
+
+ pkt_copy = rtw_skb_alloc(alloc_sz);
+
+ if(pkt_copy)
+ {
+ pkt_copy->dev = padapter->pnetdev;
+ precvframe->u.hdr.pkt = pkt_copy;
+ skb_reserve( pkt_copy, 8 - ((SIZE_PTR)( pkt_copy->data ) & 7 ));//force pkt_copy->data at 8-byte alignment address
+ skb_reserve( pkt_copy, shift_sz );//force ip_hdr at 8-byte alignment address according to shift_sz.
+ _rtw_memcpy(pkt_copy->data, (ptr + rx_report_sz), skb_len);
+ precvframe->u.hdr.rx_head = pkt_copy->head;
+ precvframe->u.hdr.rx_data = precvframe->u.hdr.rx_tail = pkt_copy->data;
+ precvframe->u.hdr.rx_end = skb_end_pointer(pkt_copy);
+ }
+ else
+ {
+ if((pattrib->mfrag == 1)&&(pattrib->frag_num == 0))
+ {
+ DBG_8192C("rtl8723as_recv_tasklet: alloc_skb fail , drop frag frame \n");
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ break;
+ }
+
+ precvframe->u.hdr.pkt = rtw_skb_clone(precvbuf->pskb);
+ if(precvframe->u.hdr.pkt)
+ {
+ _pkt *pkt_clone = precvframe->u.hdr.pkt;
+
+ pkt_clone->data = ptr + rx_report_sz;
+ skb_reset_tail_pointer(pkt_clone);
+ precvframe->u.hdr.rx_head = precvframe->u.hdr.rx_data = precvframe->u.hdr.rx_tail
+ = pkt_clone->data;
+ precvframe->u.hdr.rx_end = pkt_clone->data + skb_len;
+ }
+ else
+ {
+ DBG_8192C("rtl8723as_recv_tasklet: rtw_skb_clone fail\n");
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ break;
+ }
+ }
+
+ recvframe_put(precvframe, skb_len);
+ //recvframe_pull(precvframe, drvinfo_sz + RXDESC_SIZE);
+
+ if (pHalData->ReceiveConfig & RCR_APPFCS)
+ recvframe_pull_tail(precvframe, IEEE80211_FCS_LEN);
+
+ // move to drv info position
+ ptr += RXDESC_SIZE;
+
+ // update drv info
+ if (pHalData->ReceiveConfig & RCR_APP_BA_SSN) {
+ //rtl8723s_update_bassn(padapter, pdrvinfo);
+ ptr += 4;
+ }
+
+#ifdef CONFIG_CONCURRENT_MODE
+ if(rtw_buddy_adapter_up(padapter))
+ {
+ if(pre_recv_entry(precvframe, precvbuf, (struct phy_stat*)ptr) != _SUCCESS)
+ {
+ RT_TRACE(_module_rtl871x_recv_c_,_drv_err_,
+ ("recvbuf2recvframe: recv_entry(precvframe) != _SUCCESS\n"));
+ }
+ }
+ else
+#endif
+ {
+ if (pattrib->physt)
+ update_recvframe_phyinfo(precvframe, (struct phy_stat*)ptr);
+
+ if (rtw_recv_entry(precvframe) != _SUCCESS)
+ {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("%s: rtw_recv_entry(precvframe) != _SUCCESS\n",__FUNCTION__));
+ }
+ }
+ }
+
+ // Page size of receive package is 128 bytes alignment =>DMA AGG
+ // refer to _InitTransferPageSize()
+ pkt_offset = _RND128(pkt_offset);
+ precvbuf->pdata += pkt_offset;
+ ptr = precvbuf->pdata;
+ precvframe = NULL;
+ pkt_copy = NULL;
+ }
+
+ rtw_enqueue_recvbuf(precvbuf, &precvpriv->free_recv_buf_queue);
+ } while (1);
+
+}
+#else
+static void rtl8723as_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;
+ _irqL irql;
+
+
+ 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) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("rtl8723as_recv_tasklet: no enough recv frame!\n"));
+ 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;
+
+ update_recvframe_attrib(precvframe, (struct recv_stat*)ptr);
+
+ // fix Hardware RX data error, drop whole recv_buffer
+ if ((!(pHalData->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err)
+ {
+ DBG_8192C("%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) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ ("%s: next pkt len(%p,%d) exceed ptail(%p)!\n",
+ __FUNCTION__, 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++;
+ }
+ }
+ #endif
+ DBG_8192C("%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)
+ {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_crit_, ("rtl8723as_recv_tasklet: no enough memory to allocate SKB!\n"));
+ 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 (padapter->bDriverStopped == _FALSE) {
+#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);
+ if (pHalData->ReceiveConfig & RCR_APPFCS)
+ recvframe_pull_tail(precvframe, IEEE80211_FCS_LEN);
+
+ // move to drv info position
+ ptr += RXDESC_SIZE;
+
+ // update drv info
+ if (pHalData->ReceiveConfig & RCR_APP_BA_SSN) {
+// rtl8723s_update_bassn(padapter, pdrvinfo);
+ ptr += 4;
+ }
+
+#ifdef CONFIG_CONCURRENT_MODE
+ if(rtw_buddy_adapter_up(padapter))
+ {
+ if(pre_recv_entry(precvframe, precvbuf, (struct phy_stat*)ptr) != _SUCCESS)
+ {
+ RT_TRACE(_module_rtl871x_recv_c_,_drv_err_,
+ ("recvbuf2recvframe: recv_entry(precvframe) != _SUCCESS\n"));
+ }
+ }
+ else
+#endif
+ {
+ if (pattrib->physt)
+ update_recvframe_phyinfo(precvframe, (struct phy_stat*)ptr);
+
+ if (rtw_recv_entry(precvframe) != _SUCCESS)
+ {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("rtl8723as_recv_tasklet: rtw_recv_entry(precvframe) != _SUCCESS\n"));
+ }
+ }
+ }
+
+ // Page size of receive package is 128 bytes alignment => DMA agg
+ // refer to _InitTransferPageSize()
+ pkt_offset = _RND128(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 rtl8723as_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;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("alloc recv_buf fail!\n"));
+ 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) {
+ DBG_871X("%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))rtl8723as_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 rtl8723as_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;
+ }
+}
+