转: 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结构体变量
代码以下:
- 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总线上就可以找到该设备。
二、网络设备驱动
接下来是网络设备的线索,网络设备驱动大体步骤以下:
a -- 定义一个net_device结构体变量ndev
代码以下:
b -- 初始化ndev变量并分配内存
代码以下:
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设备注册到网络子系统
代码以下:
三、 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在移动设备中,相对功耗比较大,所以,对于功耗、电源管理也会在驱动中体现。
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设备的功能进行选择的,大体初始化流程以下伪代码:
- 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芯片内部的固件程序与主机上的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;
-
-
- 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;
-
-
- 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)批量写操做
- 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;
-
- }
完成以上批量数据的读写操做以后,你们可能会疑问:这不是通常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。伪代码以下:
- 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函数中,建立一个批量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);
-
- 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()函数中,经过usb_fill_bulk_urb()初始化批量URB,而且提交给USB核心,也即USB读取数据操做流程的第三、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;
-
-
- 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模块为接收数据作初始化准备时,分配了URB和DMA缓冲区。而在usb_read_port()函数中初始化URB和提交URB。