Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1925ad8 authored by Larry Finger's avatar Larry Finger Committed by Greg Kroah-Hartman
Browse files

staging: r8723au: Add source files for new driver - part 3



The Realtek USB device RTL8723AU is found in Lenovo Yoga 13 tablets.
A driver for it has been available in a GitHub repo for several months.
This commit contains the third part of source files. The source
is arbitrarily split to avoid E-mail files that are too large.

Jes Sorensen at RedHat has made many improvements to the vendor code,
and he has been doing the testing. I do not have access to this device.

Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Cc: Jes Sorensen <Jes.Sorensen@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f7c92d2c
Loading
Loading
Loading
Loading
+4501 −0

File added.

Preview size limit exceeded, changes collapsed.

+187 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 * 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 _MLME_OSDEP_C_

#include <osdep_service.h>
#include <drv_types.h>
#include <mlme_osdep.h>
#include <rtw_ioctl_set.h>

void rtw_os_indicate_connect23a(struct rtw_adapter *adapter)
{
	rtw_cfg80211_indicate_connect(adapter);

	netif_carrier_on(adapter->pnetdev);

	if (adapter->pid[2] != 0)
		rtw_signal_process(adapter->pid[2], SIGALRM);
}

void rtw_os_indicate_scan_done23a(struct rtw_adapter *padapter, bool aborted)
{
	rtw_cfg80211_indicate_scan_done(wdev_to_priv(padapter->rtw_wdev),
					aborted);
}

static struct rt_pmkid_list backupPMKIDList[NUM_PMKID_CACHE];

void rtw_reset_securitypriv23a(struct rtw_adapter *adapter)
{
	u8	backupPMKIDIndex = 0;
	u8	backupTKIPCountermeasure = 0x00;
	unsigned long backupTKIPcountermeasure_time = 0;

	if (adapter->securitypriv.dot11AuthAlgrthm ==
	    dot11AuthAlgrthm_8021X) { /* 802.1x */
		/*  We have to backup the PMK information for WiFi PMK
		 *  Caching test item.
		 *  Backup the btkip_countermeasure information.
		 *  When the countermeasure is trigger, the driver have to
		 *  disconnect with AP for 60 seconds.
		 */
		memset(&backupPMKIDList[0], 0x00, sizeof(struct rt_pmkid_list) *
		       NUM_PMKID_CACHE);

		memcpy(&backupPMKIDList[0], &adapter->securitypriv.PMKIDList[0],
		       sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE);
		backupPMKIDIndex = adapter->securitypriv.PMKIDIndex;
		backupTKIPCountermeasure = adapter->securitypriv.btkip_countermeasure;
		backupTKIPcountermeasure_time = adapter->securitypriv.btkip_countermeasure_time;

		memset((unsigned char *)&adapter->securitypriv, 0,
		       sizeof (struct security_priv));
		/* Restore the PMK information to securitypriv structure
		 * for the following connection.
		 */
		memcpy(&adapter->securitypriv.PMKIDList[0], &backupPMKIDList[0],
		       sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE);
		adapter->securitypriv.PMKIDIndex = backupPMKIDIndex;
		adapter->securitypriv.btkip_countermeasure = backupTKIPCountermeasure;
		adapter->securitypriv.btkip_countermeasure_time = backupTKIPcountermeasure_time;

		adapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen;
		adapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled;
	} else {  /* reset values in securitypriv */
		struct security_priv *psec_priv = &adapter->securitypriv;

		/* open system */
		psec_priv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
		psec_priv->dot11PrivacyAlgrthm = _NO_PRIVACY_;
		psec_priv->dot11PrivacyKeyIndex = 0;

		psec_priv->dot118021XGrpPrivacy = _NO_PRIVACY_;
		psec_priv->dot118021XGrpKeyid = 1;

		psec_priv->ndisauthtype = Ndis802_11AuthModeOpen;
		psec_priv->ndisencryptstatus = Ndis802_11WEPDisabled;
	}
}

void rtw_os_indicate_disconnect23a(struct rtw_adapter *adapter)
{
	/* Do it first for tx broadcast pkt after disconnection issue! */
	netif_carrier_off(adapter->pnetdev);

	rtw_cfg80211_indicate_disconnect(adapter);

	rtw_reset_securitypriv23a(adapter);
}

