Linux 下wifi 驅動開發(四)—— USB接口WiFi驅動淺析

轉: http://blog.csdn.net/zqixiao_09/article/details/51146149緩存

 

前面學習了SDIO接口的WiFi驅動,如今咱們來學習一下USB接口的WiFi驅動,兩者的區別在於接口不一樣。而USB接口的設備驅動,咱們前面也有學習,好比USB攝像頭驅動、USB鼠標驅動,一樣都符合LinuxUSB驅動結構:網絡

 

        USB設備驅動(字符設備、塊設備、網絡設備)框架

                                               |less

                                        USB 核心函數

                                               |學習

                              USB主機控制器驅動spa

 

        不一樣之處只是在於USB攝像頭驅動是字符設備,而咱們今天要學習的WiFi驅動是網絡設備;固然由咱們編寫的部分仍是USB設備驅動部分,下面進入USB接口WiFi驅動的分析,如何分析呢?咱們下面從這幾個方面入手:操作系統

        從硬件層面上看,WIFI設備與CPU通訊是經過USB接口的與其餘WIFI設備之間的通訊是經過無線射頻(RF).net

        從軟件層面上看,Linux操做系統要管理WIFI設備,那麼就要將WIFI設備掛載到USB總線上,經過USB子系統實現管理。而同時爲了對接網絡,又將WIFI設備封裝成一個網絡設備code

        咱們以USB接口的WIFI模塊進行分析:

a -- 從USB總線的角度去看,它是USB設備;

b -- 從Linux設備的分類上看,它又是網絡設備;

c -- 從WIFI自己的角度去看,它又有本身獨特的功能及屬性,所以它又是一個私有的設備;

經過上述的分析,咱們只要抓住這三條線索深刻去分析它的驅動源碼,整個WIFI驅動框架就會浮如今你眼前。

 

1、框架整理

一、USB設備驅動

      如今咱們先從USB設備開始,要寫一個USB設備驅動,那麼大體步驟以下:

a -- 須要針對該設備定義一個USB驅動,對應到代碼中即定義一個usb_driver結構體變量

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. struct usb_driver xxx_usb_wifi_driver;  

b -- 填充該設備的usb_driver結構體成員變量

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. static struct usb_driver xxx_usb_wifi_driver = {  
  2.     .name = "XXX_USB_WIFI",  
  3.     .probe = xxx_probe,  
  4.     .disconnect = xxx_disconnect,  
  5.     .suspend = xxx_suspend,  
  6.     .resume = xxx_resume,  
  7.     .id_table = xxx_table,  
  8. };  

c -- 將該驅動註冊到USB子系統

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. usb_register(&xxx_usb_wifi_driver);  

 

      以上步驟只是一個大體的USB驅動框架流程,而最大和最複雜的工做是填充usb_driver結構體成員變量。以上步驟的主要工做是將USB接口的WIFI設備掛載到USB總線上,以便Linux系統在USB總線上就可以找到該設備。

二、網絡設備驅動

      接下來是網絡設備的線索,網絡設備驅動大體步驟以下:

a -- 定義一個net_device結構體變量ndev

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. struct net_device *ndev;  

 

b -- 初始化ndev變量並分配內存

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. ndev=alloc_etherdev();  

c -- 填充ndev -> netdev_ops結構體成員變量

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. static const struct net_device_ops xxx_netdev_ops= {  
  2.     .ndo_init = xxx_ndev_init,  
  3.     .ndo_uninit = xxx _ndev_uninit,  
  4.     .ndo_open = netdev_open,  
  5.     .ndo_stop = netdev_close,  
  6.     .ndo_start_xmit = xxx_xmit_entry,  
  7.     .ndo_set_mac_address = xxx_net_set_mac_address,  
  8.     .ndo_get_stats = xxx_net_get_stats,  
  9.     .ndo_do_ioctl = xxx_ioctl,  
  10. };  

d -- 填充ndev->wireless_handlers結構體成員變量,該變量是無線擴展功能

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. ndev->wireless_handlers = (struct iw_handler_def *)&xxx_handlers_def;  

e -- 將ndev設備註冊到網絡子系統

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. register_netdev(ndev);  

三、 WIFI設備自己私有的功能及屬性

      如自身的配置及初始化、創建與用戶空間的交互接口、自身功能的實現等。

a -- 自身的配置及初始化

代碼以下:                

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. xxx_read_chip_info();  
  2.   
  3. xxx_chip_configure();  
  4.   
  5. xxx_hal_init();  

b -- 主要是在proc和sys文件系統上創建與用戶空間的交互接口

代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. xxx_drv_proc_init();  
  2.   
  3. xxx_ndev_notifier_register();  

c -- 自身功能的實現

      WIFI的網絡及接入原理,如掃描等。同時因爲WIFI在移動設備中,相對功耗比較大,所以,對於功耗、電源管理也會在驅動中體現。

 

