前面學(xué)習(xí)了SDIO接口的WiFi驅(qū)動(dòng),現(xiàn)在我們來學(xué)習(xí)一下USB接口的WiFi驅(qū)動(dòng),二者的區(qū)別在于接口不同。而USB接口的設(shè)備驅(qū)動(dòng),我們前面也有學(xué)習(xí),比如USB攝像頭驅(qū)動(dòng)、USB鼠標(biāo)驅(qū)動(dòng),同樣都符合LinuxUSB驅(qū)動(dòng)結(jié)構(gòu):
? ? ? ? USB設(shè)備驅(qū)動(dòng)(字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? USB 核心
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? USB主機(jī)控制器驅(qū)動(dòng)
? ? ? ? 不同之處只是在于USB攝像頭驅(qū)動(dòng)是字符設(shè)備,而我們今天要學(xué)習(xí)的WiFi驅(qū)動(dòng)是網(wǎng)絡(luò)設(shè)備;當(dāng)然由我們編寫的部分還是USB設(shè)備驅(qū)動(dòng)部分,下面進(jìn)入U(xiǎn)SB接口WiFi驅(qū)動(dòng)的分析,如何分析呢?我們下面從這幾個(gè)方面入手:
? ? ? ? 從硬件層面上看,WIFI設(shè)備與CPU通信是通過USB接口的,與其他WIFI設(shè)備之間的通信是通過無線射頻(RF)。
? ? ? ? 從軟件層面上看,Linux操作系統(tǒng)要管理WIFI設(shè)備,那么就要將WIFI設(shè)備掛載到USB總線上,通過USB子系統(tǒng)實(shí)現(xiàn)管理。而同時(shí)為了對(duì)接網(wǎng)絡(luò),又將WIFI設(shè)備封裝成一個(gè)網(wǎng)絡(luò)設(shè)備。
? ? ? ? 我們以USB接口的WIFI模塊進(jìn)行分析:
a -- 從USB總線的角度去看,它是USB設(shè)備;
b -- 從Linux設(shè)備的分類上看,它又是網(wǎng)絡(luò)設(shè)備;
c -- 從WIFI本身的角度去看,它又有自己獨(dú)特的功能及屬性,因此它又是一個(gè)私有的設(shè)備;
通過上述的分析,我們只要抓住這三條線索深入去分析它的驅(qū)動(dòng)源碼,整個(gè)WIFI驅(qū)動(dòng)框架就會(huì)浮現(xiàn)在你眼前。
一、框架整理
1、USB設(shè)備驅(qū)動(dòng)
? ? ? 現(xiàn)在我們先從USB設(shè)備開始,要寫一個(gè)USB設(shè)備驅(qū)動(dòng),那么大致步驟如下:
a -- 需要針對(duì)該設(shè)備定義一個(gè)USB驅(qū)動(dòng),對(duì)應(yīng)到代碼中即定義一個(gè)usb_driver結(jié)構(gòu)體變量
代碼如下:
[cpp]?view plaincopy
struct?usb_driver?xxx_usb_wifi_driver;??
b -- 填充該設(shè)備的usb_driver結(jié)構(gòu)體成員變量
代碼如下:
[cpp]?view plaincopy
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 -- 將該驅(qū)動(dòng)注冊(cè)到USB子系統(tǒng)
代碼如下:
[cpp]?view plaincopy
usb_register(&xxx_usb_wifi_driver);??
? ? ? 以上步驟只是一個(gè)大致的USB驅(qū)動(dòng)框架流程,而最大和最復(fù)雜的工作是填充usb_driver結(jié)構(gòu)體成員變量。以上步驟的主要工作是將USB接口的WIFI設(shè)備掛載到USB總線上,以便Linux系統(tǒng)在USB總線上就能夠找到該設(shè)備。
2、網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)
? ? ? 接下來是網(wǎng)絡(luò)設(shè)備的線索,網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)大致步驟如下:
a -- 定義一個(gè)net_device結(jié)構(gòu)體變量ndev
代碼如下:
[cpp]?view plaincopy
struct?net_device?*ndev;??
b -- 初始化ndev變量并分配內(nèi)存
代碼如下:
[cpp]?view plaincopy
ndev=alloc_etherdev();??
c -- 填充ndev -> netdev_ops結(jié)構(gòu)體成員變量
代碼如下:
[cpp]?view plaincopy
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結(jié)構(gòu)體成員變量,該變量是無線擴(kuò)展功能
代碼如下:
[cpp]?view plaincopy
ndev->wireless_handlers?=?(struct?iw_handler_def?*)&xxx_handlers_def;??
e -- 將ndev設(shè)備注冊(cè)到網(wǎng)絡(luò)子系統(tǒng)
代碼如下:
[cpp]?view plaincopy
register_netdev(ndev);??
3、?WIFI設(shè)備本身私有的功能及屬性
? ? ? 如自身的配置及初始化、建立與用戶空間的交互接口、自身功能的實(shí)現(xiàn)等。
a -- 自身的配置及初始化
代碼如下: ? ? ? ? ? ? ? ?
[cpp]?view plaincopy
xxx_read_chip_info();?? ?? xxx_chip_configure();?? ?? xxx_hal_init();??
b -- 主要是在proc和sys文件系統(tǒng)上建立與用戶空間的交互接口
代碼如下:
[cpp]?view plaincopy
xxx_drv_proc_init();?? ?? xxx_ndev_notifier_register();??
c -- 自身功能的實(shí)現(xiàn)
? ????WIFI的網(wǎng)絡(luò)及接入原理,如掃描等。同時(shí)由于WIFI在移動(dòng)設(shè)備中,相對(duì)功耗比較大,因此,對(duì)于功耗、電源管理也會(huì)在驅(qū)動(dòng)中體現(xiàn)。
二、USB 設(shè)備驅(qū)動(dòng)分析
? ? ? ? 在分析之前,我們需要理解在整個(gè)wifi模塊中,USB充當(dāng)什么角色?它的作用是什么?實(shí)質(zhì)上wifi模塊上的數(shù)據(jù)傳輸有兩端,一端是wifi芯片與wifi芯片之間,通過無線射頻(RF)進(jìn)行數(shù)據(jù)傳輸;另一端則是wifi芯片與CPU之間,通過USB進(jìn)行數(shù)據(jù)傳輸。
? ? ? ?了解Linux的USB驅(qū)動(dòng)的讀者都知道,USB驅(qū)動(dòng)分為兩種:一種是USB主機(jī)驅(qū)動(dòng);另一種是USB設(shè)備驅(qū)動(dòng)。而我們的USB接口的wifi模塊對(duì)于CPU(主機(jī))來說,屬于USB設(shè)備,因此采用USB設(shè)備驅(qū)動(dòng)。
? ? ? ?有了以上信息之后,我們先讓Linux系統(tǒng)識(shí)別該USB接口的wifi模塊,首先我們?cè)隍?qū)動(dòng)源碼中大致添加以下幾步工作(前面分析過,這里只看步驟,不看代碼):
a -- 定義一個(gè)usb_driver結(jié)構(gòu)體變量
b -- 填充該設(shè)備的usb_driver結(jié)構(gòu)體成員變量
c -- 將該驅(qū)動(dòng)注冊(cè)到USB子系統(tǒng)
? ? ? 簡單完成以上幾步工作,再加上板級(jí)文件(arch/mach-xxx.c)對(duì)USB設(shè)備的支持,Linux的USB子系統(tǒng)幾乎可以掛載該wifi模塊為USB設(shè)備了。但是這并不是我們最終想要的結(jié)果。我們還要讓Linux系統(tǒng)知道它掛載的USB設(shè)備屬于無線網(wǎng)絡(luò)設(shè)備,同時(shí)能夠訪問它,利用它實(shí)施無線網(wǎng)絡(luò)的工作。
? ? ?我們都知道,若要讓USB設(shè)備真正工作起來,需要對(duì)USB設(shè)備的4個(gè)層次(設(shè)備、配置、接口、端點(diǎn))進(jìn)行初始化。當(dāng)然這四個(gè)層次并不是一定都要進(jìn)行初始化,而是根據(jù)你的USB設(shè)備的功能進(jìn)行選擇的,大致初始化流程如下偽代碼:
[cpp]?view plaincopy
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;?? ?? ?????? ?????? ????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芯片內(nèi)部的固件程序與主機(jī)上的Linux系統(tǒng)進(jìn)行數(shù)據(jù)通信。USB設(shè)備通信不像普通字符設(shè)備那樣采用I/O內(nèi)存和I/O端口的訪問,而是采用一種稱為URB(USB Request Block)的USB請(qǐng)求塊,URB在整個(gè)USB子系統(tǒng)中,相當(dāng)于通電設(shè)備中的“電波”,USB主機(jī)與設(shè)備的通信,通過“電波”來傳遞。下面我們就來編寫USB接口的讀寫操作函數(shù),偽代碼如下:
[cpp]?view plaincopy
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;?? ?? ?????? ????pops->_read_port_cancel?=?&usb_read_port_cancel;?? ????pops->_write_port_cancel?=?&usb_write_port_cancel;?? ?? }??
? ? ? ? ?在進(jìn)行批量數(shù)據(jù)的讀寫時(shí),如usb_read_port()和usb_write_port()函數(shù),需要完成urb創(chuàng)建、初始化、提交、完成處理這個(gè)完整的流程。偽代碼如下:
1)批量讀操作
[cpp]?view plaincopy
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;?? ?? ?????? ????purb?=precvbuf->purb;?? ?? ?????? ????usb_fill_bulk_urb(purb,?pusbd,?pipe,?? ????????precvbuf->pbuf,?? ????????MAX_RECVBUF_SZ,?? ????????usb_read_port_complete,?? ????????precvbuf);?? ?????? ?????? ????err?=usb_submit_urb(purb,?GFP_ATOMIC);?? ?? }??
2)批量寫操作
[cpp]?view plaincopy
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;?? ?? ?????? ?????purb?=?pxmitbuf->pxmit_urb[0];?? ?? ?????? ????usb_fill_bulk_urb(purb,?pusbd,?pipe,?? ????????pxmitframe->buf_addr,?? ????????cnt,?? ????????usb_write_port_complete,?? ????????pxmitbuf);?? ?? ?????? ????status?=?usb_submit_urb(purb,GFP_ATOMIC);?? ?? ????return?ret;?? ?? }??
? ? ? ? 完成以上批量數(shù)據(jù)的讀寫操作之后,大家可能會(huì)疑問:這不是一般USB設(shè)備驅(qū)動(dòng)的操作流程嗎?貌似和wifi沒有半毛錢的關(guān)系啊!從上面看,確實(shí)和wifi沒有任何聯(lián)系,但是以上只是一個(gè)鋪墊。我們一直強(qiáng)調(diào)USB接口在wifi模塊中充當(dāng)什么角色,既然是接口,那么它就是為數(shù)據(jù)傳輸而生。所以,和wifi扯上關(guān)系的就在于usb_read_port()和usb_write_port()這兩個(gè)函數(shù)。
三、讀寫函數(shù)分析
? ? ? ?USB接口在wifi模塊中的最重要兩個(gè)函數(shù)是usb_read_port()和usb_write_port()。那它們是怎么和wifi扯上關(guān)系的呢?我們可以從以下三個(gè)方面去分析:
a -- 首先需要明確wifi模塊是USB設(shè)備,主控(CPU)端是USB主機(jī);
b -- USB主機(jī)若需要對(duì)wifi模塊進(jìn)行數(shù)據(jù)的讀寫時(shí),就必須經(jīng)過USB接口;
c -- 既然涉及到數(shù)據(jù)的讀寫操作,必然要用相應(yīng)的讀寫函數(shù),那么usb_read_port()和usb_write_port()即是它們的讀寫函數(shù)。
? ? ? ?我們先從讀數(shù)據(jù)開始進(jìn)行分析,在分析之前,我們必須了解USB設(shè)備驅(qū)動(dòng)的讀數(shù)據(jù)過程。USB讀取數(shù)據(jù)操作流程如下:
a -- 通過usb_alloc_urb()函數(shù)創(chuàng)建并分配一個(gè)URB,作為傳輸U(kuò)SB數(shù)據(jù)的載體;
b -- 創(chuàng)建并分配DMA緩沖區(qū),以DMA方式快速傳輸數(shù)據(jù);
c -- 初始化URB,根據(jù)wifi的傳輸數(shù)據(jù)量,我們需要初始化為批量URB,相應(yīng)操作函數(shù)為usb_fill_bulk_urb();
d -- 將URB提交到USB核心;
e -- 提交成功后,URB的完成函數(shù)將被USB核心調(diào)用。
? ? ? ? 我們知道只有當(dāng)wifi模塊有數(shù)據(jù)可讀時(shí),主控端才能成功地讀取數(shù)據(jù)。那么wifi模塊什么時(shí)候有數(shù)據(jù)可讀呢?——下面重點(diǎn)來了!wifi模塊通過RF端接收到無線網(wǎng)絡(luò)數(shù)據(jù),然后緩存到wifi芯片的RAM中,此時(shí),wifi模塊就有數(shù)據(jù)可讀了。
? ? ? ?經(jīng)過上面的分析,我們找到了一條USB接口與wifi模塊扯上關(guān)系的線索,就是wifi模塊的接收數(shù)據(jù),會(huì)引發(fā)USB接口的讀數(shù)據(jù);
? ? ? 現(xiàn)在,我們轉(zhuǎn)到wifi模塊的接收函數(shù)中,看看是不是真的這樣?
? ? ? 在wifi接收函數(shù)初始化中,我們可以看到usb_alloc_urb()創(chuàng)建一個(gè)中斷URB。偽代碼如下:
[cpp]?view plaincopy
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);??? ???? ????precvpriv->int_in_buf?=?rtw_zmalloc(INTERRUPT_MSG_FORMAT_LEN);???? ?????? ????_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函數(shù)中,創(chuàng)建一個(gè)批量URB和一個(gè)DMA緩沖區(qū)。偽代碼如下:
[cpp]?view plaincopy
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);??? ???? ????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);???? ????precvbuf->pbuf?=?precvbuf->pallocated_buf;???? ????if(precvbuf->pallocated_buf?==?NULL)???? ????????return?_FAIL;???? ????#endif?//CONFIG_USE_USB_BUFFER_ALLOC_RX???? ???????? ????return?res;???? }????
? ? ? 在usb_read_port()函數(shù)中,通過usb_fill_bulk_urb()初始化批量URB,并且提交給USB核心,也即USB讀取數(shù)據(jù)操作流程的第3、4步。在usb_fill_bulk_urb()函數(shù)中,初始化URB的完成函數(shù)usb_read_port_complete(),只有當(dāng)URB提交完成后,函數(shù)usb_read_port_complete()將被調(diào)用。偽代碼如下:
[cpp]?view plaincopy
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;???? ???? ?????? ????pipe?=?ffaddr2pipehdl(pdvobj,?addr);???? ???? ????usb_fill_bulk_urb(purb,?pusbd,?pipe,????? ????????????????????precvbuf->pbuf,???? ????????????????????????????MAX_RECVBUF_SZ,???? ????????????????????????????usb_read_port_complete,???? ????????????????????????????precvbuf);?? ???? ????err?=?usb_submit_urb(purb,?GFP_ATOMIC);???? ???? ????return?ret;???? }????
? ? ?通過上面的代碼,我們可以得知在wifi模塊為接收數(shù)據(jù)做初始化準(zhǔn)備時(shí),分配了URB和DMA緩沖區(qū)。而在usb_read_port()函數(shù)中初始化URB和提交URB。
< 未完待續(xù).....>
總結(jié)
以上是生活随笔為你收集整理的Linux 下wifi 驱动开发(四)—— USB接口WiFi驱动浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。