void rtw_report_sec_ie23a(struct rtw_adapter *adapter, u8 authmode, u8 *sec_ie)
{
	uint	len;
	u8	*buff, *p, i;
	union iwreq_data wrqu;

	RT_TRACE(_module_mlme_osdep_c_, _drv_info_,
		 ("+rtw_report_sec_ie23a, authmode =%d\n", authmode));

	buff = NULL;
	if (authmode == _WPA_IE_ID_) {
		RT_TRACE(_module_mlme_osdep_c_, _drv_info_,
			 ("rtw_report_sec_ie23a, authmode =%d\n", authmode));

		buff = kzalloc(IW_CUSTOM_MAX, GFP_KERNEL);
		if (!buff)
			return;
		p = buff;

		p += sprintf(p, "ASSOCINFO(ReqIEs =");

		len = sec_ie[1]+2;
		len =  (len < IW_CUSTOM_MAX) ? len : IW_CUSTOM_MAX;

		for (i = 0; i < len; i++)
			p += sprintf(p, "%02x", sec_ie[i]);

		p += sprintf(p, ")");

		memset(&wrqu, 0, sizeof(wrqu));

		wrqu.data.length = p-buff;

		wrqu.data.length = (wrqu.data.length < IW_CUSTOM_MAX) ?
				   wrqu.data.length : IW_CUSTOM_MAX;

		kfree(buff);
	}
}

#ifdef CONFIG_8723AU_AP_MODE
void rtw_indicate_sta_assoc_event23a(struct rtw_adapter *padapter,
				  struct sta_info *psta)
{
	struct sta_priv *pstapriv = &padapter->stapriv;
	union iwreq_data wrqu;

	if (psta == NULL)
		return;

	if (psta->aid > NUM_STA)
		return;

	if (pstapriv->sta_aid[psta->aid - 1] != psta)
		return;

	wrqu.addr.sa_family = ARPHRD_ETHER;

	memcpy(wrqu.addr.sa_data, psta->hwaddr, ETH_ALEN);

	DBG_8723A("+rtw_indicate_sta_assoc_event23a\n");
}

void rtw_indicate_sta_disassoc_event23a(struct rtw_adapter *padapter,
				     struct sta_info *psta)
{
	struct sta_priv *pstapriv = &padapter->stapriv;
	union iwreq_data wrqu;

	if (psta == NULL)
		return;

	if (psta->aid > NUM_STA)
		return;

	if (pstapriv->sta_aid[psta->aid - 1] != psta)
		return;

	wrqu.addr.sa_family = ARPHRD_ETHER;

	memcpy(wrqu.addr.sa_data, psta->hwaddr, ETH_ALEN);

	DBG_8723A("+rtw_indicate_sta_disassoc_event23a\n");
}
#endif
+970 −0

File added.

Preview size limit exceeded, changes collapsed.

+429 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 * 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 _OSDEP_SERVICE_C_

#include <osdep_service.h>
#include <drv_types.h>
#include <recv_osdep.h>
#include <linux/vmalloc.h>

#define RT_TAG	('1178')

/*
* Translate the OS dependent @param error_code to OS independent RTW_STATUS_CODE23a
* @return: one of RTW_STATUS_CODE23a
*/
inline int RTW_STATUS_CODE23a(int error_code)
{
	if (error_code >= 0)
		return _SUCCESS;
	return _FAIL;
}

inline u8 *_rtw_vmalloc(u32 sz)
{
	u8	*pbuf;
	pbuf = vmalloc(sz);

	return pbuf;
}

inline u8 *_rtw_zvmalloc(u32 sz)
{
	u8	*pbuf;
	pbuf = _rtw_vmalloc(sz);
	if (pbuf != NULL)
		memset(pbuf, 0, sz);

	return pbuf;
}

inline void _rtw_vmfree(u8 *pbuf, u32 sz)
{
	vfree(pbuf);
}

void _rtw_init_queue23a(struct rtw_queue *pqueue)
{
	INIT_LIST_HEAD(&pqueue->queue);
	spin_lock_init(&pqueue->lock);
}

u32 _rtw_queue_empty23a(struct rtw_queue *pqueue)
{
	if (list_empty(&pqueue->queue))
		return true;
	else
		return false;
}

u32	rtw_get_current_time(void)
{
	return jiffies;
}

