Linux内核USB驱动架构:USB卷一_DS部份
Linuxlinux硬件驱动架构
Linuxlinux硬件驱动架构linux硬件驱动(usb)??模块:模块用来装载到内核中,用来实现设备驱动程序。
??linux对于一个硬件的驱动,采用两种方式读取:??1.直接加载到内核代码中,启动内核时就会驱动此硬件设备??2.以模块方式读取,编程分解成一个.o的文件,当应用程序须要时读取入内核空间运转??so(通常说的硬件驱动其实就是一个硬件驱动模块及.o文件)??设备文件(设备节点):??设备文件(设备节点)指定(主设备号)&&(次设备号)??主设备号:对应着确认的驱动程序。
??(声明设备所使用的驱动程序,设备号相当于硬件驱动程序的一个标识)??次设备号:区分相同属性,相同采用方法,相同边线,相同操作方式??设备号从/proc/drives中获取,so先有驱动程序在内核中,才有设备节点在目录中。
??scsi(并口):通常采用的的usb存储设备,就是演示scsi硬盘而展开设计的。
??linux硬件驱动架构:??.o驱动模块文件--(如果须要采用这个驱动程序,首先必须读取运转它)-->insmod*.o--(驱动程序根据字符设备类型或块设备类型(鼠标属字符设备,硬盘属块设备))向系统登记注册-->登记注册顺利之后系统回到一个主设备号---(根据主设备号建立一个置放在/dev目录下的设备文件)-->(mknod用以建立设备文件须要使用设备号这个参数)----->我们出访硬件时,就可以对设备文件通过open,read,write等命令展开,而驱动就可以发送至适当的read,write操作方式而根据自己模块中的适当函数展开。
??上层调用api.o驱动drive.o??与模块有关的一些东西:??1./lib/modules/2.6.**目录,下面是针对当前内核版本的模块。
??2.查阅模块的倚赖关系与否恰当(depmod设置)??3.加载模块而不需要知道具体的模块位置(modprobe)??4.文件/etc/modules.conf文件,当kernel须要时轻易回去该文件中搜寻别称读取??modprobe用于加载系统已经通过depmod登记过的模块,insmod一般是针对具体的.o进行文件的加载。
Linux内核USB驱动架构 USB设备驱动架构
1
五者关系图如下:
usb_device{} .descriptor *config *ep_in[] *ep_out[] ...... 设备
usb_device_descriptor{} 设备描述符
.bNumConfigurations usb_host_config{}[]
......
多个配置
usb_host_endpoint{}*[] ......
.bNumEndpoints
usb_host_endpoint{}[] ......
多个端点
usb_host_endpoint{} .desc .urb_list ...... 一个端点
usb_endpoint_descriptor{} 端点描述符
.bEndpointAddress .bmAttributes
3
成员变量及意义解说:
设备描述符 usb_device_descriptor{}
.bLength .bDescriptorType .bDeviceClass .bDeviceSubClass .bDeviceProtocol .bMaxPacketSize0 .idVendor .idProduct
1.每个端点拥有一个 urb_list 2.urb 的数据从端点进行收发 3.HCD 每收到一个 urb,就
Linux下的硬件驱动——USB设备
Linux下的硬件驱动——USB设备什么是USB设备?USB即Universal Serial Bus,翻译过来就是通用串行总线。
它是一种规范化的、快速的、热插拔的串行输入/输出接口。
USB接口常被用于连接鼠标、键盘、打印机、扫描仪、音频设备、存储设备等外围设备。
Linux下的USB驱动在Linux系统中,每个USB设备都需要一个相应的驱动程序来驱动。
从Linux 2.4开始,内核提供了完整的USB设备支持。
对于每个USB设备,内核都会自动加载对应的驱动程序。
Linux下的USB设备驱动程序主要分为以下几个部分:USB核心驱动程序USB核心驱动程序是操作系统内核中处理USB设备的核心模块,负责与各种类型的USB设备进行通信,包括主机控制器、USB总线、USB设备等。
它与驱动程序和应用程序之间起到了桥梁的作用,为驱动程序提供了USB设备的基础支持。
USB设备驱动程序USB设备驱动程序是与特定USB设备相对应的驱动程序,为USB设备提供具体的读写功能和其他控制功能。
USB核心驱动程序和USB设备驱动程序之间的接口USB核心驱动程序和USB设备驱动程序之间的接口是指USB层和应用程序层之间的接口,负责传递各种USB操作的命令和数据。
如何编译一个USB设备驱动编译一个USB设备驱动程序需要按照以下步骤进行:步骤一:安装必要的软件包首先需要安装编译和调试USB设备驱动所需的软件包,包括编译工具链、内核源代码、内核头文件等。
sudo apt-get install build-essential linux-source linux-headers-`una me -r`步骤二:编写代码现在可以编写USB设备驱动程序的代码,此处不做详细介绍。
步骤三:编译代码在终端窗口中进入USB设备驱动程序所在的目录下,输入以下命令进行编译:make此命令将会编译USB设备驱动程序,并生成一个将驱动程序与内核进行连接的模块文件。
Linux下USBcore的工作原理及设备驱动技术Linux
Linux下USBcore的工作原理及设备驱动技术LinuxLinux下USB core的工作原理及设备驱动技术Linux下USB core的工作原理及设备驱动技术Linux下USB core的工作原理及设备驱动技术Linux以其稳定、高效、易定制、硬件支持广泛、源代码开放等特点,已在嵌入式领域迅速崛起,被国际上许多大型的跨国企业用作嵌入式产品的系统平台。
USB是Universal Serial Bus (通用串行总线)的缩写,是1995年由Microsoft、Compaq、IBM等公司联合制定的一种新的PC串行通信协议。
它是一种快速、灵活的总线接口。
与其它通信接口相比较,USB接口的最大特点是易于使用,这也是USB的主要设计目标。
USB 的成功得益于在USB标准中除定义了通信的物理层和电器层标准外。
还定义了一套相对完整的软件协议堆栈。
这使得多数USB设备都很容易在各种平台上工作。
作为一种高速总线接口,USB适用于多种设备(如数码相机、MP3播放器、高速数据采集设备等)。
另外,USB接口还支持热插拔,而且所有的配置过程都由系统自动完成,无须用户干预。
1 Linux下的USB设备驱动在Linux内核的不断升级过程中,驱动程序的结构相对稳定。
由于USB设备也是外围设备的一种,因此,它的驱动程序结构与普通设备的驱动程序相同。
Linux系统的设备分为字符设备(CharDevice)和块设备(BlockDevice)。
字符设备支持面向块字符的I/O操作,它不通过系统的快速缓存,而只支持顺序存取。
块设备则支持面向块的I/O 操作,所有块设备的I/O操作都通过在内核地址空间的I/O缓冲区进行,可以支持几乎任意长度和任意位置上的I/O请求。
块设备与字符设备还有一点不同,就是块设备必须能够随机存取(RandomAccess),字符设备则没有这个要求。
典型的字符设备包括鼠标、键盘、串行口等,而块设备主要包括硬盘软盘设备、CD-Rom 等。
Linux下的硬件驱动——USB设备
Linux下的硬件驱动——USB设备(上)(驱动配置部分)USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题。
本文着力从Linux系统下设备驱动的架构,去阐述怎样去使用和配置以及怎样编制USB设备驱动。
对于一般用户,可以使我们明晰Linux设备驱动方式,为更好地配置和使用USB设备提供了方便;而对于希望开发Linux系统下USB设备驱动的程序员,提供了初步学习USB驱动架构的机会。
前言USB是英文"Universal Serial Bus"的缩写,意为"通用串行总线"。
是由Compaq(康柏)、DEC、IBM、Intel、NEC、微软以及Northern Telecom(北方电讯)等公司于1994年11月共同提出的,主要目的就是为了解决接口标准太多的弊端。
USB使用一个4针插头作为标准插头,并通过这个标准接头,采用菊花瓣形式把所有外设连接起来,它采用串行方式传输数据,目前最大数据传输率为12Mbps, 支持多数据流和多个设备并行操作,允许外设热插拔。
目前USB接口虽然只发展了2代(USB1.0/1.1,USB2.0),但是USB综合了一个多平台标准的所有优点 -- 包括降低成本,增加兼容性,可连接大量的外部设备,融合先进的功能和品质。
使其逐步成为PC接口标准,进入了高速发展期。
那么对于使用Linux系统,正确支持和配置常见的USB设备,就是其使用必不可少的关键一步。
相关技术基础模块(驱动程序)模块(module)是在内核空间运行的程序,实际上是一种目标对象文件,没有链接,不能独立运行,但是可以装载到系统中作为内核的一部分运行,从而可以动态扩充内核的功能。
模块最主要的用处就是用来实现设备驱动程序。
Linux下对于一个硬件的驱动,可以有两种方式:直接加载到内核代码中,启动内核时就会驱动此硬件设备。
LinuxUSB3.0驱动分析(一)——USB设备基础概念
LinuxUSB3.0驱动分析(⼀)——USB设备基础概念⼀.基础概念在终端⽤户看来,USB设备为主机提供了多种多样的附加功能,如⽂件传输,声⾳播放等,但对USB主机来说,它与所有USB设备的接⼝都是⼀致的。
⼀个USB设备由3个功能模块组成:USB总线接⼝、USB逻辑设备和功能单元:a -- 这⾥的USB总线接⼝指的是USB设备中的串⾏接⼝引擎(SIE);b -- USB逻辑设备被USB系统软件看作是⼀个端点的集合;c -- 功能单元被客户软件看作是⼀个接⼝的集合。
SIE、端点和接⼝都是USB设备的组成单元;为了更好地描述USB设备的特征,USB提出了设备架构的概念。
从这个⾓度来看,可以认为USB设备是由⼀些配置、接⼝和端点组成,即⼀个USB设备可以含有⼀个或多个配置,在每个配置中可含有⼀个或多个接⼝,在每个接⼝中可含有若⼲个端点。
其中,配置和接⼝是对USB设备功能的抽象,实际的数据传输由端点来完成。
在使⽤USB设备前,必须指明其采⽤的配置和接⼝。
这个步骤⼀般是在设备接⼊主机时设备进⾏枚举时完成的这些单元之间的关系如下:设备通常有⼀个或多个配置;配置通常有⼀个或多个接⼝;接⼝通常有⼀个或多个设置;接⼝有零或多个端点。
这样的概念太抽象了,可以这样看:有⼀个设备,如⽀持视频和⾳频的⼀个播放器。
那么,对于上⾯提到的4个描述符,对它们设置的时候,它们分别对于哪⼀个描述符呢?从我现在的理解来看,这样⼀个设备对应⼀个设备描述符,⽀持视频的功能对应⼀个接⼝描述符,⽀持⾳频功能的对应⼀个接⼝描述符。
为了⽀持视频,在下层有多个端⼝同时⼯作为提供视频数据传输的⽀持,所以有多个端点描述符。
⼆.USB描述符USB设备使⽤各种描述符来说明其设备架构,包括设备描述符、配置描述符、接⼝描述符、端点描述符和字符串描述符,他们通常被保存在USB设备的固件程序中。
路径:include/uapi/linux/usb/ch9.h1、设备描述符设备代表⼀个USB设备,它由⼀个或多个配置组成。
Linux内核USB驱动程序框架
25.2 USB驱动程序框架Linux内核提供了完整的USB驱动程序框架。
USB总线采用树形结构,在一条总线上只能有唯一的主机设备。
Linux内核从主机和设备两个角度观察USB总线结构。
本节介绍Linux内核USB驱动程序框架。
25.2.1 Linux内核USB驱动框架图25-2是Linux内核从主机和设备两个角度观察USB总线结构的示意图。
从图25-2中可以看出,Linux内核USB驱动是按照主机驱动和设备驱动两套体系实现的,下面介绍两套体系的结构和特点。
1.基本结构图25-2的左侧是主机驱动结构。
主机驱动的最底层是USB主机控制器,提供了OHCI/EHCI/UHCI这3种类型的总线控制功能。
在USB控制器的上一层是主机控制器的驱动,分别对应OHCI/EHCI/UHCI这3种类型的总线接口。
USB核心部分连接了USB控制器驱动和设备驱动,是两者之间的转换接口。
USB设备驱动层提供了各种设备的驱动程序。
USB主机部分的设计结构完全是从USB总线特点出发的。
在USB总线上可以连接各种不同类型的设备,包括字符设备、块设备和网络设备。
所有类型的USB设备都是用相同的电气接口,使用的传输协议也基本相同。
向用户提供某种特定类型的USB设备时,需要处理USB总线协议。
内核完成所有的USB总线协议处理,并且向用户提供编程接口。
图25-2 Linux内核USB总线结构图25-2右侧是设备驱动结构。
与USB主机类似,USB设备提供了相同的层次结构与之对应。
但是在USB设备一侧使用名为Gadget API的结构作为核心。
Gadget API是Linux内核实现的对应USB设备的核心结构。
Gadget API屏蔽了USB设备控制器的细节,控制具体的USB设备实现。
2.设备每个USB设备提供了不同级别的配置信息。
一个USB设备可以包含一个或多个配置,不同的配置使设备表现出不同的特点。
其中,设备的配置是通过接口组成的。
Linux内核定义了USB设备描述结构如下:struct usb_device_descriptor {__u8 bLength; // 设备描述符长度__u8 bDescriptorType; // 设备类型__le16 bcdUSB; // USB版本号(使用BCD编码)__u8 bDeviceClass; // USB设备类型__u8 bDeviceSubClass; // USB设备子类型__u8 bDeviceProtocol; // USB设备协议号__u8 bMaxPacketSize0; // 传输数据的最大包长__le16 idVendor; // 厂商编号__le16 idProduct; // 产品编号__le16 bcdDevice; // 设备出厂号__u8 iManufacturer; // 厂商字符串索引__u8 iProduct; // 产品字符串索引__u8 iSerialNumber; // 产品序列号索引__u8 bNumConfigurations; // 最大的配置数量} __attribute__ ((packed));从usb_device_descriptor结构定义看出,一个设备描述符定义了与USB设备有关的所有信息。
Linux设备驱动程序学习(17)-USB 驱动程序(二)
Linux设备驱动程序学习(17)-USB 驱动程序(二) (2009-07-14 00:38) 分类:Linux设备驱动程序内核使用2.6.29.4USB设备其实很复杂,但是Linux内核提供了一个称为USB core的子系统来处理了大部分的复杂工作,所以这里所描述的是驱动程序和USB core之间的接口。
在USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。
对于这四个层次的简单描述如下:设备通常具有一个或多个的配置配置经常具有一个或多个的接口接口通常具有一个或多个的设置接口没有或具有一个以上的端点设备很明显,地代表了一个插入的USB设备,在内核使用数据结构 struct usb_device来描述整个USB设备。
(include/linux/usb.h)struct device dev;//一般的设备接口数据结构struct usb_device_descriptor descriptor;//USB设备描述符struct usb_host_config *config;//设备的所有配置struct usb_host_config *actconfig;//被激活的设备配置struct usb_host_endpoint *ep_in[16];//输入端点数组struct usb_host_endpoint *ep_out[16];//输出端点数组char**rawdescriptors;//每个配置的raw描述符unsigned short bus_mA;//可使用的总线电流u8 portnum;//父端口号u8 level;//USB HUB的层数unsigned can_submit:1;//URB可被提交标志unsigned discon_suspended:1;//暂停时断开标志unsigned persist_enabled:1;//USB_PERSIST使能标志unsigned have_langid:1;//string_langid存在标志unsigned authorized:1;unsigned authenticated:1;unsigned wusb:1;//无线USB标志int string_langid;//字符串语言ID/* static strings from the device *///设备的静态字符串char*product;//产品名char*manufacturer;//厂商名char*serial;//产品串号struct list_head filelist;//此设备打开的usbfs文件#ifdef CONFIG_USB_DEVICE_CLASSstruct device *usb_classdev;//用户空间访问的为usbfs设备创建的USB 类设备#endif#ifdef CONFIG_USB_DEVICEFSstruct dentry *usbfs_dentry;//设备的usbfs入口#endifint maxchild;//(若为HUB)接口数struct usb_device *children[USB_MAXCHILDREN];//连接在这个HUB 上的子设备int pm_usage_cnt;//自动挂起的使用计数u32 quirks;atomic_t urbnum;//这个设备所提交的URB计数unsigned long active_duration;//激活后使用计时#ifdef CONFIG_PM //电源管理相关struct delayed_work autosuspend;//自动挂起的延时struct work_struct autoresume;//(中断的)自动唤醒需求struct mutex pm_mutex;//PM的互斥锁unsigned long last_busy;//最后使用的时间int autosuspend_delay;unsigned long connect_time;//第一次连接的时间配置一个USB设备可以有多个配置,并可在它们之间转换以改变设备的状态。
LinuxUSB驱动框架分析研究
Linux USB驱动框架分析(一)事实上,Linux地设备驱动都遵循一个惯例——表征驱动程序(用driver更贴切一些,应该称为驱动器比较好吧)地结构体,结构体里面应该包含了驱动程序所需要地所有资源.用OO地术语来说,就是这个驱动器对象所拥有地属性及成员.由于Linux地内核用c来编写,所以我们也按照这种结构化地思想来分析代码,但我还是希望从OO地角度来阐述这些细节.这个结构体地名字有驱动开发人员决定,比如说,鼠标可能有一个叫做mouse_dev 地struct,键盘可能由一个keyboard_dev地struct(dev fordevice,我们做地只是设备驱动).而这次我们来分析一下Linux内核源码中地一个usb-skeleton(就是usb驱动地骨架咯),自然,他定义地设备结构体就叫做usb-skel:struct usb_skel {struct usb_device * udev; /* the usb device for this device */struct usb_interface * interface; /* the interface for this device */struct semaphore limit_sem; /* limiting the number of writes in progress */unsigned char * bulk_in_buffer; /* the buffer to receive data */size_t bulk_in_size; /* the size of the receive buffer */__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */struct kref kref;};这里我们得补充说明一下一些USB地协议规范细节.USB能够自动监测设备,并调用相应地驱动程序处理设备,所以其规范实际上是相当复杂地,幸好,我们不必理会大部分细节问题,因为Linux已经提供相应地解决方案.就我现在地理解来说,USB地驱动分为两块,一块是USB地bus驱动,这个东西,Linux内核已经做好了,我们可以不管,但我们至少要了解他地功能.形象得说,USB地bus驱动相当于铺出一条路来,让所有地信息都可以通过这条USB通道到达该到地地方,这部分工作由usb_core来完成.当设备接到USB接口时,usb_core就检测该设备地一些信息,例如生产厂商ID和产品地ID,或者是设备所属地class、subclass跟protocol,以便确定应该调用哪一个驱动处理该设备.里面复杂细节我们不用管,我们要做地是另一块工作——usb地设备驱动.也就是说,我们就等着usb_core告诉我们要工作了,我们才工作.对于usb规范定义地设备,他们有一个设备地框架,对于开发人员来说,他大概如图所示:从开发人员地角度看,每一个usb设备有若干个配置(configuration)组成,每个配置又可以有多个接口(interface),每个接口又有多个设置(setting图中没有给出),而接口本身可能没有端点或者多个端点(endpoint).USB地数据交换通过端点来进行,主机与各个端点之间建立起单向地管道来传输数据.而这些接口可以分为四类:控制(control)用于配置设备、获取设备信息、发送命令或者获取设备地状态报告中断(interrupt)当USB宿主要求设备传输数据时,中断端点会以一个固定地速率传送少量数据,还用于发送数据到USB设备以控制设备,一般不用于传送大量数据.批量(bulk)用于大量数据地可靠传输,如果总线上地空间不足以发送整个批量包,它会被分割成多个包传输.等时(isochronous)大量数据地不可靠传输,不保证数据地到达,但保证恒定地数据流,多用于数据采集.Linux中用struct usb_host_endpoint来描述USB端点,每个usb_host_endpoint中包含一个struct usb_endpoint_descriptor结构体,当中包含该端点地信息以及设备自定义地各种信息,这些信息包括:bEndpointAddress(b for byte)8位端点地址,其地址还隐藏了端点方向地信息(之前说过,端点是单向地),可以用掩码USB_DIR_OUT和USB_DIR_IN来确定.bmAttributes端点地类型,结合USB_ENDPOINT_XFERTYPE_MASK可以确定端点是USB_ENDPOINT_XFER_ISOC(等时)、USB_ENDPOINT_XFER_BULK(批量)还是USB_ENDPOINT_XFER_INT(中断).wMaxPacketSize端点一次处理地最大字节数.发送地BULK包可以大于这个数值,但会被分割传送. bInterval如果端点是中断类型,该值是端点地间隔设置,以毫秒为单位.在逻辑上,一个USB设备地功能划分是通过接口来完成地.比如说一个USB扬声器,可能会包括有两个接口:一个用于键盘控制,另外一个用于音频流传输.而事实上,这种设备需要用到不同地两个驱动程序来操作,一个控制键盘,一个控制音频流.但也有例外,比如蓝牙设备,要求有两个接口,第一用于ACL跟EVENT地传输,另外一个用于SCO链路,但两者通过一个驱动控制.在Linux上,接口使用structusb_interface来描述,以下是该结构体中比较重要地字段:struct usb_host_interface *altsetting(注意不是usb_interface)其实据我理解,他应该是每个接口地设置,虽然名字上有点奇怪.该字段是一个设置地数组(一个接口可以有多个设置),每个usb_host_interface都包含一套由struct usb_host_endpoint定义地端点配置.但这些配置次序是不定地.unsigned num_altstting可选设置地数量,即altsetting所指数组地元素个数.struct usb_host_interface *cur_altsetting当前活动地设置,指向altsetting数组中地一个.int minor当捆绑到该接口地USB驱动程序使用USB主设备号时,USB core分配地次设备号.仅在成功调用usb_register_dev之后才有效.除了它可以用struct usb_host_config来描述之外,到现在为止,我对配置地了解不多.而整个USB设备则可以用struct usb_device来描述,但基本上只会用它来初始化函数地接口,真正用到地应该是我们之前所提到地自定义地一个结构体.Linux USB驱动框架分析(二)好,了解过USB一些规范细节之后,我们现在来看看Linux地驱动框架.事实上,Linux 地设备驱动,特别是这种hotplug地USB设备驱动,会被编译成模块,然后在需要时挂在到内核.要写一个Linux地模块并不复杂,以一个helloworld为例:#include#includeMODULE_LICENSE(“GPL”);static int hello_init(void){printk(KERN_ALERT “Hello World!\n”);return 0;}static int hello_exit(void){printk(KERN_ALERT “G OODBYE!\n”);}module_init(hello_init);module_exit(hello_exit);这个简单地程序告诉大家应该怎么写一个模块,MODULE_LICENSE告诉内核该模块地版权信息,很多情况下,用GPL或者BSD,或者两个,因为一个私有模块一般很难得到社区地帮助.module_init和module_exit用于向内核注册模块地初始化函数和模块退出函数.如程序所示,初始化函数是hello_init,而退出函数是hello_exit.另外,要编译一个模块通常还需要用到内核源码树种地makefile,所以模块地Makefile 可以写成:ifneq ($(KERNELRELEASE),)obj-m:= hello.o#usb-dongle.oelseKDIR:= /usr/src/linux-headers-$(shell uname -r)BDIR:= $(shell pwd)default:$(MAKE) -C $(KDIR) M=$(PWD) modules.PHONY: cleanclean:make -C $(KDIR) M=$(BDIR) cleanendif可以用insmod跟rmmod来验证模块地挂在跟卸载,但必须用root地身份登陆命令行,用普通用户加su或者sudo在Ubuntu上地测试是不行地.Linux USB驱动框架分析(三)下面分析一下usb-skeleton地源码.这个范例程序可以在linux-2.6.17/drivers/usb下找到,其他版本地内核程序源码可能有所不同,但相差不大.大家可以先找到源码看一看,先有个整体印象.之前已经提到,模块先要向内核注册初始化跟销毁函数:static int __init usb_skel_init(void){int result;/* register this driver with the USB subsystem */result = usb_register(&skel_driver);if (result)err("usb_register failed. Error number %d", result);return result;}static void __exit usb_skel_exit(void){/* deregister this driver with the USB subsystem */usb_deregister(&skel_driver);}module_init (usb_skel_init);module_exit (usb_skel_exit);MODULE_LICENSE("GPL");从代码开来,这个init跟exit函数地作用只是用来注册驱动程序,这个描述驱动程序地结构体是系统定义地标准结构struct usb_driver,注册和注销地方法很简单,usb_register(struct *usb_driver), usb_deregister(struct *usb_driver).那这个结构体需要做些什么呢?他要向系统提供几个函数入口,跟驱动地名字:static struct usb_driver skel_driver = {.name = "skeleton",.probe = skel_probe,.disconnect = skel_disconnect,.id_table = skel_table,};从代码看来,usb_driver需要初始化四个东西:模块地名字skeleton,probe函数skel_probe,disconnect函数skel_disconnect,以及id_table.在解释skel_driver各个成员之前,我们先来看看另外一个结构体.这个结构体地名字有开发人员自定义,它描述地是该驱动拥有地所有资源及状态:struct usb_skel {struct usb_device * udev; /* the usb device for this device */struct usb_interface * interface; /* the interface for this device */struct semaphore limit_sem; /* limiting the number of writes in progress */unsigned char * bulk_in_buffer; /* the buffer to receive data */size_t bulk_in_size; /* the size of the receive buffer */__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */struct kref kref;};我们先来对这个usb_skel作个简单分析,他拥有一个描述usb设备地结构体udev,一个接口interface,用于并发访问控制地semaphore(信号量) limit_sem,用于接收数据地缓冲bulk_in_buffer及其尺寸bulk_in_size,然后是批量输入输出端口地址bulk_in_endpointAddr、bulk_out_endpointAddr,最后是一个内核使用地引用计数器.他们地作用我们将在后面地代码中看到.我们再回过头来看看skel_driver.name用来告诉内核模块地名字是什么,这个注册之后由系统来使用,跟我们关系不大.id_table用来告诉内核该模块支持地设备.usb子系统通过设备地production ID和vendor ID地组合或者设备地class、subclass跟protocol地组合来识别设备,并调用相关地驱动程序作处理.我们可以看看这个id_table到底是什么东西:/* Define these values to match your devices */#define USB_SKEL_VENDOR_ID 0xfff0#define USB_SKEL_PRODUCT_ID 0xfff0/* table of devices that work with this driver */static struct usb_device_id skel_table [] = {{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },{ } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, skel_table);MODULE_DEVICE_TABLE地第一个参数是设备地类型,如果是USB设备,那自然是usb(如果是PCI设备,那将是pci,这两个子系统用同一个宏来注册所支持地设备.这涉及PCI设备地驱动了,在此先不深究).后面一个参数是设备表,这个设备表地最后一个元素是空地,用于标识结束.代码定义了USB_SKEL_VENDOR_ID是0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是说,当有一个设备接到集线器时,usb子系统就会检查这个设备地vendor ID和product ID,如果它们地值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备地驱动.Linux USB驱动框架分析(四)probe是usb子系统自动调用地一个函数,有USB设备接到硬件集线器时,usb子系统会根据production ID和vendor ID地组合或者设备地class、subclass跟protocol地组合来识别设备调用相应驱动程序地probe(探测)函数,对于skeleton来说,就是skel_probe.系统会传递给探测函数一个usb_interface *跟一个struct usb_device_id *作为参数.他们分别是该USB设备地接口描述(一般会是该设备地第0号接口,该接口地默认设置也是第0号设置)跟它地设备ID描述(包括Vendor ID、Production ID等).probe函数比较长,我们分段来分析这个函数:dev->udev = usb_get_dev(interface_to_usbdev(interface));dev->interface = interface;在初始化了一些资源之后,可以看到第一个关键地函数调用——interface_to_usbdev.他同uo一个usb_interface来得到该接口所在设备地设备描述结构.本来,要得到一个usb_device 只要用interface_to_usbdev就够了,但因为要增加对该usb_device地引用计数,我们应该在做一个usb_get_dev地操作,来增加引用计数,并在释放设备时用usb_put_dev来减少引用计数.这里要解释地是,该引用计数值是对该usb_device地计数,并不是对本模块地计数,本模块地计数要由kref来维护.所以,probe一开始就有初始化kref.事实上,kref_init操作不单只初始化kref,还将其置设成1.所以在出错处理代码中有kref_put,它把kref地计数减1,如果kref计数已经为0,那么kref会被释放.kref_put地第二个参数是一个函数指针,指向一个清理函数.注意,该指针不能为空,或者kfree.该函数会在最后一个对kref地引用释放时被调用(如果我地理解不准确,请指正).下面是内核源码中地一段注释及代码:/*** kref_put - decrement refcount for object.* @kref: object.* @release: pointer to the function that will clean up the object when the* last reference to the object is released.* This pointer is required, and it is not acceptable to pass kfree* in as this function.** Decrement the refcount, and if 0, call release().* Return 1 if the object was removed, otherwise return 0. Beware, if this* function returns 0, you still can not count on the kref from remaining in* memory. Only use the return value if you want to see if the kref is now* gone, not present.*/int kref_put(struct kref *kref, void (*release)(struct kref *kref)){W ARN_ON(release == NULL);W ARN_ON(release == (void (*)(struct kref *))kfree);/** if current count is one, we are the last user and can release object* right now, avoiding an atomic operation on 'refcount'*/if ((atomic_read(&kref->refcount) == 1) ||(atomic_dec_and_test(&kref->refcount))) {release(kref);return 1;}return 0;}当我们执行打开操作时,我们要增加kref地计数,我们可以用kref_get,来完成.所有对struct kref地操作都有内核代码确保其原子性.得到了该usb_device之后,我们要对我们自定义地usb_skel各个状态跟资源作初始化.这部分工作地任务主要是向usb_skel注册该usb设备地端点.这里可能要补充以下一些关于usb_interface_descriptor地知识,但因为内核源码对该结构体地注释不多,所以只能靠个人猜测.在一个usb_host_interface结构里面有一个usb_interface_descriptor叫做desc地成员,他应该是用于描述该interface地一些属性,其中bNumEndpoints是一个8位(b for byte)地数字,他代表了该接口地端点数.probe然后遍历所有地端点,检查他们地类型跟方向,注册到usb_skel中./* set up the endpoint information *//* use only the first bulk-in and bulk-out endpoints */iface_desc = interface->cur_altsetting;for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {endpoint = &iface_desc->endpoint.desc;if ( !dev->bulk_in_endpointAddr &&((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) = = USB_DIR_IN) &&((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) = = USB_ENDPOINT_XFER_BULK)) {/* we found a bulk in endpoint */buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);dev->bulk_in_size = buffer_size;dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);if (!dev->bulk_in_buffer) {err("Could not allocate bulk_in_buffer");goto error;}}if (!dev->bulk_out_endpointAddr &&((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)= =USB_DIR_OUT) &&((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)= = USB_ENDPOINT_XFER_BULK)) {/* we found a bulk out endpoint */dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;}}if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {err("Could not find both bulk-in and bulk-out endpoints");goto error;}Linux USB驱动框架分析(五)接下来地工作是向系统注册一些以后会用地信息.首先我们来说明一下usb_set_intfdata(),他向内核注册一个data,这个data地结构可以是任意地,这段程序向内核注册了一个usb_skel结构,就是我们刚刚看到地被初始化地那个,这个data可以在以后用usb_get_intfdata来得到.usb_set_intfdata(interface, dev);retval = usb_register_dev(interface, &skel_class);然后我们向这个interface注册一个skel_class结构.这个结构又是什么?我们就来看看这到底是个什么东西:static struct usb_class_driver skel_class = {.name = "skel%d",.fops = &skel_fops,.minor_base = USB_SKEL_MINOR_BASE,};它其实是一个系统定义地结构,里面包含了一个名字、一个文件操作结构体还有一个次设备号地基准值.事实上它才是定义真正完成对设备IO操作地函数.所以他地核心内容应该是skel_fops.这里补充一些我个人地估计:因为usb设备可以有多个interface,每个interface 所定义地IO操作可能不一样,所以向系统注册地usb_class_driver要求注册到某一个interface,而不是device,因此,usb_register_dev地第一个参数才是interface,而第二个参数就是某一个usb_class_driver.通常情况下,linux系统用主设备号来识别某类设备地驱动程序,用次设备号管理识别具体地设备,驱动程序可以依照次设备号来区分不同地设备,所以,这里地次设备号其实是用来管理不同地interface地,但由于这个范例只有一个interface,在代码上无法求证这个猜想.static struct file_operations skel_fops = {.owner = THIS_MODULE,.read = skel_read,.write = skel_write,.open = skel_open,.release = skel_release,};这个文件操作结构中定义了对设备地读写、打开、释放(USB设备通常使用这个术语release).他们都是函数指针,分别指向skel_read、skel_write、skel_open、skel_release 这四个函数,这四个函数应该有开发人员自己实现.当设备被拔出集线器时,usb子系统会自动地调用disconnect,他做地事情不多,最重要地是注销class_driver(交还次设备号)和interface地data:dev = usb_get_intfdata(interface);usb_set_intfdata(interface, NULL);/* give back our minor */usb_deregister_dev(interface, &skel_class);然后他会用kref_put(&dev->kref, skel_delete)进行清理,kref_put地细节参见前文.到目前为止,我们已经分析完usb子系统要求地各个主要操作,下一部分我们在讨论一下对USB设备地IO操作.Linux USB驱动框架分析(六)说到usb子系统地IO操作,不得不说usb request block,简称urb.事实上,可以打一个这样地比喻,usb总线就像一条高速公路,货物、人流之类地可以看成是系统与设备交互地数据,而urb就可以看成是汽车.在一开始对USB规范细节地介绍,我们就说过USB地endpoint有4种不同类型,也就是说能在这条高速公路上流动地数据就有四种.但是这对汽车是没有要求地,所以urb可以运载四种数据,不过你要先告诉司机你要运什么,目地地是什么.我们现在就看看struct urb地具体内容.它地内容很多,为了不让我地理解误导各位,大家最好还是看一看内核源码地注释,具体内容参见源码树下include/linux/usb.h.在这里我们重点介绍程序中出现地几个关键字段:struct usb_device *dev /*urb所发送地目标设备*/unsigned int pipe一个管道号码,该管道记录了目标设备地端点以及管道地类型.每个管道只有一种类型和一个方向,它与他地目标设备地端点相对应,我们可以通过以下几个函数来获得管道号并设置管道类型:unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)把指定USB设备地指定端点设置为一个控制OUT端点.unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)把指定USB设备地指定端点设置为一个控制IN端点.unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)把指定USB设备地指定端点设置为一个批量OUT端点.unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)把指定USB设备地指定端点设置为一个批量OUT端点.unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)把指定USB设备地指定端点设置为一个中断OUT端点.unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)把指定USB设备地指定端点设置为一个中断IN端点.unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)把指定USB设备地指定端点设置为一个等时OUT端点.unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)把指定USB设备地指定端点设置为一个等时IN端点.unsigned int transfer_flags当不使用DMA时,应该transfer_flags |= URB_NO_TRANSFER_DMA_MAP(按照代码地理解,希望没有错).int status当一个urb把数据送到设备时,这个urb会由系统返回给驱动程序,并调用驱动程序地urb完成回调函数处理.这时,status记录了这次数据传输地有关状态,例如传送成功与否.成功地话会是0.要能够运货当然首先要有车,所以第一步当然要创建urb:struct urb *usb_alloc_urb(int isoc_packets, int mem_flags);第一个参数是等时包地数量,如果不是乘载等时包,应该为0,第二个参数与kmalloc 地标志相同.要释放一个urb可以用:void usb_free_urb(struct urb *urb);要承载数据,还要告诉司机目地地信息跟要运地货物,对于不同地数据,系统提供了不同地函数,对于中断urb,我们用void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,void *transfer_buffer, int buffer_length,usb_complete_t complete, void *context, int interval);这里要解释一下,transfer_buffer是一个要送/收地数据地缓冲,buffer_length是它地长度,complete是urb完成回调函数地入口,context由用户定义,可能会在回调函数中使用地数据,interval就是urb被调度地间隔.对于批量urb和控制urb,我们用:void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,void *transfer_buffer, int buffer_length, usb_complete_t complete, void *context);void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,unsigned char* setup_packet,void *transfer_buffer, int buffer_length, usb_complete_t complete,void *context);控制包有一个特殊参数setup_packet,它指向即将被发送到端点地设置数据报地数据.对于等时urb,系统没有专门地fill函数,只能对各urb字段显示赋值.有了汽车,有了司机,下一步就是要开始运货了,我们可以用下面地函数来提交urb int usb_submit_urb(struct urb *urb, int mem_flags);mem_flags有几种:GFP_A TOMIC、GFP_NOIO、GFP_KERNEL,通常在中断上下文环境我们会用GFP_ATOMIC.当我们地卡车运货之后,系统会把它调回来,并调用urb完成回调函数,并把这辆车作为函数传递给驱动程序.我们应该在回调函数里面检查status字段,以确定数据地成功传输与否.下面是用urb来传送数据地细节./* initialize the urb properly */usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, writesize, skel_write_bulk_callback, dev);urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/* send the data out the bulk port */retval = usb_submit_urb(urb, GFP_KERNEL);这里skel_write_bulk_callback就是一个完成回调函数,而他做地主要事情就是检查数据传输状态和释放urb:dev = (struct usb_skel *)urb->context;/* sync/async unlink faults aren't errors */if (urb->status && !(urb->status = = -ENOENT || urb->status == -ECONNRESET || urb->status = = -ESHUTDOWN)) {dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);}/* free up our allocated buffer */usb_buffer_free(urb->dev, urb->transfer_buffer_length,urb->transfer_buffer, urb->transfer_dma);事实上,如果数据地量不大,那么可以不一定用卡车来运货,系统还提供了一种不用urb地传输方式,而usb-skeleton地读操作正是采用这种方式实现:/* do a blocking bulk read to get data from the device */retval = usb_bulk_msg(dev->udev,usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),dev->bulk_in_buffer,min(dev->bulk_in_size, count),&bytes_read, 10000);/* if the read was successful, copy the data to userspace */if (!retval) {if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))retval = -EFAULT;elseretval = bytes_read;}程序使用了usb_bulk_msg来传送数据,它地原型如下:int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,void *data,int len, int *actual length, int timeout)这个函数会阻塞等待数据传输完成或者等到超时,data是输入/输出缓冲,len是它地大小,actual length是实际传送地数据大小,timeout是阻塞超时.对于控制数据,系统提供了另外一个函数,他地原型是:Int usb_contrl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,__u8 requesttype, __u16 value, __u16 index, void *data,__u16 size, int timeout);request是控制消息地USB请求值、requesttype是控制消息地USB请求类型,value是控制消息地USB消息值,index是控制消息地USB消息索引.具体是什么,暂时不是很清楚,希望大家提供说明.至此,Linux下地USB驱动框架分析基本完成了.看了上面地驱动框架分析,感觉受益匪浅.对于一些内容,我自己查漏补缺.首先我们按照顺序,看内核模块地注册以及释放函数如下:static int __init usb_skel_init(void){int result;/* register this driver with the USB subsystem */result = usb_register(&skel_driver);if (result)err("usb_register failed. Error number %d", result);return result;}static void __exit usb_skel_exit(void){/* deregister this driver with the USB subsystem */usb_deregister(&skel_driver);}module_init (usb_skel_init);module_exit (usb_skel_exit);MODULE_LICENSE("GPL");这个注册与销毁使用了usb_register(struct *usb_driver)以及usb_deregister(struct *usb_driver);那这个结构体需要做些什么呢?他要向系统提供几个函数入口,跟驱动地名字:static struct usb_driver skel_driver = {.owner = THIS_MODULE,.name = "skeleton",.id_table = skel_table,.probe = skel_probe,.disconnect = skel_disconnect,};从代码看来,usb_driver需要初始化四个东西:模块地名字skeleton,probe函数kel_probe, disconnect函数skel_disconnect,以及id_table.对于id_table,有如下函数入口:/* Define these values to match your devices */#define USB_SKEL_VENDOR_ID 0xfff0#define USB_SKEL_PRODUCT_ID 0xfff0/* table of devices that work with this driver */static struct usb_device_id skel_table [] = {{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },{ } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, skel_table);/*#define USB_DEVICE(vend,prod) \.match_flags = USB_DEVICE_ID_MA TCH_DEVICE, .idVendor = (vend), \.idProduct = (prod)*/打个比方就是我们要出去走亲戚,肯定知道是去看哪个亲戚,当然我们也肯定知道去他家地路怎么走,而这个函数,就是在我们插入usb设备时,usb子系统就会检查设备地vendor ID和product ID,如果它们地值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备地驱动.于是,就开始调用probe函数,做一些int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);函数作用如下:Called to see if the driver is willing to manage a particular interface on a device. If it is, probe returns zero and uses * usb_set_intfdata() to associate driver-specific data with the * interface. It may also use usb_set_interface() to specify the appropriate altsetting. If unwilling to manage the interface,return -ENODEV, if genuine IO errors occured, an appropriate negative errno value.就是根据id_table入口中地内容与插入地设备是否匹配,来决定是否处理设备上地特定接口.probe是usb子系统自动调用地一个函数,有USB设备接到硬件集线器时,usb子系统会根据production ID和vendor ID地组合或者设备地class、subclass跟protocol地组合来识别设备调用相应驱动程序地probe(探测)函数,对于skeleton来说,就是skel_probe.系统会传递给探测函数一个usb_interface *跟一个struct usb_device_id *作为参数.他们分别是该USB设备地接口描述(一般会是该设备地第0号接口,该接口地默认设置也是第0号设置)跟它地设备ID描述(包括Vendor ID、Production ID等).(2)我们来看probe函数:static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id){struct usb_skel *dev = NULL;struct usb_host_interface *iface_desc;struct usb_endpoint_descriptor *endpoint;size_t buffer_size;int i;int retval = -ENOMEM;/* allocate memory for our device state and initialize it */dev = kmalloc(sizeof(struct usb_skel), GFP_KERNEL);if (dev == NULL) {err("Out of memory");goto error;}memset(dev, 0x00, sizeof (*dev));kref_init(&dev->kref);dev->udev = usb_get_dev(interface_to_usbdev(interface));dev->interface = interface;/* set up the endpoint information *//* use only the first bulk-in and bulk-out endpoints */iface_desc = interface->cur_altsetting;for (i = 0; i < iface_desc->desc.bNumEndpoints; i) {endpoint = &iface_desc->endpoint[i].desc;if (!dev->bulk_in_endpointAddr &&(endpoint->bEndpointAddress & USB_DIR_IN) &&((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)== USB_ENDPOINT_XFER_BULK)) {/* we found a bulk in endpoint */buffer_size = endpoint->wMaxPacketSize;dev->bulk_in_size = buffer_size;dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);if (!dev->bulk_in_buffer) {err("Could not allocate bulk_in_buffer");goto error;}}if (!dev->bulk_out_endpointAddr &&!(endpoint->bEndpointAddress & USB_DIR_IN) &&((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)== USB_ENDPOINT_XFER_BULK)) {/* we found a bulk out endpoint */dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;}}if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { err("Could not find both bulk-in and bulk-out endpoints");goto error;}/* save our data pointer in this interface device */usb_set_intfdata(interface, dev);/* we can register the device now, as it is ready */retval = usb_register_dev(interface, &skel_class);if (retval) {/* something prevented us from registering this driver */err("Not able to get a minor for this device.");usb_set_intfdata(interface, NULL);goto error;}/* let the user know what node this device is now attached to */info("USB Skeleton device now attached to USBSkel-%d", interface->minor);return 0;error:if (dev)kref_put(&dev->kref, skel_delete);return retval;}2-9:对函数地变量定义以及初始化.10-16:对设备地申请内存处理.首先我们看下skeleton函数地自定义结构体:/* Structure to hold all of our device specific stuff */struct usb_skel {struct usb_device *udev; /* the usb device for this device */struct usb_interface *interface; /* the interface for this device */struct semaphore limit_sem; /* limiting the number of writes in progress */unsigned char *bulk_in_buffer; /* the buffer to receive data */size_t bulk_in_size; /* the size of the receive buffer */__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */struct kref kref;struct mutex io_mutex; /* synchronize I/O with disconnect */ };我们先来对这个usb_skel作个简单分析,他拥有一个描述usb设备地结构体udev,一个接口interface,用于并发访问控制地semaphore(信号量) limit_sem,用于接收数据地缓冲bulk_in_buffer及其尺寸bulk_in_size,然后是批量输入输出端口地址bulk_in_endpointAddr、bulk_out_endpointAddr,最后是一个内核使用地引用计数器kref.这个引用计数在我们地probe函数中正好被设置,17:kref_init(&dev->kref);19:dev->udev = usb_get_dev(interface_to_usbdev(interface));20:dev->interface = interface;在17行,我们对usb_skel中地kref结构体进行初始化,设置这个引用计数器地值,这个值用来说明对模块地引用次数,初始化函数如下:/**** kref_init - initialize object.* @kref: object in question.*/void kref_init(struct kref *kref){atomic_set(&kref->refcount,1);smp_mb();}这个函数不仅用来初始化kref,而且用原子地方式设置refcount地值为1.所以在出错处理代码中有kref_put,它把kref地计数减1,如果kref计数已经为0,那么kref会被释放,kref地释放会在下面分析.在初始化了一些资源之后,可以看到第一个关键地函数调用——interface_to_usbdev.他同uo一个usb_interface来得到该接口所在设备地设备描述结构.本来,要得到一个usb_device 只要用interface_to_usbdev就够了,但因为要增加对该usb_device地引用计数,我们应该在做一个usb_get_dev地操作,来增加引用计数,并在释放设备时用usb_put_dev来减少引用计数.其中对interface_to_usbdev(interface)函数地定义如下:#define interface_to_usbdev(intf) \container_of(intf->dev.parent, struct usb_device, dev)这个函数是通过将usb接口地引用计数,通过container函数,指向usb_devices结构.对于container_of函数,参见/yinkaizhong/article/details/4093795 21-55:对我们自定义地usb_skel各个状态跟资源作初始化.这部分工作地任务主要是向usb_skel注册该usb设备地端点.这里可能要补充以下一些关于usb_interface_descriptor地知识,但因为内核源码对该结构体地注释不多,所以只能靠个人猜测.在一个usb_host_interface 结构里面有一个usb_interface_descriptor叫做desc地成员,他应该是用于描述该interface地一些属性,其中bNumEndpoints是一个8位(b for byte)地数字,他代表了该接口地端点数.probe然后遍历所有地端点,检查他们地类型跟方向,注册到usb_skel中.56-58:向系统注册一些以后会用地地信息.首先我们来说明一下usb_set_intfdata(),他向内核注册一个data,这个data地结构可以是任意地,这段程序向内核注册了一个usb_skel 结构.就是我们刚刚看到地被初始化地那个.之所以要把他注册,是因为我们定义地usb_skel 结构不是全局变量,其他地函数需要使用地时候,可以后用usb_get_intfdata来得到.我们可以再内核源码中找到对它地定义:(/linux/usb.h)static inline void usb_set_intfdata(struct usb_interface *intf, void *data)。
Linux下的USB总线驱动
Linux下的USB总线驱动一.USB理论1. USB概念概述USB1.0版本速度1.5Mbps(低速USB)USB1.1版本速度12Mbps(全速USB)USB2.0版本速度480Mbps(高速USB)USB驱动由USB主机控制器驱动和USB设备驱动组成。
USB主机控制器是用来控制USB 设备和CPU之间通信的,USB主机控制器驱动主要用来驱动芯片上的主机控制器硬件。
US B设备驱动主要是指具体的例如USB鼠标,USB键盘灯设备的驱动。
一般的通用的Linux设备,如U盘、USB鼠标、USB键盘,都不需要工程师再编写驱动,需要编写的是特定厂商、特定芯片的驱动,而且往往也可以参考内核中已经提供的驱动模板。
USB只是一个总线,真正的USB设备驱动的主体工作仍然是USB设备本身所属类型的驱动,如字符设备、tty设备、块设备、输入设备等。
2. USB主机控制器USB主机控制器属于南桥芯片的一部分,通过PCI总线和处理器通信。
USB主机控制器分为UHCI(英特尔提出)、OHCI(康柏和微软提出)、EHCI。
其中OHCI驱动程序用来为非PC系统上以及带有SiS和ALi芯片组的PC主办上的USB芯片提供支持。
UHCI驱动程序多用来为大多数其他PC主板(包括Intel和Via)上的USB芯片提供支持。
ENCI兼容OHC I和UHCI。
UHCI的硬件线路比OHCI简单,所以成本较低,但需要较复杂的驱动程序,CP U负荷稍重。
主机控制器驱动程序完成的功能主要包括:解析和维护URB,根据不同的端点进行分类缓存URB;负责不同USB传输类型的调度工作;负责USB数据的实际传输工作;实现虚拟跟HUB的功能。
3. USB设备与USB驱动的匹配USB设备与USB驱动怎么匹配的呢?实际上USB设备中有一个模块叫固件,是固件信息和USB驱动进行的匹配。
固件是固化在集成电路内部的程序代码,USB固件中包含了USB设备的出厂信息,标识该设备的厂商ID、产品ID、主版本号和次版本号等。
Linux内核USB驱动架构:USB卷一_DS部份
USB卷一DS部份LK版本:2.6.35.32013年1月14日 任务目标:USB设备、配置、接口、设置以及端点的五者关系图:图示:USB设备:一个USB接口对应一种功能对应一个USB驱动一个USB设备对应有一个或多个配置;一个配置对应一个或多个的USB接口;一个USB接口又具有一个或多个的设置;一个设置又会拥有一个或多个的USB端点。
配置接口端点端点....接口端点端点.... USB驱动程序USB驱动程序配置接口端点端点........Linux-USB子系统框架图:内核中USB子系统的结构:USB拓扑结构:主机的责任:为了与USB设备通信,计算机需要硬件和软件共同来支持USB主机功能。
其中,硬件由USB主机控制器和含有一个或多个USB端口的跟集线器构成。
典型的计算机拥有一个或多个硬件主机控制器。
每个控制器支持多个端口。
主机负责管理总线。
主机需要知道哪些设备正挂在总线上,以及每个设备有怎样的能力。
主机还必须尽可能保证总线上所有设备能够按照各自的需要收发数据。
主机完成下述的所有任务。
1、检测设备开机时,集线器会令主机得知所有已连接的USB设备。
在被称作“枚举”的过程中,主角会决定使用那种总线速率、指定地址,并请求额外的信息。
开机后,若有设备连接或移除,集线器将把此事件通知主机,主机便枚举新连接的设备或从它应用程序的可用设备清单上撤销全部已移除的设备。
2、管理数据流主机管理总线上的数据流。
可能有许多设备会要求在同一时间内传输数据。
主机控制器将可用的时间分成诸多小段,给与每项传输任务一部分可用时间。
USB3.0主机可同时收发超高速数据,也可在USB2.0速率下完成传输。
USB2.0总线每次只以一种速率在一个方向上传输数据。
枚举过程中,设备驱动程序将(向系统)索要传输所需的带宽,以保证时序。
如果所要求的带宽无法被满足,驱动程序便索要更小的带宽或等待直到所要求的带宽得到满足。
得不到时序保证的传输,将等到总线不再忙碌的时候再使用剩余带宽。
linux usb设备驱动和通信原理
linux usb设备驱动和通信原理Linux USB设备驱动和通信原理一、介绍USB(Universal Serial Bus,通用串行总线)是一种常见的外部设备连接标准,可以连接各种设备,如打印机、键盘、鼠标、摄像头等。
在Linux系统中,USB设备驱动是用于管理和控制USB设备的软件模块。
本文将介绍Linux USB设备驱动的基本原理和USB设备与主机之间的通信过程。
二、USB设备驱动1. 驱动模型在Linux系统中,USB设备驱动使用一种称为USB核心(USB Core)的模型来管理和控制USB设备。
USB核心提供了一组功能强大的API,供驱动程序使用。
驱动程序通过注册到USB核心,并提供一组回调函数来处理USB设备的各种事件和请求。
2. 驱动加载当插入一个USB设备时,Linux内核会自动加载与之对应的驱动程序。
内核根据设备的厂商ID(Vendor ID)和产品ID(Product ID)来匹配设备和驱动程序。
如果找到匹配的驱动程序,内核会加载该驱动程序,并调用其初始化函数。
3. 驱动程序结构一个USB设备驱动程序通常由以下几个部分组成:- 初始化函数:用于初始化驱动程序的数据结构和资源。
- 描述符匹配函数:用于匹配设备的描述符和驱动程序。
- 事件处理函数:用于处理设备的插入、拔出等事件。
- 控制请求处理函数:用于处理来自主机的控制请求。
- 数据传输函数:用于处理设备和主机之间的数据传输。
三、USB设备与主机通信原理1. 设备描述符USB设备在与主机通信之前,需要提供一个设备描述符,用于描述设备的基本信息,如设备类别、厂商ID、产品ID等。
主机通过设备描述符来识别和配置设备。
2. 端点和传输类型USB设备与主机之间的通信是通过端点(Endpoint)来实现的。
一个USB设备通常包含多个端点,每个端点都有一个唯一的端点地址和传输类型。
主机通过端点地址来选择要与之通信的端点,通过传输类型来确定数据传输的方式,如控制传输、批量传输、中断传输和等时传输。
Linux USB驱动工作流程
Linux USB驱动工作流程标签:linux工作structdescriptor数据结构2012-06-15 10:18 3988人阅读评论(1) 收藏举报分类:Linux driver(35)USB(7)版权声明:本文为博主原创文章,未经博主允许不得转载。
1. USB主机在Linux驱动中,USB驱动处于最底层的是USB主机控制器硬件,在其之上运行的是USB主机控制器驱动,主机控制器之上为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。
因此,在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制插入其中的USB设备,后者控制USB设备如何与主机通信。
Linux内核USB核心负责USB驱动管理和协议处理的主要工作。
主机控制器驱动和设备驱动之间的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;通过全局变量维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。
2. USB设备Linux内核中USB设备侧驱动程序分为3个层次:UDC驱动程序、Gadget API和Gadget驱动程序。
UDC驱动程序直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。
当前Gadget API是UDC驱动程序回调函数的简单包装。
Gadget驱动程序具体控制USB设备功能的实现,使设备表现出“网络连接”、“打印机”或“USB Mass Storage”等特性,它使用Gadget API控制UDC实现上述功能。
Gadget API把下层的UDC驱动程序和上层的Gadget驱动程序隔离开,使得在Linux系统中编写USB设备侧驱动程序时能够把功能的实现和底层通信分离。
3. 在USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。
一文搞懂USB设备端驱动框架【转】
⼀⽂搞懂USB设备端驱动框架【转】hello ⼤家好,今天带领⼤家学习⼀下USB 设备端驱动内核版本:4.4.941. Linux USB ⼦系统在介绍设备端驱动前,我们先来看看 Linux USB⼦系统。
这⾥的⼦系统是相对于整个Linux kernel 来说的,⽽⾮单⼀设备。
从整体概括了USB主机端和设备端的通信框架。
Linux kernel 中早已集成了较为完善的USB协议栈,由于其规模庞⼤,包含多个类别的设备驱动,所以Linux系统中的USB协议栈也被称为USB⼦系统。
1.1 主机端主机端,简化抽象三层:各种类设备驱动:mass sotrage, CDC, HID等USB 设备驱动:USB 核⼼处理主机控制器驱动:不同的USB主机控制器(OHCI/EHCI/UHCI),抽象为HDC。
1.2 设备端设备端,也抽象为三层:设备功能驱动:mass sotage , CDC, HID 等,对应主机端的类设备驱动Gadget 设备驱动:中间层,向下直接和UDC通信,建⽴链接;向上提供通⽤接⼝,屏蔽USB请求以及传输细节。
设备控制器驱动:UDC驱动,直接处理USB设备控制器。
2. USB 设备驱动2.1 gadget 驱动框架拆解1我们将USB 设备端驱动拆解⼀下,其驱动框架如下:上⽂提到,Gadget 设备层起着⾄关重要的作⽤。
为上层提供通⽤的驱动框架,与下层UDC通过Gadget Interface 建⽴联系。
其中Compsite Framwork 提供了⼀个通⽤的usb_gadget_driver 模板,包括各种⽅法供上层Function driver 使⽤。
(driver/usb/gadget/compsite.c)从上图我们可以看出,对于USB设备端驱动开发⽽⾔,更多的关注的是Function driver这层。
USB 控制相关过程,内核提供了⼀个中间层帮我们屏蔽掉了。
2.2 gadget 驱动框架拆解2内核版本:Linux Kernel 4.4.94,我们以这个版本进⾏拆解分析4.x 的内核相对于3.x的内核在gadget 驱动上分解的更加完善,显得⽬录结构,层次分明,分⼯合理,更便于理解。
Linux设备驱动之USBhub驱动
Linux设备驱动之USB hub驱动Linux设备驱动之USB hub驱动------------------------------------------本文系本站原创,欢迎!请注明出处:------------------------------------------一:前言继UHCI的驱动之后,我们对USB Control的运作有了一定的了解.在接下来的分析中,我们对USB设备的驱动做一个全面的分析,我们先从HUB的驱动说起.关于HUB,usb2.0 spec上有详细的定义,基于这部份的代码位于linux-2.6.25/drivers/usb/core下,也就是说,这部份代码是位于core下,和具体设备是无关的,因为各厂商的hub都是按照spec的要求来设计的.二:UHCI驱动中的root hub记得在分析UHCI驱动的时候,曾详细分析过root hub的初始化操作.为了分析方便,将代码片段列出如下:usb_add_hcd() à usb_alloc_dev():struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1){…………//usb_device,内嵌有struct device结构,对这个结构进行初始化device_initialize(&dev->dev);dev->dev.bus = &usb_bus_type;dev->dev.type = &usb_device_type;…………}一看到前面对dev的赋值,根据我们对设备模型的理解,一旦这个device进行注册,就会发生driver和device的匹配过程了.不过,现在还不是分析这个过程的时候,我们先来看一下,USB子系统中的两种驱动.三:USB子系统中的两种驱动linux-2.6.25/drivers/usb/core/driver.c中,我们可以找到两种register driver的方式,分别为usb_register_driver()和usb_register_device_driver().分别来分析一下这两个接口.usb_register_device_driver()接口的代码如下:int usb_register_device_driver(struct usb_device_driver *new_udriver,struct module *owner){int retval = 0;if (usb_disabled())return -ENODEV;new_udriver->drvwrap.for_devices = 1;new_udriver-> = (char *) new_udriver->name;new_udriver->drvwrap.driver.bus = &usb_bus_type;new_udriver->drvwrap.driver.probe = usb_probe_device;new_udriver->drvwrap.driver.remove = usb_unbind_device;new_udriver->drvwrap.driver.owner = owner;retval = driver_register(&new_udriver->drvwrap.driver);if (!retval) {pr_info(“%s: registered new device driver %s\n”,usbcore_name, new_udriver->name);usbfs_update_special();} else {printk(KERN_ERR “%s: error %d registering device ““ driver %s\n”,usbcore_name, retval, new_udriver->name);}return retval;}首先,通过usb_disabled()来判断一下usb是否被禁用,如果被禁用,当然就不必执行下面的流程了,直接退出即可.从上面的代码,很明显可以看到, struct usb_device_driver 对struct device_driver进行了一次封装,我们注意一下这里的赋值操作:new_udriver->drvwrap.for_devices = 1.等等.这些在后面都是用派上用场的.usb_register_driver()的代码如下:int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name){int retval = 0;if (usb_disabled())return -ENODEV;new_driver->drvwrap.for_devices = 0;new_driver-> = (char *) new_driver->name;new_driver->drvwrap.driver.bus = &usb_bus_type;new_driver->drvwrap.driver.probe = usb_probe_interface;new_driver->drvwrap.driver.remove = usb_unbind_interface;new_driver->drvwrap.driver.owner = owner;new_driver->drvwrap.driver.mod_name = mod_name;spin_lock_init(&new_driver->dynids.lock);INIT_LIST_HEAD(&new_driver->dynids.list);retval = driver_register(&new_driver->drvwrap.driver);if (!retval) {pr_info(“%s: registered new interface dr iver %s\n”,usbcore_name, new_driver->name);usbfs_update_special();usb_create_newid_file(new_driver);} else {printk(KERN_ERR “%s: error %d registering interface ““ driver %s\n”,usbcore_name, retval, new_driver->name);}return retval;}很明显,在这里接口里,将new_driver->drvwrap.for_devices设为了0.而且两个接口的porbe()函数也不一样.其实,对于usb_register_driver()可以看作是usb设备中的接口驱动,而usb_register_device_driver()是一个单纯的USB设备驱动.四: hub的驱动分析4.1: usb_bus_type->match()的匹配过程usb_bus_type->match()用来判断驱动和设备是否匹配,它的代码如下:static int usb_device_match(struct device *dev, struct device_driver *drv){/* 整理by *///usb device的情况if (is_usb_device(dev)) {/* interface drivers never match devices */ if (!is_usb_device_driver(drv))return 0;/* TODO: Add real matching code */ return 1;}//interface的情况else {struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id;/*整理by */if (is_usb_device_driver(drv))return 0;intf = to_usb_interface(dev);usb_drv = to_usb_driver(drv);id = usb_match_id(intf, usb_drv->id_table);if (id)return 1;id = usb_match_dynamic_id(intf, usb_drv);if (id)return 1;}return 0;}这里的match会区分上面所说的两种驱动,即设备的驱动和接口的驱动. is_usb_device()的代码如下:static inline int is_usb_device(const struct device *dev){return dev->type == &usb_device_type;}很明显,对于root hub来说,这个判断是肯定会满足的.static inline int is_usb_device_driver(struct device_driver *drv){return container_of(drv, struct usbdrv_wrap, driver)->for_devices;}回忆一下,我们在分析usb_register_device_driver()的时候,不是将new_udriver->drvwrap.for_devices置为了1么?所以对于usb_register_device_driver()注册的驱动来说,这里也是会满足的.因此,对应root hub的情况,从第一个if就会匹配到usb_register_device_driver()注册的驱动.对于接口的驱动,我们等遇到的时候再来进行分析.4.2:root hub的驱动入口既然我们知道,root hub会匹配到usb_bus_type->match()的驱动,那这个驱动到底是什么呢?我们从usb子系统的初始化开始说起.在linux-2.6.25/drivers/usb/core/usb.c中.有这样的一段代码:subsys_initcall(usb_init);对于subsys_initcall()我们已经不陌生了,在很多地方都会遇到它.在系统初始化的时候,会调用到它对应的函数.在这里,即为usb_init().在usb_init()中,有这样的代码片段:static int __init usb_init(void){…………retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);if (!retval)goto out;……}在这里终于看到usb_register_device_driver()了. usb_generic_driver会匹配到所有usb 设备.定义如下:struct usb_device_driver usb_generic_driver = {.name = “usb”,.probe = generic_probe,.disconnect = generic_disconnect,#ifdefCONFIG_PM.suspend = generic_suspend,.resume = generic_resume,#endif.supports_autosuspend = 1,};现在是到分析probe()的时候了.我们这里说的并不是usb_generic_driver中的probe,而是封装在struct usb_device_driver中的driver对应的probe函数.在上面的分析, usb_register_device_driver()将封装的driver的probe()函数设置为了usb_probe_device().代码如下:static int usb_probe_device(struct device *dev){struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);struct usb_device *udev;int error = -ENODEV;dev_dbg(dev, “%s\n”, __FUNCTION__);//再次判断dev是否是usb deviceif (!is_usb_device(dev)) /* Sanity check */return error;udev = to_usb_device(dev);/* TODO: Add real matching code *//* The device should always appear to be in use* unless the driver suports autosuspend.*///pm_usage_t: autosuspend计数.如果此计数为1,则不允许autosuspendudev->pm_usage_t = !(udriver->supports_autosuspend);error = udriver->probe(udev);return error;}首先,可以通过container_of()将封装的struct device, struct device_driver转换为struct usb_device和struct usb_device_driver.然后,再执行一次安全检查,判断dev是否是属于一个usb device.在这里,我们首次接触到了hub suspend.如果不支持suspend(udriver->supports_autosuspend为0),则udev->pm_usage_t被设为1,也就是说,它不允许设备suspend.否则,将其初始化为0. 最后,正如你所看到的,流程转入到了usb_device_driver->probe().对应到root hub,流程会转入到generic_probe().代码如下:static int generic_probe(struct usb_device *udev){int err, c;/* put device-specific files into sysfs */usb_create_sysfs_dev_files(udev);/* Choose and set the configuration.This registers the interfaces* with the driver core and lets interface drivers bind to them.*/if (udev->authorized == 0)dev_err(&udev->dev, “Device is not authorized for usage\n”);else {//选择和设定一个配置c = usb_choose_configuration(udev);if (c >= 0) {err = usb_set_configuration(udev, c);if (err) {dev_err(&udev->dev, “can’t set config #%d, error %d\n”,c, err);/* This need not be fatal.The user can try to* set other configurations. */}}}/* USB device state == configured ... usable */usb_notify_add_device(udev);return 0;}usb_create_sysfs_dev_files()是在sysfs中显示几个属性文件,不进行详细分析,有兴趣的可以结合之前分析的>来看下代码.usb_notify_add_device()是有关notify链表的操作,这里也不做详细分析.至于udev->authorized,在root hub的初始化中,是会将其初始化为1的.后面的逻辑就更简单了.为root hub 选择一个配置然后再设定这个配置.还记得我们在分析root hub的时候,在usb_new_device()中,会将设备的所有配置都取出来,然后将它们放到了usb_device-> config.现在这些信息终于会派上用场了.不太熟悉的,可以看下本站之前有关usb控制器驱动的文档.Usb2.0 spec上规定,对于hub设备,只能有一个config,一个interface,一个endpoint.实际上,在这里,对hub的选择约束不大,反正就一个配置,不管怎么样,选择和设定都是这个配置.不过,为了方便以后的分析,我们还是跟进去看下usb_choose_configuration()和usb_set_configuration()的实现.实际上,经过这两个函数之后,设备的probe()过程也就会结束了.4.2.1:usb_choose_configuration()函数分析usb_choose_configuration()的代码如下://为usb device选择一个合适的配置int usb_choose_configuration(struct usb_device *udev){int i;int num_configs;int insufficient_power = 0;struct usb_host_config *c, *best;best = NULL;//config数组c = udev->config;//config项数num_configs = udev->descriptor.bNumConfigurations;//遍历所有配置项for (i = 0; istruct usb_interface_descriptor *desc = NULL;/* It’s possible that a config has no interfaces! *///配置项的接口数目//取配置项的第一个接口if (c->desc.bNumInterfaces > 0)desc = &c->intf_cache[0]->altsetting->desc;/** HP’s USB bus-powered keyboard has only one configuration * and it claims to be self-powered; other devices may have* similar errors in their descriptors.If the next test* were allowed to execute, such configurations would always* be rejected and the devices would not work as expected.* In the meantime, we run the risk of selecting a config* that requires external power at a time when that power* isn’t available.It seems to be the lesser of two evils.** Bugzilla #6448 reports a device that appears to crash* when it receives a GET_DEVICE_STATUS request!We don’t * have any other way to tell whether a device is self-powered,* but since we don’t use that information anywhere but here,* the call has been removed.** Maybe the GET_DEVICE_STATUS call and the test below can* be reinstated when device firmwares bee more reliable.* Don’t hold your breath.*/#if 0/* Rule out self-powered configs for a bus-powered device */ if (bus_powered && (c->desc.bmAttributes &USB_CONFIG_ATT_SELFPOWER))continue;#endif/** The next test may not be as effective as it should be.* Some hubs have errors in their descriptor, claiming* to be self-powered when they are really bus-powered.* We will overestimate the amount of current such hubs* make available for each port.** This is a fairly benign sort of failure.It won’t* cause us to reject configurations that we should have* accepted.*//* Rule out configs that draw too much bus current *///电源不足.配置描述符中的电力是所需电力的1/2if (c->desc.bMaxPower * 2 > udev->bus_mA) {insufficient_power++;continue;}/* When the first config’s first interface is one of Microsoft’s* pet nonstandard Ethernet-over-USB protocols, ignore it unless* this kernel has enabled the necessary host side driver.*/if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) {#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)continue;#elsebest = c;#endif}/* From the remaining configs, choose the first one whose* first interface is for a non-vendor-specific class.* Reason: Linux is more likely to have a class driver* than a vendor-specific driver. *///选择一个不是USB_CLASS_VENDOR_SPEC的配置else if (udev->descriptor.bDeviceClass !=USB_CLASS_VENDOR_SPEC &&(!desc || desc->bInterfaceClass !=USB_CLASS_VENDOR_SPEC)) {best = c;break;}/* If all the remaining configs are vendor-specific,* choose the first one. */else if (!best)best = c;}if (insufficient_power > 0)dev_info(&udev->dev, “rejected %d configuration%s ““due to insufficient available bus power\n”,insufficient_power, plural(insufficient_power));//如果选择好了配置,返回配置的序号,否则,返回-1if (best) {i = best->desc.bConfigurationValue;dev_info(&udev->dev,“configuration #%d chosen from %d choice%s\n”,i, num_configs, plural(num_configs));} else {i = -1;dev_warn(&udev->dev,“no configuration chosen from %d choice%s\n”,num_configs, plural(num_configs));}return i;}Linux按照自己的喜好选择好了配置之后,返回配置的序号.不过对于HUB来说,它有且仅有一个配置.4.2.2:usb_set_configuration()函数分析既然已经选好配置了,那就告诉设备选好的配置,这个过程是在usb_set_configuration()中完成的.它的代码如下:int usb_set_configuration(struct usb_device *dev, int configuration){int i, ret;struct usb_host_config *cp = NULL;struct usb_interface **new_interfaces = NULL;int n, nintf;if (dev->authorized == 0 || configuration == -1) configuration = 0;else {for (i = 0; i descriptor.bNumConfigurations; i++) {if (dev->config.desc.bConfigurationValue ==configuration) {cp = &dev->config;break;}}}if ((!cp && configuration != 0))return -EINV AL;/* The USB spec says configuration 0 means unconfigured. * But if a device includes a configuration numbered 0,* we will accept it as a correctly configured state.* Use -1 if you really want to unconfigure the device.*/if (cp && configuration == 0)dev_warn(&dev->dev, “config 0 descriptor??\n”);首先,根据选择好的配置号找到相应的配置,在这里要注意了, dev->config[]数组中的配置并不是按照配置的序号来存放的,而是按照遍历到顺序来排序的.因为有些设备在发送配置描述符的时候,并不是按照配置序号来发送的,例如,配置2可能在第一次GET_CONFIGURATION 就被发送了,而配置1可能是在第二次GET_CONFIGURATION才能发送.取得配置描述信息之后,要对它进行有效性判断,注意一下本段代码的最后几行代码:usb2.0 spec上规定,0号配置是无效配置,但是可能有些厂商的设备并末按照这一约定,所以在linux 中,遇到这种情况只是打印出警告信息,然后尝试使用这一配置./* Allocate memory for new interfaces before doing anything else,* so that if we run out then nothing will have changed. */n = nintf = 0;if (cp) {//接口总数nintf = cp->desc.bNumInterfaces;//interface指针数组,new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),GFP_KERNEL);if (!new_interfaces) {dev_err(&dev->dev, “Out of memory\n”);return -ENOMEM;}for (; nnew_interfaces[n] = kzalloc(sizeof(struct usb_interface),GFP_KERNEL);if (!new_interfaces[n]) {dev_err(&dev->dev, “Out of memory\n”);ret = -ENOMEM;free_interfaces:while (--n >= 0)kfree(new_interfaces[n]);kfree(new_interfaces);return ret;}}//如果总电源小于所需电流,打印警告信息i = dev->bus_mA - cp->desc.bMaxPower * 2;if (idev_warn(&dev->dev, “new config #%d exceeds power ““limit by %dmA\n”,configuration, -i);}在这里,注要是为new_interfaces分配空间,要这意的是, new_interfaces是一个二级指针,它的最终指向是struct usb_interface结构.特别的,如果总电流数要小于配置所需电流,则打印出警告消息.实际上,这种情况在usb_choose_configuration()中已经进行了过滤./* Wake up the device so we can send it the Set-Config request */ //要对设备进行配置了,先唤醒它ret = usb_autoresume_device(dev);if (ret)goto free_interfaces;/* if it’s already configured, clear out old state first.* getting rid of old interfaces means unbinding their drivers.*///不是处于ADDRESS状态,先清除设备的状态if (dev->state != USB_STATE_ADDRESS)usb_disable_device(dev, 1); /* Skip ep0 *///发送控制消息,选取配置ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),USB_REQ_SET_CONFIGURATION, 0, configuration, 0,NULL, 0, USB_CTRL_SET_TIMEOUT);if (ret/* All the old state is gone, so what else can we do?* The device is probably useless now anyway.*/cp = NULL;}//dev->actconfig存放的是当前设备选取的配置dev->actconfig = cp;if (!cp) {usb_set_device_state(dev, USB_STA TE_ADDRESS);usb_autosuspend_device(dev);goto free_interfaces;}//将状态设为CONFIGUREDusb_set_device_state(dev, USB_STA TE_CONFIGURED);接下来,就要对设备进行配置了,首先,将设备唤醒.回忆一下我们在分析UHCI驱动时,列出来的设备状态图.只有在ADDRESS状态才能转入到CONFIG状态.(SUSPEND状态除外). 所以,如果设备当前不是处于ADDRESS状态,就需要将设备的状态初始化.usb_disable_device()函数是个比较重要的操作,在接下来再对它进行详细分析.接着,发送SET_CONFIGURA TION的Control消息给设备,用来选择配置最后,将dev->actconfig指向选定的配置,将设备状态设为CONFIG/* Initialize the new interface structures and the* hc/hcd/usbcore interface/endpoint state.*///遍历所有的接口for (i = 0; istruct usb_interface_cache *intfc;struct usb_interface *intf;struct usb_host_interface *alt;cp->interface = intf = new_interfaces;intfc = cp->intf_cache;intf->altsetting = intfc->altsetting;intf->num_altsetting = intfc->num_altsetting;//是否关联的接口描述符,定义在minor usb 2.0 spec中intf->intf_assoc = find_iad(dev, cp, i);kref_get(&intfc->ref);//选择0号设置alt = usb_altnum_to_altsetting(intf, 0);/* No altsetting 0?We’ll assume the first altsetting.* We could use a GetInterface call, but if a device is* so non-pliant that it doesn’t have altsetting 0* then I would n’t trust its reply anyway.*///如果0号设置不存在,选排在第一个设置if (!alt)alt = &intf->altsetting[0];//当前的配置intf->cur_altsetting = alt;usb_enable_interface(dev, intf);intf->dev.parent = &dev->dev;intf->dev.driver = NULL;intf->dev.bus = &usb_bus_type;intf->dev.type = &usb_if_device_type;intf->dev.dma_mask = dev->dev.dma_mask;device_initialize(&intf->dev);mark_quiesced(intf);sprintf(&intf->dev.bus_id[0], “%d-%s:%d.%d”,dev->bus->busnum, dev->devpath,configuration, alt->desc.bInterfaceNumber);}kfree(new_interfaces);if (cp->string == NULL)cp->string = usb_cache_string(dev, cp->desc.iConfiguration);之前初始化的new_interfaces在这里终于要派上用场了.初始化各接口,从上面的初始化过程中,我们可以看出:Intf->altsetting,表示接口的各种设置Intf->num_altsetting:表示接口的设置数目Intf->intf_assoc:接口的关联接口(定义于minor usb 2.0 spec)Intf->cur_altsetting:接口的当前设置.结合之前在UHCI中的分析,我们总结一下:Usb_dev->config,其实是一个数组,存放设备的配置.usb_dev->config[m]-> interface[n]表示第m个配置的第n个接口的intercace结构.(m,bsp; dev->bus->busnum, dev->devpath,configuration, alt->desc.bInterfaceNumber);dev指的是这个接口所属的usb_dev,结合我们之前在UHCI中关于usb设备命名方式的描述.可得出它的命令方式如下:USB总线号-设备路径:配置号.接口号.例如,在我的虚拟机上:[rootlocalhost devices]# pwd/sys/bus/usb/devices[rootlocalhost devices]# ls1-0:1.0usb1[rootlocalhost devices]#可以得知,系统只有一个usb control.1-0:1.0:表示,第一个usb cont意思上看来,它是标记接口为停止状态.它的”反函数”是mark_active().两个函数如下示:static inline void mark_active(struct usb_interface *f){f->is_active = 1;f->dev.power.power_state.event = PM_EVENT_ON;}static inline void mark_quiesced(struct usb_interface *f){f->is_active = 0;f->dev.power.power_state.event = PM_EVENT_SUSPEND; }从代码看来,它只是对接口的活动标志(is_active)进行了设置./* Now that all the interfaces are set up, register them* to trigger binding of drivers to interfaces.probe()* routines may install different altsettings and may* claim() any interfaces not yet bound.Many class drivers* need that: CDC, audio, video, etc.*///注册每一个接口?for (i = 0; istruct usb_interface *intf = cp->interface;dev_dbg(&dev->dev,“addi ng %s (config #%d, interface %d)\n”,intf->dev.bus_id, configuration,intf->cur_altsetting->desc.bInterfaceNumber);ret = device_add(&intf->dev);if (ret != 0) {dev_err(&dev->dev, “device_add(%s) --> %d\n”,intf->dev.bus_id, ret);continue;}usb_create_sysfs_intf_files(intf);}//使设备suspendusb_autosuspend_device(dev);return 0;}最后,注册intf内嵌的device结构.设备配置完成了,为了省电,可以将设备置为SUSPEND状态.这个函数中还有几个比较重要的子函数,依次分析如下:1: usb_disable_device()函数.顾名思义,这个函数是将设备disable掉.代码如下:void usb_disable_device(struct usb_device *dev, int skip_ep0){int i;dev_dbg(&dev->dev, “%s nuking %s URBs\n”, __FUNCTION__, skip_ep0 ? “non-ep0” : “all”);for (i = skip_ep0; iusb_disable_endpoint(dev, i);usb_disable_endpoint(dev, i + USB_DIR_IN);}dev->toggle[0] = dev->toggle[1] = 0;/* getting rid of interfaces will disconnect* any drivers bound to them (a key side effect)*/if (dev->actconfig) {for (i = 0; i actconfig->desc.bNumInterfaces; i++) {struct usb_interface *interface;/* remove this interface if it has been registered */interface = dev->actconfig->interface;if (!device_is_registered(&interface->dev))continue;dev_dbg(&dev->dev, “unregistering interface %s\n”,interface->dev.bus_id);usb_remove_sysfs_intf_files(interface);device_del(&interface->dev);}/* Now that the interfaces are unbound, nobody should* try to access them.*/for (i = 0; i actconfig->desc.bNumInterfaces; i++) {put_device(&dev->actconfig->interface->dev);dev->actconfig->interface = NULL;}dev->actconfig = NULL;if (dev->state == USB_STATE_CONFIGURED)usb_set_device_state(dev, USB_STATE_ADDRESS);}}第二个参数是skip_ep0.是表示是否跳过ep0.为1表示跳过,为0表示清除掉设备中的所有endpoint.这个函数可以分为两个部份,一部份是对usb_dev中的endpoint进行操作,一方面是释放usb_dev的选定配置项.对于第一部份:从代码中可能看到,如果skip_ep0为1,那就是从1开始循环,所以,就跳过了ep0.另外,一个端点号对应了两个端点,一个IN,一个OUT.IN端点比OUT端点要大USB_DIR_IN.另外,既然设备都已经被禁用了,那toggle也应该回归原位了.因些将两个方向的toggle都设为0. usb_disable_endpoint()是一个很有意思的处理.它的代码如下:void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr){unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;struct usb_host_endpoint *ep;if (!dev)return;//在dev->ep_out和dev->ep_in删除endpointif (usb_endpoint_out(epaddr)) {ep = dev->ep_out[epnum];dev->ep_out[epnum] = NULL;} else {ep = dev->ep_in[epnum];dev->ep_in[epnum] = NULL;}//禁用掉此ep.包括删除ep上提交的urb 和ep上的QHif (ep) {ep->enabled = 0;usb_hcd_flush_endpoint(dev, ep);usb_hcd_disable_endpoint(dev, ep);}}在dev->ep_in[]/dev->ep_out[]中删除endpoint,这点很好理解.比较难以理解的是后面的两个操作,即usb_hcd_flush_endpoint()和usb_hcd_disable_endpoint().根据之前分析的UHCI的驱动,我们得知,对于每个endpoint都有一个传输的qh,这个qh上又挂上了要传输的urb.因此,这两个函数一个是删除urb,一个是删除qh.usb_hcd_flush_endpoint()的代码如下:void usb_hcd_flush_endpoint(struct usb_device *udev,struct usb_host_endpoint *ep){struct usb_hcd *hcd;struct urb *urb;if (!ep)return;might_sleep();hcd = bus_to_hcd(udev->bus);/* No more submits can occur *///在提交urb时,将urb加到ep->urb_list上的时候要持锁//因此,这里持锁的话,无法发生中断和提交urbspin_lock_irq(&hcd_urb_list_lock);rescan://将挂在ep->urb_list上的所有urb unlink.注意这里unlink一般只会设置urb->unlinked的//值,不会将urb从ep->urb_list上删除.只有在UHCI的中断处理的时候,才会调用//uhci_giveback_urb()将其从ep->urb_list中删除list_for_each_entry (urb, &ep->urb_list, urb_list) {int is_in;if (urb->unlinked)continue;usb_get_urb (urb);is_in = usb_urb_dir_in(urb);spin_unlock(&hcd_urb_list_lock);/* kick hcd */unlink1(hcd, urb, -ESHUTDOWN);dev_dbg (hcd->self.controller,“shutdown urb %p ep%d%s%s\n”,urb, usb_endpoint_num(&ep->desc),is_in ? “in” : “out”,({char *s;switch (usb_endpoint_type(&ep->desc)) {case USB_ENDPOINT_XFER_CONTROL:s = ““; break;case USB_ENDPOINT_XFER_BULK:s = “-bulk”; break;case USB_ENDPOINT_XFER_INT:s = “-intr”; break;default:s = “-iso”; break;};s;}));usb_put_urb (urb);/* list contents may have changed *///在这里解开锁了,对应ep->urb_list上又可以提交urb. //这里释放释的话,主要是为了能够产生中断spin_lock(&hcd_urb_list_lock);goto rescan;}spin_unlock_irq(&hcd_urb_list_lock);/* Wait until the endpoint queue is pletely empty *///等待urb被调度完while (!list_empty (&ep->urb_list)) {spin_lock_irq(&hcd_urb_list_lock);/* The list may have changed while we acquired the spinlock */urb = NULL;if (!list_empty (&ep->urb_list)) {urb = list_entry (ep->urb_list.prev, struct urb,urb_list);usb_get_urb (urb);}spin_unlock_irq(&hcd_urb_list_lock);if (urb) {usb_kill_urb (urb);usb_put_urb (urb);}}}仔细体会这里的代码,为什么在前一个循环中,要使用goto rescan重新开始这个循环呢?这是因为在后面已经将自旋锁释放了,因此,就会有这样的可能,在函数中操作的urb,可能已经被调度完释放了.因此,再对这个urb操作就会产生错误.所以,需要重新开始这个循环.那后一个循环又是干什么的呢?后一个循环就是等待urb被调度完.有人就会有这样的疑问了,这里一边等待,然后endpoint一边还提交urb,那这个函数岂不是要耗掉很长时间?在这里,不要忘记了前面的操作,在调这个函数之前, usb_disable_endpoint()已经将这个endpoint禁用了,也就是说该endpoint不会产生新的urb.因为,在后一个循环中,只需要等待那些被unlink的urb调度完即可.在usb_kill_urb()中,会一直等待,直到这个urb被调度完成为止.可能有人又会有这样的疑问:Usb_kill_urb()中也有unlink urb的操作,为什么这里要分做两个循环呢?另外的一个函数是usb_hcd_disable_endpoint().代码如下:void usb_hcd_disable_endpoint(struct usb_device *udev,struct usb_host_endpoint *ep){struct usb_hcd *hcd;might_sleep();hcd = bus_to_hcd(udev->bus);if (hcd->driver->endpoint_disable)hcd->driver->endpoint_disable(hcd, ep);}从上面的代码可以看到,操作转向了hcd->driver的endpoint_disable()接口.以UHCI为例.在UHCI中,对应的接口为:static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,struct usb_host_endpoint *hep){struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_qh *qh;spin_lock_irq(&uhci->lock);qh = (struct uhci_qh *) hep->hcpriv;if (qh == NULL)goto done;while (qh->state != QH_STA TE_IDLE) { ++uhci->num_waiting;spin_unlock_irq(&uhci->lock);wait_event_interruptible(uhci->waitqh, qh->state == QH_STATE_IDLE); spin_lock_irq(&uhci->lock);--uhci->num_waiting;}uhci_free_qh(uhci, qh);done:spin_unlock_irq(&uhci->lock);}这个函数没啥好说的,就是在uhci->waitqh上等待队列状态变为QH_STATE_IDLE.来回忆一下,qh在什么情况下才会变为QH_STATE_IDLE呢? 是在qh没有待传输的urb的时候.然后,将qh释放.现在我们来接着看usb_disable_device()的第二个部份.第二部份主要是针对dev->actconfig进行的操作, dev->actconfig存放的是设备当前的配置,现在要将设备设回Address状态.就些东西当然是用不了上的了.释放dev->actconfig->interface[]中的各元素,注意不要将dev->actconfig->interface[]所指向的信息释放了,它都是指向dev->config[]-> intf_cache[]中的东西,这些东西一释放,usb device在Get_ Configure所获得的信息就会部丢失了.就这样, usb_disable_device()函数也走到了尾声.2: usb_cache_string()函数这个函数我们在分析UHCI的时候已经接触过,但末做详细的分析.首先了解一下这个函数的作用,有时候,为了形象的说明,会提供一个字符串形式的说明.例如,对于配置描述符来说,它的iConfiguration就表示一个字符串索引,然后用Get_String就可以取得这个索引所对应的字串了.不过,事情并不是这么简单.因为字符串对应不同的编码,所以这里还会对应有编码的处理.来看具体的代码:char *usb_cache_string(struct usb_device *udev, int index){char *buf;char *smallbuf = NULL;int len;if (indexreturn NULL;//不知道字符到底有多长,就按最长256字节处理buf = kmalloc(256, GFP_KERNEL);if (buf) {len = usb_string(udev, index, buf, 256);//取到字符了,分配合适的长度if (len > 0) {smallbuf = kmalloc(++len, GFP_KERNEL);if (!smallbuf)return buf;//将字符copy过去memcpy(smallbuf, buf, len);}//释放旧空间kfree(buf);}return smallbuf;}这个函数没啥好说的,流程转入到usb_string中.代码如下:int usb_string(struct usb_device *dev, int index, char *buf, size_t size) {unsigned char *tbuf;int err;unsigned int u, idx;if (dev->state == USB_STATE_SUSPENDED)return -EHOSTUNREACH;if (sizereturn -EINV AL;buf[0] = 0;tbuf = kmalloc(256, GFP_KERNEL);if (!tbuf)return -ENOMEM;/* get langid for strings if it’s not yet known *///先取得设备支持的编码IDif (!dev->have_langid) {//以0号序号和编码0,Get_String就可得到设备所支持的编码列表err = usb_string_sub(dev, 0, 0, tbuf);//如果发生了错误,或者是取得的数据超短(最短为4字节)if (errdev_err(&dev->dev,“string descriptor 0 read error: %d\n”,err);goto errout;} else if (errdev_err(&dev->dev, “string desc riptor 0 too short\n”);err = -EINV AL;goto errout;}//取设备支持的第一个编码else {dev->have_langid = 1;dev->string_langid = tbuf[2] | (tbuf[3]/* always use the first langid listed */dev_dbg(&dev->dev, “default language 0x%04x\n”,dev->string_langid);}}//以编码ID和序号Index作为参数Get_String取得序号对应的字串err = usb_string_sub(dev, dev->string_langid, index, tbuf);if (errgoto errout;//空一个字符来用来存放结束符size--; /* leave room for trailing NULL char in output buffer */ //两字节一组,(Unicode编码的)for (idx = 0, u = 2; uif (idx >= size)break;//如果高字节有值,说明它不是ISO-8859-1编码的,将它置为? //否则,就将低位的值存放到buf中if (tbuf[u+1]) /* high byte */buf[idx++] = ‘?’;/* non ISO-8859-1 character */elsebuf[idx++] = tbuf;}//在最后一位赋0,字串结尾buf[idx] = 0;//返回字串的长度,(算上了最后的结尾字符)err = idx;//如果该描述符不是STRING描述符,打印出错误提示if (tbuf[1] != USB_DT_STRING)dev_dbg(&dev->dev,“wrong descriptor type %02x for string %d (\”%s\”)\n”,tbuf[1], index, buf);。
LinuxUSB驱动框架分析
Linux USB驱动框架分析USB(Universal Serial Bus)是一种通用的串行总线标准,其可用于连接计算机系统与外部设备(键盘、鼠标、闪存等)。
为了实现 Linux 系统下的 USB 设备驱动程序,内核提供了一个 USB 驱动框架,本文将对该框架进行分析。
USB 设备的基本组成部分在分析 USB 驱动框架之前,有必要对 USB 设备和 USB 数据传输方式进行简要介绍。
USB 设备USB 设备由三个部分组成:B 总线USB 总线负责提供电力和数据传输,是 USB 设备和主机之间连接的物理媒介。
B 主控USB 主控负责对 USB 总线进行管理,发现并控制所连接的 USB 设备,并负责执行 USB 设备和主机之间的数据传输。
B 设备USB 设备通常由芯片组和外部电路组成,其中芯片组通常由 USB 控制器和设备特定的控制器芯片组成。
USB 数据传输方式USB 设备和主机之间的数据传输方式包括:1.控制传输用于控制和配置设备,如读取设备描述符和配置。
2.批量传输用于大数据块的传输,如打印机的数据传输。
3.中断传输用于周期性的小数据块传输,如鼠标和键盘输入。
4.等时传输用于实时传输,如音频数据传输。
USB 驱动框架内核提供的 USB 驱动框架可使开发者对 USB 设备进行驱动,其主要包括以下组件:B subsystem(USB 子系统)USB subsystem 是一个内核子系统,其主要负责 USB 总线管理、USB 主控管理、USB 设备驱动等工作。
B core(USB 核心)USB core 是 USB 驱动框架的核心,其主要负责识别和配置 USB 设备,并提供USB 核心驱动和 USB 设备驱动之间的接口。
B 核心驱动和 USB 设备驱动USB 核心驱动和 USB 设备驱动是相互独立的模块,它们共同构成了 USB 驱动框架。
USB 核心驱动主要负责 USB 设备的底层管理,USB 设备驱动则负责处理读写操作等操作。
Linux下USB内核-学习笔记
Linux下USB内核学习笔记分类:USB2007.8.30 09:39 作者:寂寞17路| 评论:0 | 阅读:302Linux下USB内核之学习笔记(一)Linux下USB子系统软件结构为USB内核(USB驱动,USBD )处于系统的中心,对于它进行研究是能够进行USB驱动开发(包括客户驱动和主机驱动)的第一步。
它为客户端驱动和主机控制器驱动提供了主要数据结构和接口函数,主要有四类功能:客户端驱动管理,USB设备的配置和管理,主机控制器的管理,协议控制命令集和数据传输的管理。
具体代码主要集中在linux/drivers/usb下的usb.c, usb.h中Linux下USB内核之学习笔记(二)主要数据结构分析主要有四个数据结构,分别是USB设备usb_device,保存了一个USB设备的信息,包括设备地址,设备描述符,配置描述符,等等USB总线系统usb_bus,保存了一个USB总线系统的信息,包括总线上设备地址信息,根集线器,带宽使用情况等。
一个USB总线系统肯定有一个主机控制器和一个根集线器。
Linux 支持多USB总线系统客户端驱动程序usb_driver,保存了客户驱动信息,包括驱动名称,以及驱动提供给USB内核使用的函数指针等(USB Request Block)urb,是进行USB通信的数据结构。
Linux的USB子系统只使用这么一种数据结构来进行USB通信,urb包含了建立任何USB传输所需的所有信息,并贯穿于USB 协议栈对数据处理的整个过程。
下面是对各部分进行详细分析。
struct usb_device { ; //代表一个USB设备int devnum; ; ; //分配的设备地址,1-127enum {USB_SPEED_UNKNOWN = 0, /* enumerating */USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */USB_SPEED_HIGH /* usb 2.0 */} speed; //设备速度,低速/全速/高速struct usb_device *tt; /* usb1.1 device on usb2.0 bus */,事务处理解释器int ttport; ; /* device/hub port on that tt */设备所连接的具有事务处理解释器功能的集线器端口atomic_t refcnt; ; /* Reference count */引用计数struct semaphore serialize; //用于同步unsigned int toggle[2] ; ; /* one bit for each endpoint ([0] = IN, [1] = OUT) */用于同步切换的位图,每个端点占用1位,[0]表示输入,[1]输出unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; [0] = IN, [1] = OUT */表示端点是否处于停止状态的位图int epmaxpacketin[16] ; ;/* INput endpoint specific maximums */输入端点的最大包长int epmaxpacketout[16] ; ; /* OUTput endpoint specific maximums */输出端点的最大包长struct usb_device *parent; //表示设备所连的上游集线器指针struct usb_bus *bus; /* Bus we're part of */设备所属的USB总线系统struct usb_device_descriptor descriptor;/* Descriptor */ 设备描述符struct usb_config_descriptor *config; /* All of the configs */指向设备的配置描述符和其所包含的接口描述符,端点描述符的指针struct usb_config_descriptor *actconfig;/* the active configuration */当前的配置描述符指针char **rawdescriptors; ; ;/* Raw descriptors for each config */int have_langid; /* whether string_langid is valid yet *// 是否有string_langidint string_langid; /* language ID for strings */和字符描述符相关的语言IDvoid *hcpriv; ; ; /* Host Controller private data */设备在HCD层占用的资源指针,对USB内核层是透明的/* usbdevfs inode list */ 设备在usbdevfs中的inode列表struct list_head inodes;struct list_head filelist;/** Child devices - these can be either new devices* (if this is a hub device), or different instances* of this same device.** Each instance needs its own set of data structures.*/只对当前设备是集线器的情况有效int maxchild; /* Number of ports if hub */ hub的下游端口数struct usb_device *children[USB_MAXCHILDREN]; hub所连设备指针};struct usb_bus { //USB总线系统int busnum; ; ; /* Bus number (in order of reg) */当前总线系统的序列号,Linux 支持多总线系统并为它们编号#ifdef DEVNUM_ROUND_ROBINint devnum_next; /* Next open device number in round-robin allocation */#endif /* DEVNUM_ROUND_ROBIN */给连接到子系统上的设备分配设备号的数据结构struct usb_devmap devmap; /* Device map */给连接到子系统上的设备分配设备号的数据结构struct usb_operations *op; /* Operations (specific to the HC) */HCD为USB内核提供的一系统函数集指针struct usb_device *root_hub; /* Root hub */指向根Hub的指针struct list_head bus_list; 双向链表指针,USB内核用一个双向链表来维护系统中所有USB总线系统void *hcpriv; /* Host Controller private data */与主机控制器相关数据,对USB内核层是透明int bandwidth_allocated; /* on this Host Controller; applies to Int. and Isoc. pipes; measured in microseconds/frame; range is 0..900, where 900 = 90% of a 1-millisecond frame */当前子系统的带宽使用情况,单位是微秒/帧,取值范围[0,900]int bandwidth_int_reqs; ; /* number of Interrupt requesters */子系统中当前的中断传输的数量int bandwidth_isoc_reqs; /* number of Isoc. requesters */子系统中当前的实时传输的数量/* usbdevfs inode list */ 在usbdevfs中的inode列表struct list_head inodes;atomic_t refcnt;};struct usb_driver { //客户端驱动程序为USB内核提供的调用接口const char *name; //客户端驱动程序的字符串名称,用于避免重复安装和卸载void *(*probe)(//给USB内核提供的函数,用于判断驱动程序是否能对设备的某个接口进行驱动,如能则分配资源struct usb_device *dev, ; ;/* the device */unsigned intf, ; ; /* what interface */const struct usb_device_id *id /* from id_table */);void (*disconnect)(struct usb_device *, void *);//给USB内核提供的函数,用于释放设备的某个接口所占用的资源struct list_head driver_list;//对应的双向指针,USB内核通过一个双向指针链表维护USB子系统中所用的客户端驱动程序struct file_operations *fops;int minor; 驱动的次版本号struct semaphore serialize;/* ioctl -- userspace apps can talk to drivers through usbdevfs */int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf);/* support for "new-style" USB hotplugging* binding policy can be driven from user mode too*/const struct usb_device_id *id_table;/* suspend before the bus suspends;* disconnect or resume when the bus resumes */// void (*suspend)(struct usb_device *dev);// void (*resume)(struct usb_device *dev);};typedef struct urb //USB Request Block,包含了建立任何USB传输所需的所有信息,并贯穿于USB协议栈对数据处理的整个过程{spinlock_t lock; & nbsp; // lock for the URBvoid *hcpriv; // private data for host controller与主机控制器相关数据,对USB内核层是透明struct list_head urb_list; // list pointer to all active urbs双向指针,用于将此URB连接到处于活动的URB双向链表中struct urb *next; // pointer to next URB 指向下一个URB的指针struct usb_device *dev; // pointer to associated USB device 接受此URB的USB设备指针unsigned int pipe;// pipe information表示设备的某个端点和客户端驱动程序之间的管道int status; ; ; // returned status 返回状态unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | etc.USB_DISABLE_SPD //拒绝短数据包,即比最大传输包长度小的数据包USB_ISO_ASAP //用于实时传输,告诉主机控制器立即进行此请求的数据传输。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
USB卷一DS部份LK版本:2.6.35.32013年1月14日 任务目标:USB设备、配置、接口、设置以及端点的五者关系图:图示:USB设备:一个USB接口对应一种功能对应一个USB驱动一个USB设备对应有一个或多个配置;一个配置对应一个或多个的USB接口;一个USB接口又具有一个或多个的设置;一个设置又会拥有一个或多个的USB端点。
配置接口端点端点....接口端点端点.... USB驱动程序USB驱动程序配置接口端点端点........Linux-USB子系统框架图:内核中USB子系统的结构:USB拓扑结构:主机的责任:为了与USB设备通信,计算机需要硬件和软件共同来支持USB主机功能。
其中,硬件由USB主机控制器和含有一个或多个USB端口的跟集线器构成。
典型的计算机拥有一个或多个硬件主机控制器。
每个控制器支持多个端口。
主机负责管理总线。
主机需要知道哪些设备正挂在总线上,以及每个设备有怎样的能力。
主机还必须尽可能保证总线上所有设备能够按照各自的需要收发数据。
主机完成下述的所有任务。
1、检测设备开机时,集线器会令主机得知所有已连接的USB设备。
在被称作“枚举”的过程中,主角会决定使用那种总线速率、指定地址,并请求额外的信息。
开机后,若有设备连接或移除,集线器将把此事件通知主机,主机便枚举新连接的设备或从它应用程序的可用设备清单上撤销全部已移除的设备。
2、管理数据流主机管理总线上的数据流。
可能有许多设备会要求在同一时间内传输数据。
主机控制器将可用的时间分成诸多小段,给与每项传输任务一部分可用时间。
USB3.0主机可同时收发超高速数据,也可在USB2.0速率下完成传输。
USB2.0总线每次只以一种速率在一个方向上传输数据。
枚举过程中,设备驱动程序将(向系统)索要传输所需的带宽,以保证时序。
如果所要求的带宽无法被满足,驱动程序便索要更小的带宽或等待直到所要求的带宽得到满足。
得不到时序保证的传输,将等到总线不再忙碌的时候再使用剩余带宽。
3、错误检查传输数据时,主机将添加错误检查位。
接收数据时,设备将对数据进行错误检查的计算,并将结果与错误检查位进行比较。
若结果不匹配,设备将不返回对接收数据的确认,主机便得知该数据该被重新发送。
4、提供并管理电源高功耗的USB2.0设备可从总线上获取高达500mA的电流。
而高功耗的超高速设备则可从USB3.0总线上获取高达900mA的电流。
在某些电池供电的主机和集线器上,端口只支持低功耗设备,这些设备的电源被限制在100mA(对于USB2.0设备)或150mA(对于超高速设备)以下。
为了节电,总线空闲时,主机可要求设备进入低功耗状态,减少它们对总线电流的使用。
5、与外围设备交换数据设备的职责:主机发起通信时,设备必须响应。
设备必须完成所有下述任务。
1、检测与芯片的通信2、响应标准请求设备必须对由主机计算机在枚举期间发出的标准请求作出响应。
主机可能在枚举完成后的任何时候发送请求。
所用设备必须对如下请求作出响应:查询设备功能和状态、请求设备采取其他动作。
收到请求时,设备会将数据或状态信息置于缓冲区里,以便发送到主机。
对于有些请求(如选择配置),设备会采取另外的动作来响应主机。
USB规范规定了请求和类,而厂商还可确定另外的请求。
在接到不支持的请求时,设备将以状态代码作为响应。
3、错误检查接到包含错误检查位的数据时,设备将会进行错误检查计算。
设备以响应/不响应的方式,告诉主机是否要重新发送数据。
设备也会检测主机发送的表示“主机已经接收到数据”的确认信息。
一般由设备控制器的硬件完成这些功能。
4、管理电源设备可能会有自己的电源,或从总线获取电源,再或者同时从这两个来源获得电源。
主机可请求设备进入低功耗的“挂起”(suspend)状态,这种状态要求设备只消耗不多于2.5mA 的总线电流。
有些设备支持远程唤醒(remote wakeup),可请求退出挂起状态。
5、与主机交互数据控制器芯片的硬件负责处理对总线数据进行规范格式的工作。
这包括为要发送数据添加错误检查位,检查收到的数据是有有错误,以及在总线上发送和接收具体的位信号。
五者关系图如下:susb_device{} 设备.descriptor *config...... usb_host_config{}[] ...... usb_host_config{} .desc ...... *interface[]usb_interface{}[] ...... usb_interface{} *altsetting *cur_altsetting .num_altsetting......usb_host_interface{}[] ...... usb_host_interface{} *endpoint .desc ......usb_host_endpoint{}[]...... usb_host_endpoint{} .desc ...... 一个配置一个接口一个设置一个端点 .urb_listusb_device_descriptor{} 配置描述符 usb_config_descriptor{}usb_interface_descriptor{} 接口描述符usb_endpoint_descriptor{} 端点描述符 1.定义这个端点的数据输入/输出方向由.bEndpointAddress 的bit7决定,0为输出,1为输入。
2.至于如何判断是中断、等时、控制或批量传输的类型:可根据.bmAttributes 的bit1和bit0决定,00表示控制传输,01表示等时传输,10表示批量传输,11表示中断传输*ep_in[]*ep_out[] .bNumConfigurations设备描述符USB_MAXINTERFACES.bNumEndpoints .bEndpointAddress.bmAttributes 1.每个端点拥有一个urb_list 2.urb 的数据从端点进行收发3.HCD 每收到一个urb ,就会将它添加到这个urb 指定的urb 队列中,例:list_add_tail(&urb->urb_list, &urb->ep->urb_list)usb_host_endpoint{}*[] .0..... usb_host_endpoint*{}[]...... 多个配置多个接口 多个设置 多个端点 指向ep_in、ep_out的来源:#define USB_DIR_OUT 0#define USB_ENDPOINT_DIR_MASK 0x80static inline int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd){return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); }void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep,...){int is_out = usb_endpoint_dir_out(&ep->desc);......if (is_out || is_control)dev->ep_out[epnum] = ep;if (!is_out || is_control)dev->ep_in[epnum] = ep;}成员变量及意义解说:struct usb_device_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 bcdUSB; __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; __u8 bMaxPacketSize0; __le16 idVendor; __le16 idProduct; __u8 bNumConfigurations;...... }usb_device_descriptor{}.bLength .bDescriptorType .bDeviceClass .bDeviceSubClass .bDeviceProtocol .bMaxPacketSize0 .idVendor .idProduct...... 设备描述符usb_config_descriptor{} 配置描述符.bLength.bDescriptorType .bNumInterfaces .bConfigurationValue .iConfiguration .bmAttributes .bMaxPower接口描述符usb_interface_descriptor{}.bLength .bDescriptorType .bInterfaceNumber .bAlternateSetting .bNumEndpoints .bInterfaceClass .bInterfaceSubClass .bInterfaceProtocol .iInterface 端点描述符usb_endpoint_descriptor{}.bLength .bDescriptorType .bEndpointAddress .bmAttributes .wMaxPacketSize .bInterval .bRefresh .bSynchAddress字符串描述符usb_string_descriptor{} .bLength.bDescriptorType .wData[1]集线器类例子: bLength :描述符的长度 #define USB_DT_DEVICE_SIZE 18; bDescriptorType :值为USB_DT_DEVICE :0x01bcdUSB :USB spec 的版本号,如果是2.0版本,值为0x0200,如是1.1版本,则值 为0x0110。
bDeviceClass bDeviceSubClass bDeviceProtocolidVendor idProductbMaxPacketSize0:端点0一次可以处理的最大字节数。
Q:为什么要放在设备描述符里呢?A:由于它自己没有一个描述符,而每个设备又都有这么一个端点所以这个信息被保存在了设备描述符里,其中,这个值只能是 8,16,32或64这四者之一,高速模式值只能为64, 低速模式 只能为8,对于中速,可以为8,16,32,64,单位字节。