- Linux那些事儿之我是USB
- 肖林甫 肖季东 任桥伟
- 2433字
- 2020-08-27 00:52:23
21.设备的生命线(二)
现在设备的struct usb_device结构体已经准备好了,只是还不怎么饱满,Hub接下来就会给它做“整容手术”,往里边儿塞点什么,充实一些内容,比如:将设备的状态设置为Powered,也就是加电状态;因为此时还不知道设备支持的速度,于是将设备的speed成员暂时先设置为USB_SPEED_UNKNOWN;设备的级别level当然会被设置为Hub的level加上1;还有为设备能够从Hub那里获得的电流赋值;为了保证通信畅通,Hub还会为设备在总线上选择一个独一无二的地址。
表 1.20.1
前面讲过,设备要想从Powered状态发展到下一个Default状态,必须收到一个复位信号并成功复位。那Hub接下来的动作就很明显了,复位设备,复位成功后,设备就会进入Default状态。
现在就算设备成功复位了,进入了Default状态,同时,Hub也会获得设备真正的速度。那根据这个速度,起码能够知道端点0一次能够处理的最大数据长度,协议中规定,对于高速设备,这个值为64字节,对于低速设备,这个值为8字节,而对于全速设备可能为8字节、16字节、32字节、64字节其中的一个。
设备也该进入Address状态了。
设备要想进入Address状态很容易,只要Hub使用core中定义的一个函数usb_control_ msg,给设备发送SET_ADDRESS请求,设备就进入Address状态了。设备的address,就是表1.20.1所示的Devnum。
那现在咱就来说说usb_control_ msg函数,它在drivers/usb/core/message.c中定义。
120 int usb_control_ msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, 121 __u16 value, __u16 index, void *data, __u16 size, int timeout) 122 { 123 struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); 124 int ret; 125 126 if (!dr) 127 return -ENOMEM; 128 129 dr->bRequestType= requesttype; 130 dr->bRequest = request; 131 dr->wValue = cpu_to_le16p(&value); 132 dr->wIndex = cpu_to_le16p(&index); 133 dr->wLength = cpu_to_le16p(&size); 134 135 //dbg("usb_control_ msg"); 136 137 ret = usb_internal_control_ msg(dev, pipe, dr, data, size, timeout); 138 139 kfree(dr); 140 141 return ret; 142 }
这个函数主要目的是创建一个控制urb,并把它发送给USB设备,然后等待它完成。urb是什么?前面提到过,要想和USB通信,就得创建一个urb,并且为它赋值,交给USB Core,它会找到合适的主机控制器,从而进行具体的数据传输。
123行,为一个struct usb_ctrlrequest结构体申请了内存,这里又出现了一个新生事物,它在include/linux/usb/ch9.h文件中定义。
140 struct usb_ctrlrequest { 141 __u8 bRequestType; 142 __u8 bRequest; 143 __le16 wValue; 144 __le16 wIndex; 145 __le16 wLength; 146 } __attribute__ ((packed));
这个结构完全对应于spec里的Table 9-2,它描述了主机通过控制传输发送给设备的请求(Device Requests)。主机向设备请求写信息必须得按照协议中规定好的格式,不然设备就会不明白主机是什么意思。
这个结构描述的request都是在SETUP包中发送的,Setup包是前面说到的Token PID类型中的一种,为了更好理解,这里详细介绍控制传输底层的packet情况。控制传输最少要有两个阶段的transaction:SETUP和STATUS,SETUP和STATUS中间的那个DATA阶段是可有可无的。
transaction这个词在很多地方都有,也算是个跨地区跨学科的热门词汇了,在这里称它为事务也好,会话也罢,我还是直呼它的原名transaction,可以理解为主机和设备之间形成的一次完整的交流。
USB的transaction可以包括一个Token包、一个Data包和一个Handshake包。
Token、Data和Handshake都属于四种PID类型,前面提到,如SYNC、PID、地址域、DATA、CRC,并不是所有PID类型的包都会包括的。Token包只包括SYNC、PID、地址域、CRC,并没有DATA字段,它的名字很形象,就是用来标记所在transaction里接下来动作的,对于Out和Setup Token包,里面的地址域指明了接下来要接收Data包的端点,对于In Token包,地址域指明了接下来哪个端点要发送Data包。
还有,只有主机才有权利发送Token包,协议中就是这么规定的。别嫌spec规定太多,又管这个又管那个的,没有规矩不成方圆。
与Token包相比,Data包中没了地址域,多了Data字段,这个Data字段对于低速设备最大为8字节,对于全速设备最大为1023字节,对于高速设备最大为1024字节。它就是躲在Token后边儿用来传输数据的。Handshake包的成分就非常简单了,除了SYNC,它就只包含了一个PID,通过PID取不同的值来报告一个transaction的状态,比如数据已经成功接收等。
控制传输的SETUP transaction一般来说有三个阶段,就是主机向设备发送Setup Token包,然后发送Data0包,如果一切顺利,设备回应ACK Handshake包表示没有问题,为什么加上“一般”?如果中间的那个Data0包由于某种不可知因素被损坏了,设备就什么都不会回应,这时就成两个阶段了。
SETUP transaction之后,接下来如果控制传输有DATA transaction的话,那就Data0、Data1交叉地发送数据包,这是为了实现data toggle。最后是STATUS transaction,向主机汇报前面SETUP和DATA阶段的结果,比如表示主机下达的命令已经完成了,或者主机下达的命令没有完成,或者设备正忙着那没工夫去理会主机的那些命令。
这样经过SETUP、DATA、STATUS这三个传输阶段,一个完整的控制传输完成了。主机接下来可以规划下一次的控制传输。
现在应该可以看出之前说requests都在Setup包中发送是有问题的,因为Setup包本身并没有数据字段,严格来说,它们应该都是在SETUP transaction阶段里Setup包后的Data0包中发送的。
141行,bRequestType,这个字段别看就一个字节,内容很丰富,大道理往往都包含在这种小地方。bit7就表示了控制传输中DATA transaction阶段的方向,当然,如果有DATA阶段的话。bit5~ bit 6表示request的类型,是标准的class-specific的还是vendor-specific的。bit0~ bit 4表示了这个请求针对的是设备、接口,还是端点。内核为它们专门量身定做了一批掩码,也在ch9.h文件中。
42 /* 43 * USB directions 44 * 45 * This bit flag is used in endpoint descriptors' bEndpointAddress field. 46 * It's also one of three fields in control requests bRequestType. 47 */ 48 #define USB_DIR_OUT 0 /* to device */ 49 #define USB_DIR_IN 0x80 /* to host */ 50 51 /* 52 * USB types, the second of three bRequestType fields 53 */ 54 #define USB_TYPE_MASK (0x03 << 5) 55 #define USB_TYPE_STANDARD (0x00 << 5) 56 #define USB_TYPE_CLASS (0x01 << 5) 57 #define USB_TYPE_VENDOR (0x02 << 5) 58 #define USB_TYPE_RESERVED (0x03 << 5) 59 60 /* 61 * USB recipients, the third of three bRequestType fields 62 */ 63 #define USB_RECIP_MASK 0x1f 64 #define USB_RECIP_DEVICE 0x00 65 #define USB_RECIP_INTERFACE 0x01 66 #define USB_RECIP_ENDPOINT 0x02 67 #define USB_RECIP_OTHER 0x03 68 /* From Wireless USB 1.0 */ 69 #define USB_RECIP_PORT 0x04 70 #define USB_RECIP_RPIPE 0x05
142行,bRequest,表示具体是哪个request。
143行,wValue,这个字段是request的参数,request不同,wValue就不同。
144行,wIndex,也是request的参数,bRequestType指明request针对的是设备上的某个接口或端点时,wIndex就用来指明是哪个接口或端点。
145行,wLength,控制传输中DATA transaction阶段的长度,方向已经在bRequestType那儿指明了。如果这个值为0,就表示没有DATA transaction阶段,bRequestType的方向位也就无效了。
现在回到usb_control_ msg函数中。很明显要进行控制传输,得首先创建一个struct usb_ctrlrequest结构体,填上请求的内容。129行到133行就是来使用传递过来的参数初始化这个结构体的。对于刚开始提到的SET_ADDRESS来说,bRequest的值就是USB_REQ_SET_ADDRESS,标准请求之一,ch9.h中定义有。因为SET_ADDRESS请求并不需要DATA阶段,所以wLength为0,而且这个请求是针对设备的,所以wIndex也为0。这么一来,bRequestType的值也只能为0了。因为是设置设备地址的,总得把要设置的地址发给设备,不然设备会不知道主机是什么意思,所以请求的参数wValue就是之前Hub已经指定好的devnum。其实SET_ADDRESS请求各个部分的值spec 9.4.6里都有规定。
接下来先看139行,走到这儿就表示这次通信已经完成了,那么struct usb_ctrlrequest结构体也就没用了,没用的东西最好精简掉。