inline u32 rtw_systime_to_ms23a(u32 systime)
{
	return systime * 1000 / HZ;
}

inline u32 rtw_ms_to_systime23a(u32 ms)
{
	return ms * HZ / 1000;
}

/*  the input parameter start use the same unit as returned
 * by rtw_get_current_time
 */
inline s32 rtw_get_passing_time_ms23a(u32 start)
{
	return rtw_systime_to_ms23a(jiffies-start);
}

inline s32 rtw_get_time_interval_ms23a(u32 start, u32 end)
{
	return rtw_systime_to_ms23a(end-start);
}

#define RTW_SUSPEND_LOCK_NAME "rtw_wifi"

inline void rtw_suspend_lock_init(void)
{
}

inline void rtw_suspend_lock_uninit(void)
{
}

inline void rtw_lock_suspend(void)
{
}

inline void rtw_unlock_suspend(void)
{
}

/* Open a file with the specific @param path, @param flag, @param mode
 * @param fpp the pointer of struct file pointer to get struct
 * file pointer while file opening is success
 * @param path the path of the file to open
 * @param flag file operation flags, please refer to linux document
 * @param mode please refer to linux document
 * @return Linux specific error code
 */
static int openFile(struct file **fpp, char *path, int flag, int mode)
{
	struct file *fp;

	fp = filp_open(path, flag, mode);
	if (IS_ERR(fp)) {
		*fpp = NULL;
		return PTR_ERR(fp);
	} else {
		*fpp = fp;
		return 0;
	}
}

/* Close the file with the specific @param fp
 * @param fp the pointer of struct file to close
 * @return always 0
 */
static int closeFile(struct file *fp)
{
	filp_close(fp, NULL);
	return 0;
}

static int readFile(struct file *fp, char *buf, int len)
{
	int rlen = 0, sum = 0;

	if (!fp->f_op || !fp->f_op->read)
		return -EPERM;

	while (sum < len) {
		rlen = fp->f_op->read(fp, buf+sum, len-sum, &fp->f_pos);
		if (rlen > 0)
			sum += rlen;
		else if (0 != rlen)
			return rlen;
		else
			break;
	}
	return  sum;
}

static int writeFile(struct file *fp, char *buf, int len)
{
	int wlen = 0, sum = 0;

	if (!fp->f_op || !fp->f_op->write)
		return -EPERM;

	while (sum < len) {
		wlen = fp->f_op->write(fp, buf+sum, len-sum, &fp->f_pos);
		if (wlen > 0)
			sum += wlen;
		else if (0 != wlen)
			return wlen;
		else
			break;
	}
	return sum;
}

/* Test if the specifi @param path is a file and readable
 * @param path the path of the file to test
 * @return Linux specific error code
 */
static int isFileReadable(char *path)
{
	struct file *fp;
	int ret = 0;
	mm_segment_t oldfs;
	char buf;

	fp = filp_open(path, O_RDONLY, 0);
	if (IS_ERR(fp)) {
		ret = PTR_ERR(fp);
	} else {
		oldfs = get_fs();
		set_fs(get_ds());

		if (1 != readFile(fp, &buf, 1))
			ret = PTR_ERR(fp);

		set_fs(oldfs);
		filp_close(fp, NULL);
	}
	return ret;
}

/* Open the file with @param path and retrive the file content into
 * memory starting from @param buf for @param sz at most
 * @param path the path of the file to open and read
 * @param buf the starting address of the buffer to store file content
 * @param sz how many bytes to read at most
 * @return the byte we've read, or Linux specific error code
 */
static int retriveFromFile(char *path, u8 *buf, u32 sz)
{
	int ret = -1;
	mm_segment_t oldfs;
	struct file *fp;

	if (path && buf) {
		ret = openFile(&fp, path, O_RDONLY, 0);
		if (!ret) {
			DBG_8723A("%s openFile path:%s fp =%p\n",
				  __func__, path, fp);

			oldfs = get_fs(); set_fs(get_ds());
			ret = readFile(fp, buf, sz);
			set_fs(oldfs);
			closeFile(fp);

			DBG_8723A("%s readFile, ret:%d\n", __func__, ret);
		} else {
			DBG_8723A("%s openFile path:%s Fail, ret:%d\n",
				  __func__, path, ret);
		}
	} else {
		DBG_8723A("%s NULL pointer\n", __func__);
		ret =  -EINVAL;
	}
	return ret;
}