2、USB 設備驅動分析

        在分析以前,咱們須要理解在整個wifi模塊中,USB充當什麼角色?它的做用是什麼?實質上wifi模塊上的數據傳輸有兩端,一端是wifi芯片與wifi芯片之間,經過無線射頻(RF)進行數據傳輸;另外一端則是wifi芯片與CPU之間,經過USB進行數據傳輸

       瞭解Linux的USB驅動的讀者都知道,USB驅動分爲兩種:一種是USB主機驅動;另外一種是USB設備驅動。而咱們的USB接口的wifi模塊對於CPU(主機)來講,屬於USB設備,所以採用USB設備驅動。

       有了以上信息以後,咱們先讓Linux系統識別該USB接口的wifi模塊,首先咱們在驅動源碼中大體添加如下幾步工做(前面分析過,這裏只看步驟,不看代碼):

a -- 定義一個usb_driver結構體變量

b -- 填充該設備的usb_driver結構體成員變量

c -- 將該驅動註冊到USB子系統

      簡單完成以上幾步工做,再加上板級文件(arch/mach-xxx.c)對USB設備的支持,Linux的USB子系統幾乎能夠掛載該wifi模塊爲USB設備了。可是這並非咱們最終想要的結果。咱們還要讓Linux系統知道它掛載的USB設備屬於無線網絡設備,同時可以訪問它,利用它實施無線網絡的工做

     咱們都知道,若要讓USB設備真正工做起來,須要對USB設備的4個層次(設備、配置、接口、端點)進行初始化。固然這四個層次並非必定都要進行初始化,而是根據你的USB設備的功能進行選擇的,大體初始化流程以下僞代碼:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf)  
  2. {  
  3.     int    i;  
  4.     u8     val8;  
  5.     int    status= _FAIL;  
  6.     struct dvobj_priv *pdvobjpriv;  
  7.   
  8.     //設備  
  9.     struct usb_device *pusbd;  
  10.     struct usb_device_descriptor *pdev_desc;  
  11.   
  12.     //配置  
  13.     struct usb_host_config *phost_conf;  
  14.     struct usb_config_descriptor *pconf_desc;  
  15.   
  16.     //接口  
  17.     struct usb_host_interface *phost_iface;  
  18.     struct usb_interface_descriptor *piface_desc;  
  19.       
  20.     //端點  
  21.     struct usb_host_endpoint *phost_endp;  
  22.     struct usb_endpoint_descriptor *pendp_desc;  
  23.   
  24.   
  25.     //設備的初始化  
  26.     pdvobjpriv->pusbintf = usb_intf ;  
  27.     pusbd =pdvobjpriv->pusbdev = interface_to_usbdev(usb_intf);  
  28.     usb_set_intfdata(usb_intf, pdvobjpriv);  
  29.     pdev_desc =&pusbd->descriptor;  
  30.   
  31.       
  32.     //配置的初始化  
  33.     phost_conf =pusbd->actconfig;  
  34.     pconf_desc =&phost_conf->desc;  
  35.   
  36.    
  37.     //接口的初始化  
  38.     phost_iface =&usb_intf->altsetting[0];  
  39.     piface_desc =&phost_iface->desc;  
  40.   
  41.       
  42.     //端點的初始化,因爲wifi模塊屬於網絡設備,傳輸批量數據,所以須要初始化爲批量端點,端點方向(輸入、輸出)等。同時,因爲wifi驅動功能比較多,須要初始化幾個輸入輸出端點。  
  43.     for (i = 0; i <pdvobjpriv->nr_endpoint; i++)  
  44.     {  
  45.         phost_endp = phost_iface->endpoint +i;  
  46.         if (phost_endp)  
  47.         {  
  48.             pendp_desc =&phost_endp->desc;  
  49.   
  50.             //檢查是否爲輸入端點  
  51.             usb_endpoint_is_bulk_in(pendp_desc);  
  52.   
  53.             //檢查是否爲輸出端點  
  54.             usb_endpoint_is_bulk_out(pendp_desc);  
  55.         }  
  56.   
  57.     }  
  58.   
  59.     usb_get_dev(pusbd);  
  60.   
  61. }  

 

      完成以上的初始化工做以後,接下來咱們須要理清一下USB接口的做用,它是wifi芯片內部的固件程序與主機上的Linux系統進行數據通訊。USB設備通訊不像普通字符設備那樣採用I/O內存和I/O端口的訪問,而是採用一種稱爲URB(USB Request Block)的USB請求塊,URB在整個USB子系統中,至關於通電設備中的「電波」,USB主機與設備的通訊,經過「電波」來傳遞。下面咱們就來編寫USB接口的讀寫操做函數,僞代碼以下:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. void xxx_wifi_usb_intf_ops(struct _io_ops     *pops)  
  2. {  
  3.     //當須要進行簡單數據的讀取時,採用如下操做  
  4.     pops->_read8 = &usb_read8;  
  5.     pops->_read16 = &usb_read16;  
  6.     pops->_read32 = &usb_read32;  
  7.   
  8.     //當須要進行批量數據的讀取時,採用如下操做  
  9.     pops->_read_port = &usb_read_port;     
  10.   
  11.     //當須要進行簡單數據的寫時,採用如下操做  
  12.     pops->_write8 = &usb_write8;  
  13.     pops->_write16 = &usb_write16;  
  14.     pops->_write32 = &usb_write32;  
  15.     pops->_writeN = &usb_writeN;  
  16.   
  17.     //當須要進行批量數據的寫時,採用如下操做  
  18.     pops->_write_port = &usb_write_port;  
  19.   
  20.     //取消讀寫urb  
  21.     pops->_read_port_cancel = &usb_read_port_cancel;  
  22.     pops->_write_port_cancel = &usb_write_port_cancel;  
  23.   
  24. }  

 

         在進行批量數據的讀寫時,如usb_read_port()和usb_write_port()函數,須要完成urb建立、初始化、提交、完成處理這個完整的流程。僞代碼以下:

