| /****************************************************************************** |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| #define _RTL8192CU_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 <wifi.h> |
| #include <rtl8723a_hal.h> |
| |
| int rtl8723au_init_recv_priv(struct rtw_adapter *padapter) |
| { |
| struct recv_priv *precvpriv = &padapter->recvpriv; |
| int i, size, res = _SUCCESS; |
| struct recv_buf *precvbuf; |
| unsigned long tmpaddr; |
| unsigned long alignment; |
| struct sk_buff *pskb; |
| |
| tasklet_init(&precvpriv->recv_tasklet, |
| (void(*)(unsigned long))rtl8723au_recv_tasklet, |
| (unsigned long)padapter); |
| |
| precvpriv->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); |
| if (!precvpriv->int_in_urb) |
| DBG_8723A("alloc_urb for interrupt in endpoint fail !!!!\n"); |
| precvpriv->int_in_buf = kzalloc(USB_INTR_CONTENT_LENGTH, GFP_KERNEL); |
| if (!precvpriv->int_in_buf) |
| DBG_8723A("alloc_mem for interrupt in endpoint fail !!!!\n"); |
| |
| size = NR_RECVBUFF * sizeof(struct recv_buf); |
| precvpriv->precv_buf = kzalloc(size, GFP_KERNEL); |
| if (!precvpriv->precv_buf) { |
| res = _FAIL; |
| RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, |
| "alloc recv_buf fail!\n"); |
| goto exit; |
| } |
| |
| precvbuf = (struct recv_buf *)precvpriv->precv_buf; |
| |
| for (i = 0; i < NR_RECVBUFF; i++) { |
| INIT_LIST_HEAD(&precvbuf->list); |
| |
| precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL); |
| if (!precvbuf->purb) |
| break; |
| |
| precvbuf->adapter = padapter; |
| |
| precvbuf++; |
| } |
| |
| skb_queue_head_init(&precvpriv->rx_skb_queue); |
| skb_queue_head_init(&precvpriv->free_recv_skb_queue); |
| |
| for (i = 0; i < NR_PREALLOC_RECV_SKB; i++) { |
| size = MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ; |
| pskb = __netdev_alloc_skb(padapter->pnetdev, size, GFP_KERNEL); |
| |
| if (pskb) { |
| pskb->dev = padapter->pnetdev; |
| |
| tmpaddr = (unsigned long)pskb->data; |
| alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1); |
| skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignment)); |
| |
| skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb); |
| } |
| |
| pskb = NULL; |
| } |
| |
| exit: |
| return res; |
| } |
| |
| void rtl8723au_free_recv_priv(struct rtw_adapter *padapter) |
| { |
| int i; |
| struct recv_buf *precvbuf; |
| struct recv_priv *precvpriv = &padapter->recvpriv; |
| |
| precvbuf = (struct recv_buf *)precvpriv->precv_buf; |
| |
| for (i = 0; i < NR_RECVBUFF; i++) { |
| usb_free_urb(precvbuf->purb); |
| |
| if (precvbuf->pskb) |
| dev_kfree_skb_any(precvbuf->pskb); |
| |
| precvbuf++; |
| } |
| |
| kfree(precvpriv->precv_buf); |
| |
| usb_free_urb(precvpriv->int_in_urb); |
| kfree(precvpriv->int_in_buf); |
| |
| if (skb_queue_len(&precvpriv->rx_skb_queue)) |
| DBG_8723A(KERN_WARNING "rx_skb_queue not empty\n"); |
| |
| skb_queue_purge(&precvpriv->rx_skb_queue); |
| |
| if (skb_queue_len(&precvpriv->free_recv_skb_queue)) { |
| DBG_8723A(KERN_WARNING "free_recv_skb_queue not empty, %d\n", |
| skb_queue_len(&precvpriv->free_recv_skb_queue)); |
| } |
| |
| skb_queue_purge(&precvpriv->free_recv_skb_queue); |
| } |
| |
| struct recv_stat_cpu { |
| u32 rxdw0; |
| u32 rxdw1; |
| u32 rxdw2; |
| u32 rxdw3; |
| u32 rxdw4; |
| u32 rxdw5; |
| }; |
| |
| void update_recvframe_attrib(struct recv_frame *precvframe, |
| struct recv_stat *prxstat) |
| { |
| struct rx_pkt_attrib *pattrib; |
| struct recv_stat_cpu report; |
| struct rxreport_8723a *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 = (struct rxreport_8723a *)&report; |
| |
| pattrib = &precvframe->attrib; |
| 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; |
| } |
| |
| void update_recvframe_phyinfo(struct recv_frame *precvframe, |
| struct phy_stat *pphy_status) |
| { |
| struct rtw_adapter *padapter = precvframe->adapter; |
| struct rx_pkt_attrib *pattrib = &precvframe->attrib; |
| struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter); |
| struct phy_info *pPHYInfo = &pattrib->phy_info; |
| struct odm_packet_info pkt_info; |
| u8 *sa = NULL, *da; |
| struct sta_priv *pstapriv; |
| struct sta_info *psta; |
| struct sk_buff *skb = precvframe->pkt; |
| struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
| bool matchbssid = false; |
| u8 *bssid; |
| |
| matchbssid = !ieee80211_is_ctl(hdr->frame_control) && |
| !pattrib->icv_err && !pattrib->crc_err; |
| |
| if (matchbssid) { |
| switch (hdr->frame_control & |
| cpu_to_le16(IEEE80211_FCTL_TODS | |
| IEEE80211_FCTL_FROMDS)) { |
| case cpu_to_le16(IEEE80211_FCTL_TODS): |
| bssid = hdr->addr1; |
| break; |
| case cpu_to_le16(IEEE80211_FCTL_FROMDS): |
| bssid = hdr->addr2; |
| break; |
| case cpu_to_le16(0): |
| bssid = hdr->addr3; |
| break; |
| default: |
| bssid = NULL; |
| matchbssid = false; |
| } |
| |
| if (bssid) |
| matchbssid = ether_addr_equal( |
| get_bssid(&padapter->mlmepriv), bssid); |
| } |
| |
| pkt_info.bPacketMatchBSSID = matchbssid; |
| |
| da = ieee80211_get_DA(hdr); |
| pkt_info.bPacketToSelf = pkt_info.bPacketMatchBSSID && |
| (!memcmp(da, myid(&padapter->eeprompriv), ETH_ALEN)); |
| |
| pkt_info.bPacketBeacon = pkt_info.bPacketMatchBSSID && |
| ieee80211_is_beacon(hdr->frame_control); |
| |
| pkt_info.StationID = 0xFF; |
| if (pkt_info.bPacketBeacon) { |
| if (check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE) == true) |
| sa = padapter->mlmepriv.cur_network.network.MacAddress; |
| /* to do Ad-hoc */ |
| } else { |
| sa = ieee80211_get_SA(hdr); |
| } |
| |
| pstapriv = &padapter->stapriv; |
| psta = rtw_get_stainfo23a(pstapriv, sa); |
| if (psta) { |
| pkt_info.StationID = psta->mac_id; |
| /* printk("%s ==> StationID(%d)\n", __func__, pkt_info.StationID); */ |
| } |
| pkt_info.Rate = pattrib->mcs_rate; |
| |
| ODM_PhyStatusQuery23a(&pHalData->odmpriv, pPHYInfo, |
| (u8 *)pphy_status, &pkt_info); |
| precvframe->psta = NULL; |
| if (pkt_info.bPacketMatchBSSID && |
| (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true)) { |
| if (psta) { |
| precvframe->psta = psta; |
| rtl8723a_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->psta = psta; |
| } |
| rtl8723a_process_phy_info(padapter, precvframe); |
| } |
| } |