转载请注明出处:http://blog.csdn.net/Righthek 谢谢!
上一篇文章已经提到USB接口在wifi模块中的最重要两个函数是usb_read_port()和usb_write_port()。那它们是怎么和wifi扯上关系的呢?我们可以从以下三个方面去分析:
1、首先需要明确wifi模块是USB设备,主控(CPU)端是USB主机;
2、USB主机若需要对wifi模块进行数据的读写时,就必须经过USB接口;
3、既然涉及到数据的读写操作,必然要用相应的读写函数,那么usb_read_port()和usb_write_port()即是它们的读写函数。
我们先从读数据开始进行分析,在分析之前,我们必须了解USB设备驱动的读数据过程。USB读取数据操作流程如下:
(1)通过usb_alloc_urb()函数创建并分配一个URB,作为传输USB数据的载体;
(2)创建并分配DMA缓冲区,以DMA方式快速传输数据;
(3)初始化URB,根据wifi的传输数据量,我们需要初始化为批量URB,相应操作函数为usb_fill_bulk_urb();
(4)将URB提交到USB核心;
(5)提交成功后,URB的完成函数将被USB核心调用。
现在我们一步步地详细分析整个过程,所谓的创建和分配,实质上是对内存的分配。作为一名Linux驱动开发程序员,必须了解Linux内存管理相关知识及合理使用内存。
那么我们应该怎样合理地创建和分配URB和DMA缓冲区呢?很明显,我们应该在用的时候分配,在不用的时候释放。
那么问题来了……什么时候在用,又什么时候不用呢?问题很简单,就是主控端读数据时分配,读完后释放,而只有当wifi模块有数据可读时,主控端才能成功地读取数据。那么wifi模块什么时候有数据可读呢?——下面重点来了!wifi模块通过RF端接收到无线网络数据,然后缓存到wifi芯片的RAM中,此时,wifi模块就有数据可读了。
经过上面的分析,我们找到了一条USB接口与wifi模块扯上关系的线索,就是wifi模块的接收数据,会引发USB接口的读数据;
现在,我们转到wifi模块的接收函数中,看看是不是真的这样?
在wifi接收函数初始化中,我们可以看到usb_alloc_urb()创建一个中断URB。伪代码如下:
int xxxwifi_init_recv(_adapter *padapter) { struct recv_priv *precvpriv = &padapter->recvpriv; int i, res = _SUCCESS; struct recv_buf *precvbuf; tasklet_init(&precvpriv->recv_tasklet, (void(*)(unsigned long))rtl8188eu_recv_tasklet, (unsigned long)padapter); precvpriv->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); //创建一个中断URB precvpriv->int_in_buf = rtw_zmalloc(INTERRUPT_MSG_FORMAT_LEN); //init recv_buf _rtw_init_queue(&precvpriv->free_recv_buf_queue); _rtw_init_queue(&precvpriv->recv_buf_pending_queue); precvpriv -> pallocated_recv_buf = rtw_zmalloc(NR_RECVBUFF *sizeof(struct recv_buf) + 4); precvbuf = (struct recv_buf*)precvpriv->precv_buf; for(i=0; i < NR_RECVBUFF ; i++) { _rtw_init_listhead(&precvbuf->list); _rtw_spinlock_init(&precvbuf->recvbuf_lock); precvbuf->alloc_sz = MAX_RECVBUF_SZ; res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf); precvbuf->ref_cnt = 0; precvbuf->adapter =padapter; precvbuf++; } precvpriv->free_recv_buf_queue_cnt = NR_RECVBUFF; skb_queue_head_init(&precvpriv->rx_skb_queue); #ifdef CONFIG_PREALLOC_RECV_SKB { int i; SIZE_PTR tmpaddr=0; SIZE_PTR alignment=0; struct sk_buff *pskb=NULL; skb_queue_head_init(&precvpriv->free_recv_skb_queue); for(i=0; i<NR_PREALLOC_RECV_SKB; i++) { pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ); if(pskb) { pskb->dev = padapter->pnetdev; tmpaddr = (SIZE_PTR)pskb->data; alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1); skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignment)); skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb); } pskb=NULL; } } #endif return res; }
在rtw_os_recvbuf_resource_alloc函数中,创建一个批量URB和一个DMA缓冲区。伪代码如下:
int rtw_os_recvbuf_resource_alloc(_adapter *padapter, struct recv_buf *precvbuf) { int res=_SUCCESS; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); struct usb_device *pusbd = pdvobjpriv->pusbdev; precvbuf->irp_pending = _FALSE; precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL); //创建一个批量URB precvbuf->pskb = NULL; precvbuf->reuse = _FALSE; precvbuf->pallocated_buf = precvbuf->pbuf = NULL; precvbuf->pdata = precvbuf->phead = precvbuf->ptail = precvbuf->pend = NULL; precvbuf->transfer_len = 0; precvbuf->len = 0; #ifdef CONFIG_USE_USB_BUFFER_ALLOC_RX precvbuf->pallocated_buf = rtw_usb_buffer_alloc(pusbd, (size_t)precvbuf->alloc_sz, &precvbuf->dma_transfer_addr); //创建一个DMA缓冲区 precvbuf->pbuf = precvbuf->pallocated_buf; if(precvbuf->pallocated_buf == NULL) return _FAIL; #endif //CONFIG_USE_USB_BUFFER_ALLOC_RX return res; }
在usb_read_port()函数中,通过usb_fill_bulk_urb()初始化批量URB,并且提交给USB核心,也即USB读取数据操作流程的第3、4步。在usb_fill_bulk_urb()函数中,初始化URB的完成函数usb_read_port_complete(),只有当URB提交完成后,函数usb_read_port_complete()将被调用。伪代码如下:
static u32 usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem) { struct recv_buf *precvbuf = (struct recv_buf *)rmem; _adapter *adapter = pintfhdl->padapter; struct dvobj_priv *pdvobj = adapter_to_dvobj(adapter); struct pwrctrl_priv *pwrctl = dvobj_to_pwrctl(pdvobj); struct recv_priv *precvpriv = &adapter->recvpriv; struct usb_device *pusbd = pdvobj->pusbdev; rtl8188eu_init_recvbuf(adapter, precvbuf); precvpriv->rx_pending_cnt++; purb = precvbuf->purb; //translate DMA FIFO addr to pipehandle pipe = ffaddr2pipehdl(pdvobj, addr); usb_fill_bulk_urb(purb, pusbd, pipe, precvbuf->pbuf, MAX_RECVBUF_SZ, usb_read_port_complete, precvbuf);//context is precvbuf err = usb_submit_urb(purb, GFP_ATOMIC); return ret; }
通过上面的代码,我们可以得知在wifi模块为接收数据做初始化准备时,分配了URB和DMA缓冲区。而在usb_read_port()函数中初始化URB和提交URB。
它为什么要这样做呢?目的是什么?
接下来,我们将在下一篇文章中为大家揭晓。
转载请注明出处:http://blog.csdn.net/Righthek 谢谢!