wifi驱动的理解(3)——usb接口在wifi模块中的角色

转载请注明出处: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。伪代码如下:

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int xxxwifi_init_recv(_adapter *padapter)  
  2. {  
  3.     struct recv_priv *precvpriv = &padapter->recvpriv;  
  4.     int i, res = _SUCCESS;  
  5.     struct recv_buf *precvbuf;  
  6.   
  7.     tasklet_init(&precvpriv->recv_tasklet, (void(*)(unsigned long))rtl8188eu_recv_tasklet, (unsigned long)padapter);  
  8.   
  9.     precvpriv->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); //创建一个中断URB  
  10.   
  11.     precvpriv->int_in_buf = rtw_zmalloc(INTERRUPT_MSG_FORMAT_LEN);  
  12.     //init recv_buf  
  13.     _rtw_init_queue(&precvpriv->free_recv_buf_queue);  
  14.     _rtw_init_queue(&precvpriv->recv_buf_pending_queue);  
  15.   
  16.     precvpriv -> pallocated_recv_buf = rtw_zmalloc(NR_RECVBUFF *sizeof(struct recv_buf) + 4);  
  17.     precvbuf = (struct recv_buf*)precvpriv->precv_buf;  
  18.   
  19.     for(i=0; i < NR_RECVBUFF ; i++)  
  20.     {  
  21.         _rtw_init_listhead(&precvbuf->list);  
  22.         _rtw_spinlock_init(&precvbuf->recvbuf_lock);  
  23.         precvbuf->alloc_sz = MAX_RECVBUF_SZ;  
  24.   
  25.         res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf);  
  26.   
  27.         precvbuf->ref_cnt = 0;  
  28.         precvbuf->adapter =padapter;  
  29.         precvbuf++;  
  30.     }  
  31.     precvpriv->free_recv_buf_queue_cnt = NR_RECVBUFF;  
  32.   
  33.     skb_queue_head_init(&precvpriv->rx_skb_queue);  
  34.   
  35. #ifdef CONFIG_PREALLOC_RECV_SKB  
  36.     {  
  37.         int i;  
  38.         SIZE_PTR tmpaddr=0;  
  39.         SIZE_PTR alignment=0;  
  40.         struct sk_buff *pskb=NULL;  
  41.         skb_queue_head_init(&precvpriv->free_recv_skb_queue);  
  42.         for(i=0; i<NR_PREALLOC_RECV_SKB; i++)  
  43.         {  
  44.             pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);  
  45.             if(pskb)  
  46.             {  
  47.                 pskb->dev = padapter->pnetdev;  
  48.                 tmpaddr = (SIZE_PTR)pskb->data;  
  49.                 alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);  
  50.                 skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignment));  
  51.                 skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);  
  52.             }  
  53.             pskb=NULL;  
  54.         }  
  55.     }  
  56. #endif  
  57.     return res;  
  58. }  

 

        在rtw_os_recvbuf_resource_alloc函数中,创建一个批量URB和一个DMA缓冲区。伪代码如下:

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int rtw_os_recvbuf_resource_alloc(_adapter *padapter, struct recv_buf *precvbuf)  
  2. {  
  3.     int res=_SUCCESS;  
  4.     struct dvobj_priv   *pdvobjpriv = adapter_to_dvobj(padapter);  
  5.     struct usb_device   *pusbd = pdvobjpriv->pusbdev;  
  6.   
  7.     precvbuf->irp_pending = _FALSE;  
  8.     precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL); //创建一个批量URB  
  9.   
  10.     precvbuf->pskb = NULL;  
  11.     precvbuf->reuse = _FALSE;  
  12.     precvbuf->pallocated_buf  = precvbuf->pbuf = NULL;  
  13.     precvbuf->pdata = precvbuf->phead = precvbuf->ptail = precvbuf->pend = NULL;  
  14.     precvbuf->transfer_len = 0;  
  15.     precvbuf->len = 0;  
  16.   
  17.     #ifdef CONFIG_USE_USB_BUFFER_ALLOC_RX  
  18.     precvbuf->pallocated_buf = rtw_usb_buffer_alloc(pusbd, (size_t)precvbuf->alloc_sz, &precvbuf->dma_transfer_addr);  //创建一个DMA缓冲区  
  19.     precvbuf->pbuf = precvbuf->pallocated_buf;  
  20.     if(precvbuf->pallocated_buf == NULL)  
  21.         return _FAIL;  
  22.     #endif //CONFIG_USE_USB_BUFFER_ALLOC_RX  
  23.       
  24.     return res;  
  25. }  

 

        在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()将被调用。伪代码如下:

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static u32 usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem)  
  2. {     
  3.     struct recv_buf *precvbuf = (struct recv_buf *)rmem;  
  4.     _adapter        *adapter = pintfhdl->padapter;  
  5.     struct dvobj_priv   *pdvobj = adapter_to_dvobj(adapter);  
  6.     struct pwrctrl_priv *pwrctl = dvobj_to_pwrctl(pdvobj);  
  7.     struct recv_priv    *precvpriv = &adapter->recvpriv;  
  8.     struct usb_device   *pusbd = pdvobj->pusbdev;  
  9.   
  10.     rtl8188eu_init_recvbuf(adapter, precvbuf);        
  11.   
  12.     precvpriv->rx_pending_cnt++;  
  13.   
  14.     purb = precvbuf->purb;  
  15.   
  16.     //translate DMA FIFO addr to pipehandle  
  17.     pipe = ffaddr2pipehdl(pdvobj, addr);  
  18.   
  19.     usb_fill_bulk_urb(purb, pusbd, pipe,   
  20.                     precvbuf->pbuf,  
  21.                             MAX_RECVBUF_SZ,  
  22.                             usb_read_port_complete,  
  23.                             precvbuf);//context is precvbuf  
  24.   
  25.     err = usb_submit_urb(purb, GFP_ATOMIC);  
  26.   
  27.     return ret;  
  28. }  

 

        通过上面的代码,我们可以得知在wifi模块为接收数据做初始化准备时,分配了URB和DMA缓冲区。而在usb_read_port()函数中初始化URB和提交URB。

        它为什么要这样做呢?目的是什么?

         接下来,我们将在下一篇文章中为大家揭晓。

转载请注明出处:http://blog.csdn.net/Righthek 谢谢!

posted on 2017-01-16 00:56  Red_Point  阅读(1740)  评论(0编辑  收藏  举报

导航