/******************************************************************************
|
*
|
* Copyright(c) 2007 - 2017 Realtek Corporation.
|
*
|
* 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.
|
*
|
*****************************************************************************/
|
#define _RTL8703BS_RECV_C_
|
|
#include <rtl8703b_hal.h>
|
|
#ifdef CONFIG_SDIO_RX_COPY
|
s32 rtl8703bs_recv_hdl(_adapter *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;
|
|
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);
|
|
return RTW_RFRAME_UNAVAIL;
|
}
|
|
/* 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 (!rtw_hal_rcr_check(padapter, 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) && rtw_hal_rcr_check(padapter, 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 (rtw_hal_rcr_check(padapter, 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;
|
}
|
|
pre_recv_entry(precvframe, pattrib->physt ? ptr : NULL);
|
|
} 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);
|
|
#ifdef CONFIG_RTW_NAPI
|
#ifdef CONFIG_RTW_NAPI_V2
|
if (padapter->registrypriv.en_napi) {
|
struct dvobj_priv *d;
|
struct _ADAPTER *a;
|
u8 i;
|
|
d = adapter_to_dvobj(padapter);
|
for (i = 0; i < d->iface_nums; i++) {
|
a = d->padapters[i];
|
if (rtw_if_up(a) == _TRUE)
|
napi_schedule(&a->napi);
|
|
}
|
}
|
#endif /* CONFIG_RTW_NAPI_V2 */
|
#endif /* CONFIG_RTW_NAPI */
|
|
return _SUCCESS;
|
}
|
|
static void rtl8703bs_recv_tasklet(void *priv)
|
{
|
_adapter *adapter = (_adapter *)priv;
|
s32 ret;
|
|
ret = rtl8703bs_recv_hdl(adapter);
|
if (ret == RTW_RFRAME_UNAVAIL
|
|| ret == RTW_RFRAME_PKT_UNAVAIL
|
) {
|
/* schedule again and hope recvframe/packet is available next time. */
|
#ifdef PLATFORM_LINUX
|
tasklet_schedule(&adapter->recvpriv.recv_tasklet);
|
#endif
|
}
|
}
|
#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 (!rtw_hal_rcr_check(padapter, 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) && rtw_hal_rcr_check(padapter, 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 (rtw_hal_rcr_check(padapter, RCR_APP_BA_SSN)) {
|
/* rtl8703s_update_bassn(padapter, pdrvinfo); */
|
ptr += 4;
|
}
|
|
if (pattrib->pkt_rpt_type == NORMAL_RX)
|
pre_recv_entry(precvframe, pattrib->physt ? ptr : NULL);
|
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)
|
{
|
struct registry_priv *regsty = adapter_to_regsty(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 = regsty->recvbuf_nr * 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 < regsty->recvbuf_nr; i++) {
|
res = sdio_initrecvbuf(precvbuf, padapter);
|
if (res == _FAIL)
|
break;
|
|
res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf, MAX_RECVBUF_SZ);
|
if (res == _FAIL) {
|
sdio_freerecvbuf(precvbuf);
|
break;
|
}
|
|
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);
|
sdio_freerecvbuf(precvbuf);
|
precvbuf++;
|
}
|
precvpriv->precv_buf = NULL;
|
}
|
|
if (precvpriv->pallocated_recv_buf) {
|
n = regsty->recvbuf_nr * 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)
|
{
|
struct registry_priv *regsty = &padapter->registrypriv;
|
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 = regsty->recvbuf_nr;
|
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);
|
sdio_freerecvbuf(precvbuf);
|
precvbuf++;
|
}
|
precvpriv->precv_buf = NULL;
|
}
|
|
if (precvpriv->pallocated_recv_buf) {
|
n = regsty->recvbuf_nr * sizeof(struct recv_buf) + 4;
|
rtw_mfree(precvpriv->pallocated_recv_buf, n);
|
precvpriv->pallocated_recv_buf = NULL;
|
}
|
}
|