/* * * wlanni.c * * * Description: * WLAN accelerator driver implementation * * Copyright (C) 2010 AVM GmbH - http://www.avm.de/ * */ #define DRV_NAME "WLAN accelerator driver" #define DRV_VERSION "2.1" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pal.h" #include "pal_cppi41.h" #include "puma5_cppi.h" #include "puma5_pp.h" #include #include #include /* VLAN_HLEN */ #define WLANNI_ACC_USE_QUEUE_FOR_TX 0 #define WLANNI_USE_QOS 0 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) #define NEW_NAPI #endif #if !defined(CONFIG_NET_DEBUG_SKBUFF_LEAK) && !defined(skb_track_caller) #define skb_track_caller(skb) do { } while (0) #endif static int debug_txlow_flag = 0; static int debug_txhigh_flag = 0; static int debug_rxlow_flag = 0; static int debug_rxhigh_flag = 0; static int debug_queue_flag = 0; static int debug_dotx_flag = 0; static int testmode = 0; /* * MAGPIE/GMII header * ethhdr (14 bytes) | vlan_ethdr (18 bytes) * ATH header (6 bytes) * HTC header (8 bytes) * ATH DATA header (36 bytes) * * K2USB header * USB header (4 bytes) * HTC header (8 bytes) * ATH DATA header (36 bytes) */ #define WLAN_USB_HDR_LEN 4 #define WLAN_MAGPIE_HDR_LEN (ETH_HLEN + VLAN_HLEN) #define WLAN_ATH_HDR_LEN 6 #define WLAN_HTC_HDR_LEN 8 #define WLAN_ATHDATA_HDR_LEN 36 /* special headers before ethernet header */ #define WLANNI_MAGPIE_L2HLEN (WLAN_MAGPIE_HDR_LEN + WLAN_ATH_HDR_LEN + WLAN_HTC_HDR_LEN + WLAN_ATHDATA_HDR_LEN) #define WLANNI_USB_L2HLEN (WLAN_USB_HDR_LEN + WLAN_HTC_HDR_LEN + WLAN_ATHDATA_HDR_LEN) /* * Related session router definitions * ---------------------------------- */ #ifdef CONFIG_ARM_AVALANCHE_PPD static int wlani_pp_prepare_pid(struct net_device *dev); static int wlani_pp_set_pid_flags(struct net_device *dev, int flags); #endif #define WLAN_RXINT_NUM (AVALANCHE_INTD_BASE_INT + WLAN_ACC_RX_INTV) #define WLAN_TXINT_NUM (AVALANCHE_INTD_BASE_INT + WLAN_ACC_TX_INTV) #define WLAN_TXCMPLINT_NUM (AVALANCHE_INTD_BASE_INT + WLAN_ACC_TXCMPL_INTV) #define WLAN_INTD_HOST_NUM 0 #define CPPI4_BD_LENGTH_FOR_CACHE 64 /* * Management pakets to magpie, should not be dropped, * also they will not be queued normaly, so we reserve * some descriptors for them. */ #define WLANNI_HIGHPRIO_RESERVE 32 /*********/ #define WLAN_ACC_PAGE_NUM_ENTRY (64) #define WLAN_ACC_NUM_PAGE (2) #define WLAN_TX_SERVICE_MAX (WLAN_ACC_PAGE_NUM_ENTRY*WLAN_ACC_NUM_PAGE) #define WLAN_RX_SERVICE_MAX (WLAN_ACC_PAGE_NUM_ENTRY*WLAN_ACC_NUM_PAGE) #define WLAN_ACC_ENTRY_TYPE (PAL_CPPI41_ACC_ENTRY_TYPE_D) /* Byte size of page = number of entries per page * size of each entry */ #define WLAN_ACC_PAGE_BYTE_SZ (WLAN_ACC_PAGE_NUM_ENTRY * ((WLAN_ACC_ENTRY_TYPE + 1) * sizeof(unsigned int*))) /* byte size of list = byte size of page * number of pages * */ #define WLAN_ACC_LIST_BYTE_SZ (WLAN_ACC_PAGE_BYTE_SZ * WLAN_ACC_NUM_PAGE) #define GET_BD_PTR(base, num) ((base) + ((num) * WLAN_BD_SIZE)) #define WLAN_QM_DESC_SIZE_CODE 10 /* ((sizeof(Cppi4HostDesc) - 24)/4) */ /* define to enable copious debugging info */ #undef WLANID_DEBUG #ifdef WLANID_DEBUG /* note: prints function name for you */ # define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) #else # define DPRINTK(fmt, args...) #endif /* max supported frame size */ #define MAX_WLAN_FRAME_SIZE 1700 #define RX_BUF_SIZE MAX_WLAN_FRAME_SIZE #define WLANNI_USB_NQUEUE 3 MODULE_AUTHOR("AVM Gmbh"); MODULE_DESCRIPTION(DRV_NAME); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); /************************************************************************/ /* WLAN HOST Descriptor type definition */ /************************************************************************/ typedef struct wlanid_desc_t { Cppi4HostDesc hw; /* The Hardware Descriptor */ int psi[3]; /* protocol specific information for fw */ struct sk_buff *skb; /* The data pointer virtual address */ } wlanid_desc_t; /************************************************************************/ typedef struct wlanid_emb_desc_t { Cppi4EmbdDesc hw; /* The Hardware Descriptor */ int psi[1]; /* WLAN info */ struct wlanid_emb_desc_t *next; int accelerated; } wlanid_emb_desc_t; typedef struct wlanid_emb_desc_queue_t { wlanid_emb_desc_t *head; wlanid_emb_desc_t *tail; unsigned int count; int stopped; /* statistics */ unsigned int maxcount; unsigned long queuecount; unsigned long requeuecount; } wlanid_emb_desc_queue_t; /************************************************************************/ /* NI driver private data */ /************************************************************************/ struct wlanni_ep_stats { unsigned long tx_bytes; unsigned long tx_packets; unsigned long tx_accelerated_packets; unsigned long tx_normal_packets; unsigned long tx_queued_packets; unsigned long tx_drop; unsigned long tx_return; unsigned long tx_start; }; typedef struct wlanid_private_t { volatile int is_open; PAL_Handle pal_hnd; /* The handle to PAL layer */ /* The Tx accumulator channel and tasklet */ unsigned int tx_acc_chan; PAL_Cppi4AccChHnd tx_acc_hnd; #if WLANNI_ACC_USE_QUEUE_FOR_TX PAL_Cppi4QueueHnd tx_queue; /* 217 */ #else Ptr tx_list_base; Cppi4EmbdDesc **tx_list; #endif struct tasklet_struct tx_tasklet; PAL_Cppi4QueueHnd tx_free_queue_hnd; /* 158 */ /* The Tx Completion channel and tasklet */ unsigned int txcmpl_acc_chan; PAL_Cppi4AccChHnd txcmpl_acc_hnd; Ptr txcmpl_list_base; wlanid_desc_t **txcmpl_list; struct tasklet_struct txcmpl_tasklet; /* WLAN Rx Accumulator info */ PAL_Cppi4AccChHnd rxAcc_chan_hnd; unsigned int rxAcc_chan; wlanid_desc_t **rxAcc_chan_list; Ptr rxAcc_chan_list_base; /* The base address of entire descriptors region */ Ptr bdPoolBase; PAL_Cppi4QueueHnd tx_qos_queue; /* 196 */ PAL_Cppi4QueueHnd tx_qos_free_queue; /* 138 */ Ptr tx_bdpool; /* * Queue to PP Prefetcher */ PAL_Cppi4QueueHnd rx_dma_input_queue; /* 234 via proxy to prefetcher */ /* * Queue to ETH-Port */ PAL_Cppi4QueueHnd eth_output_queue; /* 213 directly to the ETH-Port */ PAL_Cppi4QueueHnd usb_output_queues[WLANNI_USB_NQUEUE]; /* 206, 208, 210 */ /* copies from eth_output_queue or usb_output_queues */ PAL_Cppi4QueueHnd ep_output_queues[WLANNI_ENDPOINT_MAX]; /* * WLAN Proxy channel info */ PAL_Cppi4QueueHnd rxProxy_free_queue_hnd; /* 142 */ PAL_Cppi4QueueHnd rxProxy_queue_hnd; /* 111 */ Ptr rxProxy_bdpool; PAL_Cppi4QueueHnd rxProxy_input_queue; /* 225 */ /* * WLAN RX Proxy */ PAL_Cppi4QueueHnd rx_free_queue_hnd; /* 143 */ /* * Queue to Recycler */ PAL_Cppi4QueueHnd recycler_queue; /* 232 */ struct net_device_stats stats; spinlock_t devlock; unsigned long state; struct net_device *netdev; int ethernet_framing; unsigned l2hlen; wlanni_wlandriver_interface_t *drv; wlanid_emb_desc_queue_t drv_queue[WLANNI_ENDPOINT_MAX]; struct wlanni_ep_stats ep_stats[WLANNI_ENDPOINT_MAX]; int nqueued; unsigned long nnoproxydesc; unsigned long nqueuefull; unsigned long nnotxdecs; unsigned long txcount_high; unsigned long txcount_low; unsigned long txcount_accelerated; #ifdef NEW_NAPI struct napi_struct napi; #endif } wlanid_private_t; /************************************************************************/ static wlanid_private_t *g_wlanid_private; /* Use this MAC address if none is supplied on the command line */ static unsigned char defmac[] = { 0x00, 0x50, 0xF1, 0x80, 0x00, 0x00 }; static int __devinit wlanid_probe(struct device *dev); static int __devexit wlanid_remove(struct device *dev); static int wlanid_open(struct net_device *dev); static int wlanid_start_xmit(struct sk_buff *skb, struct net_device *dev); #ifdef NEW_NAPI static int wlanid_poll(struct napi_struct *napi, int budget); #else static int wlanid_poll(struct net_device *dev, int *budget); #endif static int wlanid_close(struct net_device *dev); static struct net_device_stats *wlanid_get_stats(struct net_device *dev); static void wlanid_set_multicast(struct net_device *dev); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) static irqreturn_t wlanid_tx_interrupt(int irq, void *dev, struct pt_regs *regs); static irqreturn_t wlanid_rx_interrupt(int irq, void *dev, struct pt_regs *regs); static irqreturn_t wlanid_txcmpl_interrupt(int irq, void *dev, struct pt_regs *regs); #else static irqreturn_t wlanid_tx_interrupt(int irq, void *dev); static irqreturn_t wlanid_rx_interrupt(int irq, void *dev); static irqreturn_t wlanid_txcmpl_interrupt(int irq, void *dev); #endif static void wlanid_do_tx_complete(unsigned long data); static int wlanid_close_rx_prep(struct net_device *dev); static int wlanid_close_rx_finish(struct net_device *dev); static int wlanid_close_tx_prep(struct net_device *dev); static int wlanid_close_tx_finish(struct net_device *dev); static void wlanni_purge_queues(wlanid_private_t *priv); /* * TODO: This should not be here. It should be in a board/platform * specific file which is *actually* aware of what devices are * present on board. */ static ssize_t wlanid_show_version(struct device_driver *drv, char *buf) { return sprintf(buf, "%s, version: %s\n", DRV_NAME, DRV_VERSION); } static DRIVER_ATTR(version, S_IRUGO, wlanid_show_version, NULL); /* ======================================================================== */ /* ======== Queue handles ================================================= */ /* ======================================================================== */ #include static inline Uint32 qMgr_of_handle(PAL_Cppi4QueueHnd hnd) { PAL_Cppi4QueueObj *obj = (PAL_Cppi4QueueObj *)hnd; return obj->queue.qMgr; } static inline Uint32 qNum_of_handle(PAL_Cppi4QueueHnd hnd) { PAL_Cppi4QueueObj *obj = (PAL_Cppi4QueueObj *)hnd; return obj->queue.qNum; } static inline int cppi4QueueLen(PAL_Handle pal_hnd, PAL_Cppi4QueueHnd hnd) { PAL_Cppi4QueueObj *obj = (PAL_Cppi4QueueObj *)hnd; int len = 0; (void)PAL_cppi4Control(pal_hnd, PAL_CPPI41_IOCTL_GET_QUEUE_ENTRY_COUNT, &obj->queue, &len); return len; } /* ======================================================================== */ /* ======== Embd descriptors ============================================== */ /* ======================================================================== */ static void embddesc_show_head(char *prefix, Cppi4EmbdDesc *bd) { printk(KERN_DEBUG "%s: EmbdDesc 0x%p\n", prefix, bd); printk(KERN_DEBUG "%s: descInfo 0x%08x (%u slots, %u psw, %u bytes)\n", prefix, bd->descInfo, ((bd->descInfo & CPPI41_EM_DESCINFO_SLOTCNT_MASK) >> CPPI41_EM_DESCINFO_SLOTCNT_SHIFT) + 1, ((bd->descInfo & CPPI41_EM_DESCINFO_PSWSIZE_MASK) >> CPPI41_EM_DESCINFO_PSWSIZE_SHIFT), ((bd->descInfo & CPPI41_EM_DESCINFO_PKTLEN_MASK) >> CPPI41_EM_DESCINFO_PKTLEN_SHIFT)); printk(KERN_DEBUG "%s: tagInfo 0x%08x (src %u/%u/%u, dst 0x%x)\n", prefix, bd->tagInfo, ((bd->tagInfo & CPPI41_EM_TAGINFO_SRCPORT_MASK) >> CPPI41_EM_TAGINFO_SRCPORT_SHIFT), ((bd->tagInfo & CPPI41_EM_TAGINFO_SRCCHN_MASK) >> CPPI41_EM_TAGINFO_SRCCHN_SHIFT), ((bd->tagInfo & CPPI41_EM_TAGINFO_SRCSUBCHN_MASK) >> CPPI41_EM_TAGINFO_SRCSUBCHN_SHIFT), ((bd->tagInfo & CPPI41_EM_TAGINFO_DSTTAG_MASK) >> CPPI41_EM_TAGINFO_DSTTAG_SHIFT)); printk(KERN_DEBUG "%s: pktInfo 0x%08x (error %u, pkttype %d, eop %u, protspec %u, return %s, %s, qmgr %u qnum %u)\n", prefix, bd->pktInfo, ((bd->pktInfo & CPPI41_EM_PKTINFO_PKTERROR_MASK) >> CPPI41_EM_PKTINFO_PKTERROR_SHIFT), ((bd->pktInfo & CPPI41_EM_PKTINFO_PKTTYPE_MASK) >> CPPI41_EM_PKTINFO_PKTTYPE_SHIFT), ((bd->pktInfo & CPPI41_EM_PKTINFO_EOPIDX_MASK) >> CPPI41_EM_PKTINFO_EOPIDX_SHIFT), ((bd->pktInfo & CPPI41_EM_PKTINFO_PROTSPEC_MASK) >> CPPI41_EM_PKTINFO_PROTSPEC_SHIFT), ((bd->pktInfo & CPPI41_EM_PKTINFO_RETPOLICY_MASK) >> CPPI41_EM_PKTINFO_RETPOLICY_SHIFT) ? "unlinked" : "linked", ((bd->pktInfo & CPPI41_EM_PKTINFO_ONCHIP_MASK) >> CPPI41_EM_PKTINFO_ONCHIP_SHIFT) ? "onship" : "offship", ((bd->pktInfo & CPPI41_EM_PKTINFO_RETQMGR_MASK) >> CPPI41_EM_PKTINFO_RETQMGR_SHIFT), ((bd->pktInfo & CPPI41_EM_PKTINFO_RETQ_MASK) >> CPPI41_EM_PKTINFO_RETQ_SHIFT)); } static void embdbuf_show(char *prefix, int slots, Cppi4EmbdBuf buf[]) { int i; for (i=0; i < slots; i++) { printk(KERN_DEBUG "%s: Buf%dInfo 0x%08x (valid %d, bmgr %u, pool %u, len %u)\n", prefix, i, buf[i].BufInfo, ((buf[i].BufInfo & CPPI41_EM_BUF_VALID_MASK) >> CPPI41_EM_BUF_VALID_SHIFT), ((buf[i].BufInfo & CPPI41_EM_BUF_MGR_MASK) >> CPPI41_EM_BUF_MGR_SHIFT), ((buf[i].BufInfo & CPPI41_EM_BUF_POOL_MASK) >> CPPI41_EM_BUF_POOL_SHIFT), ((buf[i].BufInfo & CPPI41_EM_BUF_LEN_MASK) >> CPPI41_EM_BUF_LEN_SHIFT)); printk(KERN_DEBUG "%s: Buf%dPtr 0x%08x\n", prefix, i, buf[i].BufPtr); if (buf[i].BufInfo & CPPI41_EM_BUF_VALID_MASK) { char hex[128]; int j; for (j=0; j < 32; j++) { sprintf(&hex[j*2], "%02x", ((char *)buf[i].BufPtr)[j]); } printk(KERN_DEBUG "%s: Buf%dData %s\n", prefix, i, hex); } } } static void embddesc_show(char *prefix, Cppi4EmbdDesc *bd) { Uint32 *psi; int slots = ((bd->descInfo & CPPI41_EM_DESCINFO_SLOTCNT_MASK) >> CPPI41_EM_DESCINFO_SLOTCNT_SHIFT) + 1; int psw = ((bd->descInfo & CPPI41_EM_DESCINFO_PSWSIZE_MASK) >> CPPI41_EM_DESCINFO_PSWSIZE_SHIFT); size_t size; int i; size = sizeof(Cppi4EmbdDesc) - (sizeof(Cppi4EmbdBuf)*EMSLOTCNT); size += sizeof(Cppi4EmbdBuf) * slots; embddesc_show_head(prefix, bd); embdbuf_show(prefix, slots, bd->Buf); printk(KERN_DEBUG "%s: EPI[0] 0x%08x\n", prefix, bd->EPI[0]); printk(KERN_DEBUG "%s: EPI[1] 0x%08x\n", prefix, bd->EPI[1]); psi = (Uint32 *)(((unsigned char *)bd)+size); for (i=0; i < psw; i++) { printk(KERN_DEBUG "%s: psi[%d] 0x%08x\n", prefix, i, psi[i]); } } static void wlanid_embdesc_show(char *prefix, wlanid_emb_desc_t *bd) { embddesc_show(prefix, (Cppi4EmbdDesc *)&bd->hw); printk(KERN_DEBUG "%s: psi[0] %d\n", prefix, bd->psi[0]); printk(KERN_DEBUG "%s: accelerated %d\n", prefix, bd->accelerated); } /* ======================================================================== */ /* ======== Host descriptors ============================================== */ /* ======================================================================== */ void hostdesc_show(char *prefix, Cppi4HostDesc *bd) { printk(KERN_DEBUG "%s: descInfo 0x%08x (%u psw, %u bytes)\n", prefix, bd->descInfo, ((bd->descInfo & PAL_CPPI4_HOSTDESC_PROT_WORD_CNT_MASK) >> PAL_CPPI4_HOSTDESC_PROT_WORD_CNT_SHIFT), ((bd->descInfo & PAL_CPPI4_HOSTDESC_PKT_LEN_MASK) >> PAL_CPPI4_HOSTDESC_PKT_LEN_SHIFT)); printk(KERN_DEBUG "%s: tagInfo 0x%08x (src %u/%u/%u, dst 0x%x)\n", prefix, bd->tagInfo, ((bd->tagInfo & PAL_CPPI4_HOSTDESC_SRCTAG_PORT_MASK) >> PAL_CPPI4_HOSTDESC_SRCTAG_PORT_SHIFT), ((bd->tagInfo & PAL_CPPI4_HOSTDESC_SRCTAG_CHN_MASK) >> PAL_CPPI4_HOSTDESC_SRCTAG_CHN_SHIFT), ((bd->tagInfo & PAL_CPPI4_HOSTDESC_SRCTAG_SUBCHN_MASK) >> PAL_CPPI4_HOSTDESC_SRCTAG_SUBCHN_SHIFT), ((bd->tagInfo & PAL_CPPI4_HOSTDESC_DSTTAG_MASK) >> PAL_CPPI4_HOSTDESC_DSTTAG_SHIFT)); printk(KERN_DEBUG "%s: pktInfo 0x%08x (error %u, pkttype %d, protspec %u, return %s, %s, qmgr %u qnum %u)\n", prefix, bd->pktInfo, ((bd->pktInfo & PAL_CPPI4_HOSTDESC_PKT_ERR_MASK) >> PAL_CPPI4_HOSTDESC_PKT_ERR_SHIFT), ((bd->pktInfo & PAL_CPPI4_HOSTDESC_PKT_TYPE_MASK) >> PAL_CPPI4_HOSTDESC_PKT_TYPE_SHIFT), ((bd->pktInfo & PAL_CPPI4_HOSTDESC_PKT_PROTSPEC_MASK) >> PAL_CPPI4_HOSTDESC_PKT_PROTSPEC_SHIFT), ((bd->pktInfo & PAL_CPPI4_HOSTDESC_PKT_RETPLCY_MASK) >> PAL_CPPI4_HOSTDESC_PKT_RETPLCY_SHIFT) ? "unlinked" : "linked", ((bd->pktInfo & PAL_CPPI4_HOSTDESC_DESC_LOC_MASK) >> PAL_CPPI4_HOSTDESC_DESC_LOC_SHIFT) ? "onchip" : "offchip", ((bd->pktInfo & PAL_CPPI4_HOSTDESC_PKT_RETQMGR_MASK) >> PAL_CPPI4_HOSTDESC_PKT_RETQMGR_SHIFT), ((bd->pktInfo & PAL_CPPI4_HOSTDESC_PKT_RETQNUM_MASK) >> PAL_CPPI4_HOSTDESC_PKT_RETQNUM_SHIFT)); printk(KERN_DEBUG "%s: buffLen %d\n", prefix, bd->buffLen); printk(KERN_DEBUG "%s: bufPtr 0x%08x\n", prefix, bd->bufPtr); printk(KERN_DEBUG "%s: nextBDPtr %p\n", prefix, bd->nextBDPtr); printk(KERN_DEBUG "%s: orgBuffLen %d\n", prefix, bd->orgBuffLen); printk(KERN_DEBUG "%s: orgBufPtr 0x%08x\n", prefix, bd->orgBufPtr); printk(KERN_DEBUG "%s: netInfoWord0 0x%08x\n", prefix, bd->netInfoWord0); printk(KERN_DEBUG "%s: netInfoWord1 0x%08x\n", prefix, bd->netInfoWord1); } static void wlanid_desc_show(char *prefix, wlanid_desc_t *bd) { hostdesc_show(prefix, &bd->hw); printk(KERN_DEBUG "%s: psi[0] %d\n", prefix, bd->psi[0]); printk(KERN_DEBUG "%s: psi[1] %d\n", prefix, bd->psi[1]); printk(KERN_DEBUG "%s: psi[2] %d\n", prefix, bd->psi[2]); printk(KERN_DEBUG "%s: skb %p\n", prefix, bd->skb); } /* ======================================================================== */ static void embddesc_push_to_queue(PAL_Cppi4QueueHnd qhnd, Cppi4EmbdDesc *bd, void *phybd, unsigned int size) { if (phybd == 0) phybd = (void *)PAL_CPPI4_VIRT_2_PHYS(bd); if (debug_queue_flag) { printk(KERN_DEBUG "WLANNI: push emb bd %p (phy %p) to queue %d/%d\n", bd, phybd, qMgr_of_handle(qhnd), qNum_of_handle(qhnd)); } PAL_CPPI4_CACHE_WRITEBACK((unsigned long) bd, CPPI4_BD_LENGTH_FOR_CACHE); PAL_cppi4QueuePush(qhnd, (Ptr) phybd, WLAN_QM_DESC_SIZE_CODE, size); } static void embddesc_push_to_returnqueue(PAL_Handle pal_hnd, Cppi4EmbdDesc *bd, void *phybd) { /* * return packet to the original free queue */ Cppi4Queue queue; PAL_Cppi4QueueHnd *returnqueue; queue.qMgr = (bd->pktInfo & CPPI41_EM_PKTINFO_RETQMGR_MASK) >> CPPI41_EM_PKTINFO_RETQMGR_SHIFT; queue.qNum = (bd->pktInfo & CPPI41_EM_PKTINFO_RETQ_MASK) >> CPPI41_EM_PKTINFO_RETQ_SHIFT; returnqueue = PAL_cppi4QueueOpen(pal_hnd, queue); PAL_CPPI4_CACHE_WRITEBACK((unsigned long) bd, CPPI4_BD_LENGTH_FOR_CACHE); PAL_cppi4QueuePush(returnqueue, phybd, WLAN_QM_DESC_SIZE_CODE, 0); PAL_cppi4QueueClose(pal_hnd, returnqueue); } static inline void embddesc_set_returnqueue(Cppi4EmbdDesc *bd, int qmgr, int qnum) { bd->pktInfo &= ~(CPPI41_EM_PKTINFO_RETQMGR_MASK|CPPI41_EM_PKTINFO_RETQ_MASK); bd->pktInfo |= (qmgr << CPPI41_EM_PKTINFO_RETQMGR_SHIFT) | (qnum << CPPI41_EM_PKTINFO_RETQ_SHIFT); } static inline void hostdesc_set_returnqueue(Cppi4HostDesc *bd, int qmgr, int qnum) { bd->pktInfo &= ~(PAL_CPPI4_HOSTDESC_PKT_RETQMGR_MASK|PAL_CPPI4_HOSTDESC_PKT_RETQNUM_MASK); bd->pktInfo |= (qmgr << PAL_CPPI4_HOSTDESC_PKT_RETQMGR_SHIFT) | (qnum << PAL_CPPI4_HOSTDESC_PKT_RETQNUM_SHIFT); } /* ======================================================================== */ static inline void embdescqueue_queue_tail(wlanid_private_t *priv, wlanid_emb_desc_t *bd, unsigned char ep) { wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; bd->next = 0; if (queue->tail) { /* at least one element */ queue->tail->next = bd; queue->tail = bd; } else { /* list empty */ queue->tail = queue->head = bd; } queue->count++; priv->nqueued++; if (queue->count > queue->maxcount) queue->maxcount = queue->count; queue->queuecount++; } static inline void embdescqueue_queue_head(wlanid_private_t *priv, wlanid_emb_desc_t *bd, unsigned char ep) { wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; if (queue->head) { /* at least one element */ bd->next = queue->head; queue->head = bd; } else { /* list empty */ bd->next = 0; queue->head = queue->tail = bd; } queue->count++; priv->nqueued++; if (queue->count > queue->maxcount) queue->maxcount = queue->count; queue->requeuecount++; } static inline wlanid_emb_desc_t * embdescqueue_dequeue(wlanid_private_t *priv, unsigned char ep) { wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; wlanid_emb_desc_t *bd = queue->head; if (queue->head) { if (queue->head == queue->tail) { /* only one element */ queue->head = queue->tail = 0; } else { /* at least one element left */ queue->head = queue->head->next; } queue->count--; priv->nqueued--; } return bd; } static inline void embdescqueue_remove(wlanid_private_t *priv, wlanid_emb_desc_queue_t *queue, wlanid_emb_desc_t *prev, wlanid_emb_desc_t *p) { if (p == queue->head) queue->head = p->next; if (p == queue->tail) queue->tail = prev; if (prev) prev->next = p->next; queue->count--; priv->nqueued--; } static inline int embdescqueue_is_empty(wlanid_private_t *priv, unsigned char ep) { wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; return queue->head == 0; } static inline void embdescqueue_start_queue(wlanid_private_t *priv, unsigned char ep) { wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; queue->stopped = 0; } static inline void embdescqueue_stop_queue(wlanid_private_t *priv, unsigned char ep) { wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; queue->stopped = 1; } static inline int embdescqueue_is_stopped(wlanid_private_t *priv, unsigned char ep) { wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; return queue->stopped; } static inline int embdescqueue_len(wlanid_private_t *priv, unsigned char ep) { wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; return queue->count; } struct wlanni_ep_stats *ep_stats(wlanid_private_t *priv, unsigned char ep) { return &priv->ep_stats[ep%WLANNI_ENDPOINT_MAX]; } /* ======================================================================== */ int wlanni_register_wlandriver(wlanni_wlandriver_interface_t *drv) { wlanid_private_t *priv = g_wlanid_private; enum wlanni_wlandriver_type drvtype; unsigned ep; if (priv == 0) panic("wlanni_register_wlandriver: g_wlanid_private not set"); if (priv->drv) { if (priv->drv == drv) { printk(KERN_WARNING "wlanni: double register\n"); return 1; } printk(KERN_ERR "wlanni: other driver always registered\n"); return -1; } if (drv->version == 1) { drvtype = wlanni_wlandriver_type_magpie; priv->ethernet_framing = 1; priv->l2hlen = WLANNI_MAGPIE_L2HLEN; } else { switch (drv->type) { default: case wlanni_wlandriver_type_magpie: drvtype = wlanni_wlandriver_type_magpie; priv->ethernet_framing = 1; priv->l2hlen = WLANNI_MAGPIE_L2HLEN; break; case wlanni_wlandriver_type_usb: drvtype = wlanni_wlandriver_type_usb; priv->ethernet_framing = 0; priv->l2hlen = WLANNI_USB_L2HLEN; break; } } switch (drvtype) { case wlanni_wlandriver_type_magpie: for (ep = 0; ep < WLANNI_ENDPOINT_MAX; ep++) { priv->ep_output_queues[ep] = priv->eth_output_queue; } break; case wlanni_wlandriver_type_usb: for (ep = 0; ep < WLANNI_ENDPOINT_MAX; ep++) { unsigned qoff = (drv->ep2queue_map[ep] % WLANNI_USB_NQUEUE); priv->ep_output_queues[ep] = priv->usb_output_queues[qoff]; } break; } priv->drv = drv; return 0; } EXPORT_SYMBOL(wlanni_register_wlandriver); int wlanni_unregister_wlandriver(wlanni_wlandriver_interface_t *drv) { wlanid_private_t *priv = g_wlanid_private; unsigned ep; if (priv == 0) panic("unregister_wlanni_driver: g_wlanid_private not set"); if (priv->drv == 0) { printk(KERN_WARNING "wlanni: not registered, while unregistering.\n"); return 1; } if (priv->drv != drv) { printk(KERN_ERR "wlanni: other driver registered, while unregistering.\n"); return -1; } if (priv->is_open) wlanni_purge_queues(priv); /* reset for testmode */ priv->ethernet_framing = 1; priv->l2hlen = 0; for (ep = 0; ep < WLANNI_ENDPOINT_MAX; ep++) priv->ep_output_queues[ep] = priv->eth_output_queue; priv->drv = 0; return 0; } EXPORT_SYMBOL(wlanni_unregister_wlandriver); wlanni_wlandriver_interface_t *wlanni_get_wlandriver(void) { wlanid_private_t *priv = g_wlanid_private; return priv ? priv->drv : 0; } EXPORT_SYMBOL(wlanni_get_wlandriver); /* ======================================================================== */ #if defined(CONFIG_ARM_AVALANCHE_PPD) && WLANNI_USE_QOS static int wlanni_select_qos(struct sk_buff *skb) { skb->pp_packet_info.ti_session.cluster = 0; skb->pp_packet_info.ti_session.priority = 3 - (skb->pp_packet_info.ti_session.priority >> 1); return 0; } static TI_PP_QOS_CLST_CFG wlanni_qos_cluster_db; #define MAX_IP_PACKET_SIZE 1514 static int wlanni_setup_qos (struct net_device *dev) { int rc; TI_PP_QOS_QUEUE *qcfg; // // Cluster 0 // // Setup 4 QOS queues, one that gets line speed and then trickles down to the other. // wlanni_qos_cluster_db.qos_q_cnt = 4; // Queue 0 qcfg = &wlanni_qos_cluster_db.qos_q_cfg[0]; qcfg->q_num = WLAN_CPPI4x_QoS_HIGH_TX_QNUM - PAL_CPPI41_QPDSP_FW_Q_BASE; qcfg->flags = 0; qcfg->egr_q = WLAN_CPPI4x_TX_QNUM; qcfg->it_credit = (300*1024*1024)/40000/8; /* / / <8 bits in byte> */ qcfg->max_credit = MAX_IP_PACKET_SIZE * 32; qcfg->congst_thrsh = MAX_IP_PACKET_SIZE * 32; // Queue 1 qcfg = &wlanni_qos_cluster_db.qos_q_cfg[1]; qcfg->q_num = WLAN_CPPI4x_QoS_HIGH_MED_TX_QNUM - PAL_CPPI41_QPDSP_FW_Q_BASE; qcfg->flags = 0; qcfg->egr_q = WLAN_CPPI4x_TX_QNUM; qcfg->it_credit = 0; qcfg->max_credit = MAX_IP_PACKET_SIZE * 32; qcfg->congst_thrsh = MAX_IP_PACKET_SIZE * 32; // Queue 2 qcfg = &wlanni_qos_cluster_db.qos_q_cfg[2]; qcfg->q_num = WLAN_CPPI4x_QoS_LOW_MED_TX_QNUM - PAL_CPPI41_QPDSP_FW_Q_BASE; qcfg->flags = 0; qcfg->egr_q = WLAN_CPPI4x_TX_QNUM; qcfg->it_credit = 0; qcfg->max_credit = MAX_IP_PACKET_SIZE * 32; qcfg->congst_thrsh = MAX_IP_PACKET_SIZE * 64; // Queue 3 qcfg = &wlanni_qos_cluster_db.qos_q_cfg[3]; qcfg->q_num = WLAN_CPPI4x_QoS_LOW_TX_QNUM - PAL_CPPI41_QPDSP_FW_Q_BASE; qcfg->flags = 0; qcfg->egr_q = WLAN_CPPI4x_TX_QNUM; qcfg->it_credit = 0; qcfg->max_credit = MAX_IP_PACKET_SIZE * 32; qcfg->congst_thrsh = MAX_IP_PACKET_SIZE * 300; // Cluster 0 wlanni_qos_cluster_db.global_credit = 0; wlanni_qos_cluster_db.max_global_credit = MAX_IP_PACKET_SIZE * 32; wlanni_qos_cluster_db.egr_congst_thrsh1 = MAX_IP_PACKET_SIZE * 4; wlanni_qos_cluster_db.egr_congst_thrsh2 = MAX_IP_PACKET_SIZE * 28; wlanni_qos_cluster_db.egr_congst_thrsh3 = MAX_IP_PACKET_SIZE * 32; wlanni_qos_cluster_db.egr_congst_thrsh4 = MAX_IP_PACKET_SIZE * 48; rc = ti_ppm_qos_cluster_disable (WLAN_CPPI4x_QoS_CLUSTER_IDX); rc = ti_ppm_qos_cluster_setup (WLAN_CPPI4x_QoS_CLUSTER_IDX, &wlanni_qos_cluster_db); rc = ti_ppm_qos_cluster_enable (WLAN_CPPI4x_QoS_CLUSTER_IDX); dev->vpid_block.qos_cluster[0] = &wlanni_qos_cluster_db; dev->vpid_block.qos_clusters_count = 1; return rc; } static int wlanni_shutdown_qos (struct net_device *dev) { int rc; rc = ti_ppm_qos_cluster_disable (WLAN_CPPI4x_QoS_CLUSTER_IDX); dev->vpid_block.qos_clusters_count = 0; return rc; } #endif /* * TODO: change engress handler to set ti_meta_info to wlan node */ int wlanni_packet_received(struct sk_buff *skb) { wlanid_private_t *priv = g_wlanid_private; wlanid_desc_t *wbd; unsigned int len; if (debug_rxlow_flag) printk(KERN_DEBUG "WLANNI: RX: LOW: start (skb %p)\n", skb); if (priv == 0) panic("wlanni_packet_received: g_wlanid_private not set"); if (priv->is_open == 0) { if (net_ratelimit()) printk(KERN_ERR "wlanni_packet_received: interface %s not up\n", priv->netdev->name); dev_kfree_skb_any(skb); return -1; } if (skb->dev == 0) /* send by cpmac for testing */ skb->dev = priv->netdev; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) if (skb->dev->hard_header) skb_push(skb, skb->data - skb->mac.raw); #else if (skb->dev->hard_header_len) skb_push(skb, skb->data - skb_mac_header(skb)); #endif if (priv->ethernet_framing && skb->len < ETH_ZLEN) len = ETH_ZLEN; else len = skb->len; wbd = (wlanid_desc_t *)PAL_cppi4QueuePop(priv->rx_free_queue_hnd); if (wbd == 0) { priv->stats.rx_dropped++; if (net_ratelimit()) printk(KERN_ERR "WLANNI: RX: LOW: failed to get wbd, skb %p dropped\n", skb); dev_kfree_skb_any(skb); return 0; } wbd = PAL_CPPI4_PHYS_2_VIRT(wbd); PAL_CPPI4_CACHE_INVALIDATE(wbd, CPPI4_BD_LENGTH_FOR_CACHE); if (debug_rxlow_flag) { printk(KERN_DEBUG "WLANNI: RX: LOW: alloc wbd %p (skb %p)\n", wbd, wbd->skb); wlanid_desc_show("WLANNI: RX: LOW", wbd); } if (wbd->skb) dev_kfree_skb_any(wbd->skb); len += 4; /* simulate Ethernet CRC */ wbd->hw.descInfo = (PAL_CPPI4_HOSTDESC_DESC_TYPE_HOST << PAL_CPPI4_HOSTDESC_DESC_TYPE_SHIFT) | (1 << PAL_CPPI4_HOSTDESC_PROT_WORD_CNT_SHIFT) | len; /* Indicate src port, so PP can assign PID */ wbd->hw.tagInfo = (CPPI41_SRCPORT_WLAN << PAL_CPPI4_HOSTDESC_SRCTAG_PORT_SHIFT) | TI_PP_PID_NO_DST_TAG; /* wbd->hw.pktInfo set in wlanid_rx_bd_init */ wbd->hw.netInfoWord0 = (0x40 | PP_WLAN_PID_BASE) << 16; wbd->hw.netInfoWord1 = 0; wbd->hw.bufPtr = PAL_CPPI4_VIRT_2_PHYS((unsigned int) skb->data); wbd->hw.buffLen = len; // wbd->hw.nextBDPtr = 0; wbd->hw.orgBufPtr = wbd->hw.bufPtr; wbd->hw.orgBuffLen = wbd->hw.buffLen; wbd->psi[0] = skb->dev->ifindex; wbd->skb = skb; if (debug_rxlow_flag) { printk(KERN_DEBUG "WLANNI: RX: LOW: send wbd %p to prefetcher (skb %p, dev %s (%d))\n", wbd, wbd->skb, skb->dev->name, wbd->psi[0]); wlanid_desc_show("WLANNI: RX: LOW", wbd); } #ifdef CONFIG_DEBUG_SLAB /* * be sure that "redzone 1" is written back to memory */ PAL_CPPI4_CACHE_WRITEBACK((unsigned long) skb->head, 32); #endif PAL_CPPI4_CACHE_WRITEBACK((unsigned long) skb->data, skb->len); PAL_CPPI4_CACHE_WRITEBACK((unsigned long) wbd, CPPI4_BD_LENGTH_FOR_CACHE); PAL_cppi4QueuePush(priv->rx_dma_input_queue, (Ptr) PAL_CPPI4_VIRT_2_PHYS(wbd), WLAN_QM_DESC_SIZE_CODE, len); return 0; } EXPORT_SYMBOL(wlanni_packet_received); /**************************************************************************/ /*! \fn wlanid_tx_bd_link_skb ************************************************************************** * * \brief Links an skb to a Tx Descriptor * * \param[in] Net Device * \param[in] Descriptor * \param[in] SK buff * \return length of final packet **************************************************************************/ static unsigned int wlanid_tx_bd_link_skb(struct net_device *dev, wlanid_desc_t * bd, struct sk_buff *skb) { wlanid_private_t *priv = netdev_priv(dev); unsigned int len; unsigned int queueNum; if (priv->ethernet_framing && skb->len < ETH_ZLEN) len = ETH_ZLEN; else len = skb->len; bd->hw.descInfo = (PAL_CPPI4_HOSTDESC_DESC_TYPE_HOST << PAL_CPPI4_HOSTDESC_DESC_TYPE_SHIFT) | (1 << PAL_CPPI4_HOSTDESC_PROT_WORD_CNT_SHIFT) | len; bd->hw.bufPtr = PAL_CPPI4_VIRT_2_PHYS((unsigned int) skb->data); bd->hw.buffLen = len; bd->skb = skb; bd->psi[0] = skb->ti_meta_info; /* wlan mode index */ if (dev->vpid_block.qos_clusters_count) queueNum = WLAN_CPPI4x_QoS_HIGH_TX_QNUM; /* + (priority & 0x3); */ else queueNum = WLAN_CPPI4x_TX_QNUM; /* 217 */ /* * Set SYNC Q PTID info in egress descriptor */ if (skb->pp_packet_info.ti_pp_flags == TI_PPM_SESSION_INGRESS_RECORDED) { memcpy(&(bd->hw.netInfoWord0), skb->pp_packet_info.ti_epi_header, 8); bd->hw.netInfoWord1 &= ~(0xFFFF); bd->hw.netInfoWord1 |= queueNum; /* after QPDSP, push to QoS/WLAN Queues */ } else { bd->hw.netInfoWord1 = queueNum; } #ifdef CONFIG_DEBUG_SLAB /* * be sure that "redzone 1" is written back to memory */ PAL_CPPI4_CACHE_WRITEBACK(skb->head, 32); #endif /* * be sure skb_shinfo() is written back to memory */ PAL_CPPI4_CACHE_WRITEBACK(skb_shinfo(skb), sizeof(struct skb_shared_info)); PAL_CPPI4_CACHE_WRITEBACK((unsigned long) skb->data, skb->len); PAL_CPPI4_CACHE_WRITEBACK((unsigned long) bd, CPPI4_BD_LENGTH_FOR_CACHE); return len; } /**************************************************************************/ /*! \fn wlanid_rx_bd_init ************************************************************************** * * \brief INIT RX Descriptor * * \param[in] Pointer to Descriptor * \param[in] Queue Manager * \param[in] Queue Number * \return None **************************************************************************/ static void wlanid_rx_bd_init(wlanid_desc_t * bd, int qmgr, int qnum) { bd->hw.descInfo = PAL_CPPI4_HOSTDESC_DESC_TYPE_HOST << PAL_CPPI4_HOSTDESC_DESC_TYPE_SHIFT; bd->hw.pktInfo = (PAL_CPPI4_HOSTDESC_PKT_TYPE_ETH << PAL_CPPI4_HOSTDESC_PKT_TYPE_SHIFT) | (PAL_CPPI4_HOSTDESC_PKT_RETPLCY_LINKED << PAL_CPPI4_HOSTDESC_PKT_RETPLCY_SHIFT) | (PAL_CPPI4_HOSTDESC_DESC_LOC_OFFCHIP << PAL_CPPI4_HOSTDESC_DESC_LOC_SHIFT) | (qmgr << PAL_CPPI4_HOSTDESC_PKT_RETQMGR_SHIFT) | (qnum << PAL_CPPI4_HOSTDESC_PKT_RETQNUM_SHIFT); } /**************************************************************************/ /*! \fn wlanid_rx_bd_link_skb ************************************************************************** * * \brief Links an skb to an Rx Descriptor * * \param[in] Net Device * \param[in] Descriptor * \param[in] SK buff * \return length of final packet **************************************************************************/ static void wlanid_rx_bd_link_skb(struct net_device *dev, wlanid_desc_t * bd, struct sk_buff *skb) { skb_track_caller(skb); /* * Invalidate the skb data cache: Do it before the data is presented to the * DMA */ #ifdef CONFIG_DEBUG_SLAB /* * be sure that "redzone 1" is written back to memory */ PAL_CPPI4_CACHE_WRITEBACK(skb->head, 32); #endif /* * be sure skb_shinfo() is written back to memory */ PAL_CPPI4_CACHE_WRITEBACK(skb_shinfo(skb), sizeof(struct skb_shared_info)); PAL_CPPI4_CACHE_INVALIDATE(skb->data, RX_BUF_SIZE); skb->dev = dev; skb_reserve(skb, NET_IP_ALIGN); /* 16 byte align the IP fields. */ bd->hw.orgBuffLen = RX_BUF_SIZE - NET_IP_ALIGN; bd->hw.orgBufPtr = PAL_CPPI4_VIRT_2_PHYS(skb->data); bd->skb = skb; bd->psi[0] = 0; /* * Write the BD to the RAM */ PAL_CPPI4_CACHE_WRITEBACK(bd, CPPI4_BD_LENGTH_FOR_CACHE); } /** Tx processing functions start here **/ /**************************************************************************/ /*! \fn wlanid_do_tx_complete ************************************************************************** * * \brief Links an skb to a Rx Descriptor * * \param[in] Net Device * \return None **************************************************************************/ static void wlanid_do_tx_complete(unsigned long data) { struct net_device *dev = (struct net_device *) data; wlanid_private_t *priv = netdev_priv(dev); wlanid_desc_t *bd; int packets_processed = 0; int done = 1; if (debug_txhigh_flag) printk(KERN_DEBUG "WLANNI: TX: HIGH: CMPL: start\n"); while (avalanche_intd_get_interrupt_count (WLAN_INTD_HOST_NUM, WLAN_ACC_TXCMPL_CHNUM)) { while ((bd = (wlanid_desc_t *) ((unsigned long) *priv-> txcmpl_list & QMGR_QUEUE_N_REG_D_DESC_ADDR_MASK))) { bd = PAL_CPPI4_PHYS_2_VIRT(bd); PAL_CPPI4_CACHE_INVALIDATE(bd, CPPI4_BD_LENGTH_FOR_CACHE); priv->stats.tx_packets++; priv->stats.tx_bytes += bd->skb->len; if (debug_txhigh_flag) printk(KERN_DEBUG "WLANNI: TX: HIGH: CMPL: free skb %p for client %d\n", bd->skb, bd->psi[0]); /* * If someone using Host->PP queue without Tx completition */ hostdesc_set_returnqueue(&bd->hw, PPFW_CPPI4x_FDB_QMGR, PPFW_CPPI4x_FDB_QNUM(PAL_CPPI4x_PRTY_LOW)); dev_kfree_skb_any(bd->skb); bd->skb = NULL; PAL_CPPI4_CACHE_WRITEBACK((unsigned long)bd, CPPI4_BD_LENGTH_FOR_CACHE); PAL_cppi4QueuePush(priv->tx_qos_free_queue, (Ptr) PAL_CPPI4_VIRT_2_PHYS(bd), WLAN_QM_DESC_SIZE_CODE, 0); packets_processed++; priv->txcmpl_list++; /* * did enough work, move now.. */ if (!test_bit(0, &priv->state) && (packets_processed == WLAN_TX_SERVICE_MAX)) { done = 0; goto out; } } /* * Update the list entry for next time */ priv->txcmpl_list = PAL_cppi4AccChGetNextList(priv->txcmpl_acc_hnd); avalanche_intd_set_interrupt_count(WLAN_INTD_HOST_NUM, WLAN_ACC_TXCMPL_CHNUM, 1); } out: if (debug_txhigh_flag) printk(KERN_DEBUG "WLANNI: TX: HIGH: CMPL: end (done %d, %d pakets)\n", done, packets_processed); if (done) { avalanche_intd_write_eoi(WLAN_ACC_TXCMPL_INTV); } else { tasklet_schedule(&priv->txcmpl_tasklet); } } static wlanid_emb_desc_t * embddesc_copy_for_tx(wlanid_private_t *priv, Cppi4EmbdDesc *bd, void *phyaddr, int accelerated, int lowprio) { wlanid_emb_desc_t *wbd; int total_len; int eop; if (lowprio) { int nfree = cppi4QueueLen(priv->pal_hnd, priv->tx_free_queue_hnd); if (nfree > WLANNI_HIGHPRIO_RESERVE) { wbd = (wlanid_emb_desc_t *) PAL_cppi4QueuePop(priv->tx_free_queue_hnd); } else { embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, phyaddr, 0); priv->nqueuefull++; return 0; } } else { wbd = (wlanid_emb_desc_t *) PAL_cppi4QueuePop(priv->tx_free_queue_hnd); } if (wbd == 0) { static unsigned long suppressed = 0; embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, phyaddr, 0); priv->nnotxdecs++; if (net_ratelimit()) { if (suppressed) printk(KERN_WARNING "WLANNI: no more tx descriptors (%lu messages suppressed)\n", suppressed); else printk(KERN_WARNING "WLANNI: no more tx descriptors\n"); suppressed = 0; } else { suppressed++; } return 0; } wbd = PAL_CPPI4_PHYS_2_VIRT(wbd); PAL_CPPI4_CACHE_INVALIDATE(wbd, CPPI4_BD_LENGTH_FOR_CACHE); /* overwrite to be sure */ wbd->hw.descInfo = CPPI41_EM_DESCINFO_DTYPE_EMBEDDED | CPPI41_EM_DESCINFO_SLOTCNT_MYCNT | (1 << CPPI41_EM_DESCINFO_PSWSIZE_SHIFT); total_len = (bd->descInfo & CPPI41_EM_DESCINFO_PKTLEN_MASK) >> CPPI41_EM_DESCINFO_PKTLEN_SHIFT; eop = (bd->pktInfo & CPPI41_EM_PKTINFO_EOPIDX_MASK) >> CPPI41_EM_PKTINFO_EOPIDX_SHIFT; wbd->psi[0] = *((int *)(bd+1)); wbd->next = 0; wbd->accelerated = accelerated; wbd->hw.tagInfo = bd->tagInfo; eop = (bd->pktInfo & CPPI41_EM_PKTINFO_EOPIDX_MASK) >> CPPI41_EM_PKTINFO_EOPIDX_SHIFT; wbd->hw.pktInfo = (CPPI41_EM_PKTINFO_PKTTYPE_ETH << CPPI41_EM_PKTINFO_PKTTYPE_SHIFT) | (bd->pktInfo & CPPI41_EM_PKTINFO_PROTSPEC_MASK) | (CPPI41_EM_PKTINFO_RETPOLICY_UNLINKED << CPPI41_EM_PKTINFO_RETPOLICY_SHIFT) | (eop << CPPI41_EM_PKTINFO_EOPIDX_SHIFT) | (CPPI4_EM_PKTINFO_ONCHIP_OFFCHIP << CPPI41_EM_PKTINFO_ONCHIP_SHIFT) | (WLAN_OUT_CPPI4x_FD_QMGR << CPPI41_EM_PKTINFO_RETQMGR_SHIFT) | (WLAN_OUT_CPPI4x_FD_QNUM << CPPI41_EM_PKTINFO_RETQ_SHIFT) ; wbd->hw.EPI[0] = bd->EPI[0]; wbd->hw.EPI[1] = bd->EPI[1]; wbd->hw.Buf[0] = bd->Buf[0]; memset(&bd->Buf[0], 0, sizeof(Cppi4EmbdBuf)); wbd->hw.Buf[1] = bd->Buf[1]; memset(&bd->Buf[1], 0, sizeof(Cppi4EmbdBuf)); wbd->hw.Buf[2] = bd->Buf[2]; memset(&bd->Buf[2], 0, sizeof(Cppi4EmbdBuf)); wbd->hw.Buf[3] = bd->Buf[3]; memset(&bd->Buf[3], 0, sizeof(Cppi4EmbdBuf)); /* * Push descriptor directly back to the return queue * not using the recycler. THis is possible because there * are no buffers in the descriptors. * recycler is too slow for packets from DOCSIS */ embddesc_push_to_returnqueue(priv->pal_hnd, (Cppi4EmbdDesc *)bd, phyaddr); wbd->hw.descInfo &= ~CPPI41_EM_DESCINFO_PKTLEN_MASK; wbd->hw.descInfo |= (total_len << CPPI41_EM_DESCINFO_PKTLEN_SHIFT) & CPPI41_EM_DESCINFO_PKTLEN_MASK; return wbd; } static void setup_tx_packet_info(wlanni_tx_packet_info_t *p, wlanid_emb_desc_t *bd, enum wlanni_tx_reason reason) { unsigned int ti_meta_data; int total_len; Uint32 buf0len; ti_meta_data = bd->psi[0]; total_len = (bd->hw.descInfo & CPPI41_EM_DESCINFO_PKTLEN_MASK) >> CPPI41_EM_DESCINFO_PKTLEN_SHIFT; buf0len = (bd->hw.Buf[0].BufInfo & CPPI41_EM_BUF_LEN_MASK) >> CPPI41_EM_BUF_LEN_SHIFT; p->version = WLANNI_TX_PACKET_INFO_VERSION; p->pktinfo = ti_meta_data; p->pktlen = total_len; p->hdr = (unsigned char *)bd->hw.Buf[0].BufPtr; p->hlen = buf0len; p->accelerated = bd->accelerated; p->reason = reason; if (reason == wlanni_tx_reason_firsttime) PAL_CPPI4_CACHE_INVALIDATE(p->hdr, p->hlen); } static void wlanni_check_queue(wlanid_private_t *priv, unsigned ep) { wlanni_tx_packet_info_t txinfo; wlanid_emb_desc_t *bd; int rc; if (embdescqueue_is_stopped(priv, ep)) return; while ((bd = embdescqueue_dequeue(priv, ep)) != 0) { setup_tx_packet_info(&txinfo, bd, wlanni_tx_reason_fromqueue); if (unlikely(priv->drv == 0)) { if (testmode) { embddesc_push_to_queue(priv->eth_output_queue, (Cppi4EmbdDesc *)bd, 0, txinfo.pktlen); ep_stats(priv, 0)->tx_packets++; ep_stats(priv, 0)->tx_bytes += txinfo.pktlen; ep_stats(priv, 0)->tx_queued_packets++; if (txinfo.accelerated) ep_stats(priv, 0)->tx_accelerated_packets++; else ep_stats(priv, 0)->tx_normal_packets++; } else { embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, 0, 0); } continue; } rc = (*priv->drv->may_transmit)(&txinfo); if (unlikely(rc < 0)) { /* error, drop packet */ embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, 0, 0); ep_stats(priv, ep)->tx_drop++; continue; } if (unlikely(rc > 0)) { /* return packet to WLAN driver for queueing */ bd->hw.tagInfo = TI_PP_PID_NO_DST_TAG; embddesc_push_to_queue(priv->rxProxy_input_queue, (Cppi4EmbdDesc *)bd, 0, txinfo.pktlen); ep_stats(priv, ep)->tx_return++; continue; } rc = (*priv->drv->prepare_transmit)(&txinfo); if (rc == 0) { /* send now */ PAL_CPPI4_CACHE_WRITEBACK((unsigned long)txinfo.hdr, txinfo.hlen); embddesc_push_to_queue(priv->ep_output_queues[ep], (Cppi4EmbdDesc *)bd, 0, txinfo.pktlen); ep_stats(priv, ep)->tx_packets++; ep_stats(priv, ep)->tx_bytes += txinfo.pktlen; if (txinfo.accelerated) ep_stats(priv, ep)->tx_accelerated_packets++; else ep_stats(priv, ep)->tx_normal_packets++; continue; } if (rc > 0) { /* requeue packet */ embdescqueue_queue_head(priv, bd, ep); embdescqueue_stop_queue(priv, ep); return; } /* error, drop packet */ embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, 0, 0); ep_stats(priv, ep)->tx_drop++; } } static void wlanni_check_queues(wlanid_private_t *priv) { unsigned ep; for (ep = 0; ep < WLANNI_ENDPOINT_MAX; ep++) wlanni_check_queue(priv, ep); } static void wlanni_purge_queues(wlanid_private_t *priv) { wlanid_emb_desc_t *bd; unsigned ep; for (ep = 0; ep < WLANNI_ENDPOINT_MAX; ep++) { embdescqueue_start_queue(priv, ep); while ((bd = embdescqueue_dequeue(priv, ep)) != 0) { embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, 0, 0); ep_stats(priv, ep)->tx_drop++; } } } void wlanni_start_transmit_for_ep(unsigned ep) { wlanid_private_t *priv = g_wlanid_private; int stopped = embdescqueue_is_stopped(priv, ep); embdescqueue_start_queue(priv, ep); if (stopped) { if (debug_dotx_flag) printk(KERN_DEBUG "WLANNI: TX: LOW: wakeup ep %u\n", ep); ep_stats(priv, ep)->tx_start++; tasklet_schedule(&priv->tx_tasklet); } } EXPORT_SYMBOL(wlanni_start_transmit_for_ep); /* * return number of descriptors in hardware tx queue * or < 0 if no driver is registered */ int wlanni_tx_hw_queue_len_for_ep(unsigned ep) { wlanid_private_t *priv = g_wlanid_private; PAL_Cppi4QueueHnd ep_output_queue; if ((ep_output_queue = priv->ep_output_queues[ep%WLANNI_ENDPOINT_MAX])) return cppi4QueueLen(priv->pal_hnd, ep_output_queue); return 0; } EXPORT_SYMBOL(wlanni_tx_hw_queue_len_for_ep); /* * return number of descriptors in ep (software) tx queue * or < 0 if no driver is registered */ int wlanni_tx_sw_queue_len_for_ep(unsigned ep) { wlanid_private_t *priv = g_wlanid_private; return embdescqueue_len(priv, ep); } EXPORT_SYMBOL(wlanni_tx_sw_queue_len_for_ep); /* * interate sw queue and call function for each element. * if function returns: * 0 - keep packet in queue * >= 1 - return packet to WLAN driver for queueing there * with callback "packet_return" * <= -1 - Error, drop packet */ int wlanni_tx_sw_queue_iterate(unsigned ep, u32 pktinfo, int (*walkfunc)(wlanni_tx_packet_info_t *p, u32 pktinfo)) { wlanid_private_t *priv = g_wlanid_private; wlanid_emb_desc_queue_t *queue = &priv->drv_queue[ep%WLANNI_ENDPOINT_MAX]; wlanid_emb_desc_t *bd; wlanid_emb_desc_t *next; wlanid_emb_desc_t *prev = 0; wlanni_tx_packet_info_t txinfo; int count = 0; int rc; for (bd = queue->head; bd; bd = next) { next = bd->next; setup_tx_packet_info(&txinfo, bd, wlanni_tx_reason_checkqueue); /* just pass through pktinfo (never ever read from or write to it!!!) */ rc = (*walkfunc)(&txinfo, pktinfo); if (unlikely(rc < 0)) { /* error, drop packet */ embdescqueue_remove(priv, queue, prev, bd); embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, 0, 0); ep_stats(priv, ep)->tx_drop++; continue; } if (unlikely(rc > 0)) { /* return packet to WLAN driver for queueing */ embdescqueue_remove(priv, queue, prev, bd); count++; bd->hw.tagInfo = TI_PP_PID_NO_DST_TAG; embddesc_push_to_queue(priv->rxProxy_input_queue, (Cppi4EmbdDesc *)bd, 0, txinfo.pktlen); ep_stats(priv, ep)->tx_return++; continue; } prev = bd; } return count; } EXPORT_SYMBOL(wlanni_tx_sw_queue_iterate); /**************************************************************************/ /*! \fn wlanid_do_tx ************************************************************************** * * \param[in] Net Device * \return None **************************************************************************/ static void wlanid_do_tx(unsigned long data) { struct net_device *dev = (struct net_device *) data; wlanid_private_t *priv = netdev_priv(dev); wlanni_tx_packet_info_t txinfo; wlanid_emb_desc_t *bd; void *phybd; int packets_processed = 0; int packets_queued = 0; int done = 1; int nirqs = 0; if (debug_txlow_flag) printk(KERN_DEBUG "WLANNI: TX: LOW: start\n"); wlanni_check_queues(priv); while (avalanche_intd_get_interrupt_count (WLAN_INTD_HOST_NUM, WLAN_ACC_TX_CHNUM)) { nirqs++; #if WLANNI_ACC_USE_QUEUE_FOR_TX while ((phybd = (void *)PAL_cppi4QueuePop(priv->tx_queue))) #else while ((phybd = (void *) ((unsigned long) *priv-> tx_list & QMGR_QUEUE_N_REG_D_DESC_ADDR_MASK))) #endif { Cppi4EmbdDesc *rbd; int accelerated = 0; u32 pktinfo; unsigned ep; int lowprio; int rc; if ((rbd = (Cppi4EmbdDesc *)IO_PHY2VIRT((Uint32)phybd)) == 0) { rbd = PAL_CPPI4_PHYS_2_VIRT(phybd); priv->txcount_low++; } else { priv->txcount_accelerated++; accelerated = 1; } PAL_CPPI4_CACHE_INVALIDATE(rbd, CPPI4_BD_LENGTH_FOR_CACHE); packets_processed++; #if !WLANNI_ACC_USE_QUEUE_FOR_TX priv->tx_list++; #endif if (debug_txlow_flag) { printk(KERN_DEBUG "WLANNI: TX: LOW: = 1 =========%s\n", accelerated ? " accelerated" : "============"); embddesc_show("WLANNI: TX: LOW", rbd); } pktinfo = *((int *)(rbd+1)); ep = wlanni_pktinfo_get_ep(pktinfo); lowprio = !wlanni_pktinfo_get_highprio(pktinfo); bd = embddesc_copy_for_tx(priv, rbd, phybd, accelerated, lowprio); if (bd == 0) { embdescqueue_start_queue(priv, ep); wlanni_check_queue(priv, ep); goto check_next; } if (debug_txlow_flag) { printk(KERN_DEBUG "WLANNI: TX: LOW: = 2 =====================\n"); wlanid_embdesc_show("WLANNI: TX: LOW", bd); } setup_tx_packet_info(&txinfo, bd, wlanni_tx_reason_firsttime); ep = wlanni_pktinfo_get_ep(txinfo.pktinfo); if (unlikely(priv->drv == 0)) { if (testmode) { embddesc_push_to_queue(priv->eth_output_queue, (Cppi4EmbdDesc *)bd, 0, txinfo.pktlen); ep_stats(priv, 0)->tx_packets++; ep_stats(priv, 0)->tx_bytes += txinfo.pktlen; if (txinfo.accelerated) ep_stats(priv, 0)->tx_accelerated_packets++; else ep_stats(priv, 0)->tx_normal_packets++; } else { embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, 0, 0); } } else { rc = (*priv->drv->may_transmit)(&txinfo); if (unlikely(rc < 0)) { /* error, drop packet */ embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, 0, 0); ep_stats(priv, ep)->tx_drop++; } else if (unlikely(rc > 0)) { /* return packet to WLAN driver for queueing */ bd->hw.tagInfo = TI_PP_PID_NO_DST_TAG; embddesc_push_to_queue(priv->rxProxy_input_queue, (Cppi4EmbdDesc *)bd, 0, txinfo.pktlen); ep_stats(priv, ep)->tx_return++; } else { /* packet may be transmited. */ if (embdescqueue_is_empty(priv, ep)) { /* try to send now */ rc = (*priv->drv->prepare_transmit)(&txinfo); if (rc == 0) { /* send now */ PAL_CPPI4_CACHE_WRITEBACK((unsigned long)txinfo.hdr, txinfo.hlen); embddesc_push_to_queue(priv->ep_output_queues[ep], (Cppi4EmbdDesc *)bd, 0, txinfo.pktlen); ep_stats(priv, ep)->tx_packets++; ep_stats(priv, ep)->tx_bytes += txinfo.pktlen; if (txinfo.accelerated) ep_stats(priv, ep)->tx_accelerated_packets++; else ep_stats(priv, ep)->tx_normal_packets++; } else if (rc > 0) { /* queue packet for endpoint */ embdescqueue_queue_tail(priv, bd, ep); embdescqueue_start_queue(priv, ep); packets_queued++; } else { /* error, drop packet */ embddesc_push_to_queue(priv->recycler_queue, (Cppi4EmbdDesc *)bd, 0, 0); ep_stats(priv, ep)->tx_drop++; } } else { /* queue packet for endpoint */ embdescqueue_queue_tail(priv, bd, ep); embdescqueue_start_queue(priv, ep); packets_queued++; } } } check_next: /* * did enough work, move now.. */ if (!test_bit(0, &priv->state) && (packets_processed == WLAN_TX_SERVICE_MAX)) { done = 0; goto out; } } #if !WLANNI_ACC_USE_QUEUE_FOR_TX /* * Update the list entry for next time */ priv->tx_list = PAL_cppi4AccChGetNextList(priv->tx_acc_hnd); #endif avalanche_intd_set_interrupt_count(WLAN_INTD_HOST_NUM, WLAN_ACC_TX_CHNUM, 1); } out: if (debug_txlow_flag || debug_dotx_flag) { printk(KERN_DEBUG "WLANNI: TX: LOW: end (done %d, %d pakets, %d queued, irqs %d)\n", done, packets_processed, packets_queued, nirqs); } wlanni_check_queues(priv); if (nirqs) { if (done) { avalanche_intd_write_eoi(WLAN_ACC_TX_INTV); } else { tasklet_schedule(&priv->tx_tasklet); } } } /**************************************************************************/ /*! \fn wlanid_start_xmit ************************************************************************** * * \brief Transmit Function * * \param[in] SK buff * \param[in] Net Device * \return OK or error **************************************************************************/ static int wlanid_start_xmit(struct sk_buff *skb, struct net_device *dev) { wlanid_private_t *priv = netdev_priv(dev); wlanid_desc_t *bd; unsigned int len; if (debug_txhigh_flag) printk(KERN_DEBUG "WLANNI: TX: HIGH: start (skb %p)\n", skb); /* * get a free Tx descriptor */ if (!(bd = (wlanid_desc_t *) PAL_cppi4QueuePop(priv->tx_qos_free_queue))) { /* * This should not occur in this driver (because of what is done later * in this function) */ priv->nnoproxydesc++; return NETDEV_TX_BUSY; } bd = PAL_CPPI4_PHYS_2_VIRT(bd); PAL_CPPI4_CACHE_INVALIDATE(bd, CPPI4_BD_LENGTH_FOR_CACHE); hostdesc_set_returnqueue(&bd->hw, WLAN_CPPI4x_TX_COMP_QMGR, WLAN_CPPI4x_TX_COMP_QNUM); len = wlanid_tx_bd_link_skb(dev, bd, skb); dev->trans_start = jiffies; if (debug_txhigh_flag) { printk(KERN_DEBUG "WLANNI: TX: HIGH: push to QOS %d/%d (skb %p, ti_meta_info %u)\n", qMgr_of_handle(priv->tx_qos_queue), qNum_of_handle(priv->tx_qos_queue), skb, skb->ti_meta_info); wlanid_desc_show("WLANNI: TX: HIGH", bd); } priv->txcount_high++; /* * Push to QPDSP Q - then after handle PTID it will be send to the WLAN Tx * queues */ PAL_cppi4QueuePush(priv->tx_qos_queue, (Uint32 *) PAL_CPPI4_VIRT_2_PHYS(bd), WLAN_QM_DESC_SIZE_CODE, len); return NETDEV_TX_OK; } int wlanni_packet_send(struct sk_buff *skb, u32 pktinfo) { wlanid_private_t *priv = g_wlanid_private; if (priv == 0) panic("wlanni_packet_send: g_wlanid_private not set"); if (priv->is_open == 0) { if (net_ratelimit()) printk(KERN_ERR "wlanni_packet_send: interface %s not up\n", priv->netdev->name); dev_kfree_skb_any(skb); return -1; } if (wlanni_pktinfo_get_l2hlen(pktinfo) == 0) wlanni_pktinfo_set_l2hlen(&pktinfo, priv->l2hlen); skb->ti_meta_info = pktinfo; skb->dev = priv->netdev; dev_queue_xmit(skb); return 0; } EXPORT_SYMBOL(wlanni_packet_send); /**************************************************************************/ /*! \fn wlanid_txcompl_interrupt ************************************************************************** * * \brief Transmit Complete ISR * * \param[in] IRQ * \param[in] Device * \param[in] regs * \return OK or error **************************************************************************/ static irqreturn_t wlanid_txcmpl_interrupt(int irq, void *dev #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) , struct pt_regs *regs #endif ) { wlanid_private_t *priv = netdev_priv((struct net_device *) dev); if (debug_txhigh_flag) printk(KERN_DEBUG "WLANNI: TXCMPL: got irq\n"); tasklet_schedule(&priv->txcmpl_tasklet); return IRQ_RETVAL(1); } /**************************************************************************/ /*! \fn wlanid_tx_interrupt ************************************************************************** * * \brief Transmit ISR * * \param[in] IRQ * \param[in] Device * \param[in] regs * \return OK or error **************************************************************************/ static irqreturn_t wlanid_tx_interrupt(int irq, void *dev #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) , struct pt_regs *regs #endif ) { wlanid_private_t *priv = netdev_priv((struct net_device *) dev); if (debug_txlow_flag) printk(KERN_DEBUG "WLANNI: TX: LOW: got irq\n"); tasklet_schedule(&priv->tx_tasklet); return IRQ_RETVAL(1); } /** Rx processing functions go here **/ /**************************************************************************/ /*! \fn wlanid_do_rx_complete ************************************************************************** * * \brief Rx Complete handler * * \param[in] Net Device * \param[in] Processed packets budget * \return Number of processed packets **************************************************************************/ static int wlanid_do_rx_complete(struct net_device *dev, int budget) { wlanid_private_t *priv = netdev_priv(dev); wlanid_desc_t *bd; int packets_processed = 0; if (debug_rxhigh_flag) printk(KERN_DEBUG "WLANNI: RX: HIGH: budget %d\n", budget); while (avalanche_intd_get_interrupt_count (WLAN_INTD_HOST_NUM, priv->rxAcc_chan)) { while ((bd = (wlanid_desc_t *) ((unsigned long) *priv-> rxAcc_chan_list & QMGR_QUEUE_N_REG_D_DESC_ADDR_MASK))) { struct sk_buff *newskb; int is_rx; bd = PAL_CPPI4_PHYS_2_VIRT(bd); PAL_CPPI4_CACHE_INVALIDATE(bd, CPPI4_BD_LENGTH_FOR_CACHE); is_rx = ((bd->hw.tagInfo & PAL_CPPI4_HOSTDESC_SRCTAG_PORT_MASK) >> PAL_CPPI4_HOSTDESC_SRCTAG_PORT_SHIFT) == CPPI41_SRCPORT_WLAN; if (debug_rxhigh_flag) { printk(KERN_DEBUG "WLANNI: RX: HIGH: rx bd %p (skb %p, dev %d) %s\n", bd, bd->skb, bd->psi[0], is_rx ? "receive" : "powersave"); wlanid_desc_show("WLANNI: RX: HIGH", bd); } /* * get a new skb for this bd */ if (!test_bit(0, &priv->state)) { /* if not cleaning up .. */ if ((newskb = dev_alloc_skb(RX_BUF_SIZE))) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) struct net_device *dev = __dev_get_by_index(bd->psi[0]); #else struct net_device *dev = __dev_get_by_index(&init_net, bd->psi[0]); #endif /* .. and able to get a new buffer */ struct sk_buff *xskb; xskb = bd->skb; if (is_rx) { skb_put(xskb, bd->hw.buffLen - 4); /* remove simulated CRC */ dev->last_rx = jiffies; priv->stats.rx_packets++; priv->stats.rx_bytes += bd->hw.buffLen; /* * Keep SYNC Q PTID info in skb for egress */ if (bd->hw.netInfoWord1) { memcpy(xskb->pp_packet_info.ti_epi_header, &(bd->hw.netInfoWord0), 8); xskb->pp_packet_info.ti_pp_flags = TI_PPM_SESSION_INGRESS_RECORDED; } /* * ... then send the packet up */ xskb->dev = dev; xskb->pkt_type = PACKET_HOST; xskb->protocol = eth_type_trans(xskb, dev); if (debug_rxhigh_flag) printk(KERN_DEBUG "WLANNI: RX: HIGH: send to host (skb %p, dev %s (%d))\n", xskb, dev->name, bd->psi[0]); if (dev == priv->netdev) { /* paket will be received at wlanni0 ... */ if (in_irq()) netif_rx(xskb); else netif_receive_skb(xskb); } else { extern int ti_protocol_handler (struct net_device* dev, struct sk_buff *skb); xskb->dev = priv->netdev; if (ti_protocol_handler(priv->netdev, xskb) == 0) { xskb->dev = dev; /* paket will be received at ath0, guest, sta0 ... */ if (in_irq()) netif_rx(xskb); else netif_receive_skb(xskb); } } } else { if (debug_txhigh_flag) printk(KERN_DEBUG "WLANNI: TX: HIGH: return skb %p\n", xskb); if (priv->drv && priv->drv->packet_return) { u32 pktinfo = bd->psi[0]; skb_put(xskb, bd->hw.buffLen); if ((*priv->drv->packet_return)(xskb, pktinfo) != 0) dev_kfree_skb_any(xskb); } else { dev_kfree_skb_any(xskb); } } /* * Prepare to return to free Proxy queue */ wlanid_rx_bd_link_skb(dev, bd, newskb); } else { priv->stats.rx_dropped++; if (net_ratelimit()) printk(KERN_ERR "WLANNI: RX: HIGH: failed to get skb for bd, dropped\n"); } } packets_processed++; priv->rxAcc_chan_list++; /* * Return to free queue */ PAL_cppi4QueuePush(priv->rxProxy_free_queue_hnd, (Uint32 *) PAL_CPPI4_VIRT_2_PHYS(bd), WLAN_QM_DESC_SIZE_CODE, 0); /* * thats it, we did enough. Jump out now! */ if (budget == packets_processed) goto out; } /* * Update the list entry for next time */ priv->rxAcc_chan_list = PAL_cppi4AccChGetNextList(priv->rxAcc_chan_hnd); avalanche_intd_set_interrupt_count(WLAN_INTD_HOST_NUM, priv->rxAcc_chan, 1); } out: return packets_processed; } /**************************************************************************/ /*! \fn wlanid_poll ************************************************************************** * * \brief Polling function * * \param[in] Net Device * \param[in] Processed packets budget * \return Number of processed packets **************************************************************************/ #ifdef NEW_NAPI static int wlanid_poll(struct napi_struct *napi, int budget) { wlanid_private_t *priv = container_of(napi, wlanid_private_t, napi); struct net_device *dev = priv->netdev; int work_done; if (debug_rxhigh_flag) printk(KERN_DEBUG "WLANNI: RX: HIGH: poll\n"); work_done = wlanid_do_rx_complete(dev, budget); if (work_done < budget) { netif_rx_complete(dev, &priv->napi); avalanche_intd_write_eoi(WLAN_ACC_RX_INTV); } /* * poll should return 0 if finished, 1 if not */ return work_done; } #else static int wlanid_poll(struct net_device *dev, int *budget) { int orig_budget = min(*budget, dev->quota); int done = 1; int work_done; if (debug_rxhigh_flag) printk(KERN_DEBUG "WLANNI: RX: HIGH: poll\n"); work_done = wlanid_do_rx_complete(dev, orig_budget); if (likely(work_done > 0)) { *budget -= work_done; dev->quota -= work_done; done = (work_done < orig_budget); /* "done" would be zero if we hit * budget */ } /* * order is important here. If we do EOI before calling netif_rx_complete, * an interrupt can occur just before we take ourselves out of the poll * list; we will not schedule NAPI thread on that interrupt, no further Rx * interrupts and Rx will stall forever. Scary... */ if (done) { netif_rx_complete(dev); avalanche_intd_write_eoi(WLAN_ACC_RX_INTV); } return !done; /* poll should return 0 if finished, 1 if not */ } #endif /**************************************************************************/ /*! \fn wlanid_rx_interrupt ************************************************************************** * * \brief Receive ISR * * \param[in] IRQ * \param[in] Device * \param[in] regs * \return OK or error **************************************************************************/ static irqreturn_t wlanid_rx_interrupt(int irq, void *dev_instance #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) , struct pt_regs *regs #endif ) { struct net_device *dev = (struct net_device *) dev_instance; if (debug_rxhigh_flag) printk(KERN_DEBUG "WLANNI: RX: HIGH: got irq\n"); #ifdef NEW_NAPI { wlanid_private_t *priv = netdev_priv(dev); netif_rx_schedule(dev, &priv->napi); } #else /* * if poll routine is not running, start it now. */ netif_rx_schedule(dev); #endif return IRQ_RETVAL(1); } /** Driver init/open/close functions go here **/ /**************************************************************************/ /*! \fn wlanid_free_rxpool ************************************************************************** * * \brief Rx Pool FREE function * Note this will not rewind the queue pushes * (not needed as queues are reset at start) * * \param[in] Net Device * \param[in] Pool Base Address * \param[in] SK buffers number * \return None **************************************************************************/ static void wlanid_free_rxpool(struct net_device *dev, Ptr pool_base, int num_skb) { wlanid_desc_t *desc; int j; if (pool_base) { for (j = 0; j < num_skb; j++) { desc = GET_BD_PTR(pool_base, j); dev_kfree_skb_any(desc->skb); } } } /**************************************************************************/ /*! \fn wlanid_init_rxpool ************************************************************************** * * \brief Allocate and initialize Rx BD pool * * \param[in] Net Device * \param[in] Queue Handle * \param[in] Queue Manager * \param[in] Descriptors number * \param[in] Queue Number * \return Pool Address **************************************************************************/ static int wlanid_init_rxpool(struct net_device *dev) { wlanid_private_t *priv = netdev_priv(dev); Ptr ptr; int i = 0; /* * Allocate BD pools.. */ ptr = (Ptr) (unsigned int) priv->bdPoolBase; /* * .. and prepare the BDs... */ for (i = 0; i < WLAN_RXPROXY_HOST_BD_NUM+WLAN_RX_HOST_BD_NUM; i++) { wlanid_desc_t *bd = GET_BD_PTR(ptr, i); PAL_osMemSet(bd, 0, WLAN_BD_SIZE); if (i < WLAN_RXPROXY_HOST_BD_NUM) { struct sk_buff *skb = dev_alloc_skb(RX_BUF_SIZE); if (!skb) { DPRINTK(" Unable to allocate %dth Rx buffers\n", i); goto rewind; } wlanid_rx_bd_init(bd, WLAN_CPPI4x_FBD_QMGR, WLAN_CPPI4x_FBD_QNUM); wlanid_rx_bd_link_skb(dev, bd, skb); PAL_cppi4QueuePush(priv->rxProxy_free_queue_hnd, (Ptr) PAL_CPPI4_VIRT_2_PHYS(bd), WLAN_QM_DESC_SIZE_CODE, 0); } else { wlanid_rx_bd_init(bd, WLAN_RX_CPPI4x_FBD_QMGR, WLAN_RX_CPPI4x_FBD_QNUM); PAL_cppi4QueuePush(priv->rx_free_queue_hnd, (Ptr) PAL_CPPI4_VIRT_2_PHYS(bd), WLAN_QM_DESC_SIZE_CODE, 0); } } priv->rxProxy_bdpool = ptr; return 0; rewind: wlanid_free_rxpool(dev, ptr, i); return -1; } /************************************************************************/ /* */ /* Interrupt Accumulator Channels INIT routine */ /* */ /************************************************************************/ static int wlanid_init_acc_chan(PAL_Handle pal_hnd, int chan_num, Cppi4Queue queue, PAL_Cppi4AccChHnd * acc_hnd) { Cppi4AccumulatorCfg cfg; *acc_hnd = NULL; #if WLANNI_ACC_USE_QUEUE_FOR_TX if (chan_num == WLAN_ACC_TX_CHNUM) { cfg.accChanNum = chan_num; cfg.pacingTickCnt = 160; /* Wait for 40*25uS == 1000uS == 1ms */ cfg.monitor.pacingMode = 1; cfg.monitor.pktCountThresh = 1; cfg.queue = queue; cfg.mode = 1; if (!(*acc_hnd = PAL_cppi4AccChOpen(pal_hnd, &cfg))) { DPRINTK(" Unable to open accumulator channel #%d\n", chan_num); return -1; } return 0; } #endif cfg.accChanNum = chan_num; cfg.list.maxPageEntry = WLAN_ACC_PAGE_NUM_ENTRY; /* This is entries per page * (and we have 2 pages) */ cfg.list.listEntrySize = WLAN_ACC_ENTRY_TYPE; /* Only interested in register * 'D' which has the desc * pointer */ cfg.list.listCountMode = 0; /* Zero indicates null terminated list. */ cfg.list.pacingMode = 1; /* Wait for time since last interrupt */ cfg.pacingTickCnt = 40; /* Wait for 1000uS == 1ms */ cfg.list.stallAvoidance = 1; /* Use the stall avoidance feature */ cfg.list.maxPageCnt = WLAN_ACC_NUM_PAGE; /* Use two pages */ cfg.queue = queue; cfg.mode = 0; if (chan_num == WLAN_ACC_TX_CHNUM) { cfg.list.pacingMode = 2; /* Wait for time since first packet */ cfg.pacingTickCnt = 40; /* Wait for 1000uS == 1ms */ } /* * kmalloc returns cache line aligned memory unless you are debugging the * slab allocator (2.6.18) */ if (!(cfg.list.listBase = kzalloc(WLAN_ACC_LIST_BYTE_SZ, GFP_KERNEL))) { DPRINTK(" Unable to allocate list page\n"); return -1; } PAL_CPPI4_CACHE_WRITEBACK((unsigned long) cfg.list.listBase, WLAN_ACC_LIST_BYTE_SZ); cfg.list.listBase = (Ptr) PAL_CPPI4_VIRT_2_PHYS((Ptr) cfg.list.listBase); if (!(*acc_hnd = PAL_cppi4AccChOpen(pal_hnd, &cfg))) { DPRINTK(" Unable to open accumulator channel #%d\n", chan_num); kfree(cfg.list.listBase); return -1; } return 0; } /**************************************************************************/ /*! \fn wlanid_open_tx ************************************************************************** * * \brief Open Tx routine * * \param[in] Net Device * \return OK or error **************************************************************************/ static int wlanid_open_tx(struct net_device *dev) { wlanid_private_t *priv = netdev_priv(dev); Cppi4Queue queue; /* used generically */ queue.qMgr = RECYCLE_INFRA_RX_QMGR; queue.qNum = RECYCLE_INFRA_RX_Q(0); /* 232 */ if (!(priv->recycler_queue = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open recycler queue\n"); goto err; } tasklet_init(&priv->tx_tasklet, wlanid_do_tx, (unsigned long) dev); priv->tx_acc_hnd = NULL; priv->tx_acc_chan = WLAN_ACC_TX_CHNUM; queue.qMgr = WLAN_CPPI4x_TX_QMGR; queue.qNum = WLAN_CPPI4x_TX_QNUM; /* 217 */ #if WLANNI_ACC_USE_QUEUE_FOR_TX if (!(priv->tx_queue = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open tx queue\n"); goto err; } #endif if (wlanid_init_acc_chan(priv->pal_hnd, priv->tx_acc_chan, queue, &priv->tx_acc_hnd)) { DPRINTK(" unable to init Tx Acc\n"); goto err; } #if !WLANNI_ACC_USE_QUEUE_FOR_TX priv->tx_list_base = priv->tx_list = PAL_cppi4AccChGetNextList(priv->tx_acc_hnd); #endif /* * request the Tx IRQs */ if (request_irq (WLAN_TXINT_NUM, wlanid_tx_interrupt, IRQF_DISABLED, "WLAN TX", dev)) { DPRINTK(KERN_ERR "%s: unable to get IRQ #%d!\n", __FUNCTION__, WLAN_TXINT_NUM); goto err; } queue.qMgr = WLAN_OUT_CPPI4x_FD_QMGR; queue.qNum = WLAN_OUT_CPPI4x_FD_QNUM; /* 158 */ if (!(priv->tx_free_queue_hnd = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open Tx Queue #%d!\n", i); goto err; } /************************************************/ /* * Initialize WLAN TxCmpl queue */ tasklet_init(&priv->txcmpl_tasklet, wlanid_do_tx_complete, (unsigned long) dev); queue.qMgr = WLAN_CPPI4x_TX_COMP_QMGR; queue.qNum = WLAN_CPPI4x_TX_COMP_QNUM; /* 127 */ PAL_cppi4QueueClose(priv->pal_hnd, PAL_cppi4QueueOpen(priv->pal_hnd, queue)); /* * Init the Tx complete accumulator channel */ if (wlanid_init_acc_chan (priv->pal_hnd, WLAN_ACC_TXCMPL_CHNUM, queue, &priv->txcmpl_acc_hnd)) { DPRINTK(KERN_ERR "%s: unable to open accumulator channel!\n", __FUNCTION__); goto err; } priv->txcmpl_list_base = priv->txcmpl_list = PAL_cppi4AccChGetNextList(priv->txcmpl_acc_hnd); /* * request the TxCmpl IRQs */ if (request_irq (WLAN_TXCMPLINT_NUM, wlanid_txcmpl_interrupt, IRQF_DISABLED, "WLAN TX CMPL", dev)) { DPRINTK(KERN_ERR "%s: unable to get IRQ #%d!\n", __FUNCTION__, WLAN_TXCMPLINT_NUM); goto err; } { /************************************************/ /* */ /* * Initialize System Generic Priority Tx queues */ /* */ /************************************************/ queue.qMgr = PPFW_CPPI4x_HOST_TO_PP_PRTY_QMGR; queue.qNum = PPFW_CPPI4x_HOST_TO_PP_PRTY_QNUM(PAL_CPPI4x_PRTY_LOW); /* 196 */ // queue.qNum = PPFW_CPPI4x_TX_EGRESS_HOST_QNUM(0); if (!(priv->tx_qos_queue = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open Tx QOS Queue #%d!\n", i); goto err; } /************************************************/ /************************************************/ /* */ /* * Initialize Free Host Descriptors Tx queues */ /* */ /************************************************/ queue.qMgr = PPFW_CPPI4x_FDB_QMGR; queue.qNum = PPFW_CPPI4x_FDB_QNUM(PAL_CPPI4x_PRTY_LOW); /* 138 */ if (!(priv->tx_qos_free_queue = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open Tx Queue #%d!\n", queue.qNum); goto err; } /************************************************/ } /************************************************/ /* */ /* * Initialize Queue Handle from transmit to ETH */ /* */ /************************************************/ queue.qMgr = CPMAC_CPPI4x_TX_QMGR; queue.qNum = CPMAC_CPPI4x_TX_QNUM(1); /* 213 low prio of ethernet driver */ if (!(priv->eth_output_queue = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open ETH Tx Queue #%d!\n", queue.qNum); goto err; } /************************************************/ /* */ /* * Initialize Queue Handle from transmit to USB */ /* */ /************************************************/ queue.qMgr = USB_CPPI4x_TX_QMGR; queue.qNum = USB_CPPI4x_EP1_TX_QNUM(0); /* 206 prio of USB EP1 */ if (!(priv->usb_output_queues[0] = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open USB EP1 Tx Queue #%d!\n", queue.qNum); goto err; } queue.qMgr = USB_CPPI4x_TX_QMGR; queue.qNum = USB_CPPI4x_EP2_TX_QNUM(0); /* 208 prio of USB EP2 */ if (!(priv->usb_output_queues[1] = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open USB EP2 Tx Queue #%d!\n", queue.qNum); goto err; } queue.qMgr = USB_CPPI4x_TX_QMGR; queue.qNum = USB_CPPI4x_EP3_TX_QNUM(0); /* 210 prio of USB EP3 */ if (!(priv->usb_output_queues[2] = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open USB EP3 Tx Queue #%d!\n", queue.qNum); goto err; } return 0; err: wlanid_close_tx_prep(dev); wlanid_close_tx_finish(dev); return -1; } /**************************************************************************/ /*! \fn wlanid_open_rx ************************************************************************** * * \brief Open Rx routine * * \param[in] Net Device * \return Ok or error **************************************************************************/ static int wlanid_open_rx(struct net_device *dev) { wlanid_private_t *priv = netdev_priv(dev); Cppi4Queue queue; /* used generically */ /* * Prepare WLAN proxy channel * ============================= */ { /* * Open WLAN Proxy Tx channels * ----------------------------- * Input queue is predefined from Q[225] for Channel 19 */ { volatile Cppi4TxChInitCfg ProxyTxChInfo; volatile Cppi4RxChInitCfg ProxyRxChInfo; PAL_Cppi4TxChHnd ProxyTxChHdl; PAL_Cppi4RxChHnd ProxyRxChHdl; ProxyTxChInfo.chNum = WLAN_CPPI4x_WLAN_TO_HOST_PROXY_CHNUM; /* 19 */ ProxyTxChInfo.dmaNum = PAL_CPPI41_DMA_BLOCK1; ProxyTxChInfo.tdQueue.qMgr = DMA1_CPPI4x_FTD_QMGR; ProxyTxChInfo.tdQueue.qNum = DMA1_CPPI4x_FTD_QNUM; /* 150 */ ProxyTxChInfo.defDescType = CPPI41_DESC_TYPE_EMBEDDED; DPRINTK(" Call PAL_cppi4TxChOpen channel=%d\n", ProxyTxChInfo.chNum); ProxyTxChHdl = PAL_cppi4TxChOpen(priv->pal_hnd, (Cppi4TxChInitCfg *) (&ProxyTxChInfo), NULL); if (ProxyTxChHdl == NULL) { DPRINTK(" Unable to open %d channel \n", ProxyTxChInfo.chNum); goto err; } PAL_cppi4EnableTxChannel(ProxyTxChHdl, NULL); /* * Prepare Proxy rx queues * ----------------------- * Free queues are from Q[142] * Rx complete queues are Q[111] */ queue.qMgr = WLAN_CPPI4x_RX_QMGR; queue.qNum = WLAN_CPPI4x_RX_QNUM(0); /* 111 */ if (! (priv->rxProxy_queue_hnd = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" Unable to open queue %d \n", queue.qNum); goto err; } queue.qMgr = WLAN_CPPI4x_FBD_QMGR; queue.qNum = WLAN_CPPI4x_FBD_QNUM; /* 142 */ if (! (priv->rxProxy_free_queue_hnd = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" Unable to open free desc queue %d\n", queue.qNum); goto err; } queue.qMgr = WLAN_RX_CPPI4x_FBD_QMGR; queue.qNum = WLAN_RX_CPPI4x_FBD_QNUM; /* 143 */ if (! (priv->rx_free_queue_hnd = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" Unable to open free desc queue %d\n", queue.qNum); goto err; } if (wlanid_init_rxpool(dev)) { DPRINTK(" Unable to init BD pool for channel\n"); goto err; } /* * Open Docsis Proxy Rx channels * ----------------------------- * Free queues are from Q[142] * Output queues are Q[111] */ ProxyRxChInfo.chNum = WLAN_CPPI4x_WLAN_TO_HOST_PROXY_CHNUM; /* 19 */ ProxyRxChInfo.dmaNum = PAL_CPPI41_DMA_BLOCK1; ProxyRxChInfo.rxCompQueue.qMgr = WLAN_CPPI4x_RX_QMGR; ProxyRxChInfo.rxCompQueue.qNum = WLAN_CPPI4x_RX_QNUM(0); /* 111 */ ProxyRxChInfo.sopOffset = 0; // SOF skip=0 ProxyRxChInfo.defDescType = CPPI41_DESC_TYPE_HOST; ProxyRxChInfo.retryOnStarvation = 0; ProxyRxChInfo.u.hostPktCfg.fdbQueue[0].qMgr = WLAN_CPPI4x_FBD_QMGR; ProxyRxChInfo.u.hostPktCfg.fdbQueue[0].qNum = WLAN_CPPI4x_FBD_QNUM; ProxyRxChInfo.u.hostPktCfg.fdbQueue[1].qMgr = WLAN_CPPI4x_FBD_QMGR; ProxyRxChInfo.u.hostPktCfg.fdbQueue[1].qNum = WLAN_CPPI4x_FBD_QNUM; ProxyRxChInfo.u.hostPktCfg.fdbQueue[2].qMgr = WLAN_CPPI4x_FBD_QMGR; ProxyRxChInfo.u.hostPktCfg.fdbQueue[2].qNum = WLAN_CPPI4x_FBD_QNUM; ProxyRxChInfo.u.hostPktCfg.fdbQueue[3].qMgr = WLAN_CPPI4x_FBD_QMGR; ProxyRxChInfo.u.hostPktCfg.fdbQueue[3].qNum = WLAN_CPPI4x_FBD_QNUM; DPRINTK(" Call PAL_cppi4RxChOpen channel=%d\n", ProxyRxChInfo.chNum); ProxyRxChHdl = PAL_cppi4RxChOpen(priv->pal_hnd, (Cppi4RxChInitCfg *) (&ProxyRxChInfo), NULL); if (ProxyRxChHdl == NULL) { DPRINTK(" Unable to open %d channel \n", ProxyRxChInfo.chNum); goto err; } PAL_cppi4EnableRxChannel(ProxyRxChHdl, NULL); } } /*************************************************************** * Prepare Accumulator channel * ============================ */ { { /* * Open WLAN Accumulator channels * -------------------------------- * Acc channel is 9 * !!!! queues are the same as rxProxy queues, Q[111] */ priv->rxAcc_chan_hnd = NULL; priv->rxAcc_chan = WLAN_ACC_RX_CHNUM; queue.qMgr = 0; queue.qNum = WLAN_CPPI4x_RX_QNUM(0); /* 111 */ if (wlanid_init_acc_chan(priv->pal_hnd, priv->rxAcc_chan, queue, &priv->rxAcc_chan_hnd)) { DPRINTK(" Unable to open accumulator channel for channel\n"); goto err; } priv->rxAcc_chan_list_base = priv->rxAcc_chan_list = PAL_cppi4AccChGetNextList(priv->rxAcc_chan_hnd); } } /*************************************************************** * Prepare WLAN Rx channels * ==================== */ { Cppi4Queue queue; queue.qMgr = WLAN_CPPI4x_IN_PROXY_QMGR; queue.qNum = WLAN_CPPI4x_IN_PROXY_QNUM; /* 234 */ if (! (priv->rx_dma_input_queue = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open Rx Queue \n"); goto err; } } { /* * Prepare WLAN Rx queues * --------------------- * Input queue is predefined from Q[234] for Channel 3 * Free queues are from Q[157].. * Rx complete queues are from Q[56].. - Prefetcher queues */ Cppi4TxChInitCfg txCh; Cppi4RxChInitCfg rxCh; PAL_Cppi4TxChHnd cppi4TxChHnd; PAL_Cppi4RxChHnd cppi4RxChHnd; /* * Set up Tx channel */ txCh.chNum = WLAN_CPPI4x_IN_PROXY_CHNUM; /* 3 */ txCh.dmaNum = PAL_CPPI41_DMA_BLOCK1; txCh.tdQueue.qMgr = DMA1_CPPI4x_FTD_QMGR; txCh.tdQueue.qNum = DMA1_CPPI4x_FTD_QNUM; /* 150 */ txCh.defDescType = CPPI41_DESC_TYPE_HOST; cppi4TxChHnd = PAL_cppi4TxChOpen(priv->pal_hnd, &txCh, NULL); if (!cppi4TxChHnd) { printk("%s: infra channel setup failed\n", __FUNCTION__); goto err; } PAL_cppi4EnableTxChannel(cppi4TxChHnd, NULL); /* * Set up Rx channel */ rxCh.chNum = WLAN_CPPI4x_IN_PROXY_CHNUM; /* 3 */ rxCh.dmaNum = PAL_CPPI41_DMA_BLOCK1; rxCh.defDescType = CPPI41_DESC_TYPE_EMBEDDED; rxCh.sopOffset = 0; rxCh.rxCompQueue.qMgr = PPFW_CPPI4x_RX_INGRESS_QMGR; rxCh.rxCompQueue.qNum = PPFW_CPPI4x_RX_INGRESS_QNUM(PAL_CPPI4x_PRTY_LOW); /* 56 */ rxCh.u.embeddedPktCfg.fdQueue.qMgr = WLAN_IN_CPPI4x_FD_QMGR; rxCh.u.embeddedPktCfg.fdQueue.qNum = WLAN_IN_CPPI4x_FD_QNUM; /* 157 */ rxCh.u.embeddedPktCfg.numBufSlot = EMSLOTCNT - 1; rxCh.u.embeddedPktCfg.sopSlotNum = 1; rxCh.u.embeddedPktCfg.fBufPool[0].bMgr = BUF_POOL_MGR0; rxCh.u.embeddedPktCfg.fBufPool[0].bPool = BMGR0_POOL10; rxCh.u.embeddedPktCfg.fBufPool[1].bMgr = BUF_POOL_MGR0; rxCh.u.embeddedPktCfg.fBufPool[1].bPool = BMGR0_POOL10; rxCh.u.embeddedPktCfg.fBufPool[2].bMgr = BUF_POOL_MGR0; rxCh.u.embeddedPktCfg.fBufPool[2].bPool = BMGR0_POOL11; rxCh.u.embeddedPktCfg.fBufPool[3].bMgr = BUF_POOL_MGR0; rxCh.u.embeddedPktCfg.fBufPool[3].bPool = BMGR0_POOL11; cppi4RxChHnd = PAL_cppi4RxChOpen(priv->pal_hnd, &rxCh, NULL); if (!cppi4RxChHnd) { printk("%s: infra channel setup failed\n", __FUNCTION__); goto err; } PAL_cppi4EnableRxChannel(cppi4RxChHnd, NULL); } queue.qMgr = WLAN_CPPI4x_WLAN_TO_HOST_PROXY_QMGR; queue.qNum = WLAN_CPPI4x_WLAN_TO_HOST_PROXY_QNUM; /* 225 */ if (!(priv->rxProxy_input_queue = PAL_cppi4QueueOpen(priv->pal_hnd, queue))) { DPRINTK(" unable to open Rx Queue \n"); goto err; } /* * request the Rx IRQs */ if (request_irq(WLAN_RXINT_NUM, wlanid_rx_interrupt, IRQF_DISABLED, "WLAN RX", dev)) { DPRINTK(" unable to get IRQ #%d !\n", WLAN_RXINT_NUM); goto err; } return 0; err: wlanid_close_rx_prep(dev); wlanid_close_rx_finish(dev); return -1; } /**************************************************************************/ /*! \fn wlanid_open ************************************************************************** * * \brief CNI Device Open API * * \param[in] Net Device * \return Ok **************************************************************************/ static int wlanid_open(struct net_device *dev) { wlanid_private_t *priv = netdev_priv(dev); DPRINTK(" Enter \n"); #ifdef CONFIG_ARM_AVALANCHE_PPD wlani_pp_set_pid_flags(dev, 0); #endif if (wlanid_open_tx(dev)) return -1; if (wlanid_open_rx(dev)) return -1; netif_start_queue(dev); #ifdef NEW_NAPI napi_enable(&priv->napi); #endif priv->is_open = 1; return 0; } /************************************************************************/ /* */ /* */ /* */ /************************************************************************/ static int wlanid_close_tx_prep(struct net_device *dev) { /* * disable the Tx IRQs */ disable_irq(WLAN_TXINT_NUM); disable_irq(WLAN_TXCMPLINT_NUM); return 0; } /*----------------------------------------------------------------------*/ static int wlanid_close_tx_finish(struct net_device *dev) { wlanid_private_t *priv = netdev_priv(dev); int i; tasklet_kill(&priv->tx_tasklet); tasklet_kill(&priv->txcmpl_tasklet); /* * de-init the Tx complete accumulator channels */ PAL_cppi4AccChClose(priv->tx_acc_hnd, NULL); PAL_cppi4AccChClose(priv->txcmpl_acc_hnd, NULL); #if !WLANNI_ACC_USE_QUEUE_FOR_TX if (priv->tx_list_base) { kfree(priv->tx_list_base); priv->tx_list_base = 0; } #endif if (priv->txcmpl_list_base) { kfree(priv->txcmpl_list_base); priv->txcmpl_list_base = 0; } if (priv->tx_qos_queue) { PAL_cppi4QueueClose(priv->pal_hnd, priv->tx_qos_queue); priv->tx_qos_queue = 0; } if (priv->tx_qos_free_queue) { PAL_cppi4QueueClose(priv->pal_hnd, priv->tx_qos_free_queue); priv->tx_qos_free_queue = 0; } if (priv->eth_output_queue) { PAL_cppi4QueueClose(priv->pal_hnd, priv->eth_output_queue); priv->eth_output_queue = 0; } for (i=0; i< 3; i++) { if (priv->usb_output_queues[i]) { PAL_cppi4QueueClose(priv->pal_hnd, priv->usb_output_queues[i]); priv->usb_output_queues[i] = 0; } } if (priv->recycler_queue) { PAL_cppi4QueueClose(priv->pal_hnd, priv->recycler_queue); priv->recycler_queue = 0; } free_irq(WLAN_TXINT_NUM, dev); free_irq(WLAN_TXCMPLINT_NUM, dev); return 0; } /************************************************************************/ /************************************************************************/ /* */ /* */ /* */ /************************************************************************/ static int wlanid_close_rx_prep(struct net_device *dev) { /* * disable the Rx IRQs */ disable_irq(WLAN_RXINT_NUM); return 0; } /*----------------------------------------------------------------------*/ static int wlanid_close_rx_finish(struct net_device *dev) { wlanid_private_t *priv = netdev_priv(dev); PAL_cppi4AccChClose(priv->rxAcc_chan_hnd, NULL); priv->rxAcc_chan_hnd = 0; { if (priv->rxProxy_free_queue_hnd) { PAL_cppi4QueueClose(priv->pal_hnd, priv->rxProxy_free_queue_hnd); priv->rxProxy_free_queue_hnd = 0; } if (priv->rx_free_queue_hnd) { PAL_cppi4QueueClose(priv->pal_hnd, priv->rx_free_queue_hnd); priv->rx_free_queue_hnd = 0; } if (priv->rxProxy_bdpool) { wlanid_free_rxpool(dev, priv->rxProxy_bdpool, WLAN_RXPROXY_HOST_BD_NUM+WLAN_RX_HOST_BD_NUM); priv->rxProxy_bdpool = 0; } if (priv->rxAcc_chan_list_base) { kfree(priv->rxAcc_chan_list_base); priv->rxAcc_chan_list_base = 0; } if (priv->rx_dma_input_queue) { PAL_cppi4QueueClose(priv->pal_hnd, priv->rx_dma_input_queue); priv->rx_dma_input_queue = 0; } } /* * free the Rx IRQs */ free_irq(WLAN_RXINT_NUM, dev); return 0; } /************************************************************************/ static int wlanid_close(struct net_device *dev) { wlanid_private_t *priv = netdev_priv(dev); unsigned long flags; priv->is_open = 0; netif_stop_queue(dev); #ifdef NEW_NAPI napi_disable(&priv->napi); #endif #ifdef CONFIG_ARM_AVALANCHE_PPD wlani_pp_set_pid_flags(dev, TI_PP_PID_DISCARD_ALL_RX); #endif /* * Do the urgent stuff first */ wlanid_close_tx_prep(dev); wlanid_close_rx_prep(dev); spin_lock_irqsave(&priv->devlock, flags); set_bit(0, &priv->state); #ifdef NEW_NAPI if (wlanid_do_rx_complete(dev, WLAN_RX_HOST_BD_NUM) == WLAN_RX_HOST_BD_NUM) avalanche_intd_write_eoi(WLAN_ACC_RX_INTV); #else if (wlanid_do_rx_complete(dev, WLAN_RX_HOST_BD_NUM)) avalanche_intd_write_eoi(WLAN_ACC_RX_INTV); #endif wlanid_do_tx_complete((unsigned long) dev); wlanni_purge_queues(priv); clear_bit(0, &priv->state); spin_unlock_irqrestore(&priv->devlock, flags); /* * finish rest of janitorial stuff now */ wlanid_close_tx_finish(dev); wlanid_close_rx_finish(dev); return 0; } static struct net_device_stats *wlanid_get_stats(struct net_device *dev) { wlanid_private_t *priv = netdev_priv((struct net_device *) dev); return &priv->stats; } static void wlanid_set_multicast(struct net_device *dev) { } /* TODO: Hack this function to setup device address according to taste */ static void wlanid_hard_addr_setup(struct net_device *dev) { memcpy(dev->dev_addr, defmac, dev->addr_len); memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); } static void wlanid_netdev_setup(struct net_device *dev) { dev->open = wlanid_open; dev->hard_start_xmit = wlanid_start_xmit; #ifndef NEW_NAPI dev->poll = wlanid_poll; dev->weight = WLAN_RX_SERVICE_MAX; #endif dev->stop = wlanid_close; dev->get_stats = wlanid_get_stats; dev->set_multicast_list = wlanid_set_multicast; #ifdef NEW_NAPI { wlanid_private_t *priv = netdev_priv(dev); netif_napi_add(dev, &priv->napi, wlanid_poll, WLAN_RX_SERVICE_MAX); } #endif /* * TODO: study the effects of these features */ /* * dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA */ ether_setup(dev); wlanid_hard_addr_setup(dev); } /* structure describing the CNID driver */ static struct device_driver wlanid_driver = { .name = "wlanni", .bus = NULL, .probe = wlanid_probe, .remove = wlanid_remove, .suspend = NULL, .resume = NULL, }; #ifdef CONFIG_ARM_AVALANCHE_PPD /* * WLAN PID handles * --------------- */ /* PID/VPID definitions */ #define PP_WLAN_SR_DELAY 200 #ifndef CONFIG_TI_PACKET_PROCESSOR static TI_PP_PID wlan_pid; #endif /* * Set PID Flags * ------------- */ static int wlani_pp_set_pid_flags(struct net_device *dev, int flags) { #ifdef CONFIG_TI_PACKET_PROCESSOR ti_ppm_set_pid_flags(dev->pid_handle, flags); #else ti_ppd_set_pid_flags(&wlani_pid_list[0], flags); #endif /* * this delay is to make sure all the packets with the PID successfully * egress throgh the respective ports. */ mdelay(PP_WLAN_SR_DELAY); return 0; } /* * Create WLAN PID range, PID, VPID * ------------------------------- * Default Q when no match - 225 (proxy ch queue) * Default Q when match - 217 (WLAN TX) */ static int wlani_pp_prepare_pid(struct net_device *dev) { TI_PP_PID_RANGE pid_range_wlan; #ifdef CONFIG_TI_PACKET_PROCESSOR TI_PP_PID wlan_pid; #endif int ret_val; /* * Config WLAN PID range * -------------------- */ pid_range_wlan.type = TI_PP_PID_TYPE_DOCSIS; /* to get protocol specific * infomartion (wlan node) */ pid_range_wlan.port_num = CPPI41_SRCPORT_WLAN; pid_range_wlan.count = PP_WLAN_PID_COUNT; pid_range_wlan.base_index = PP_WLAN_PID_BASE; #ifdef CONFIG_TI_PACKET_PROCESSOR if (ti_ppm_config_pid_range(&pid_range_wlan)) #else if (ti_ppd_config_pid_range(&pid_range_wlan)) #endif DPRINTK(" config_pid_range failed\n"); /* * Create WLAN NI PIDs * --------------- */ wlan_pid.type = pid_range_wlan.type; wlan_pid.ingress_framing = TI_PP_PID_INGRESS_ETHERNET | TI_PP_PID_INGRESS_IPV6 | TI_PP_PID_INGRESS_IPV4; wlan_pid.pri_mapping = 1; /* Num prio Qs for fwd */ wlan_pid.dflt_pri_drp = 0; wlan_pid.dflt_dst_tag = TI_PP_PID_NO_DST_TAG; wlan_pid.dflt_fwd_q = WLAN_CPPI4x_WLAN_TO_HOST_PROXY_QNUM; /* 225 */ wlan_pid.tx_pri_q_map[0] = WLAN_CPPI4x_TX_QNUM; /* 217 */ /* default Q for Egress */ wlan_pid.tx_hw_data_len = 0; wlan_pid.pid_handle = PP_WLAN_PID_BASE; #ifdef CONFIG_TI_PACKET_PROCESSOR if ((ret_val = ti_ppm_create_pid(&wlan_pid)) < 0) #else if ((ret_val = ti_ppd_create_pid(&wlan_pid))) #endif { DPRINTK(" create_pid failed with error code %d.\n", ret_val); wlan_pid.pid_handle = -1; } dev->pid_handle = wlan_pid.pid_handle; /* * Create WLAN NI VPIDs * ---------------- */ #ifdef CONFIG_TI_PACKET_PROCESSOR dev->vpid_block.type = TI_PP_ETHERNET; dev->vpid_block.parent_pid_handle = dev->pid_handle; dev->vpid_block.egress_mtu = 0; dev->vpid_block.priv_tx_data_len = 0; #if WLANNI_USE_QOS dev->qos_setup_hook = wlanni_setup_qos; dev->qos_shutdown_hook = wlanni_shutdown_qos; dev->qos_select_hook = wlanni_select_qos; #else dev->qos_setup_hook = 0; dev->qos_shutdown_hook = 0; dev->qos_select_hook = 0; #endif #endif return 0; } #endif /* TODO: in a fully linux framework compliant driver, * we will receive the resources: io, mem and intr * in the device structure. */ static int __devinit wlanid_probe(struct device *dev) { struct net_device *netdev = NULL; wlanid_private_t *priv = NULL; int ret; (void)driver_create_file(&wlanid_driver, &driver_attr_version); /* * dev and priv zeroed in alloc_netdev. Thank God for small mercies... */ netdev = alloc_netdev(sizeof(wlanid_private_t), "wlanni%d", wlanid_netdev_setup); if (netdev == NULL) { printk("wlani0: Unable to alloc new net device\n"); return -ENOMEM; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) SET_MODULE_OWNER(netdev); #endif SET_NETDEV_DEV(netdev, dev); platform_set_drvdata(to_platform_device(dev), netdev); g_wlanid_private = priv = netdev_priv(netdev); priv->netdev = netdev; ret = register_netdev(netdev); if (ret) { DPRINTK("Unable to register device named %s (%p)...\n", netdev->name, netdev); return ret; } DPRINTK("Registered device named %s (%p)...\n", netdev->name, netdev); /* * get a PAL handle */ priv->pal_hnd = PAL_cppi4Init(NULL, NULL); /* * Allocate BD pool.. */ if (!(priv->bdPoolBase = PAL_cppi4AllocDesc(priv->pal_hnd, PAL_CPPI41_QUEUE_MGR0, /* * we are using MPEG pool, be carefull */ DMAC_MPEG_RX_EMBEDDED_BD_NUM, MPEG_BD_SIZE))) { DPRINTK(": Unable to allocate Rx BD pool!\n"); } else { DPRINTK(": Desc Pool has been allocated at %p\n", priv->bdPoolBase); } #ifdef CONFIG_ARM_AVALANCHE_PPD /* * Config WLAN NI PID/VPID */ wlani_pp_prepare_pid(netdev); #endif spin_lock_init(&priv->devlock); return 0; } static int __devexit wlanid_remove(struct device *dev) { struct net_device *netdev = platform_get_drvdata(to_platform_device(dev)); wlanid_private_t *priv = netdev_priv(netdev); PAL_cppi4DeallocDesc(priv->pal_hnd, PAL_CPPI41_QUEUE_MGR0, priv->bdPoolBase); PAL_cppi4Exit(priv->pal_hnd, NULL); unregister_netdev(netdev); platform_device_unregister(to_platform_device(dev)); return 0; } static int wlanni_write_cmds (struct file *file, const char *buffer, unsigned long count, void *data) { char pp_cmd[100]; char* argv[10]; int argc = 0; char* ptr_cmd; char* delimitters = " \n\t"; char* ptr_next_tok; /* Validate the length of data passed. */ if (count > 100) count = 100; /* Initialize the buffer before using it. */ memset ((void *)&pp_cmd[0], 0, sizeof(pp_cmd)); memset ((void *)&argv[0], 0, sizeof(argv)); /* Copy from user space. */ if (copy_from_user (&pp_cmd, buffer, count)) return -EFAULT; ptr_next_tok = &pp_cmd[0]; ptr_cmd = strsep(&ptr_next_tok, delimitters); if (ptr_cmd == NULL) return -1; /* Parse all the commands typed. */ do { /* Extract the first command. */ argv[argc++] = ptr_cmd; /* Validate if the user entered more commands.*/ if (argc >=10) { printk(KERN_ERR "WLANNI: too many parameters dropping the command\n"); return -EIO; } /* Get the next valid command. */ ptr_cmd = strsep(&ptr_next_tok, delimitters); } while (ptr_cmd != NULL); /* We have an extra argument when strsep is used instead of strtok */ argc--; /* Display Command Handlers */ if (strcmp(argv[0], "txlow") == 0) { debug_txlow_flag = 1; printk(KERN_DEBUG "WLANNI: debug %s on\n", argv[0]); } else if (strcmp(argv[0], "txhigh") == 0) { debug_txhigh_flag = 1; printk(KERN_DEBUG "WLANNI: debug %s on\n", argv[0]); } else if (strcmp(argv[0], "rxlow") == 0) { debug_rxlow_flag = 1; printk(KERN_DEBUG "WLANNI: debug %s on\n", argv[0]); } else if (strcmp(argv[0], "rxhigh") == 0) { debug_rxhigh_flag = 1; printk(KERN_DEBUG "WLANNI: debug %s on\n", argv[0]); } else if (strcmp(argv[0], "notxlow") == 0) { debug_txlow_flag = 0; printk(KERN_DEBUG "WLANNI: debug %s off\n", argv[0]); } else if (strcmp(argv[0], "notxhigh") == 0) { debug_txhigh_flag = 0; printk(KERN_DEBUG "WLANNI: debug %s off\n", argv[0]); } else if (strcmp(argv[0], "norxlow") == 0) { debug_rxlow_flag = 0; printk(KERN_DEBUG "WLANNI: debug %s off\n", argv[0]); } else if (strcmp(argv[0], "norxhigh") == 0) { debug_rxhigh_flag = 0; printk(KERN_DEBUG "WLANNI: debug %s off\n", argv[0]); } else if (strcmp(argv[0], "on") == 0 || strcmp(argv[0], "all") == 0) { debug_txhigh_flag = 1; debug_txlow_flag = 1; debug_rxhigh_flag = 1; debug_rxlow_flag = 1; printk(KERN_DEBUG "WLANNI: debug on\n"); } else if (strcmp(argv[0], "off") == 0) { debug_txhigh_flag = 0; debug_txlow_flag = 0; debug_rxhigh_flag = 0; debug_rxlow_flag = 0; debug_queue_flag = 0; printk(KERN_DEBUG "WLANNI: debug off\n"); } else if (strcmp(argv[0], "queue") == 0) { debug_queue_flag = 1; printk(KERN_DEBUG "WLANNI: debug %s on\n", argv[0]); } else if (strcmp(argv[0], "noqueue") == 0) { debug_queue_flag = 0; printk(KERN_DEBUG "WLANNI: debug %s on\n", argv[0]); } else if (strcmp(argv[0], "dotx") == 0) { debug_dotx_flag = 1; printk(KERN_DEBUG "WLANNI: debug %s on\n", argv[0]); } else if (strcmp(argv[0], "nodotx") == 0) { debug_dotx_flag = 0; printk(KERN_DEBUG "WLANNI: debug %s off\n", argv[0]); } else if (strcmp(argv[0], "testmode") == 0) { testmode = 1; printk(KERN_DEBUG "WLANNI: testmode on\n"); } else if (strcmp(argv[0], "notestmode") == 0) { testmode = 0; printk(KERN_DEBUG "WLANNI: testmode off\n"); } else { printk(KERN_DEBUG "WLANNI: unknown command %s\n", argv[0]); } return count; } /*----------------------------------------------------------------------*/ static int debug_show(struct seq_file *m, void *v) { seq_printf(m, "txlow: %s\n", debug_txlow_flag ? "on" : "off"); seq_printf(m, "txhigh: %s\n", debug_txhigh_flag ? "on" : "off"); seq_printf(m, "rxlow: %s\n", debug_rxlow_flag ? "on" : "off"); seq_printf(m, "rxhigh: %s\n", debug_rxhigh_flag ? "on" : "off"); seq_printf(m, "queue: %s\n", debug_queue_flag ? "on" : "off"); seq_printf(m, "dotx: %s\n", debug_dotx_flag ? "on" : "off"); seq_printf(m, "testmode: %s\n", testmode ? "on" : "off"); return 0; } static int status_show(struct seq_file *m, void *v) { wlanid_private_t *priv = g_wlanid_private; unsigned ep; seq_printf(m, "%2s | %10s | %10s | %10s | %10s | %10s | %5s | %s\n", "ep", "count", "max count", "stopped", "queued", "requeued", "Queue", "Count"); for (ep = 0; ep < WLANNI_ENDPOINT_MAX; ep++) { wlanid_emb_desc_queue_t *p = &priv->drv_queue[ep]; unsigned long qmgr, qnum; int qlen; if (priv->ep_output_queues[ep]) { qmgr = qMgr_of_handle(priv->ep_output_queues[ep]); qnum = qNum_of_handle(priv->ep_output_queues[ep]); qlen = cppi4QueueLen(priv->pal_hnd, priv->ep_output_queues[ep]); } else { qmgr = qnum = 0; qlen = 0; } seq_printf(m, "%2u | %10u | %10u | %10s | %10lu | %10lu | %lu/%lu | %3d\n", ep, p->count, p->maxcount, p->stopped ? "Yes" : "No", p->queuecount, p->requeuecount, qmgr, qnum, qlen); } return 0; } static int stats_show(struct seq_file *m, void *v) { wlanid_private_t *priv = g_wlanid_private; unsigned ep; seq_printf(m, "no proxy descs counter %lu\n", priv->nnoproxydesc); seq_printf(m, "queue full counter %lu\n", priv->nqueuefull); seq_printf(m, "no tx descs counter %lu\n", priv->nnotxdecs); seq_printf(m, "no tx descs counter %lu\n", priv->nnotxdecs); seq_printf(m, "tx: high %lu low %lu accelerated %lu\n", priv->txcount_high, priv->txcount_low, priv->txcount_accelerated); seq_printf(m, "%2s | %10s | %10s | %11s | %10s | %10s | %10s | %10s |%10s\n", "ep", "bytes", "pkts", "accelerated", "normal", "from queue", "drop", "return", "start"); for (ep = 0; ep < WLANNI_ENDPOINT_MAX; ep++) { struct wlanni_ep_stats *stats = ep_stats(priv, ep); seq_printf(m, "%2u | %10lu | %10lu | %11lu | %10lu | %10lu | %10lu | %10lu | %10lu\n", ep, stats->tx_bytes, stats->tx_packets, stats->tx_accelerated_packets, stats->tx_normal_packets, stats->tx_queued_packets, stats->tx_drop, stats->tx_return, stats->tx_start); } return 0; } static int debug_open(struct inode *inode, struct file *file) { return single_open(file, debug_show, PDE(inode)->data); } static int status_open(struct inode *inode, struct file *file) { return single_open(file, status_show, PDE(inode)->data); } static int stats_open(struct inode *inode, struct file *file) { return single_open(file, stats_show, PDE(inode)->data); } static const struct file_operations debug_fops = { .owner = THIS_MODULE, .open = debug_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; static const struct file_operations status_fops = { .owner = THIS_MODULE, .open = status_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; static const struct file_operations stats_fops = { .owner = THIS_MODULE, .open = stats_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; static struct proc_dir_entry *dir_entry = 0; static int __init wlanid_init_module(void) { static struct platform_device *wlanid_dev; struct proc_dir_entry *file_entry; DPRINTK(KERN_INFO DRV_NAME "\n"); if (sizeof(wlanid_desc_t) > WLAN_BD_SIZE) { DPRINTK("%s fundamentally broken. Contact maintainer!\n", DRV_NAME); return -1; } /* * TODO: this should be in a board file not here. No fun registering driver * and device in the same file. */ wlanid_dev = platform_device_register_simple("wlanni", -1, NULL, 0); dir_entry = proc_net_mkdir(&init_net, "wlanni", init_net.proc_net); if (dir_entry) { dir_entry->read_proc = 0; dir_entry->write_proc = 0; } file_entry = create_proc_entry("control", S_IFREG|S_IWUSR, dir_entry); if (file_entry) { file_entry->data = NULL; file_entry->read_proc = NULL; file_entry->write_proc = wlanni_write_cmds; file_entry->owner = THIS_MODULE; } file_entry = proc_create("status", S_IRUGO, dir_entry, &status_fops); file_entry = proc_create("stats", S_IRUGO, dir_entry, &stats_fops); file_entry = proc_create("debug", S_IRUGO, dir_entry, &debug_fops); wlanid_driver.bus = platform_bus_type_ptr; if (driver_register(&wlanid_driver)) { platform_device_unregister(wlanid_dev); return -1; } return 0; } static void __exit wlanid_cleanup_module(void) { remove_proc_entry("control", dir_entry); remove_proc_entry("debug", dir_entry); remove_proc_entry("status", dir_entry); remove_proc_entry("stats", dir_entry); remove_proc_entry("wlanni", init_net.proc_net); driver_remove_file(&wlanid_driver, &driver_attr_version); driver_unregister(&wlanid_driver); } module_init(wlanid_init_module); module_exit(wlanid_cleanup_module);