在上一篇文章中我们知道了usb协议中的域、包、事务、传输的基本概念,下面我们来看看usb第一个通信过程—枚举。
枚举就是从设备读取一些信息,知道设备是什么样的设备,如何进行通信,这样主机就可以根据这些信息来加载合适的驱动程序。这部分内容同上一篇文章一样,是一些接近物理层的过程,而且其中大部分细节都由硬件模块完成,对于linux驱动工程师来说只需要了解,并不用深陷其中无法自拔。(这部分PHY层细节还是留给数字逻辑工程师去研究吧!驱动工程师只需要有这方面概念,出现问题能够思路全面就可以了)
下图是一个usb全速设备的枚举过程抓包,下面具体分析一下:
点击图片查看大图
第一步:区分设备类型
usb设备分为低速设备(1.5Mbps)、全速设备(12Mbps)、高速设备(480Mbps),它们的区分就是在设备上电第一瞬间区分的:
1, 低速设备与全速设备在设备端的区别
设备端一般有900~1575Ω的上拉电阻,全速设备该电阻在D+上,低速设备在D-上。上图Speed一列中的FS就代表全速设备了,即FullSpeed。
2, 设备是否支持高速模式
因为usb只有两根数据线,没有办法识别出低速、全速之外的高速模式。检测设备是否支持高速模式是在reset信号后由host发送一串KJKJKJ的序列,如设备支持高速状态则做出相应的调整,切换到高速模式。最初高速设备同全速设备一样,D+信号上存在1.5KΩ的上拉电阻,在切换到高速模式后D+的上拉电阻才会断开(USB设备中的控制器能够控制信号线上拉电阻的开关)连接D+/D-上的高速终端电阻(high-speed termination)。上图中Reset信号后的High speed Detection Handshake这就是检测设备是否支持高速模式的检测。如果支持高速设备在speed一列中后面的符号会变成HS,即High Speed。(其中KJ信号是指在D+、D-线上出现的约0.8V的中间电平信号,如想更详细了解这部分的细节可以参考一篇网上的描述http://www.cnblogs.com/AlwaysOnLines/p/3842886.html。)
第二步:获取设备描述符的前8个字节
usb设备在上电后默认的地址为0,而且usb设备是逐一进行枚举的,即同一时刻usb总线上也只有一个设备地址为0。host端对0地址设备发出请求来获取设备描述符的前8个字节。(枚举过程使用的是控制传输,对应的断点是0)这是要干什么呢?下面是此次获取的8个字节的抓包图,其中包括了usb协议版本,图中为1.1;设备类型,图中为Hub;设备协议,图中为None;这些都不重要!之所以只获取8个字节当然是为了得到第八个字节的数据。图中绿框已经圈出重点,这代表此设备端点0支持的的最大包大小。只有知道了这个数据才能在之后的数据传输中合理的控制包大小,大量传输数据,不然使用了设备不支持包大小就会被设备丢弃,发生传输错误了。
点击图片查看大图
第三步:设置地址
主机通过发送一个Set_Address请求来分配一个唯一的地址给设备。设备读取这个请求,返回一个确认,并保存新的地址。从此开始所有通信都使用这个新地址。从上图可以看到host给当前device分配的地址为1,之后的通信地址都有0改为1了。
第四步:获取所有描述符
确认了地址之后通信就进入正轨了,终于可以肆无忌惮的发送数据了。主机向新地址重新发送Get_Device_Descriptor命令,此次读取其设备描述符的全部字段,以了解该设备的总体信息,如VID,PID。之后host还会发出Get_Configuration_Description获取设备的配置描述符;发送Get_Device_String命令,获得字符集描述(unicode),比如产商、产品描述、型号等等。
自此usb枚举过程完成了,之后就可以按照不同设备进行相应数据的传输。