1)批量讀操做

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. static u32 usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem)  
  2. {         
  3.     int err;  
  4.     unsigned intpipe;  
  5.     PURB purb =NULL;  
  6.     structrecv_buf         *precvbuf = (structrecv_buf *)rmem;  
  7.     structusb_device    *pusbd = pdvobj->pusbdev;  
  8.   
  9.     //建立urb,這裏是在其它地方建立完成以後,傳遞過來  
  10.     purb =precvbuf->purb;  
  11.   
  12.     //初始化批量urb  
  13.     usb_fill_bulk_urb(purb, pusbd, pipe,  
  14.         precvbuf->pbuf,  
  15.         MAX_RECVBUF_SZ,  
  16.         usb_read_port_complete,  
  17.         precvbuf);//contextis precvbuf  
  18.       
  19.     //提交urb  
  20.     err =usb_submit_urb(purb, GFP_ATOMIC);  
  21.   
  22. }  

 

2)批量寫操做

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. u32 usb_write_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem)  
  2. {     
  3.     unsigned int pipe;  
  4.     intstatus;  
  5.     PURB        purb = NULL;  
  6.   
  7.     structxmit_priv       *pxmitpriv =&padapter->xmitpriv;  
  8.     structxmit_buf *pxmitbuf = (struct xmit_buf *)wmem;  
  9.     structxmit_frame *pxmitframe = (struct xmit_frame *)pxmitbuf->priv_data;  
  10.     structusb_device *pusbd = pdvobj->pusbdev;  
  11.     structpkt_attrib *pattrib = &pxmitframe->attrib;  
  12.   
  13.     //建立urb,這裏是在其它地方建立完成以後,傳遞過來  
  14.      purb = pxmitbuf->pxmit_urb[0];  
  15.   
  16.     //初始化批量urb  
  17.     usb_fill_bulk_urb(purb, pusbd, pipe,  
  18.         pxmitframe->buf_addr,//= pxmitbuf->pbuf  
  19.         cnt,  
  20.         usb_write_port_complete,  
  21.         pxmitbuf);//contextis pxmitbuf  
  22.   
  23.     //提交urb  
  24.     status = usb_submit_urb(purb,GFP_ATOMIC);  
  25.   
  26.     return ret;  
  27.   
  28. }  

        完成以上批量數據的讀寫操做以後,你們可能會疑問:這不是通常USB設備驅動的操做流程嗎?貌似和wifi沒有半毛錢的關係啊!從上面看,確實和wifi沒有任何聯繫,可是以上只是一個鋪墊。咱們一直強調USB接口在wifi模塊中充當什麼角色,既然是接口,那麼它就是爲數據傳輸而生。因此,和wifi扯上關係的就在於usb_read_port()usb_write_port()這兩個函數。

 

3、讀寫函數分析

       USB接口在wifi模塊中的最重要兩個函數是usb_read_port()和usb_write_port()。那它們是怎麼和wifi扯上關係的呢?咱們能夠從如下三個方面去分析:

a -- 首先須要明確wifi模塊是USB設備,主控(CPU)端是USB主機;

b -- USB主機若須要對wifi模塊進行數據的讀寫時,就必須通過USB接口;

c -- 既然涉及到數據的讀寫操做,必然要用相應的讀寫函數,那麼usb_read_port()和usb_write_port()便是它們的讀寫函數。

 

       咱們先從讀數據開始進行分析,在分析以前,咱們必須瞭解USB設備驅動的讀數據過程。USB讀取數據操做流程以下:

a -- 經過usb_alloc_urb()函數建立並分配一個URB,做爲傳輸USB數據的載體;

b -- 建立並分配DMA緩衝區,以DMA方式快速傳輸數據;

c -- 初始化URB,根據wifi的傳輸數據量,咱們須要初始化爲批量URB,相應操做函數爲usb_fill_bulk_urb();

d -- 將URB提交到USB核心;

e -- 提交成功後,URB的完成函數將被USB核心調用。

        咱們知道只有當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讀取數據操做流程的第三、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。

相關文章
相關標籤/搜索