/* Open the file with @param path and wirte @param sz byte of data starting
 * from @param buf into the file
 * @param path the path of the file to open and write
 * @param buf the starting address of the data to write into file
 * @param sz how many bytes to write at most
 * @return the byte we've written, or Linux specific error code
 */
static int storeToFile(char *path, u8 *buf, u32 sz)
{
	struct file *fp;
	int ret = 0;
	mm_segment_t oldfs;

	if (path && buf) {
		ret = openFile(&fp, path, O_CREAT|O_WRONLY, 0666);
		if (!ret) {
			DBG_8723A("%s openFile path:%s fp =%p\n", __func__,
				  path, fp);

			oldfs = get_fs(); set_fs(get_ds());
			ret = writeFile(fp, buf, sz);
			set_fs(oldfs);
			closeFile(fp);

			DBG_8723A("%s writeFile, ret:%d\n", __func__, ret);
		} else {
			DBG_8723A("%s openFile path:%s Fail, ret:%d\n",
				  __func__, path, ret);
		}
	} else {
		DBG_8723A("%s NULL pointer\n", __func__);
		ret =  -EINVAL;
	}
	return ret;
}

/*
* Test if the specifi @param path is a file and readable
* @param path the path of the file to test
* @return true or false
*/
int rtw_is_file_readable(char *path)
{
	if (isFileReadable(path) == 0)
		return true;
	else
		return false;
}

/* Open the file with @param path and retrive the file content into memoryi
 * starting from @param buf for @param sz at most
 * @param path the path of the file to open and read
 * @param buf the starting address of the buffer to store file content
 * @param sz how many bytes to read at most
 * @return the byte we've read
 */
int rtw_retrive_from_file(char *path, u8 *buf, u32 sz)
{
	int ret = retriveFromFile(path, buf, sz);
	return ret >= 0 ? ret : 0;
}

/* Open the file with @param path and wirte @param sz byte of
 * data starting from @param buf into the file
 * @param path the path of the file to open and write
 * @param buf the starting address of the data to write into file
 * @param sz how many bytes to write at most
 * @return the byte we've written
 */
int rtw_store_to_file(char *path, u8 *buf, u32 sz)
{
	int ret = storeToFile(path, buf, sz);
	return ret >= 0 ? ret : 0;
}

u64 rtw_modular6423a(u64 x, u64 y)
{
	return do_div(x, y);
}

u64 rtw_division6423a(u64 x, u64 y)
{
	do_div(x, y);
	return x;
}

/* rtw_cbuf_full23a - test if cbuf is full
 * @cbuf: pointer of struct rtw_cbuf
 *
 * Returns: true if cbuf is full
 */
inline bool rtw_cbuf_full23a(struct rtw_cbuf *cbuf)
{
	return (cbuf->write == cbuf->read-1) ? true : false;
}

/* rtw_cbuf_empty23a - test if cbuf is empty
 * @cbuf: pointer of struct rtw_cbuf
 *
 * Returns: true if cbuf is empty
 */
inline bool rtw_cbuf_empty23a(struct rtw_cbuf *cbuf)
{
	return (cbuf->write == cbuf->read) ? true : false;
}

/**
 * rtw_cbuf_push23a - push a pointer into cbuf
 * @cbuf: pointer of struct rtw_cbuf
 * @buf: pointer to push in
 *
 * Lock free operation, be careful of the use scheme
 * Returns: true push success
 */
bool rtw_cbuf_push23a(struct rtw_cbuf *cbuf, void *buf)
{
	if (rtw_cbuf_full23a(cbuf))
		return _FAIL;

	if (0)
		DBG_8723A("%s on %u\n", __func__, cbuf->write);
	cbuf->bufs[cbuf->write] = buf;
	cbuf->write = (cbuf->write+1)%cbuf->size;

	return _SUCCESS;
}

/**
 * rtw_cbuf_pop23a - pop a pointer from cbuf
 * @cbuf: pointer of struct rtw_cbuf
 *
 * Lock free operation, be careful of the use scheme
 * Returns: pointer popped out
 */
