赞
踩
USB支持热插拔的硬件原理
在USB集线器(hub)的每个下游端口的D+和D-上,都接了一个15K的下拉电阻到地,这样,在集线器的端口悬空时,就被这两个下拉电阻拉成低电平,而在USB设备端,在D+或D-接一个15K的上拉电阻。对于全速和高速设备,上拉电阻接在D+上,而低速设备接在D-上。当设备插入到集线器时,上拉电阻和下拉电阻分压,将差分数据线中的一条拉高,集线器检测到电平的变化,报告给主控制器。
1、一个USB设备,是如何被USB系统发现并安装对应的设备驱动程序的,从而使USB设备可以正常工作?
当一个USB设备通过usb接口,连接到主机时,实际上是链接到一个usb集线器(hub)的一个接口,集线器的接口有了电平的变化,从而知道这个端口上有了设备的接入,从而可以通知usb主控制器,在集线器的某一个接口上有设备接入,此时,要采取什么行动,就由USB主机来决定。
2、一个usb主控制器对应一条usb总线,一般,主控制器都是PCI接口的,可以通过加载对应的PCI设备驱动,使系统增加usb总线。
usb主控制器都集成了一个根集线器,在我们驱动好主控制器之后,把根集线器(root hub)作为一个设备添加到usb总线上的设备队列里,同时,从总线的驱动队列中查找是否有可以支持这个设备的驱动程序,如果查找到,就可以按照设备模型的三角关系,把设备,驱动,总线通过相应的指针关系,把他们都关联起来;如果找不到驱动,则root hub就不能正常的工作,只能在总线的设备队列中等待有驱动安装时,再匹配是否OK;如果一直都没有对应的驱动,则这条总线也就没有办法挂载其他的设备了。
root hub匹配成功驱动后,就循环运行一个进程,以发现hub的端口有没有设备插入或删除。
3、当hub的驱动安装成功之后,系统会调kthread_run(hub_thread,NULL,"khubd")来启动守护进程,khubd进程就是hub的守护进程,当设备接入usb接口后,守护进程负责发现设备,然后通过中断的方式通知主控制器,主控制器才开始调用hub的探测函数,来解析设备。只有执行kthread_should_stop时才会退出,当hub没有设备插入时,进程属于睡眠状态,只有当设备插入时,才会唤醒进程,进行处理。主控制器发现root hub接口后,调用hub_probe函数开始配置root hub。具体操作如下:
(1)通过接口描述符获得当前接口描述符,通过分析当前的接口描述符的bInterfaceSubClass成员是不是hub类成员,是不是只有一个端口,这个端口是不是中断端口,来判断是否是一个hub设备,如果通不过这些验证,表示不是hub设备,退出
(2)通过1的验证,可以证明是hub设备了,才给hub设备分配控制(struct usb_hub),并初始化hub结构体,即struct usb_hub是在这里被初始化的。
(3) 配置root hub,分配用于urb传输的缓存和DMA地址(两者是关联的),hub状态空间,hub描述符空间,分配完后,通过向设备发送请求,获得设备的描述符,通过描述符得到hub的各种参数,用这些参数配置usb_hub中的usb_device结构体。
(4) 获取hub跟host连接的管道,及管道端点的大小,这个管道是中断传输的,端点大小不能大于刚刚分配的缓冲区的大小
(5) 申请一个urb空间,用于准备传输urb请求数据。
(6) 填充hub的urb数据,包括管道,缓存,缓存长度,中断处理函数等,这个是最重要的,经过在这里的配置,当hub端口有中断信号(即有设备插入或拔开)时,就会调用这里已经配置好的中断函数,来唤醒hub守护进程,进程得到执行,开始解析端口的状态---urb是需要请求的时候才会创建的,urb创建完,提交给主机后,就被系统释放了。
(7) 通过设备接口属性,使能hub 上行端口的power-on
(8) 把刚刚申请的urb发送给主机,如果一切正常,主机就会定时来询问hub是否有中断,如果就进行中断传输,发送的urb请求是把hub的各种数据报给主控制器,让主控制器知道。
(9) 到此,hub就开始正常工作了,随时等候中断信号,并跟主控制器报告。
4、hub正常工作后,主控制器会定时向hub询问是否有中断产生,当hub端口有一个设备插入或拔除,hub就向主机控制器发送urb请求,即把hub的变化状态告诉主控制器,主机在处理完这个urb后,就会调用urb提供的完成函数来调用hub的中断函数,即hub_irq。
1.扫描hub的所有端口,确定是哪个端口发生了变化。端口是用位图来表示的,一个long型数据可以表示32个hub端口(每位表示一个端口),有多少个端口,就用多少位表示,而8位用一个字节表示,因此,最后的使用的位都是转化成了字节的的,比如一个hub有18个端口,则需要用18位来表示,但是一个字节只有8位,因此,需要用3个字节才能表示完。
2.调用kick_khubd函数,把当前hub加入到hub驱动队列hub_event_list中,然后唤醒hub守护进程wake_up(khubd_wait),开始解析hub发生的事情了。
3.hub_thread被唤醒后,将得到执行,hub_events也将得到执行
4.hub_events是分析hub事件的主函数,hub分析的相关内容都在这里执行。这个函数是一个大的死循环。
5.Hub_events处理流程
5.1先从hub_event_list中取出此次处理的hub节点,并把hub节点从原来的队列中删除,使之独立于任何链表,因为我们处理完事件后,hub结构体就要删除,因此不能保留在任何队列中。hub可以是根hub,也可以是接到根hub上的子hub,只是,不论是什么hub,都使用同样的守护进程了。
5.2.通过宏转换,找到hub节点对以的hub结构体,进而得到hub结构体对应的usb结构体,以及hub接口结构体usb_intfdev,得到这三个结构体是这个函数处理的关键。
5.3.锁住当前的hub树,因为hub只有一个守护进程,所有的hub都使用这个守护进程,而守护进程每次只能服务一个hub,因此,只要有一个hub在使用这个守护进程,就需要锁住,以防止其他hub的使用。
5.4.按位检索hub每个端口是否正在执行reset或resume操作,注意是从1开始检索的,位0表示整个hub的情况,如果当前端口正在执行reset或resume操作,则跳过对这端口的检查,否则,测试这个端口是否有状态改变,如果没有状态改变,也跳过此端口。
5.5.如果端口有状态改变,则判断端口是发生了什么状态的改变,有以下的状态
#define USB_PORT_STAT_C_CONNECTION 0x0001
#define USB_PORT_STAT_C_ENABLE 0x0002
#define USB_PORT_STAT_C_SUSPEND 0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET 0x0010
5.6.确定有状态改变后,就开始对有改变的端口的状态进行进一步的处理,此时的处理,就要根据刚刚分析得到的各种状态,分开进行处理。
5.7.首先要确定端口是否还有设备,如果有,则要把设备删除,原因是,hub端口上有两种状态发生转换,1为端口从无设备到有设备的状态,此时设备还在认证中,因此端口不应该有设备,2是端口从有到无设备的转换,此时检测到还有设备,表示设备还没有被移除,因此,需要直接把设备disable掉。
5.8.把设备disable掉的动作,需要进行反弹的检查,一个设备至少需要100ms的时间,才能表示此状态是稳定的,因此,等100ms后再判断端口是否是disable了,就可以判断了
5.9.判断设备状态为无设备插入时,需要检测是否是端口电源被disable掉了,如果是,则需要开启端口电源,如果电源是开启的,则表示设备有误,结束判断,返回上层。
5.10.经过上面的判断,能执行到这里,表示端口是有设备插入了,此时,就需要要分析端口的状态了,对端口的分析,可以尝试4次,主要是为了排除各种干扰。
5.11.为即将到来的设备分配空间(struct usb_device),对这个即将到来的设备的设备结构体设置状态为连接状态,速度为未知,电源为hub分配给的,这些都是默认状态。
5.12.准备为设备分配地址,此时是子系统软件的操作,就是查看总线上128位位图中,哪位为0的,就选择出来,查找的方式是从上次记录的下一位开始查找,如果超过128了还没有找到,就接着从0开始查找,即如果频繁的拔插usb设备,即使只有一个设备,则每次插入,设备地址都会增加1,直到到达128后,从0接着开始,此时的设备地址,还不是设备的真实地址,因为还没有发送给设备。
5.13.复位设备,通过对设备的复位来达到使能设备的目的,此时,如果复位成功,则设备的状态将成为USB_STATE_DEFAULT状态,主控制器就可以通过控制端口获得设备的描述符符,通过对设备描述符的解析,可以得到设备的速度,从而可以根据速度猜测控制端口的空间大小。
5.14.得到控制端点的大小后,就开始准备往控制端点发送urb请求,获得设备的真正的设备描述符。
5.15.获得设备描述符时分两种模式,新模式和旧模式,每种模式最多试两次,每次可以最多读三回端口。对于新模式,需要分配一个64字节的空间,用于接收从设备返回的设备描述符。通过获得的设备描述符来判断端口的类别是不是确实是设备类别的,如果是,则只需要读取一次端口,就可以判断是设备描述符了,把描述符中记录的端口的空间大小给刚刚申请的空间的对应字节赋值了。
5.16.复位设备,准备给设备设置刚刚系统选择好,但还没有设置的那个地址,这个设置可以最多尝试设置两次,之间要停留200ms。
5.17.一旦设置成功,就要把设备的状态转变为地址状态(USB_STATE_ADDRESS),同时把控制端口0给disable掉,这样,这个端口上的urb链表都被清除了。
5.18.如果新的模式能走到这里,就表明新模式成功的获得了设备描述符了,因此旧模式就不要试了,而旧模式是分两次来获得设备的描述符的,第一次是发送要求获得8字节的urb请求,经过这个请求得到的数据,就可以知道设备的端口大小到达是多大,再按照获得的端口大小,获得端口的真实的设备描述符。
5.19.接下来,就要开始把这个设备加入设备模式中去了(usb_new_device)。
5.20.首先,要获得设备的配置描述符,注意,只有设备才有配置,接口,端口都是没有配置的,接口有设置。设备有多少个配置,就要分配多少个配置的空间,用于接收,而有多少个配置,已经在设备描述符中获得了,这时只需要从设备中读取就可以。
5.21.设置配置描述符指针,同样有多少个配置多少个这样的指针
5.22.一个配置描述符有9个字节,此时需要分配出这个空间,用于接收从设备中读回的配置描述符。
5.23.循环读取设备中的配置描述符,并把读回的数据放在设备结构体中的相应位置,之后,从配置中获得接口数,接口设置,以及接口下面的端点,通通都是在这里分析的,好长的几个函数。
5.24.接着,把设备添加到设备模型中去,这部分工作由设备模型完成,主要包括完成了设备链表的添加,和查找设备驱动程序的,此处是设备的驱动,usb系统只有一个设备驱动,而且是系统已经做好的,当设备里的接口被添加时,我们的编写的驱动,就是在这里被调用,用来判断驱动是否符合设备的了。
5.25.扫描设备的所有配置,选择最优的当前配置,再用选中的这个配置去设置设备。
5.26.到此,设备的配置,设置,什么都配置妥当了,系统就可以正常的使用设备了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。