usb dev 介绍 (内容来自《linux内核修炼之道》,我只不过敲了一

Usb设备是一个非常复杂的东西,它由配置、接口、端点等构成。另外,通常所说的usb驱动其实是对usb接口的驱动,而不是整个usb设备。

1、 接口

设备可以有多个接口,每个接口代表一个功能,每个接口对应着一个驱动。设备模型的device落实在USB子系统,成了两个结构,一个是struct usb_device,一个是struct usb_interface.比如一个USB键盘,上面带一个扬声器,因此有两个接口,那这样肯定得要两个驱动程序,一个是键盘驱动程序,一个是音频流驱动程序。既然通常把这样两个整合在一起的东西叫做一个设备,所以要通过接口来区分两者。于是有了struct usb_interface:

struct usb_interface {

/* array of alternate settings for this interface, stored in no particular order */

struct usb_host_interface *altsetting;//可选的设置

struct usb_host_interface *cur_altsetting; /* 当前正在使用的设置 */

unsigned num_altsetting; /*可选设置(altsetting)的数量 */

/* If there is an interface association descriptor then it will list the associated interfaces */

struct usb_interface_assoc_descriptor *intf_assoc;

int minor; /* 分配给接口的次设备号 */

enum usb_interface_condition condition; /* state of binding */

unsigned is_active:1; /* 接口是不是处于挂起状态*/

unsigned needs_remote_wakeup:1; /*是否需要打开远程唤醒功能 */

struct device dev; /*接口设备device,设备模型中的device */

struct device *usb_dev; /* 当接口使用USB_MAJOR时会用到 */

int pm_usage_cnt; /* 电源管理计数*/

};

关于配置(configuration)和设置(setting)的解释:

配置(configuration),比如一个手机可以有多种配置,比如可以摄像,可以连接在电脑上当U盘,那么这两种情况就属于不同的配置,在手机里有相应的选择菜单,选择了哪种配置就按哪种配置工作。不同的配置代表不同的功能,所以不同配置可能需要不同的接口,比如当配置为摄像的时候,需要某个接口,当配置为U盘的时候,需要另外的接口。

设置(setting),比如一个手机各种配置都确定了,是震动还是铃声也确定了。各种功能都确定了。但声音的大小还可以变,声音可以一格一格地调,这每一格就相当于一个设置。

设备 > 配置 > 接口 > 设置

关于设备号:

linux系统中USB设备的主设备号有两个预留的:

#define USB_MAJOR 180 // LINUX为USB设备预留的主设备号

#define USB_DEVICE_MAJOR 189 // 用于usbfs

通常来讲,USB设备会与Input、video等子系统相关联,并不只是作为USB设备而存在,其对应的驱动的probe函数里使用相应的注册函数,这时候USB_MAJOR就用不着了。

这里的minor只在USB_MAJOR起作用时才起作用。

Condition:接口和驱动的绑定状态。

远程唤醒:

远程唤醒允许挂起的设备给主机发信号,通知主机它将从挂起状态恢复,如果主机处于挂起状态,就会唤醒主机,不然主机仍在睡,设备醒来也没用。

接口中的设置(setting)

Struct usb_interface中有个struct usb_host_interface结构体:

struct usb_host_interface{

struct usb_interface_descriptor desc; //接口描述符

/* array of desc.bNumEndpoint endpoints associated with this

* interface setting. these will be in no particular order.

*/

struct usb_host_endpoint *endpoint; //这个设置用到的端点

char *string; /*保存从设备里取出来的字符串描述符信息 */

unsigned char *extra; //额外的描述符,如字符串描述符,厂商定义的描述符等

int extralen; //额外描述符的长度

};

四大描述符:设备描述符、配置描述符、接口描述符、端点描述符。

Device configuration interface endpoint

(存放在usb设备的EEPROM里)

接口描述符—用于描述接口本身的信息:

struct usb_interface_descriptor {

__u8 bLength; // 描述符的字节长度(9)

__u8 bDescriptorType; // 描述符的类型,值为USB_DT_INTERFACE=0x04

__u8 bInterfaceNumber; // 接口号每个配置可以包含多个接口,这个值来区分。

__u8 bAlternateSetting; //接口使用哪个可选设置。默认选0号设置。

__u8 bNumEndpoints; //接口拥有的端点数量。(不包括0端点)

__u8 bInterfaceClass; //类

__u8 bInterfaceSubClass; // 子类

__u8 bInterfaceProtocol; // 协议

__u8 iInterface; //字符串描述符的索引信息。

} __attribute__ ((packed));

类、子类、协议:每个device或interface属于一个class,每个class下又分了subclass,subclass又根据设备所遵循的协议继续细分。

2、 端点

端点是数据传输的终点。

struct usb_host_endpoint {

struct usb_endpoint_descriptor desc; // 端点描述符

struct list_head urb_list; // 端点要处理的urb队列

void *hcpriv; // 提供给HCD-host controller driver用的

struct ep_device *ep_dev; /* For sysfs info *///供sysfs使用

unsigned char *extra; /* Extra descriptors */ //额外扩展的描述符

int extralen;

};

端点描述符:

struct usb_endpoint_descriptor {

__u8 bLength; // 描述符的字节长度

__u8 bDescriptorType;// 描述符类型,端点为USB_DT_ENDPOINT=0x05

__u8 bEndpointAddress;// 端点的方向(IN/OUT),端点的地址,端点的端点号

__u8 bmAttributes;// 属性bit1-bit0:00控制, 01等时,10批量,11中断

__le16 wMaxPacketSize; // 端点一次可以处理的最大字节数

__u8 bInterval; //USB轮询式的总线

/* NOTE: these two are _only_ in audio endpoints. */

/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */

__u8 bRefresh;

__u8 bSynchAddress;

} __attribute__ ((packed));

Urb_list:

Urb_list为端点要处理的urb队列。Urb是USB通信的主角,它包含了执行USB传输所需要的所有信息,你要想和你的USB通信,就得创建urb,并且要为它赋好值,交给USB Core,它会找到合适的host controller,从而进行具体的数据传输。设备中的每个端点都可以处理一个urb队列,urb是内核里对USB传输数据的封装。

3、 设备

struct usb_device {

int devnum; //设备的地址,USB设备在一条USB总线上的编号

char devpath [16]; // Use in messages: /port/port

enum usb_device_state state; // 设备的状态

enum usb_device_speed speed; //设备的速度

struct usb_tt *tt; // low/full speed dev, highspeed hub

int ttport; // device port on that tt hub

unsigned int toggle[2]; //one bit for each endpoint * ([0] = IN, [1] = OUT)

struct usb_device *parent; //父指针

struct usb_bus *bus; // 设备所在的那条总线

struct usb_host_endpoint ep0;// 端点0

struct device dev; // 嵌入到usb_device结构里的struce device结构

struct usb_device_descriptor descriptor;//设备描述符

struct usb_host_config *config; //设备拥有的配置

struct usb_host_config *actconfig;//当前激活的配置

struct usb_host_endpoint *ep_in[16]; //in端点

struct usb_host_endpoint *ep_out[16];//out 端点

char **rawdescriptors; //

unsigned short bus_mA; /* Current available from the bus */

u8 portnum; /* Parent port number (origin 1) */

u8 level; /* Number of USB hub ancestors */

unsigned discon_suspended:1; /* Disconnected while suspended */

unsigned have_langid:1; /* whether string_langid is valid */

int string_langid; //语言

struct list_head filelist;

int maxchild; //hub的端口数

struct usb_device *children[USB_MAXCHILDREN];// USB_MAXCHILDREN=31

int pm_usage_cnt; /* usage counter for autosuspend */

u32 quirks; //怪癖,不符合标准的内容

};

devpath:

还是拿/sys/bus/usb/devices目录举例:

在sysfs文件系统下,usb1、usb2、usb3、usb4表示计算机上连接了4条USB总线,即4个USB主机控制器。

4-0:1.0表示什么?4表示4号总线,或者说4号root hub,0 就是这里我们说的devpath,1表示配置为1号,0表示接口号为0。即:4号总线的0号端口的设备,使用的是1号配置,接口号为0. 但devpath并不是端口号。这里是root hub没有级联hub的情况.

如果有级联hub,比如3-1.3 : 1.0,如果3-1 : 1.0 是一个HUB,则下面的3号端口的设备就是3-1.3 : 1.0。总的来说,就是端口号一级一级往下加。

State:

enum usb_device_state {

USB_STATE_NOTATTACHED = 0, // 设备已经连接到USB接口上了,是hub检测到设备时的初始状态,这里的USB_STATE_NOTATTACHED就是表示设备并未attached

USB_STATE_ATTACHED, //

USB_STATE_POWERED, // 加电状态。USB设备的电源可以来自外部电源—self-powered,也可以来自hub,叫bus-powered.尽管self-powered的USB设备可能在连接上USB接口以前已经上电,但它们直到连上USB接口后才能被看做是powered

USB_STATE_RECONNECTING, /* auth */

USB_STATE_UNAUTHENTICATED, /* auth */

USB_STATE_DEFAULT, //缺省状态,在powered之后,设备必须在收到一个复位信号并成功复位后,才能使用缺省地址回应主机发过来的设备和配置描述符的请求 ???

USB_STATE_ADDRESS, //表示主机分配了一个唯一的地址给设备,此时设备可以使用缺省管道响应主机的请求。

USB_STATE_CONFIGURED, // 表示设备已经被主机配置过了,此时主机可以使用设备提供的所有功能。

USB_STATE_SUSPENDED//挂起状态,为了省电,设备在指定的时间(3ms)内如果没有发生总线传输,就要进入挂起状态。此时,USB设备要自己维护包括地址、配置在内的信息

};

上面定义了9中装填,USB2.0里只定义了6种。

usb_device_speed:

设备的速度

enum usb_device_speed {

USB_SPEED_UNKNOWN = 0, /* enumerating */

USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */

USB_SPEED_HIGH, /* usb 2.0 */

USB_SPEED_WIRELESS, /* wireless (usb 2.5) */

USB_SPEED_SUPER, /* usb 3.0 */

};

tt:

ttport,tt叫做transaction translator。可以把它想象成一块特殊的电路,是hub里面的电路,确切说是hub中的电路。HUB也有高速hub和过去的hub,但这里就有一个兼容性问题了,高速的hub是否能够支持低速/全速的设备?一般来说是不支持的,于是有了一个叫tt的电路,负责高速和低速/全速的数据转换。

toggle[2]:这个数组只有两个元素,对应IN和OUT端点,每一个端点占一位。

该数组存在的价值详解:要想和USB通信,创建一个urb,为它赋好值,交给USB core就可以了。这个urb是站在开发者的角度。实际上在USB cable里流淌的不是这样的。开发者提交的是urb,USBcable里流淌的是一个个的数据包packet。

所有的packet都是从一个SYNC同步字段开始的,SYNC是一个8bits长的二进制串,只是用来同步用的,它的最后两位标志了SYNC结束和PID(packet identifier)的开始。

PID也是一个8位的二进制串,前4位用来区分不同的packet类型,后面4位只是前四位的反码。

PID之后紧跟地址字段,每个packet都需要知道自己去哪里。这个地址字段实际上包括两部分,7位表示了总线上连接的设备或接口的地址,4位表示端点的地址,这就是为什么每条usb总线最多只能有128个设备的原因。

地址字段后是11位的帧号,值达到7FFH时归零。这个帧号并不是每个packet都有的。

帧号再往后就是data字段了。他可以有0~1024个字节不等。最后还有CRC教研字段来做扫尾工作。

对于data类型的字packet,共有四种类型,DATA0 DATA1 DATA2 DATA3.其中DATA0 DATA1就可以用来实现data toggle同步

Parent字段: 对于root hub而言,它是和主机控制器绑定在一起的,他的parent指针在主机控制器的驱动程序中已经赋值为NULL,所以对于root hub不需要再有父指针了,这个父指针是给从root hub连出来的节点用的。USB设备是从root hub开始,一个一个往外连形成一棵树。

struct usb_device_descriptor descriptor:设备描述符

struct usb_device_descriptor {

__u8 bLength; //描述符的长度

__u8 bDescriptorType; //USB_DT_DEVICE=0x01

__le16 bcdUSB; //USBspce的版本号,如果可高速,则为0200H

__u8 bDeviceClass; //类

__u8 bDeviceSubClass; //子类

__u8 bDeviceProtocol; //协议

__u8 bMaxPacketSize0; //端点0一次可以处理的最大字节数

__le16 idVendor; //厂商和产品ID

__le16 idProduct; //

__le16 bcdDevice; //设备的版本号

__u8 iManufacturer; //厂商

__u8 iProduct; //产品

__u8 iSerialNumber; //序列号

__u8 bNumConfigurations; //设备当前速度模式下支持的配置数量。

} __attribute__ ((packed));

Rawdescriptors:字符指针数组,数组里的每一项都指向一个使用GET_DESCRIPTOR请求去获得配置描述符时得到的结果。当使用GET_DESCRIPTOR去请求配置描述符时,设备返回的不仅有配置描述符,还把配置所包括的所有接口的接口描述符,以及接口里端点的端点描述符都给出了。

USB设备请求:所有的设备请求通过缺省的控制管道来响应主机的请求,既然使用的是控制管道,那这种传输方式即为控制传输,这些请求的底层packet属于setup类型,在setup包里包括了请求的各种参数。协议里同时也定义了一些标准的设备请求,并规定所有的设备必须响应它们,及时处于default或address状态。GET_DESCRIPTOR即属于标准请求。

Portnum:无论root hub还是一般hub,usb设备总要接入到hub的端口上才可以用,portnum即为这个端口号。只有root hub上行端口是没有的,因为它不插在任何Hub上,所以root hub的此域为0.

Level:层级,root hub为0级,其下面一层为level1,以此类推。

4、 配置

struct usb_host_config {

struct usb_config_descriptor desc;

char *string; /* iConfiguration string, if present */

/* List of any Interface Association Descriptors in this configuration. */

struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];

/* the interfaces associated with this configuration, stored in no particular order */

struct usb_interface *interface[USB_MAXINTERFACES];

/* Interface information available even when this is not the active configuration */

struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];