void *rtw_cbuf_pop23a(struct rtw_cbuf *cbuf)
{
	void *buf;
	if (rtw_cbuf_empty23a(cbuf))
		return NULL;

	if (0)
		DBG_8723A("%s on %u\n", __func__, cbuf->read);
	buf = cbuf->bufs[cbuf->read];
	cbuf->read = (cbuf->read+1)%cbuf->size;

	return buf;
}

/**
 * rtw_cbuf_alloc23a - allocte a rtw_cbuf with given size and do initialization
 * @size: size of pointer
 *
 * Returns: pointer of srtuct rtw_cbuf, NULL for allocation failure
 */
struct rtw_cbuf *rtw_cbuf_alloc23a(u32 size)
{
	struct rtw_cbuf *cbuf;

	cbuf = kmalloc(sizeof(*cbuf) + sizeof(void *)*size, GFP_KERNEL);

	if (cbuf) {
		cbuf->write = 0;
		cbuf->read = 0;
		cbuf->size = size;
	}

	return cbuf;
}

/**
 * rtw_cbuf_free - free the given rtw_cbuf
 * @cbuf: pointer of struct rtw_cbuf to free
 */
void rtw_cbuf_free(struct rtw_cbuf *cbuf)
{
	kfree(cbuf);
}
+225 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 * 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 _RECV_OSDEP_C_

#include <osdep_service.h>
#include <drv_types.h>

#include <wifi.h>
#include <recv_osdep.h>

#include <osdep_intf.h>
#include <ethernet.h>

#include <usb_ops.h>

/* alloc os related resource in struct recv_frame */
int rtw_os_recv_resource_alloc23a(struct rtw_adapter *padapter,
			       struct recv_frame *precvframe)
{
	int res = _SUCCESS;

	precvframe->pkt = NULL;

	return res;
}

/* alloc os related resource in struct recv_buf */
int rtw_os_recvbuf_resource_alloc23a(struct rtw_adapter *padapter,
				  struct recv_buf *precvbuf)
{
	int res = _SUCCESS;

	precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL);
	if (precvbuf->purb == NULL)
		res = _FAIL;

	precvbuf->pskb = NULL;

	return res;
}

/* free os related resource in struct recv_buf */
int rtw_os_recvbuf_resource_free23a(struct rtw_adapter *padapter,
				 struct recv_buf *precvbuf)
{
	int ret = _SUCCESS;

	usb_free_urb(precvbuf->purb);

	if (precvbuf->pskb)
		dev_kfree_skb_any(precvbuf->pskb);

	return ret;
}

void rtw_handle_tkip_mic_err23a(struct rtw_adapter *padapter, u8 bgroup)
{
	enum nl80211_key_type key_type = 0;
	union iwreq_data wrqu;
	struct iw_michaelmicfailure ev;
	struct mlme_priv *pmlmepriv  = &padapter->mlmepriv;
	struct security_priv *psecuritypriv = &padapter->securitypriv;
	unsigned long cur_time;

	if (psecuritypriv->last_mic_err_time == 0) {
		psecuritypriv->last_mic_err_time = jiffies;
	} else {
		cur_time = jiffies;

		if (cur_time - psecuritypriv->last_mic_err_time < 60*HZ) {
			psecuritypriv->btkip_countermeasure = true;
			psecuritypriv->last_mic_err_time = 0;
			psecuritypriv->btkip_countermeasure_time = cur_time;
		} else {
			psecuritypriv->last_mic_err_time = jiffies;
		}
	}

	if (bgroup)
		key_type |= NL80211_KEYTYPE_GROUP;
	else
		key_type |= NL80211_KEYTYPE_PAIRWISE;

	cfg80211_michael_mic_failure(padapter->pnetdev,
				     (u8 *)&pmlmepriv->assoc_bssid[0],
				     key_type, -1, NULL, GFP_ATOMIC);

	memset(&ev, 0x00, sizeof(ev));
	if (bgroup)
		ev.flags |= IW_MICFAILURE_GROUP;
	else
		ev.flags |= IW_MICFAILURE_PAIRWISE;

	ev.src_addr.sa_family = ARPHRD_ETHER;
	ether_addr_copy(ev.src_addr.sa_data, &pmlmepriv->assoc_bssid[0]);

	memset(&wrqu, 0x00, sizeof(wrqu));
	wrqu.data.length = sizeof(ev);
}

void rtw_hostapd_mlme_rx23a(struct rtw_adapter *padapter,
			 struct recv_frame *precv_frame)
{
}

