15.设置是接口的设置
不过这里还是有一点小悬念的,前面介绍struct usb_interface时,表示接口设置的struct usb_host_interface就被有意无意地略过了,现在看一看它的真面目,同样在include/linux/usb.h文件中定义。
70 struct usb_host_interface { 71 struct usb_interface_descriptor desc; 72 73 /* array of desc.bNumEndpoint endpoints associated with this 74 * interface setting. these will be in no particular order. 75 */ 76 struct usb_host_endpoint *endpoint; 77 78 char *string; /* iInterface string, if present */ 79 unsigned char *extra; /* Extra descriptors */ 80 int extralen; 81 };
71行,desc,接口描述符。什么是描述符?我们的生活就是一个不断遇到人,认识人的过程,有些人注定只是擦肩而过,有些人却深深地留在我们的心里,比如USB的描述符。实际上,USB的描述符是一个带有预定义格式的数据结构,里面保存了USB设备的各种属性还有相关信息,比如姓名、生产地等,我们可以通过向设备请求获得它们的内容来深刻地了解和感知一个USB设备。
USB描述符主要有四种:设备描述符、配置描述符、接口描述符和端点描述符。协议中规定一个USB设备是必须支持这四种描述符,当然也有其他一些描述符来让设备可以显得有个性一些,但这四大描述符是一个都不能少的。
这些描述符放哪儿?当然是在设备中,就等着主机去“拿”。具体在哪儿?USB设备中都会有一个叫EEPROM的东西,没错,就是放在那儿,它就是用来存储设备本身信息的。EEPROM就是电可擦写可编程ROM,它与Flash虽说都是要电擦除的,但它可以按字节擦除,Flash只能一次擦除一个block,所以如果要修改比较少的数据的话,使用它还是比较合适的。但是世界上没有完美的东西,EEPROM成本相对Flash比较高,所以一般来说USB设备中只拿它来存储一些本身特有的信息,要想存储数据,还是使用Flash吧。
接口描述符,就是描述接口本身的信息的。一个接口可以有多个设置,使用不同的设置,描述接口的信息会有所不同,所以接口描述符并没有放在struct usb_interface结构中,而是放在表示接口设置的struct usb_host_interface结构中。在include/linux/usb/ch9.h文件中定义。
294 /* USB_DT_INTERFACE: Interface descriptor */ 295 struct usb_interface_descriptor { 296 __u8 bLength; 297 __u8 bDescriptorType; 298 299 __u8 bInterfaceNumber; 300 __u8 bAlternateSetting; 301 __u8 bNumEndpoints; 302 __u8 bInterfaceClass; 303 __u8 bInterfaceSubClass; 304 __u8 bInterfaceProtocol; 305 __u8 iInterface; 306 } __attribute__ ((packed));
又看到了__attribute__,不过在这里改头换面成了__attribute__ ((packed)),意思就是告诉编译器,这个结构的元素都是1字节对齐的,不要再添加填充位了。因为这个结构和spec里的Table 9.12是完全一致的,包括字段的长度。如果不给编译器这个暗示,编译器就会依据你的平台类型在结构的每个元素之间添加一定的填充位,如果你拿这个添加了填充位的结构去向设备请求描述符,你想一想会是什么结果。
296行,bLength,描述符的字节长度。协议中规定,每个描述符必须以一个字节打头来表明描述符的长度。接口描述符的bLength应该是9,没错,ch9.h文件中紧挨着接口描述符的定义就定义了这个长度。
308 #define USB_DT_INTERFACE_SIZE 9
297行,bDescriptorType,描述符的类型。各种描述符的类型都在ch9.h文件中有定义,对应spec中的Table 9.5。对于接口描述符来说,值为USB_DT_INTERFACE,也就是0x04。
299行,bInterfaceNumber,接口号。每个配置可以包含多个接口,这个值就是它们的索引值。
300行,bAlternateSetting,接口使用的是哪个可选设置。协议中规定,接口默认使用的设置总为0号设置。
301行,bNumEndpoints,接口拥有的端点数量。这里并不包括端点0,端点0是所有的设备都必须提供的,所以这里就没必要多此一举包括它了。
302行,bInterfaceClass;303行,bInterfaceSubClass;304行,bInterfaceProtocol。这个世界上有许多USB设备,它们各有各的特点,为了区分它们,USB规范,或者说USB协议把USB设备分成了很多类,然而每个类又分成子类。这很好理解,我们的一个大学也是如此,先是分成很多个学院,然后每个学院又被分为很多个系,可能每个系下边又分了各个专业。USB协议也是这样的,首先每个Device或Interface属于一个Class,然后Class下面又分了SubClass,SubClass下面又按各种设备所遵循的不同的通信协议继续细分。USB协议中边为每一种Class,每一种SubClass,每一种Protocol定义一个数值,比如Mass Storage的Class就是0x08,Hub的Class就是0x09。
305行,iInterface,接口对应的字符串描述符的索引值。这里怎么又跳出来一个叫字符串描述符的东西?你没看错我也没说错,除了前面提到的四大描述符,还有字符串描述符。不过四大描述符是每个设备必须支持的,这个字符串描述符却是可有可无的,有了你欢喜,我也欢喜,没有也不是什么问题。使用lsusb命令:
localhost:/usr/src/linux/drivers/usb/core # lsusb Bus 001 Device 013: ID 04b4:1081 Cypress Semiconductor Corp. Bus 001 Device 001: ID 0000:0000
在上面代码的第1行里显示的是我手上的Cypress USB开发板,看里面的Cypress Semiconductor Corp.,它从哪里来?是不是应该从设备中来?设备的那几个标准描述符,整个描述符的大小也不一定放得下这么一长串,所以,一些设备专门准备了一些字符串描述符(string descriptor),就用来记这些长串的东西。字符串描述符主要就是提供一些设备接口相关的描述性信息,比如厂商的名字,产品序列号等。字符串描述符当然可以有多个,这里的索引值就是用来区分它们的。
说过了接口描述符,回到struct usb_host_interface的76行,endpoint,一个数组,表示这个设置所使用到端点。
78行,string,用来保存从设备中取出来的字符串描述符信息的,既然字符串描述符可有可无,这里的指针也有可能为空了。
79行,extra;80行,extralen,有关额外的描述符。除了前面提到的四大描述符及字符串描述符外,还有为一组设备也就是一类设备定义的描述符,和厂商为设备特别定义的描述符,extra指的就是它们,extralen表示它们的长度。