mirror of
https://github.com/shenmintao/aic8800d80.git
synced 2026-11-10 16:49:17 +00:00
Review-driven rework of the teardown lifetime (shenmintao/aic8800d80#103): - aicwf_bus_deinit(): keep the entry NULL guard (sequential double-deinit on probe-failure paths), but move dev_set_drvdata(dev, NULL) to the END of completed cleanup — after URB cancel, platform/rx/cmd teardown and the kthread_stop() join of bustx_thread. The previous placement (right after bus_stop) opened a real race: aicwf_usb_bus_txdata() re-reads drvdata and runs from bustx_thread, which is joined only later, and the TX-path state check is not a synchronization point. With the new ordering every cleanup callback and TX reader observes a non-NULL pointer throughout. - aicwf_usb_bus_stop(): NULL-check bus_if before dereferencing bus_if->bus_priv.usb (defense in depth; historical host oops RIP aicwf_usb_bus_stop+0x11 on probe-failure rollback). Verification (isolated VFIO lab, one 2357:0147 unit, sustained ping -f): a test-only fault-injection build (not included) reaches the repeated-deinit path and prints "already deinitialized, skip" with BUG=0; missing-firmware init failure runs one clean deinit; physical unplug x8, sysfs unbind and authorized cold cycles, disconnect during scanning and during connection establishment, ifdown/up x5, rmmod/insmod x3 -> BUG=0 throughout (only the pre-existing WARNs at rwnx_main.c:1529 and rwnx_cfg80211_disconnect:3328).
1382 lines
42 KiB
C
1382 lines
42 KiB
C
/**
|
||
* aicwf_bus.c
|
||
*
|
||
* bus function declarations
|
||
*
|
||
* Copyright (C) AICSemi 2018-2020
|
||
*/
|
||
|
||
#include <linux/kthread.h>
|
||
#include <linux/netdevice.h>
|
||
#include <linux/printk.h>
|
||
#include <linux/interrupt.h>
|
||
#include <linux/sched.h>
|
||
#include <linux/completion.h>
|
||
#include <linux/semaphore.h>
|
||
#include <linux/debugfs.h>
|
||
#include <linux/atomic.h>
|
||
#include <linux/vmalloc.h>
|
||
#include "lmac_msg.h"
|
||
#include "aicwf_txrxif.h"
|
||
#include "rwnx_platform.h"
|
||
#include "rwnx_defs.h"
|
||
#include "rwnx_msg_rx.h"
|
||
#include "rwnx_rx.h"
|
||
#include "aicwf_rx_prealloc.h"
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
#include "sdio_host.h"
|
||
#endif
|
||
|
||
#ifdef CONFIG_RX_TASKLET
|
||
int aicwf_tasklet_rxframes(struct aicwf_rx_priv *rx_priv);
|
||
#endif
|
||
#ifdef CONFIG_TX_TASKLET
|
||
void aicwf_tasklet_tx_process(struct aic_usb_dev *usb_dev);
|
||
#endif
|
||
extern bool aicwf_usb_rx_aggr;
|
||
|
||
#ifdef CONFIG_PREALLOC_RX_SKB
|
||
void aicwf_rxframe_queue_init_2(struct rx_frame_queue *pq, int max_len)
|
||
{
|
||
//int prio;
|
||
|
||
memset(pq, 0, offsetof(struct rx_frame_queue, queuelist) + (sizeof(struct list_head)));
|
||
pq->qmax = (u16)max_len;
|
||
INIT_LIST_HEAD(&pq->queuelist);
|
||
#if 0
|
||
memset(pq, 0, offsetof(struct rx_frame_queue, queuelist) + (sizeof(struct list_head) * num_prio));
|
||
pq->num_prio = (u16)num_prio;
|
||
pq->qmax = (u16)max_len;
|
||
|
||
for (prio = 0; prio < num_prio; prio++) {
|
||
INIT_LIST_HEAD(&pq->queuelist[prio]);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
//extern struct aic_sdio_dev *g_sdiodev;
|
||
void rxbuff_queue_flush(struct aicwf_rx_priv* rx_priv)
|
||
{
|
||
|
||
//int prio;
|
||
struct rx_frame_queue *pq = &rx_priv->rxq;
|
||
struct list_head *pos;
|
||
struct list_head *n;
|
||
struct list_head *head;
|
||
struct rx_buff *tempbuf = NULL;
|
||
|
||
head = &pq->queuelist;
|
||
list_for_each_safe(pos, n, head) {
|
||
tempbuf = list_entry(pos, struct rx_buff, queue);
|
||
list_del_init(&tempbuf->queue);
|
||
#if 0
|
||
rxbuff_free(tempbuf);
|
||
#else
|
||
aicwf_prealloc_rxbuff_free(tempbuf, &rx_priv->rxbuff_lock);
|
||
#endif
|
||
pq->qcnt--;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
int aicwf_bus_init(uint bus_hdrlen, struct device *dev)
|
||
{
|
||
int ret = 0;
|
||
struct aicwf_bus *bus_if;
|
||
|
||
if (!dev) {
|
||
AICWFDBG(LOGERROR, "device not found\n");
|
||
return -1;
|
||
}
|
||
bus_if = dev_get_drvdata(dev);
|
||
#if defined CONFIG_USB_SUPPORT && defined CONFIG_USB_NO_TRANS_DMA_MAP
|
||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
|
||
bus_if->cmd_buf = usb_alloc_coherent(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), &bus_if->bus_priv.usb->cmd_dma_trans_addr);
|
||
#else
|
||
bus_if->cmd_buf = usb_buffer_alloc(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), &bus_if->bus_priv.usb->cmd_dma_trans_addr);
|
||
#endif
|
||
#else
|
||
bus_if->cmd_buf = kzalloc(CMD_BUF_MAX, GFP_KERNEL);
|
||
#endif
|
||
if(!bus_if->cmd_buf) {
|
||
ret = -ENOMEM;
|
||
AICWFDBG(LOGERROR, "proto_attach failed\n");
|
||
goto fail;
|
||
}
|
||
memset(bus_if->cmd_buf, '\0', CMD_BUF_MAX);
|
||
|
||
init_completion(&bus_if->bustx_trgg);
|
||
init_completion(&bus_if->busrx_trgg);
|
||
#ifdef CONFIG_USB_MSG_IN_EP
|
||
if(bus_if->bus_priv.usb->msg_in_pipe) {
|
||
init_completion(&bus_if->msg_busrx_trgg);
|
||
}
|
||
#endif
|
||
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
bus_if->bustx_thread = kthread_run(sdio_bustx_thread, (void *)bus_if, "aicwf_bustx_thread");
|
||
bus_if->busrx_thread = kthread_run(sdio_busrx_thread, (void *)bus_if->bus_priv.sdio->rx_priv, "aicwf_busrx_thread");
|
||
#endif
|
||
#ifdef AICWF_USB_SUPPORT
|
||
bus_if->busrx_thread = kthread_run(usb_busrx_thread, (void *)bus_if->bus_priv.usb->rx_priv, "aicwf_busrx_thread");
|
||
bus_if->bustx_thread = kthread_run(usb_bustx_thread, (void *)bus_if, "aicwf_bustx_thread");
|
||
|
||
#if 1
|
||
//waiting for rx/tx thread init finish
|
||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
|
||
while(bus_if->busrx_thread->__state != TASK_INTERRUPTIBLE ||
|
||
bus_if->bustx_thread->__state != TASK_INTERRUPTIBLE)
|
||
#else
|
||
while(bus_if->busrx_thread->state != TASK_INTERRUPTIBLE ||
|
||
bus_if->bustx_thread->state != TASK_INTERRUPTIBLE)
|
||
#endif
|
||
{
|
||
AICWFDBG(LOGINFO, "%s waiting for rx/tx thread init finish \r\n", __func__);
|
||
msleep(100);
|
||
}
|
||
//waiting for rx/tx thread Initialization finish
|
||
#endif
|
||
|
||
#ifdef CONFIG_USB_MSG_IN_EP
|
||
if(bus_if->bus_priv.usb->msg_in_pipe)
|
||
{
|
||
bus_if->msg_busrx_thread = kthread_run(usb_msg_busrx_thread, (void *)bus_if->bus_priv.usb->rx_priv, "aicwf_msg_busrx_thread");
|
||
}
|
||
#endif
|
||
|
||
#ifdef CONFIG_RX_TASKLET//AIDEN tasklet
|
||
AICWFDBG(LOGINFO, "%s use tasklet for rx \r\n", __func__);
|
||
tasklet_init(&((bus_if->bus_priv.usb)->recv_tasklet),
|
||
(void(*)(unsigned long))aicwf_tasklet_rxframes,
|
||
(unsigned long)(bus_if->bus_priv.usb)->rx_priv);
|
||
#endif
|
||
|
||
#ifdef CONFIG_TX_TASKLET//AIDEN tasklet
|
||
AICWFDBG(LOGINFO, "%s use tasklet for tx \r\n", __func__);
|
||
tasklet_init(&((bus_if->bus_priv.usb)->xmit_tasklet),
|
||
(void(*)(unsigned long))aicwf_tasklet_tx_process,
|
||
(unsigned long)(bus_if->bus_priv.usb));
|
||
#endif
|
||
|
||
|
||
#endif
|
||
|
||
if (IS_ERR(bus_if->bustx_thread)) {
|
||
bus_if->bustx_thread = NULL;
|
||
AICWFDBG(LOGERROR, "aicwf_bustx_thread run fail\n");
|
||
goto fail;
|
||
}
|
||
|
||
if (IS_ERR(bus_if->busrx_thread)) {
|
||
bus_if->busrx_thread = NULL;
|
||
AICWFDBG(LOGERROR, "aicwf_bustx_thread run fail\n");
|
||
goto fail;
|
||
}
|
||
|
||
#ifdef CONFIG_USB_MSG_IN_EP
|
||
if(bus_if->bus_priv.usb->msg_in_pipe){
|
||
if (IS_ERR(bus_if->msg_busrx_thread)) {
|
||
bus_if->msg_busrx_thread = NULL;
|
||
txrx_err("aicwf_msg_busrx_thread run fail\n");
|
||
goto fail;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
return ret;
|
||
fail:
|
||
aicwf_bus_deinit(dev);
|
||
|
||
return ret;
|
||
}
|
||
|
||
void aicwf_usb_cancel_all_urbs(struct aic_usb_dev *usb_dev);
|
||
|
||
void aicwf_bus_deinit(struct device *dev)
|
||
{
|
||
struct aicwf_bus *bus_if;
|
||
#ifdef AICWF_USB_SUPPORT
|
||
struct aic_usb_dev *usb;
|
||
#endif
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
struct aic_sdio_dev *sdiodev;
|
||
#endif
|
||
|
||
if (!dev) {
|
||
txrx_err("device not found\n");
|
||
return;
|
||
}
|
||
AICWFDBG(LOGINFO, "%s Enter\r\n", __func__);
|
||
|
||
bus_if = dev_get_drvdata(dev);
|
||
/* 入口判空:本函数在探测失败路径上会被调用**两次**
|
||
* aicwf_bus_init() 的 `fail:` 先调一次,随后 aicwf_usb_probe() 的
|
||
* `out_free_bus:` 再调一次(用局部 bus_if 做 kfree,不依赖 drvdata)。
|
||
* 第二次进入时 drvdata 已在**上一次调用的末尾**被清空 → 必然命中本判空。
|
||
* 若不清空,第二次会对已完整拆解的对象重复执行整套 teardown。 */
|
||
if (!bus_if) {
|
||
AICWFDBG(LOGINFO, "%s: already deinitialized, skip\n", __func__);
|
||
return;
|
||
}
|
||
aicwf_bus_stop(bus_if);
|
||
|
||
#ifdef AICWF_USB_SUPPORT
|
||
usb = bus_if->bus_priv.usb;
|
||
aicwf_usb_cancel_all_urbs(usb);//AIDEN
|
||
|
||
if(g_rwnx_plat && g_rwnx_plat->enabled){
|
||
rwnx_platform_deinit(usb->rwnx_hw);
|
||
}else{
|
||
AICWFDBG(LOGINFO, "%s g_rwnx_plat->enabled is false\r\n", __func__);
|
||
}
|
||
|
||
#endif
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
sdiodev =bus_if->bus_priv.sdio;
|
||
if(g_rwnx_plat && g_rwnx_plat->enabled)
|
||
rwnx_platform_deinit(sdiodev->rwnx_hw);
|
||
#endif
|
||
|
||
if (bus_if->cmd_buf) {
|
||
#if defined CONFIG_USB_SUPPORT && defined CONFIG_USB_NO_TRANS_DMA_MAP
|
||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
|
||
usb_free_coherent(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, bus_if->cmd_buf, bus_if->bus_priv.usb->cmd_dma_trans_addr);
|
||
#else
|
||
usb_buffer_free(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, bus_if->cmd_buf, bus_if->bus_priv.usb->cmd_dma_trans_addr);
|
||
#endif
|
||
#else
|
||
kfree(bus_if->cmd_buf);
|
||
#endif
|
||
bus_if->cmd_buf = NULL;
|
||
}
|
||
|
||
if (bus_if->bustx_thread) {
|
||
AICWFDBG(LOGINFO, "%s stop to bustx_thread!! \r\n", __func__);
|
||
|
||
|
||
complete_all(&bus_if->bustx_trgg);
|
||
kthread_stop(bus_if->bustx_thread);
|
||
bus_if->bustx_thread = NULL;
|
||
}
|
||
|
||
if (usb->rx_priv)
|
||
aicwf_rx_deinit(usb->rx_priv);
|
||
|
||
rwnx_cmd_mgr_deinit(&usb->cmd_mgr);
|
||
|
||
#ifdef CONFIG_TX_TASKLET//AIDEN tasklet
|
||
tasklet_kill(&usb->xmit_tasklet);
|
||
#endif
|
||
|
||
/* 清空 drvdata:全部拆解完成之后、对象由调用方释放之前。
|
||
* (评审 shenmintao/aic8800d80#103 指出的顺序问题:此前清空点位于
|
||
* bus_stop 之后、URB/platform/rx/cmd 拆解与 TX 线程 join 之前。)
|
||
* 1) 顺序双 deinit:第二次调用靠上方入口判空在本行之后必然 skip;
|
||
* 2) 过早清空会伤害仍在运行的读者——TX 链
|
||
* usb_bustx_thread → aicwf_bus_txdata(usbdev->bus_if) →
|
||
* ops->txdata = aicwf_usb_bus_txdata()(usb.c 在该处重读
|
||
* dev_get_drvdata)仅靠 state 检查做 check-then-act、无锁,
|
||
* 且该线程直到上方 kthread_stop(bustx_thread) 才被 join——
|
||
* 必须先 join 再清;
|
||
* 3) cmd/rx/platform 拆解与 rwnx_close() 等读者在本行之前全程
|
||
* 看到非空 drvdata,行为与未打补丁树一致。 */
|
||
dev_set_drvdata(dev, NULL);
|
||
|
||
AICWFDBG(LOGINFO, "%s Exit \n", __func__);
|
||
}
|
||
|
||
void aicwf_frame_tx(void *dev, struct sk_buff *skb)
|
||
{
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev;
|
||
aicwf_bus_txdata(sdiodev->bus_if, skb);
|
||
#else
|
||
struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
|
||
struct rwnx_txhdr *txhdr = NULL;
|
||
unsigned long flags;
|
||
|
||
if (!usbdev->state) {
|
||
txrx_err("down\n");
|
||
spin_lock_irqsave(&usbdev->tx_flow_lock, flags);
|
||
if(!usbdev->tbusy)
|
||
aicwf_usb_tx_flowctrl(usbdev->rwnx_hw, true);
|
||
spin_unlock_irqrestore(&usbdev->tx_flow_lock, flags);
|
||
txhdr = (struct rwnx_txhdr *)skb->data;
|
||
kmem_cache_free(usbdev->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
|
||
dev_kfree_skb(skb);
|
||
return;
|
||
}
|
||
aicwf_bus_txdata(usbdev->bus_if, skb);
|
||
#endif
|
||
}
|
||
|
||
struct aicwf_tx_priv* aicwf_tx_init(void *arg)
|
||
{
|
||
struct aicwf_tx_priv* tx_priv;
|
||
|
||
tx_priv = kzalloc(sizeof(struct aicwf_tx_priv), GFP_KERNEL);
|
||
if (!tx_priv)
|
||
return NULL;
|
||
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
tx_priv->sdiodev = (struct aic_sdio_dev *)arg;
|
||
#else
|
||
tx_priv->usbdev = (struct aic_usb_dev *)arg;
|
||
#endif
|
||
|
||
atomic_set(&tx_priv->aggr_count, 0);
|
||
tx_priv->aggr_buf = dev_alloc_skb(MAX_AGGR_TXPKT_LEN);
|
||
if(!tx_priv->aggr_buf) {
|
||
txrx_err("Alloc bus->txdata_buf failed!\n");
|
||
kfree(tx_priv);
|
||
return NULL;
|
||
}
|
||
tx_priv->head = tx_priv->aggr_buf->data;
|
||
tx_priv->tail = tx_priv->aggr_buf->data;
|
||
|
||
return tx_priv;
|
||
}
|
||
|
||
void aicwf_tx_deinit(struct aicwf_tx_priv* tx_priv)
|
||
{
|
||
if (tx_priv && tx_priv->aggr_buf)
|
||
dev_kfree_skb(tx_priv->aggr_buf);
|
||
|
||
kfree(tx_priv);
|
||
}
|
||
|
||
#ifdef CONFIG_PREALLOC_RX_SKB
|
||
static bool aicwf_another_ptk_1(struct rx_buff *buffer)
|
||
{
|
||
u8 *read = buffer->read;
|
||
u16 aggr_len = 0;
|
||
|
||
if(read == NULL || read >= (buffer->end - 2)) {
|
||
return false;
|
||
}
|
||
|
||
if(buffer->len < 2) {
|
||
return false;
|
||
}
|
||
|
||
aggr_len = (*read | (*(read + 1) << 8));
|
||
if(aggr_len == 0) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
#else
|
||
static bool aicwf_another_ptk(struct sk_buff *skb)
|
||
{
|
||
u8 *data;
|
||
u16 aggr_len = 0;
|
||
|
||
if (skb->data == NULL || skb->len < 2) {
|
||
return false;
|
||
}
|
||
data = skb->data;
|
||
aggr_len = (*skb->data | (*(skb->data + 1) << 8));
|
||
if (aggr_len == 0) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
#endif
|
||
|
||
|
||
#if 0//AIDEN
|
||
void rwnx_frame_parser(char* tag, char* data, unsigned long len);
|
||
#endif
|
||
|
||
#ifdef CONFIG_RX_TASKLET
|
||
int aicwf_tasklet_rxframes(struct aicwf_rx_priv *rx_priv)
|
||
{
|
||
int ret = 0;
|
||
unsigned long flags = 0;
|
||
struct sk_buff *skb = NULL; /* Packet for event or data frames */
|
||
u16 pkt_len = 0;
|
||
//struct sk_buff *skb_inblock = NULL;
|
||
u16 aggr_len = 0, adjust_len = 0;
|
||
u8 *data = NULL;
|
||
u8_l *msg = NULL;
|
||
|
||
while (1) {
|
||
//aicwf_usb_rx_submit_all_urb_(rx_priv->usbdev);
|
||
spin_lock_irqsave(&rx_priv->rxqlock, flags);
|
||
if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
|
||
usb_info("no more rxdata\n");
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
|
||
break;
|
||
}
|
||
skb = aicwf_frame_dequeue(&rx_priv->rxq);
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
|
||
if (skb == NULL) {
|
||
txrx_err("skb_error\r\n");
|
||
break;
|
||
}
|
||
data = skb->data;
|
||
pkt_len = (*skb->data | (*(skb->data + 1) << 8));
|
||
//printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
|
||
#ifndef CONFIG_USB_RX_REASSEMBLE
|
||
if (pkt_len > skb->len) {
|
||
AICWFDBG(LOGERROR, "%s pkt_len:%d skb->len:%d\r\n", __func__, pkt_len, skb->len);
|
||
dev_kfree_skb(skb);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
continue;
|
||
}
|
||
#endif
|
||
if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
|
||
#if 0
|
||
aggr_len = pkt_len + RX_HWHRD_LEN;
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_ATOMIC);//8 is for ccmp mic or wep icv
|
||
if(skb_inblock == NULL){
|
||
txrx_err("no more space! skip!\n");
|
||
skb_pull(skb, adjust_len);
|
||
continue;
|
||
}
|
||
|
||
skb_put(skb_inblock, aggr_len);
|
||
memcpy(skb_inblock->data, data, aggr_len);
|
||
#if 0//AIDEN
|
||
rwnx_frame_parser((char*)__func__, skb_inblock->data + 60, aggr_len - 60);
|
||
#endif
|
||
#endif
|
||
rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb, (void *)rx_priv);
|
||
///TODO: here need to add rx data process
|
||
//skb_pull(skb, adjust_len);
|
||
}
|
||
else { // type : config
|
||
aggr_len = pkt_len;
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
msg = kmalloc(aggr_len+4, GFP_ATOMIC);
|
||
if(msg == NULL){
|
||
txrx_err("no more space for msg!\n");
|
||
aicwf_dev_skb_free(skb);
|
||
return -EBADE;
|
||
}
|
||
memcpy(msg, data, aggr_len + 4);
|
||
if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP)
|
||
rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
|
||
|
||
if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
|
||
aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
|
||
skb_pull(skb, adjust_len+4);
|
||
kfree(msg);
|
||
dev_kfree_skb(skb);
|
||
}
|
||
|
||
//dev_kfree_skb(skb);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
extern bool rx_urb_sched;
|
||
int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv)
|
||
{
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
int ret = 0;
|
||
unsigned long flags = 0;
|
||
struct sk_buff *skb = NULL;
|
||
u16 pkt_len = 0;
|
||
struct sk_buff *skb_inblock = NULL;
|
||
u16 aggr_len = 0, adjust_len = 0;
|
||
u8 *data = NULL;
|
||
u8_l *msg = NULL;
|
||
|
||
while (1) {
|
||
spin_lock_irqsave(&rx_priv->rxqlock, flags);
|
||
if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
|
||
break;
|
||
}
|
||
skb = aicwf_frame_dequeue(&rx_priv->rxq);
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
|
||
if (skb == NULL) {
|
||
txrx_err("skb_error\r\n");
|
||
break;
|
||
}
|
||
while(aicwf_another_ptk(skb)) {
|
||
data = skb->data;
|
||
pkt_len = (*skb->data | (*(skb->data + 1) << 8));
|
||
|
||
if((skb->data[2] & SDIO_TYPE_CFG) != SDIO_TYPE_CFG) { // type : data
|
||
aggr_len = pkt_len + RX_HWHRD_LEN;
|
||
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);
|
||
if(skb_inblock == NULL){
|
||
txrx_err("no more space! skip\n");
|
||
skb_pull(skb, adjust_len);
|
||
continue;
|
||
}
|
||
|
||
skb_put(skb_inblock, aggr_len);
|
||
memcpy(skb_inblock->data, data, aggr_len);
|
||
rwnx_rxdataind_aicwf(rx_priv->sdiodev->rwnx_hw, skb_inblock, (void *)rx_priv);
|
||
skb_pull(skb, adjust_len);
|
||
}
|
||
else { // type : config
|
||
aggr_len = pkt_len;
|
||
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
msg = kmalloc(aggr_len+4, GFP_KERNEL);
|
||
if(msg == NULL){
|
||
txrx_err("no more space for msg!\n");
|
||
aicwf_dev_skb_free(skb);
|
||
return -EBADE;
|
||
}
|
||
|
||
memcpy(msg, data, aggr_len + 4);
|
||
if((*(msg + 2) & 0x7f) == SDIO_TYPE_CFG_CMD_RSP)
|
||
rwnx_rx_handle_msg(rx_priv->sdiodev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
|
||
|
||
if((*(msg + 2) & 0x7f) == SDIO_TYPE_CFG_DATA_CFM)
|
||
aicwf_sdio_host_tx_cfm_handler(&(rx_priv->sdiodev->rwnx_hw->sdio_env), (u32 *)(msg + 4));
|
||
skb_pull(skb, adjust_len+4);
|
||
kfree(msg);
|
||
}
|
||
}
|
||
|
||
dev_kfree_skb(skb);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
}
|
||
|
||
aicwf_sdio_pwr_stctl(rx_priv->sdiodev, SDIO_ACTIVE_ST);
|
||
|
||
return ret;
|
||
#else //AICWF_USB_SUPPORT
|
||
int ret = 0;
|
||
unsigned long flags = 0;
|
||
#ifndef CONFIG_PREALLOC_RX_SKB
|
||
struct sk_buff *skb = NULL;/* Packet for event or data frames */
|
||
#endif
|
||
struct sk_buff *skb_inblock = NULL;
|
||
|
||
u16 pkt_len = 0;
|
||
u16 aggr_len = 0, adjust_len = 0;
|
||
u8 *data = NULL;
|
||
u8_l *msg = NULL;
|
||
//#ifdef CONFIG_USB_RX_AGGR
|
||
// struct sk_buff *skb_inblock = NULL;
|
||
u8 cnt = 0 ;
|
||
//#endif
|
||
#ifdef CONFIG_PREALLOC_RX_SKB
|
||
struct rx_buff *buffer = NULL;
|
||
if(aicwf_usb_rx_aggr){
|
||
while (1) {
|
||
spin_lock_irqsave(&rx_priv->rxqlock, flags);
|
||
if (!rx_priv->rxq.qcnt) {
|
||
usb_info("no more rxdata\n");
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
|
||
break;
|
||
}
|
||
buffer = rxbuff_dequeue(&rx_priv->rxq);
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
|
||
|
||
if (buffer == NULL) {
|
||
txrx_err("skb_error\r\n");
|
||
ASSERT_ERR(1);
|
||
break;
|
||
}
|
||
|
||
while(aicwf_another_ptk_1(buffer)) {
|
||
cnt++;
|
||
data = buffer->read;
|
||
pkt_len = (*data | (*(data + 1) << 8));
|
||
//printk("%s cnt:%d pkt_len:%d \r\n", __func__, cnt, pkt_len);
|
||
#ifndef CONFIG_USB_RX_REASSEMBLE
|
||
if (pkt_len > buffer->len) {
|
||
AICWFDBG(LOGERROR, "%s pkt_len:%d buffer->len:%d\r\n", __func__, pkt_len, buffer->len);
|
||
aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
return -EBADE;
|
||
}
|
||
#endif
|
||
|
||
if((data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
|
||
aggr_len = pkt_len + RX_HWHRD_LEN;
|
||
|
||
skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);
|
||
if (skb_inblock == NULL) {
|
||
txrx_err("no more space! skip\n");
|
||
aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
return -EBADE;
|
||
}
|
||
|
||
skb_put(skb_inblock, aggr_len);
|
||
memcpy(skb_inblock->data, data, aggr_len);
|
||
rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
|
||
buffer->read = buffer->read + aggr_len;
|
||
buffer->len -= aggr_len;
|
||
}
|
||
else { // type : config
|
||
aggr_len = pkt_len;
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
msg = kmalloc(aggr_len+4, GFP_KERNEL);
|
||
if(msg == NULL){
|
||
txrx_err("no more space for msg!\n");
|
||
aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
|
||
return -EBADE;
|
||
}
|
||
memcpy(msg, data, aggr_len + 4);
|
||
|
||
if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
|
||
rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
|
||
|
||
if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
|
||
aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
|
||
|
||
if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
|
||
rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
|
||
|
||
buffer->read = buffer->read + (adjust_len + 4);
|
||
buffer->len -= (adjust_len + 4);
|
||
kfree(msg);
|
||
}
|
||
}
|
||
aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
|
||
if (rx_urb_sched) {
|
||
schedule_work(&rx_priv->usbdev->rx_urb_work);
|
||
rx_urb_sched = false;
|
||
}
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
}
|
||
}else{
|
||
while (1) {
|
||
spin_lock_irqsave(&rx_priv->rxqlock, flags);
|
||
if (!rx_priv->rxq.qcnt) {
|
||
usb_info("no more rxdata\n");
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
|
||
break;
|
||
}
|
||
buffer = rxbuff_dequeue(&rx_priv->rxq);
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
|
||
|
||
if (buffer == NULL) {
|
||
txrx_err("skb_error\r\n");
|
||
ASSERT_ERR(1);
|
||
break;
|
||
}
|
||
data = buffer->data;
|
||
pkt_len = (*data | (*(data + 1) << 8));
|
||
//printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
|
||
#ifndef CONFIG_USB_RX_REASSEMBLE
|
||
if (pkt_len > buffer->len) {
|
||
AICWFDBG(LOGERROR, "%s pkt_len:%d buffer->len:%d\r\n", __func__, pkt_len, buffer->len);
|
||
aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
continue;
|
||
}
|
||
#endif
|
||
if((data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
|
||
skb_inblock = __dev_alloc_skb(pkt_len + RX_HWHRD_LEN + CCMP_OR_WEP_INFO, GFP_KERNEL);
|
||
if (skb_inblock == NULL) {
|
||
txrx_err("no more space! skip\n");
|
||
aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
continue;
|
||
}
|
||
|
||
skb_put(skb_inblock, pkt_len + RX_HWHRD_LEN);
|
||
memcpy(skb_inblock->data, data, pkt_len + RX_HWHRD_LEN);
|
||
rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
|
||
}
|
||
else { // type : config
|
||
aggr_len = pkt_len;
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
msg = kmalloc(aggr_len+4, GFP_KERNEL);
|
||
if(msg == NULL){
|
||
txrx_err("no more space for msg!\n");
|
||
aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
|
||
return -EBADE;
|
||
}
|
||
memcpy(msg, data, aggr_len + 4);
|
||
|
||
if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
|
||
rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
|
||
|
||
if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
|
||
aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
|
||
|
||
if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
|
||
rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
|
||
|
||
kfree(msg);
|
||
}
|
||
|
||
aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
|
||
if (rx_urb_sched) {
|
||
schedule_work(&rx_priv->usbdev->rx_urb_work);
|
||
rx_urb_sched = false;
|
||
}
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
}
|
||
}
|
||
#else
|
||
if(aicwf_usb_rx_aggr){
|
||
while (1) {
|
||
spin_lock_irqsave(&rx_priv->rxqlock, flags);
|
||
if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
|
||
usb_info("no more rxdata\n");
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
|
||
break;
|
||
}
|
||
skb = aicwf_frame_dequeue(&rx_priv->rxq);
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
|
||
|
||
if (skb == NULL) {
|
||
txrx_err("skb_error\r\n");
|
||
ASSERT_ERR(1);
|
||
break;
|
||
}
|
||
while(aicwf_another_ptk(skb)) {
|
||
cnt++;
|
||
if (cnt > 30) {
|
||
//printk("%s err, break %d\n", __func__, cnt);
|
||
//break;
|
||
}
|
||
data = skb->data;
|
||
pkt_len = (*skb->data | (*(skb->data + 1) << 8));
|
||
//printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
|
||
#ifndef CONFIG_USB_RX_REASSEMBLE
|
||
if (pkt_len > skb->len) {
|
||
AICWFDBG(LOGERROR, "%s pkt_len:%d skb->len:%d\r\n", __func__, pkt_len, skb->len);
|
||
//dev_kfree_skb(skb);
|
||
//atomic_dec(&rx_priv->rx_cnt);
|
||
//continue;
|
||
break;
|
||
}
|
||
#endif
|
||
if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
|
||
aggr_len = pkt_len + RX_HWHRD_LEN;
|
||
adjust_len = aggr_len;
|
||
skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);//8 is for ccmp mic or wep icv
|
||
if(skb_inblock == NULL){
|
||
txrx_err("no more space! skip!\n");
|
||
skb_pull(skb, adjust_len);
|
||
continue;
|
||
}
|
||
|
||
skb_put(skb_inblock, aggr_len);
|
||
memcpy(skb_inblock->data, data, aggr_len);
|
||
rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
|
||
|
||
///TODO: here need to add rx data process
|
||
|
||
skb_pull(skb, adjust_len);
|
||
|
||
}
|
||
else { // type : config
|
||
aggr_len = pkt_len;
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
msg = kmalloc(aggr_len+4, GFP_KERNEL);//hdr(4)+config(aggrlen)
|
||
if(msg == NULL){
|
||
txrx_err("no more space for msg!\n");
|
||
aicwf_dev_skb_free(skb);
|
||
return -EBADE;
|
||
}
|
||
memcpy(msg, data, aggr_len + 4);
|
||
|
||
if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
|
||
rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
|
||
|
||
if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
|
||
aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
|
||
|
||
if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
|
||
rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
|
||
|
||
|
||
skb_pull(skb, adjust_len+4);
|
||
kfree(msg);
|
||
|
||
}
|
||
}
|
||
dev_kfree_skb(skb);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
}
|
||
}else{
|
||
while (1) {
|
||
spin_lock_irqsave(&rx_priv->rxqlock, flags);
|
||
if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
|
||
usb_info("no more rxdata\n");
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
|
||
break;
|
||
}
|
||
skb = aicwf_frame_dequeue(&rx_priv->rxq);
|
||
spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
|
||
|
||
if (skb == NULL) {
|
||
txrx_err("skb_error\r\n");
|
||
ASSERT_ERR(1);
|
||
break;
|
||
}
|
||
data = skb->data;
|
||
pkt_len = (*skb->data | (*(skb->data + 1) << 8));
|
||
//printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
|
||
#ifndef CONFIG_USB_RX_REASSEMBLE
|
||
if (pkt_len > skb->len) {
|
||
AICWFDBG(LOGERROR, "%s pkt_len:%d skb->len:%d\r\n", __func__, pkt_len, skb->len);
|
||
dev_kfree_skb(skb);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
continue;
|
||
}
|
||
#endif
|
||
|
||
if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
|
||
rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb, (void *)rx_priv);
|
||
}
|
||
else { // type : config
|
||
aggr_len = pkt_len;
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
msg = kmalloc(aggr_len+4, GFP_KERNEL);
|
||
if(msg == NULL){
|
||
txrx_err("no more space for msg!\n");
|
||
aicwf_dev_skb_free(skb);
|
||
return -EBADE;
|
||
}
|
||
memcpy(msg, data, aggr_len + 4);
|
||
|
||
if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
|
||
rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
|
||
|
||
if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
|
||
aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
|
||
|
||
if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
|
||
rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
|
||
|
||
skb_pull(skb, adjust_len+4);
|
||
kfree(msg);
|
||
dev_kfree_skb(skb);
|
||
}
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return ret;
|
||
#endif //AICWF_SDIO_SUPPORT
|
||
}
|
||
|
||
|
||
#ifdef CONFIG_USB_MSG_IN_EP
|
||
int aicwf_process_msg_rxframes(struct aicwf_rx_priv *rx_priv)
|
||
{
|
||
int ret = 0;
|
||
unsigned long flags = 0;
|
||
struct sk_buff *skb = NULL; /* Packet for event or data frames */
|
||
u16 pkt_len = 0;
|
||
struct sk_buff *skb_inblock = NULL;
|
||
u16 aggr_len = 0, adjust_len = 0;
|
||
u8 *data = NULL;
|
||
u8_l *msg = NULL;
|
||
|
||
while (1) {
|
||
spin_lock_irqsave(&rx_priv->msg_rxqlock, flags);
|
||
if(aicwf_is_framequeue_empty(&rx_priv->msg_rxq)) {
|
||
usb_info("no more rxmsg\n");
|
||
spin_unlock_irqrestore(&rx_priv->msg_rxqlock,flags);
|
||
break;
|
||
}
|
||
skb = aicwf_frame_dequeue(&rx_priv->msg_rxq);
|
||
spin_unlock_irqrestore(&rx_priv->msg_rxqlock, flags);
|
||
if (skb == NULL) {
|
||
txrx_err("skb_error\r\n");
|
||
break;
|
||
}
|
||
data = skb->data;
|
||
pkt_len = (*skb->data | (*(skb->data + 1) << 8));
|
||
//printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
|
||
#if 0 //amsdu > 1600
|
||
if (pkt_len > 1600) {
|
||
dev_kfree_skb(skb);
|
||
atomic_dec(&rx_priv->rx_cnt);
|
||
continue;
|
||
}
|
||
#endif
|
||
|
||
if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
|
||
aggr_len = pkt_len + RX_HWHRD_LEN;
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);//8 is for ccmp mic or wep icv
|
||
if(skb_inblock == NULL){
|
||
txrx_err("no more space! skip!\n");
|
||
skb_pull(skb, adjust_len);
|
||
continue;
|
||
}
|
||
|
||
skb_put(skb_inblock, aggr_len);
|
||
memcpy(skb_inblock->data, data, aggr_len);
|
||
#if 0//AIDEN
|
||
rwnx_frame_parser((char*)__func__, skb_inblock->data + 60, aggr_len - 60);
|
||
#endif
|
||
rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
|
||
///TODO: here need to add rx data process
|
||
|
||
skb_pull(skb, adjust_len);
|
||
}
|
||
else { // type : config
|
||
aggr_len = pkt_len;
|
||
if (aggr_len & (RX_ALIGNMENT - 1))
|
||
adjust_len = roundup(aggr_len, RX_ALIGNMENT);
|
||
else
|
||
adjust_len = aggr_len;
|
||
|
||
msg = kmalloc(aggr_len+4, GFP_KERNEL);
|
||
if(msg == NULL){
|
||
txrx_err("no more space for msg!\n");
|
||
aicwf_dev_skb_free(skb);
|
||
return -EBADE;
|
||
}
|
||
memcpy(msg, data, aggr_len + 4);
|
||
|
||
if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
|
||
rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
|
||
|
||
if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
|
||
aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
|
||
|
||
if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
|
||
rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
|
||
|
||
|
||
skb_pull(skb, adjust_len+4);
|
||
kfree(msg);
|
||
}
|
||
|
||
dev_kfree_skb(skb);
|
||
atomic_dec(&rx_priv->msg_rx_cnt);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
|
||
#ifdef AICWF_RX_REORDER
|
||
static struct recv_msdu *aicwf_rxframe_queue_init(struct list_head *q, int qsize)
|
||
{
|
||
int i;
|
||
struct recv_msdu *req, *reqs;
|
||
|
||
reqs = vmalloc(qsize*sizeof(struct recv_msdu));
|
||
if (reqs == NULL)
|
||
return NULL;
|
||
|
||
req = reqs;
|
||
for (i = 0; i < qsize; i++) {
|
||
INIT_LIST_HEAD(&req->rxframe_list);
|
||
list_add(&req->rxframe_list, q);
|
||
//req->len = 0;
|
||
req++;
|
||
}
|
||
|
||
return reqs;
|
||
}
|
||
#endif
|
||
struct aicwf_rx_priv *aicwf_rx_init(void *arg)
|
||
{
|
||
struct aicwf_rx_priv* rx_priv;
|
||
rx_priv = kzalloc(sizeof(struct aicwf_rx_priv), GFP_KERNEL);
|
||
if (!rx_priv)
|
||
return NULL;
|
||
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
rx_priv->sdiodev = (struct aic_sdio_dev *)arg;
|
||
#else
|
||
rx_priv->usbdev = (struct aic_usb_dev *)arg;
|
||
#endif
|
||
|
||
#ifdef CONFIG_PREALLOC_RX_SKB
|
||
aicwf_rxframe_queue_init_2(&rx_priv->rxq, MAX_RXQLEN);
|
||
#else
|
||
aicwf_frame_queue_init(&rx_priv->rxq, 1, MAX_RXQLEN);
|
||
#endif
|
||
spin_lock_init(&rx_priv->rxqlock);
|
||
#ifdef CONFIG_PREALLOC_RX_SKB
|
||
spin_lock_init(&rx_priv->rxbuff_lock);
|
||
//aicwf_prealloc_init();
|
||
#endif
|
||
atomic_set(&rx_priv->rx_cnt, 0);
|
||
|
||
#ifdef CONFIG_USB_MSG_IN_EP
|
||
if(rx_priv->usbdev->msg_in_pipe){
|
||
aicwf_frame_queue_init(&rx_priv->msg_rxq, 1, MAX_RXQLEN);
|
||
spin_lock_init(&rx_priv->msg_rxqlock);
|
||
atomic_set(&rx_priv->msg_rx_cnt, 0);
|
||
}
|
||
#endif
|
||
|
||
|
||
#ifdef AICWF_RX_REORDER
|
||
INIT_LIST_HEAD(&rx_priv->rxframes_freequeue);
|
||
spin_lock_init(&rx_priv->freeq_lock);
|
||
rx_priv->recv_frames = aicwf_rxframe_queue_init(&rx_priv->rxframes_freequeue, MAX_REORD_RXFRAME);
|
||
if (!rx_priv->recv_frames) {
|
||
txrx_err("no enough buffer for free recv frame queue!\n");
|
||
kfree(rx_priv);
|
||
return NULL;
|
||
}
|
||
spin_lock_init(&rx_priv->stas_reord_lock);
|
||
INIT_LIST_HEAD(&rx_priv->stas_reord_list);
|
||
#endif
|
||
|
||
return rx_priv;
|
||
}
|
||
|
||
#ifdef AICWF_RX_REORDER
|
||
static void aicwf_recvframe_queue_deinit(struct list_head *q)
|
||
{
|
||
struct recv_msdu *req, *next;
|
||
|
||
list_for_each_entry_safe(req, next, q, rxframe_list) {
|
||
list_del_init(&req->rxframe_list);
|
||
}
|
||
}
|
||
#endif
|
||
void aicwf_rx_deinit(struct aicwf_rx_priv* rx_priv)
|
||
{
|
||
#ifdef AICWF_RX_REORDER
|
||
struct reord_ctrl_info *reord_info, *tmp;
|
||
|
||
AICWFDBG(LOGINFO, "%s Enter\n", __func__);
|
||
|
||
list_for_each_entry_safe(reord_info, tmp,
|
||
&rx_priv->stas_reord_list, list) {
|
||
reord_deinit_sta(rx_priv, reord_info);
|
||
}
|
||
|
||
#endif
|
||
AICWFDBG(LOGINFO, "stio rx thread\n");
|
||
#ifdef AICWF_SDIO_SUPPORT
|
||
if (rx_priv->sdiodev->bus_if->busrx_thread) {
|
||
complete_all(&rx_priv->sdiodev->bus_if->busrx_trgg);
|
||
kthread_stop(rx_priv->sdiodev->bus_if->busrx_thread);
|
||
rx_priv->sdiodev->bus_if->busrx_thread = NULL;
|
||
}
|
||
#endif
|
||
#ifdef AICWF_USB_SUPPORT
|
||
|
||
#ifdef CONFIG_RX_TASKLET//AIDEN tasklet
|
||
tasklet_kill(&rx_priv->usbdev->recv_tasklet);
|
||
#endif
|
||
if (rx_priv->usbdev->bus_if->busrx_thread) {
|
||
complete_all(&rx_priv->usbdev->bus_if->busrx_trgg);
|
||
kthread_stop(rx_priv->usbdev->bus_if->busrx_thread);
|
||
rx_priv->usbdev->bus_if->busrx_thread = NULL;
|
||
}
|
||
#ifdef CONFIG_USB_MSG_IN_EP
|
||
if(rx_priv->usbdev->msg_in_pipe){
|
||
if (rx_priv->usbdev->bus_if->msg_busrx_thread) {
|
||
complete_all(&rx_priv->usbdev->bus_if->msg_busrx_trgg);
|
||
kthread_stop(rx_priv->usbdev->bus_if->msg_busrx_thread);
|
||
rx_priv->usbdev->bus_if->msg_busrx_thread = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
#ifdef CONFIG_USB_RX_REASSEMBLE
|
||
if (rx_priv->rx_reassemble_skb) {
|
||
aicwf_dev_skb_free(rx_priv->rx_reassemble_skb);
|
||
}
|
||
if (rx_priv->rx_msg_reassemble_skb) {
|
||
aicwf_dev_skb_free(rx_priv->rx_msg_reassemble_skb);
|
||
}
|
||
#endif
|
||
|
||
#endif
|
||
|
||
#ifdef CONFIG_USB_MSG_IN_EP
|
||
if(rx_priv->usbdev->msg_in_pipe){
|
||
aicwf_frame_queue_flush(&rx_priv->msg_rxq);
|
||
}
|
||
#endif
|
||
|
||
#ifdef CONFIG_PREALLOC_RX_SKB
|
||
rxbuff_queue_flush(rx_priv);
|
||
#else
|
||
aicwf_frame_queue_flush(&rx_priv->rxq);
|
||
#endif
|
||
|
||
#ifdef AICWF_RX_REORDER
|
||
aicwf_recvframe_queue_deinit(&rx_priv->rxframes_freequeue);
|
||
if (rx_priv->recv_frames)
|
||
vfree(rx_priv->recv_frames);
|
||
#endif
|
||
|
||
#ifdef CONFIG_PREALLOC_RX_SKB
|
||
//aicwf_prealloc_exit();
|
||
#endif
|
||
kfree(rx_priv);
|
||
AICWFDBG(LOGINFO, "%s Exit\n", __func__);
|
||
|
||
}
|
||
|
||
bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, struct sk_buff *pkt)
|
||
{
|
||
return aicwf_frame_enq(dev, q, pkt, 0);
|
||
}
|
||
|
||
|
||
void aicwf_dev_skb_free(struct sk_buff *skb)
|
||
{
|
||
if (!skb)
|
||
return;
|
||
|
||
dev_kfree_skb_any(skb);
|
||
}
|
||
|
||
static struct sk_buff *aicwf_frame_queue_penq(struct frame_queue *pq, int prio, struct sk_buff *p)
|
||
{
|
||
struct sk_buff_head *q;
|
||
|
||
if (pq->queuelist[prio].qlen >= pq->qmax)
|
||
return NULL;
|
||
|
||
q = &pq->queuelist[prio];
|
||
__skb_queue_tail(q, p);
|
||
pq->qcnt++;
|
||
if (pq->hi_prio < prio)
|
||
pq->hi_prio = (u16)prio;
|
||
|
||
return p;
|
||
}
|
||
|
||
void aicwf_frame_queue_flush(struct frame_queue *pq)
|
||
{
|
||
int prio;
|
||
struct sk_buff_head *q;
|
||
struct sk_buff *p, *next;
|
||
|
||
for (prio = 0; prio < pq->num_prio; prio++)
|
||
{
|
||
q = &pq->queuelist[prio];
|
||
skb_queue_walk_safe(q, p, next) {
|
||
skb_unlink(p, q);
|
||
aicwf_dev_skb_free(p);
|
||
pq->qcnt--;
|
||
}
|
||
}
|
||
}
|
||
|
||
void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len)
|
||
{
|
||
int prio;
|
||
|
||
memset(pq, 0, offsetof(struct frame_queue, queuelist) + (sizeof(struct sk_buff_head) * num_prio));
|
||
pq->num_prio = (u16)num_prio;
|
||
pq->qmax = (u16)max_len;
|
||
|
||
for (prio = 0; prio < num_prio; prio++) {
|
||
skb_queue_head_init(&pq->queuelist[prio]);
|
||
}
|
||
}
|
||
|
||
struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, int *prio_out)
|
||
{
|
||
int prio;
|
||
|
||
if (pq->qcnt == 0)
|
||
return NULL;
|
||
|
||
for (prio = 0; prio < pq->hi_prio; prio++)
|
||
if (!skb_queue_empty(&pq->queuelist[prio]))
|
||
break;
|
||
|
||
if (prio_out)
|
||
*prio_out = prio;
|
||
|
||
return skb_peek_tail(&pq->queuelist[prio]);
|
||
}
|
||
|
||
bool aicwf_is_framequeue_empty(struct frame_queue *pq)
|
||
{
|
||
int prio, len = 0;
|
||
|
||
for (prio = 0; prio <= pq->hi_prio; prio++)
|
||
len += pq->queuelist[prio].qlen;
|
||
|
||
if(len > 0)
|
||
return false;
|
||
else
|
||
return true;
|
||
}
|
||
|
||
struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq)
|
||
{
|
||
struct sk_buff_head *q;
|
||
struct sk_buff *p;
|
||
int prio;
|
||
|
||
if (pq->qcnt == 0)
|
||
return NULL;
|
||
|
||
while ((prio = pq->hi_prio) > 0 && skb_queue_empty(&pq->queuelist[prio]))
|
||
pq->hi_prio--;
|
||
|
||
q = &pq->queuelist[prio];
|
||
p = __skb_dequeue(q);
|
||
if (p == NULL)
|
||
return NULL;
|
||
|
||
pq->qcnt--;
|
||
|
||
return p;
|
||
}
|
||
|
||
static struct sk_buff *aicwf_skb_dequeue_tail(struct frame_queue *pq, int prio)
|
||
{
|
||
struct sk_buff_head *q = &pq->queuelist[prio];
|
||
struct sk_buff *p = skb_dequeue_tail(q);
|
||
|
||
if (!p)
|
||
return NULL;
|
||
|
||
pq->qcnt--;
|
||
|
||
return p;
|
||
}
|
||
|
||
bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio)
|
||
{
|
||
struct sk_buff *p = NULL;
|
||
int prio_modified = -1;
|
||
|
||
if (q->queuelist[prio].qlen < q->qmax && q->qcnt < q->qmax) {
|
||
aicwf_frame_queue_penq(q, prio, pkt);
|
||
return true;
|
||
}
|
||
if (q->queuelist[prio].qlen >= q->qmax) {
|
||
prio_modified = prio;
|
||
} else if (q->qcnt >= q->qmax) {
|
||
p = aicwf_frame_queue_peek_tail(q, &prio_modified);
|
||
if (prio_modified > prio)
|
||
return false;
|
||
}
|
||
|
||
if (prio_modified >= 0) {
|
||
if (prio_modified == prio)
|
||
return false;
|
||
|
||
p = aicwf_skb_dequeue_tail(q, prio_modified);
|
||
aicwf_dev_skb_free(p);
|
||
|
||
p = aicwf_frame_queue_penq(q, prio_modified, pkt);
|
||
if (p == NULL)
|
||
txrx_err("failed\n");
|
||
}
|
||
|
||
return p != NULL;
|
||
}
|
||
|
||
#ifdef CONFIG_PREALLOC_RX_SKB
|
||
void rxbuff_free(struct rx_buff *rxbuff)
|
||
{
|
||
kfree(rxbuff->data);
|
||
kfree(rxbuff);
|
||
}
|
||
|
||
struct rx_buff *rxbuff_queue_penq(struct rx_frame_queue *pq, struct rx_buff *p)
|
||
{
|
||
|
||
struct list_head *q;
|
||
if (pq->qcnt >= pq->qmax)
|
||
return NULL;
|
||
|
||
q = &pq->queuelist;
|
||
list_add_tail(&p->queue,q);
|
||
|
||
pq->qcnt++;
|
||
|
||
return p;
|
||
}
|
||
|
||
struct rx_buff *rxbuff_dequeue(struct rx_frame_queue *pq)
|
||
{
|
||
struct rx_buff *p = NULL;
|
||
|
||
if (pq->qcnt == 0) {
|
||
printk("%s %d, rxq is empty\n", __func__, __LINE__);
|
||
return NULL;
|
||
}
|
||
|
||
if(list_empty(&pq->queuelist)) {
|
||
printk("%s %d, rxq is empty\n", __func__, __LINE__);
|
||
return NULL;
|
||
} else {
|
||
p = list_first_entry(&pq->queuelist, struct rx_buff, queue);
|
||
list_del_init(&p->queue);
|
||
pq->qcnt--;
|
||
}
|
||
|
||
return p;
|
||
}
|
||
|
||
bool aicwf_rxbuff_enqueue(struct device *dev, struct rx_frame_queue *rxq, struct rx_buff *pkt)
|
||
{
|
||
// struct rx_buff *p = NULL;
|
||
|
||
if ((rxq == NULL) || (pkt == NULL)) {
|
||
printk("%s %d, rxq or pkt is NULL\n", __func__, __LINE__);
|
||
return false;
|
||
}
|
||
|
||
if (rxq->qcnt < rxq->qmax) {
|
||
if (rxbuff_queue_penq(rxq, pkt)) {
|
||
return true;
|
||
} else {
|
||
printk("%s %d, rxbuff enqueue fail\n", __func__, __LINE__);
|
||
return false;
|
||
}
|
||
} else {
|
||
printk("%s %d, rxq or pkt is full\n", __func__, __LINE__);
|
||
return false;
|
||
}
|
||
}
|
||
#endif
|
||
|