轉: http://blog.csdn.net/zqixiao_09/article/details/51146149
前面學習了SDIO接口的WiFi驅動,現在我們來學習一下USB接口的WiFi驅動,二者的區別在於接口不同。而USB接口的設備驅動,我們前面也有學習,比如USB攝像頭驅動、USB鼠標驅動,同樣都符合LinuxUSB驅動結構:
USB設備驅動(字符設備、塊設備、網絡設備)
|
USB 核心
|
USB主機控制器驅動
不同之處只是在於USB攝像頭驅動是字符設備,而我們今天要學習的WiFi驅動是網絡設備;當然由我們編寫的部分還是USB設備驅動部分,下面進入USB接口WiFi驅動的分析,如何分析呢?我們下面從這幾個方面入手:
從硬件層面上看,WIFI設備與CPU通信是通過USB接口的,與其他WIFI設備之間的通信是通過無線射頻(RF)。
從軟件層面上看,Linux操作系統要管理WIFI設備,那么就要將WIFI設備掛載到USB總線上,通過USB子系統實現管理。而同時為了對接網絡,又將WIFI設備封裝成一個網絡設備。
我們以USB接口的WIFI模塊進行分析:
a -- 從USB總線的角度去看,它是USB設備;
b -- 從Linux設備的分類上看,它又是網絡設備;
c -- 從WIFI本身的角度去看,它又有自己獨特的功能及屬性,因此它又是一個私有的設備;
通過上述的分析,我們只要抓住這三條線索深入去分析它的驅動源碼,整個WIFI驅動框架就會浮現在你眼前。
一、框架整理
1、USB設備驅動
現在我們先從USB設備開始,要寫一個USB設備驅動,那么大致步驟如下:
a -- 需要針對該設備定義一個USB驅動,對應到代碼中即定義一個usb_driver結構體變量
代碼如下:
- struct usb_driver xxx_usb_wifi_driver;
b -- 填充該設備的usb_driver結構體成員變量
代碼如下:
- static struct usb_driver xxx_usb_wifi_driver = {
- .name = "XXX_USB_WIFI",
- .probe = xxx_probe,
- .disconnect = xxx_disconnect,
- .suspend = xxx_suspend,
- .resume = xxx_resume,
- .id_table = xxx_table,
- };
c -- 將該驅動注冊到USB子系統
代碼如下:
- usb_register(&xxx_usb_wifi_driver);
以上步驟只是一個大致的USB驅動框架流程,而最大和最復雜的工作是填充usb_driver結構體成員變量。以上步驟的主要工作是將USB接口的WIFI設備掛載到USB總線上,以便Linux系統在USB總線上就能夠找到該設備。
2、網絡設備驅動
接下來是網絡設備的線索,網絡設備驅動大致步驟如下:
a -- 定義一個net_device結構體變量ndev
代碼如下:
- struct net_device *ndev;
b -- 初始化ndev變量並分配內存
代碼如下:
- ndev=alloc_etherdev();
c -- 填充ndev -> netdev_ops結構體成員變量
代碼如下:
- static const struct net_device_ops xxx_netdev_ops= {
- .ndo_init = xxx_ndev_init,
- .ndo_uninit = xxx _ndev_uninit,
- .ndo_open = netdev_open,
- .ndo_stop = netdev_close,
- .ndo_start_xmit = xxx_xmit_entry,
- .ndo_set_mac_address = xxx_net_set_mac_address,
- .ndo_get_stats = xxx_net_get_stats,
- .ndo_do_ioctl = xxx_ioctl,
- };
d -- 填充ndev->wireless_handlers結構體成員變量,該變量是無線擴展功能
代碼如下:
- ndev->wireless_handlers = (struct iw_handler_def *)&xxx_handlers_def;
e -- 將ndev設備注冊到網絡子系統
代碼如下:
- register_netdev(ndev);
3、 WIFI設備本身私有的功能及屬性
如自身的配置及初始化、建立與用戶空間的交互接口、自身功能的實現等。
a -- 自身的配置及初始化
代碼如下:
- xxx_read_chip_info();
- xxx_chip_configure();
- xxx_hal_init();
b -- 主要是在proc和sys文件系統上建立與用戶空間的交互接口
代碼如下:
- xxx_drv_proc_init();
- xxx_ndev_notifier_register();
c -- 自身功能的實現
WIFI的網絡及接入原理,如掃描等。同時由於WIFI在移動設備中,相對功耗比較大,因此,對於功耗、電源管理也會在驅動中體現。
二、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設備的功能進行選擇的,大致初始化流程如下偽代碼:
- static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf)
- {
- int i;
- u8 val8;
- int status= _FAIL;
- struct dvobj_priv *pdvobjpriv;
- //設備
- struct usb_device *pusbd;
- struct usb_device_descriptor *pdev_desc;
- //配置
- struct usb_host_config *phost_conf;
- struct usb_config_descriptor *pconf_desc;
- //接口
- struct usb_host_interface *phost_iface;
- struct usb_interface_descriptor *piface_desc;
- //端點
- struct usb_host_endpoint *phost_endp;
- struct usb_endpoint_descriptor *pendp_desc;
- //設備的初始化
- pdvobjpriv->pusbintf = usb_intf ;
- pusbd =pdvobjpriv->pusbdev = interface_to_usbdev(usb_intf);
- usb_set_intfdata(usb_intf, pdvobjpriv);
- pdev_desc =&pusbd->descriptor;
- //配置的初始化
- phost_conf =pusbd->actconfig;
- pconf_desc =&phost_conf->desc;
- //接口的初始化
- phost_iface =&usb_intf->altsetting[0];
- piface_desc =&phost_iface->desc;
- //端點的初始化,由於wifi模塊屬於網絡設備,傳輸批量數據,因此需要初始化為批量端點,端點方向(輸入、輸出)等。同時,由於wifi驅動功能比較多,需要初始化幾個輸入輸出端點。
- for (i = 0; i <pdvobjpriv->nr_endpoint; i++)
- {
- phost_endp = phost_iface->endpoint +i;
- if (phost_endp)
- {
- pendp_desc =&phost_endp->desc;
- //檢查是否為輸入端點
- usb_endpoint_is_bulk_in(pendp_desc);
- //檢查是否為輸出端點
- usb_endpoint_is_bulk_out(pendp_desc);
- }
- }
- usb_get_dev(pusbd);
- }
完成以上的初始化工作之后,接下來我們需要理清一下USB接口的作用,它是wifi芯片內部的固件程序與主機上的Linux系統進行數據通信。USB設備通信不像普通字符設備那樣采用I/O內存和I/O端口的訪問,而是采用一種稱為URB(USB Request Block)的USB請求塊,URB在整個USB子系統中,相當於通電設備中的“電波”,USB主機與設備的通信,通過“電波”來傳遞。下面我們就來編寫USB接口的讀寫操作函數,偽代碼如下:
- void xxx_wifi_usb_intf_ops(struct _io_ops *pops)
- {
- //當需要進行簡單數據的讀取時,采用以下操作
- pops->_read8 = &usb_read8;
- pops->_read16 = &usb_read16;
- pops->_read32 = &usb_read32;
- //當需要進行批量數據的讀取時,采用以下操作
- pops->_read_port = &usb_read_port;
- //當需要進行簡單數據的寫時,采用以下操作
- pops->_write8 = &usb_write8;
- pops->_write16 = &usb_write16;
- pops->_write32 = &usb_write32;
- pops->_writeN = &usb_writeN;
- //當需要進行批量數據的寫時,采用以下操作
- pops->_write_port = &usb_write_port;
- //取消讀寫urb
- pops->_read_port_cancel = &usb_read_port_cancel;
- pops->_write_port_cancel = &usb_write_port_cancel;
- }
在進行批量數據的讀寫時,如usb_read_port()和usb_write_port()函數,需要完成urb創建、初始化、提交、完成處理這個完整的流程。偽代碼如下:
1)批量讀操作
- static u32 usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem)
- {
- int err;
- unsigned intpipe;
- PURB purb =NULL;
- structrecv_buf *precvbuf = (structrecv_buf *)rmem;
- structusb_device *pusbd = pdvobj->pusbdev;
- //創建urb,這里是在其它地方創建完成之后,傳遞過來
- purb =precvbuf->purb;
- //初始化批量urb
- usb_fill_bulk_urb(purb, pusbd, pipe,
- precvbuf->pbuf,
- MAX_RECVBUF_SZ,
- usb_read_port_complete,
- precvbuf);//contextis precvbuf
- //提交urb
- err =usb_submit_urb(purb, GFP_ATOMIC);
- }
2)批量寫操作
- u32 usb_write_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem)
- {
- unsigned int pipe;
- intstatus;
- PURB purb = NULL;
- structxmit_priv *pxmitpriv =&padapter->xmitpriv;
- structxmit_buf *pxmitbuf = (struct xmit_buf *)wmem;
- structxmit_frame *pxmitframe = (struct xmit_frame *)pxmitbuf->priv_data;
- structusb_device *pusbd = pdvobj->pusbdev;
- structpkt_attrib *pattrib = &pxmitframe->attrib;
- //創建urb,這里是在其它地方創建完成之后,傳遞過來
- purb = pxmitbuf->pxmit_urb[0];
- //初始化批量urb
- usb_fill_bulk_urb(purb, pusbd, pipe,
- pxmitframe->buf_addr,//= pxmitbuf->pbuf
- cnt,
- usb_write_port_complete,
- pxmitbuf);//contextis pxmitbuf
- //提交urb
- status = usb_submit_urb(purb,GFP_ATOMIC);
- return ret;
- }
完成以上批量數據的讀寫操作之后,大家可能會疑問:這不是一般USB設備驅動的操作流程嗎?貌似和wifi沒有半毛錢的關系啊!從上面看,確實和wifi沒有任何聯系,但是以上只是一個鋪墊。我們一直強調USB接口在wifi模塊中充當什么角色,既然是接口,那么它就是為數據傳輸而生。所以,和wifi扯上關系的就在於usb_read_port()和usb_write_port()這兩個函數。
三、讀寫函數分析
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。偽代碼如下:
- 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。