summaryrefslogtreecommitdiff
path: root/drivers/staging/rtl8723au/hal/rtl8723au_recv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/rtl8723au/hal/rtl8723au_recv.c')
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723au_recv.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/drivers/staging/rtl8723au/hal/rtl8723au_recv.c b/drivers/staging/rtl8723au/hal/rtl8723au_recv.c
new file mode 100644
index 000000000..0fec84bcb
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723au_recv.c
@@ -0,0 +1,267 @@
+/******************************************************************************
+ *
+ * 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);
+ }
+}