unsigned char *extra; /* Extra descriptors */

int extralen;

};

配置描述符struct usb_config_descriptor desc:

四大描述符之一,配置描述符:

struct usb_config_descriptor {

__u8 bLength; //描述符长度

__u8 bDescriptorType; //描述符类型:USB_DT_CONFIG = 0x02, USB_DT_OTHER_SPEED_CONFIG = 0x07(高速设备操作在低速/全速时的配置信息)

__le16 wTotalLength; // 使用GEP_DESCRIPTOR请求从设备里获得配置描述符信息时返回的数据长度,包括配置描述符,接口描述符和端点描述符等

__u8 bNumInterfaces; // 这个配置包含的接口数目

__u8 bConfigurationValue; //设置设备的配置

__u8 iConfiguration; //描述配置信息的字符串描述符的索引值

__u8 bmAttributes;//配置的一些特点

__u8 bMaxPower;//从总线那里分的最大电流

} __attribute__ ((packed));

String:配置描述符iConfiguration字段对应的字符串描述信息。

interface:配置所包含的接口

intf_cache:usb接口的缓存

陪我们走过一段别人无法替代的记忆。在那里,

usb dev 介绍 (内容来自《linux内核修炼之道》,我只不过敲了一

相关文章:

你感兴趣的文章:

标签云: