USB 设备描述符的读取过程

1. # ls /sys/bus/usb/devices/解析:
1-0:1.0 1-1 1-1:1.0 2-0:1.0 2-1 2-1:1.0 2-2 2-2.1 2-2:1.0 2-2.1:1.0 2-2.1:1.1 usb1 usb2

1) usb1 usb2 usb3 usb4表示主机连接了4条USB总线,即4条USB主机控制器

2)
  4-0:1.0 表示4号总线或者说是4号root hub,0就是root hub的0号端口,1表示配置为1号,0表示接口为0号。也就是说4号总线的0号端口上的设备使用的是1号配置
       的0号接口. 4-0, 0表示是roothub,其下行端口从1开始,
  4-0.1:1.0 多出一个.1表示root hub的0号端口下又接了一个hub,这个hub的1号端口上接了这个设备
  4-0.2:1.0 例如4号总线的roothub的0号端口下面又接了一个hub,这个hub的2号端口下面接了这个设备,这个设备使用的是1号配置的0号接口

[ 9.034896] hub 1-0:1.0: USB hub found 
[ 9.050502] hub 2-0:1.0: USB hub found
[ 9.332156] hub 3-0:1.0: USB hub found
[ 9.468292] hub 4-0:1.0: USB hub found

/sys/devices/platform/soc/ee0a0100.usb/usb4
/sys/devices/platform/soc/ee0a0100.usb/usb4/4-0:1.0      roothub
/sys/devices/platform/soc/ee0a0100.usb/usb4/4-1         
/sys/devices/platform/soc/ee0a0100.usb/usb4/4-1/4-1:1.0    roothub的下行端口1(hub的端口从1开始编号),下面挂一个设备4-1:1.0
/sys/devices/platform/soc/ee0a0100.usb/usb4/4-1/4-1:1.0/host0

 

2.获取USB设备描述符的两种策略:实现在hub.c中的hub_port_connect()中

(1)旧策略:就是Linux本身自带的策略,就是先读取8字节设备描述符操作.

(2)新策略:模仿Windows的 首次交互,在不知道bMaxPacketSize0的值的情况下,先直接发送64个字节的获取设备描述符的请求过去, 要求设备返回64个字节过来, 如果设备端点0的max packet size是32或者64,那么只要把18个 字节的设备描述传递过来就可以了,但是如果你设备端点0的max packet size就是8或者16, 而设备描述符是18个字节, 一次肯定传递不完, 那么你必然是传递了一次以后还等待着继续传递,但是从驱动 角度来说,只要获得了8个字节就够了,而对于设备,不是等着继续传吗,我直接对你做一次reset,让设备复位,这样就清掉了设备剩下的想传的数据了,然后我主机得了前8个字节我就可以知道你真正的 max packet size,然后主机就按这个真正的最大值来进行下面的传输,首先就是获得你那个18个字节的真正的完整的设备描述符,这就是Windows下面的处理方法。

花絮: 很多厂商都是按着Windows的这种策略来测试自己的设备的,他们压根儿就没有测试过请求8个字节的设备描述符,于是,也就没人能保证当你发送请求要它返回8个字节的设备描述符的时候它能够正确的响应. 所以,Linux开发者们委曲求全,把这种Windows下的策略给加了进来。 其实,Linux的那种策略才是usb spec提供的策略,而现实是,Windows没有遵守这种策略,然而厂商们出厂的时候就只是测试了能在Windows下工作,他们认为遵守Windows就是遵守了usb spec.而事实呢,却并非如 此.严格意义来说,这是Windows这边的bug,不过这种做法却引导了潮流.

如今的Linux hub.c代码里实现了这两种策略,每种试两次,原来的Linux中的那种策略叫做old scheme,沿袭Windows的策略叫做新策略。现在的做法是,具体使用哪种策略和先使用哪种策略,你作为用户你可 以在加载模块的时候可以通过下面参数自己设置,模块参数是usbcore.ko中的old_scheme_first(默认是false,即先尝试新策略)但是如果你不设置,那么默认的方法就是先使用新的这种机制试两次,然后如果不行,就使用旧机制再试两次。 usbcore.ko:old_scheme_first 先尝试使用旧策略(默认是false) usbcore.ko:use_both_schemes 如果一种策略失败,就尝试使用另一种策略(默认是true) 若use_both_schemes为false,就只试一种策略,试哪种策略由old_scheme_first决定。

# modinfo usbcore | grep scheme
parm:           old_scheme_first:start with the old device initialization scheme (bool)
parm:           use_both_schemes:try the other device initialization scheme if the first one fails (bool)
# cat /etc/modprobe.d/usb.conf 
options usbcore old_scheme_first=1

 

3.设备描述符有18字节,第8字节是bMaxPacketSize0

4.drivers/usb/core/generic.c中的int usb_choose_configuration(struct usb_device *udev)给设备选择一个默认的配置 generic_probe  usb_choose_configuration  usb_set_configuration  usb_notify_add_device  

5.usb设备枚举位置 hub_port_connect()  hub_port_init //中只是使用新策略或旧策略只获取了设备描述符!

6.choose_devnum(): 设备号在usbfs中用作文件名。 在USB-1.1和USB-2.0总线上,它们也用作设备地址,但是在USB-3.0总线上,地址由控制器硬件分配,并且通常与设备编号不同。 地址0由USB保留为默认地址,Linux的USB堆栈始终使用1作为控制器的root hub的地址。 因此,USB栈的端口#1,即wusb虚拟端口#0的地址为#2。

7.hub_is_superspeed():hub是否为super speed hub需要在其设备描述符中指定bDeviceProtocol = USB_HUB_PR_SS

8.并没有一个专门的端点描述符来描述0号端点,因为不需要,原因是endpoint zero基本上所有的特性都是在spec规定好了的, 大家都一样,所以不需要每个设备另外准备一个描述符来描述它. 其 bMaxPacketSize0在设备描述符的第8字节处。

 

posted on 2018-08-28 00:42  Hello-World3  阅读(2077)  评论(0编辑  收藏  举报

导航