21.设备的生命线(二)

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结构体也就没用了,没用的东西最好精简掉。