Linux那些事儿之我是Hub(9)While You Were Sleeping(二) – fudan

老实说,从函数一个开始的598行直到627行都没有什么可说的.其中需要一提的是,606行,调用usb_buffer_alloc()申请内存,赋给hub->buffer.614行,调用kmalloc()申请内存,赋给hub->status.622行,调用kmalloc()申请内存,赋给hub->descriptor.当然也别忘了这中间的某行,初始化一把互斥锁,hub->status_mutex.以后咱们会用得着的,到时候我们就会看到mutex_lock/mutex_unlock()这么一对函数来获得互斥锁/释放互斥锁.不过这把锁用得不多,总共也就两个函数里调用了.咱们走着瞧.

633行,get_hub_descriptor().应该说,从这一刻起,我们将跨入一个新的时代,这一行是里程碑的一行,其意义就好比1992年有一位老人在中国的南海边画了一个圈,其重要性是不言而喻的.这一行,带领我们步入了hub协议.从此摆在我们面前的将不仅仅是usb协议本身了,又加了另一座大山,那就是hub协议,其实hub协议也算usb协议,但是由于hub作为一种特殊的usb设备,在usb spec中,专门有一章是关于hub的,而这一章有150多页.这就是usb spec 2.0中的第十一章.简明起见,下面我将把这一章称作hub协议.而接下来我们的一言一行,都必须遵守hub协议了.来,先看get_hub_descriptor,这就是发送一个request,或者说一个控制传输的控制请求,以获得hub的描述符.基本上hub驱动的这些函数,大多都是来自drivers/usb/core/hub.c中:

137 /* USB 2.0 spec div 11.24.4.5 */

138 static int get_hub_descriptor(struct usb_device *hdev, void *data, int size)

139 {

140 int i, ret;

141

142 for (i = 0; i < 3; i++) {

143 ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),

144 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,

145 USB_DT_HUB << 8, 0, data, size,

146 USB_CTRL_GET_TIMEOUT);

147 if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))

148 return ret;

149 }

150 return -EINVAL;

151 }

看过usb-storage的你,一定能看懂这个函数,不过有一点我说出来一定会让你吃惊,usb_control_msg()是我们第一次遇到,不信的话可以回去查一下,usb-storage里面根本没有用到这个函数,事实上usb-storage里面自己写了这么一个函数,叫做usb_stor_control_msg().理由是,usb_control_msg()不允许你在调用这个函数的时候取消一个request,而在usb-storage里我们经常做这样的事情,或者说我们有这样的需求,那么我们当然就得自己写一个函数了.不过hub里面没这么多乱七八糟的需求,插拔U盘是一件很正常的事情,可是你说谁没事总是插拔hub?吃多了哈药六厂的新盖中盖吗?

不过有一个好消息,即usb_control_msg和我们讲过的usb_stor_control_msg的参数是一模一样的,所以我们无需再多说.每一个request怎么设置都是在spec里边规定的.对于GET_DESCRIPTOR这个request,如图所示:

hub spec规定, bmRequestType必须是10100000B,normally,GET_DESCRIPTOR的时候,bmRequestType应该等于10000000B.D7为方向位,1就说明时Device-to-host,即IN,D6…5这两位表示Request的类型,可以是标准的类型,或者是Class特定的,或者是Vendor特定的,01就表示Class特定的.D4…0表示接受者,可以是设备,可以是接口,可以是端点.这里为0表示接收者是设备.(这里就体现了相同的request,不同的requesttype的意义了.)

USB_DT_HUB等于29,而hub spec规定GET_DESCRIPTOR这个请求的wValue就该是Descriptor Type和Descriptor Index,wValue作为一个word,有16位,所以高8位放Descriptor Type,而低8位放Descriptor Index,usb 2.0 spec 11.24.2.10里规定了,hub descriptor的descriptor index必须为0.而实际上,usb spec 9.4.3里也规定了,对于非configuration descriptor和string descriptor的其他几种标准descriptor,其descriptor index必须为0.而对于configuration descriptor和string descriptor来说,这个descriptor index用来表征他们的编号,比如一个设备可以有多个configuration descriptor.编号从0开始.所以对于hub descriptor来说,wValue就是高8位表示Descriptor Type,而低8位设成0就可以了.这就是为什么”<<8”了,而Descriptor Type,spec中Table 9-5定义了各种Descriptor的type,如图:

比如Device是1,Configuration是2,而hub这里规定了,它的Descriptor type是29h.而USB_DT_HUB=(USB_TYPE_CLASS|0x09),USB_TYPE_CLASS就是0x01<<5.所以这里USB_DT_HUB就是0x29,即29h,至于他为什么不直接写成29h那就只有天知道了.也许写代码的人想故意迷惑一下读代码的人吧,因为事实上usb 2.0 spec里边很清楚地直接用29h来表示hub的descriptor type,根本不存在两个咚咚的组合.这样写代码完全是对广大观众的不负责,你说我们是不是该鄙视一下这些写代码的同志.

USB_CTRL_GET_TIMEOUT是一个宏,值为5000,即表示5秒超时.

USB_DT_HUB_NONVAR_SIZE也是一个宏,值为7,为啥为7呢?首先请你明白,我们这里要获得的是hub descriptor,这是只有hub才有的一个descriptor,就是说对于hub来说,除了通常的usb设备有的那些设备描述符,接口描述符,端点描述符以外,hub spec自己也定义了一个描述符,这就叫hub描述符.而hub描述符的前7个字节是固定的,表示什么意思也是确定的,但从第八个字节开始,一切就都充满了变数.所以前7个字节被称为非变量,即NON-Variable,而从第7个开始以后的被称为变量,即Variable.这些变量取决于hub上面端口的个数.所谓的变量不是说字节的内容本身是变化的,而是说descriptor具体有几个字节是变化的,比如hub上面有两个端口,那么这个hub的descriptor的字节数和hub上面有4个端口的情况就是不一样的,显然,有四个端口就要纪录更多的信息,当然描述符里的内容就多一些,从而描述符的字节长度也不一样,这超好理解.usb_control_msg()如果执行成功,那么返回值将是成功传输的字节长度,对这里来说,就是传输的hub描述符的字节长度.结合hub spec来看,这个长度至少应该是9,所以这里判断如果大于等于9,那就返回.当然你要问为什么这里要循环三次,这是为了防止通信错误.hub驱动中很多地方都这样做了,主要是因为很多设备在发送它们的描述符的时候总是出错.所以咱们没辙了,多试几次呗.

若不给自己设限,则人生中就没有限制你发挥的藩篱。

Linux那些事儿之我是Hub(9)While You Were Sleeping(二) – fudan

相关文章:

你感兴趣的文章:

标签云: