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接口的缓存