int rtw_recv_indicatepkt23a(struct rtw_adapter *padapter,
			 struct recv_frame *precv_frame)
{
	struct recv_priv *precvpriv;
	struct rtw_queue *pfree_recv_queue;
	struct sk_buff *skb;
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;

	precvpriv = &(padapter->recvpriv);
	pfree_recv_queue = &(precvpriv->free_recv_queue);

	skb = precv_frame->pkt;
	if (!skb) {
		RT_TRACE(_module_recv_osdep_c_, _drv_err_,
			 ("rtw_recv_indicatepkt23a():skb == NULL!!!!\n"));
		goto _recv_indicatepkt_drop;
	}

	RT_TRACE(_module_recv_osdep_c_, _drv_info_,
		 ("rtw_recv_indicatepkt23a():skb != NULL !!!\n"));
	RT_TRACE(_module_recv_osdep_c_, _drv_info_,
		 ("rtw_recv_indicatepkt23a():precv_frame->hdr.rx_data =%p\n",
		  precv_frame->pkt->data));
	RT_TRACE(_module_recv_osdep_c_, _drv_info_,
		 ("\n skb->head =%p skb->data =%p skb->tail =%p skb->end =%p skb->len =%d\n",
		  skb->head, skb->data,
		  skb_tail_pointer(skb), skb_end_pointer(skb), skb->len));

	if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) {
		struct sk_buff *pskb2 = NULL;
		struct sta_info *psta = NULL;
		struct sta_priv *pstapriv = &padapter->stapriv;
		struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
		int bmcast = is_multicast_ether_addr(pattrib->dst);

		/* DBG_8723A("bmcast =%d\n", bmcast); */

		if (!ether_addr_equal(pattrib->dst,
				      myid(&padapter->eeprompriv))) {
			/* DBG_8723A("not ap psta =%p, addr =%pM\n", psta, pattrib->dst); */
			if (bmcast) {
				psta = rtw_get_bcmc_stainfo23a(padapter);
				pskb2 = skb_clone(skb, GFP_ATOMIC);
			} else {
				psta = rtw_get_stainfo23a(pstapriv, pattrib->dst);
			}

			if (psta) {
				struct net_device *pnetdev = padapter->pnetdev;

				/* DBG_8723A("directly forwarding to the rtw_xmit23a_entry23a\n"); */

				/* skb->ip_summed = CHECKSUM_NONE; */
				skb->dev = pnetdev;
				skb_set_queue_mapping(skb, rtw_recv_select_queue23a(skb));

				rtw_xmit23a_entry23a(skb, pnetdev);

				if (bmcast)
					skb = pskb2;
				else
					goto _recv_indicatepkt_end;
			}
		} else { /*  to APself */
			/* DBG_8723A("to APSelf\n"); */
		}
	}

	skb->ip_summed = CHECKSUM_NONE;
	skb->dev = padapter->pnetdev;
	skb->protocol = eth_type_trans(skb, padapter->pnetdev);

	netif_rx(skb);

_recv_indicatepkt_end:

	precv_frame->pkt = NULL; /*  pointers to NULL before rtw_free_recvframe23a() */

	rtw_free_recvframe23a(precv_frame, pfree_recv_queue);

	RT_TRACE(_module_recv_osdep_c_, _drv_info_,
		 ("\n rtw_recv_indicatepkt23a :after netif_rx!!!!\n"));
	return _SUCCESS;

_recv_indicatepkt_drop:

	 rtw_free_recvframe23a(precv_frame, pfree_recv_queue);
	 return _FAIL;
}

void rtw_os_read_port23a(struct rtw_adapter *padapter, struct recv_buf *precvbuf)
{
	struct recv_priv *precvpriv = &padapter->recvpriv;

	/* free skb in recv_buf */
	dev_kfree_skb_any(precvbuf->pskb);

	precvbuf->pskb = NULL;

	rtw_read_port(padapter, precvpriv->ff_hwaddr, 0, precvbuf);
}

void rtw_init_recv_timer23a(struct recv_reorder_ctrl *preorder_ctrl)
{
	setup_timer(&preorder_ctrl->reordering_ctrl_timer,
		    rtw_reordering_ctrl_timeout_handler23a,
		    (unsigned long)preorder_ctrl);
}
Loading