/******************************************************************************
*
* 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.
*
******************************************************************************/
#define _RTW_RECV_C_
#include <osdep_service.h>
#include <drv_types.h>
#include <recv_osdep.h>
#include <mlme_osdep.h>
#include <linux/ip.h>
#include <linux/if_ether.h>
#include <usb_ops.h>
#include <linux/ieee80211.h>
#include <wifi.h>
#include <rtl8723a_recv.h>
#include <rtl8723a_xmit.h>
void rtw_signal_stat_timer_hdl23a(unsigned long data);
void _rtw_init_sta_recv_priv23a(struct sta_recv_priv *psta_recvpriv)
{
spin_lock_init(&psta_recvpriv->lock);
/* for (i = 0; i<MAX_RX_NUMBLKS; i++) */
/* _rtw_init_queue23a(&psta_recvpriv->blk_strms[i]); */
_rtw_init_queue23a(&psta_recvpriv->defrag_q);
}
int _rtw_init_recv_priv23a(struct recv_priv *precvpriv,
struct rtw_adapter *padapter)
{
struct recv_frame *precvframe;
int i;
int res = _SUCCESS;
spin_lock_init(&precvpriv->lock);
_rtw_init_queue23a(&precvpriv->free_recv_queue);
_rtw_init_queue23a(&precvpriv->recv_pending_queue);
_rtw_init_queue23a(&precvpriv->uc_swdec_pending_queue);
precvpriv->adapter = padapter;
for (i = 0; i < NR_RECVFRAME ; i++) {
precvframe = kzalloc(sizeof(struct recv_frame), GFP_KERNEL);
if (!precvframe)
break;
INIT_LIST_HEAD(&precvframe->list);
list_add_tail(&precvframe->list,
&precvpriv->free_recv_queue.queue);
precvframe->adapter = padapter;
precvframe++;
}
precvpriv->free_recvframe_cnt = i;
precvpriv->rx_pending_cnt = 1;
res = rtl8723au_init_recv_priv(padapter);
setup_timer(&precvpriv->signal_stat_timer, rtw_signal_stat_timer_hdl23a,
(unsigned long)padapter);
precvpriv->signal_stat_sampling_interval = 1000; /* ms */
rtw_set_signal_stat_timer(precvpriv);
return res;
}
void _rtw_free_recv_priv23a (struct recv_priv *precvpriv)
{
struct rtw_adapter *padapter = precvpriv->adapter;
struct recv_frame *precvframe;
struct list_head *plist, *ptmp;
rtw_free_uc_swdec_pending_queue23a(padapter);
list_for_each_safe(plist, ptmp, &precvpriv->free_recv_queue.queue) {
precvframe = container_of(plist, struct recv_frame, list);
list_del_init(&precvframe->list);
kfree(precvframe);
}
rtl8723au_free_recv_priv(padapter);
}
struct recv_frame *rtw_alloc_recvframe23a(struct rtw_queue *pfree_recv_queue)
{
struct recv_frame *pframe;
struct list_head *plist, *phead;
struct rtw_adapter *padapter;
struct recv_priv *precvpriv;
spin_lock_bh(&pfree_recv_queue->lock);
if (list_empty(&pfree_recv_queue->queue))
pframe = NULL;
else {
phead = get_list_head(pfree_recv_queue);
plist = phead->next;
pframe = container_of(plist, struct recv_frame, list);
list_del_init(&pframe->list);
padapter = pframe->adapter;
if (padapter) {
precvpriv = &padapter->recvpriv;
if (pfree_recv_queue == &precvpriv->free_recv_queue)
precvpriv->free_recvframe_cnt--;
}
}
spin_unlock_bh(&pfree_recv_queue->lock);
return pframe;
}
int rtw_free_recvframe23a(struct recv_frame *precvframe)
{
struct rtw_adapter *padapter = precvframe->adapter;
struct recv_priv *precvpriv = &padapter->recvpriv;
struct rtw_queue *pfree_recv_queue;
if (precvframe->pkt) {
dev_kfree_skb_any(precvframe->pkt);/* free skb by driver */
precvframe->pkt = NULL;
}
pfree_recv_queue = &precvpriv->free_recv_queue;
spin_lock_bh(&pfree_recv_queue->lock);
list_del_init(&precvframe->list);
list_add_tail(&precvframe->list, get_list_head(pfree_recv_queue));
if (padapter) {
if (pfree_recv_queue == &precvpriv->free_recv_queue)
precvpriv->free_recvframe_cnt++;
}
spin_unlock_bh(&pfree_recv_queue->lock);
return _SUCCESS;
}
int rtw_enqueue_recvframe23a(struct recv_frame *precvframe, struct rtw_queue *queue)
{
struct rtw_adapter *padapter = precvframe->adapter;
struct recv_priv *precvpriv = &padapter->recvpriv;
spin_lock_bh(&queue->lock);
list_del_init(&precvframe->list);
list_add_tail(&precvframe->list, get_list_head(queue));
if (padapter) {
if (queue == &precvpriv->free_recv_queue)
precvpriv->free_recvframe_cnt++;
}
spin_unlock_bh(&queue->lock);
return _SUCCESS;
}
/*
caller : defrag ; recvframe_chk_defrag23a in recv_thread (passive)
pframequeue: defrag_queue : will be accessed in recv_thread (passive)
using spinlock to protect
*/
static void rtw_free_recvframe23a_queue(struct rtw_queue *pframequeue)
{
struct recv_frame *hdr;
struct list_head *plist, *phead, *ptmp;
spin_lock(&pframequeue->lock);
phead = get_list_head(pframequeue);
plist = phead->next;
list_for_each_safe(plist, ptmp, phead) {
hdr = container_of(plist, struct recv_frame, list);
rtw_free_recvframe23a(hdr);
}
spin_unlock(&pframequeue->lock);
}
u32 rtw_free_uc_swdec_pending_queue23a(struct rtw_adapter *adapter)
{
u32 cnt = 0;
struct recv_frame *pending_frame;
while ((pending_frame = rtw_alloc_recvframe23a(&adapter->recvpriv.uc_swdec_pending_queue))) {
rtw_free_recvframe23a(pending_frame);
DBG_8723A("%s: dequeue uc_swdec_pending_queue\n", __func__);
cnt++;
}
return cnt;
}
int rtw_enqueue_recvbuf23a_to_head(struct recv_buf *precvbuf, struct rtw_queue *queue)
{
spin_lock_bh(&queue->lock);
list_del_init(&precvbuf->list);
list_add(&precvbuf->list, get_list_head(queue));
spin_unlock_bh(&queue->lock);
return _SUCCESS;
}
int rtw_enqueue_recvbuf23a(struct recv_buf *precvbuf, struct rtw_queue *queue)
{
unsigned long irqL;
spin_lock_irqsave(&queue->lock, irqL);
list_del_init(&precvbuf->list);
list_add_tail(&precvbuf->list, get_list_head(queue));
spin_unlock_irqrestore(&queue->lock, irqL);
return _SUCCESS;
}
struct recv_buf *rtw_dequeue_recvbuf23a (struct rtw_queue *queue)
{
unsigned long irqL;
struct recv_buf *precvbuf;
struct list_head *plist, *phead;
spin_lock_irqsave(&queue->lock, irqL);
if (list_empty(&queue->queue)) {
precvbuf = NULL;
} else {
phead = get_list_head(queue);
plist = phead->next;
precvbuf = container_of(plist, struct recv_buf, list);
list_del_init(&precvbuf->list);
}
spin_unlock_irqrestore(&queue->lock, irqL);
return precvbuf;
}
int recvframe_chkmic(struct rtw_adapter *adapter,
struct recv_frame *precvframe);
int recvframe_chkmic(struct rtw_adapter *adapter,
struct recv_frame *precvframe) {
int i, res = _SUCCESS;
u32 datalen;
u8 miccode[8];
u8 bmic_err = false, brpt_micerror = true;
u8 *pframe, *payload, *pframemic;
u8 *mickey;
/* u8 *iv, rxdata_key_idx = 0; */
struct sta_info *stainfo;
struct rx_pkt_attrib *prxattrib = &precvframe->attrib;
struct security_priv *psecuritypriv = &adapter->securitypriv;
struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
stainfo = rtw_get_stainfo23a(&adapter->stapriv, &prxattrib->ta[0]);
if (prxattrib->encrypt == WLAN_CIPHER_SUITE_TKIP) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
"recvframe_chkmic:prxattrib->encrypt == WLAN_CIPHER_SUITE_TKIP\n");
RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
"recvframe_chkmic:da = %pM\n", prxattrib->ra);
/* calculate mic code */
if (stainfo != NULL) {
if (is_multicast_ether_addr(prxattrib->ra)) {
mickey = &psecuritypriv->dot118021XGrprxmickey[prxattrib->key_index].skey[0];
RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
"recvframe_chkmic: bcmc key\n");
if (!psecuritypriv->binstallGrpkey) {
res = _FAIL;
RT_TRACE(_module_rtl871x_recv_c_,
_drv_err_,
"recvframe_chkmic:didn't inst