
28.将suspend分析到底(3)
其实之前我们见到过do_remote_wakeup,不过没有讲,现在不得不讲了。
Linux中,在USB系统里关于电源管理的部分已经做的不错了,这主要是因为USB Spec本身就对USB设备做了这方面的规定,即USB设备天生就应该支持电源管理,在USB Spec 2.0中,在第七章讲述电学特性的时候,专门有7.17.6和7.1.7.7两节介绍了USB设备的Suspend和Resume.这其中,Suspend还包括两种,一种是全局的,叫做global suspend,另一种叫做选择性的,即可以选择单个的端口进行挂起,这叫selective suspend。
而咱们这里的这个hub_port_suspend所执行的当然就是所谓的选择性挂起了,因为它针对的就是某个端口,而不是整个hub。而我们现在要说的是Remote Wakeup,从硬件角度来说,USB设备定义了一个叫做Remote Wakeup的特性,所谓Remote Wakeup指的是设备可以发送一个信号,把自己唤醒,当然实际上唤醒的是总线,或者说最后的反应是唤醒主机。最简单的例子就是USB键盘,你半天不碰计算机可能大家都睡了,可是突然间你按一下某个键,可能就把大家都给唤醒了,因为你实际上是发送了一个硬件信号。再比如Hub,可能一开始是睡眠的,但如果Hub port上有设备插入或者拔出,那么基本上就会唤醒Hub.
而一个设备是否具有Remote Wakeup这种特性,我们前面在设备的配置描述符里就已经说过,配置描述符中的bmAttributes就是标志着设备是否支持Remote Wakeup.
比如下面是我执行lsusb -v命令看到的输出信息中的一部分,这是一个键盘/鼠标的结合体。
Bus003Device002:ID0624:0294AvocentCorp. DeviceDescriptor: bLength18 bDescriptorType1 bcdUSB1.10 bDeviceClass0(DefinedatInterfacelevel) bDeviceSubClass0 bDeviceProtocol0 bMaxPacketSize08 idVendor0x0624AvocentCorp. idProduct0x0294 bcdDevice1.00 iManufacturer1Avocent iProduct2Dell03R874 iSerial0 bNumConfigurations1 ConfigurationDescriptor: bLength9 bDescriptorType2 wTotalLength59 bNumInterfaces2 bConfigurationValue1 iConfiguration4HIDKeyboard/Mouse bmAttributes0xa0 (BusPowered) RemoteWakeup MaxPower100mA InterfaceDescriptor: bLength9 bDescriptorType4 bInterfaceNumber0 bAlternateSetting0 bNumEndpoints1 bInterfaceClass3HumanInterfaceDevices bInterfaceSubClass1BootInterfaceSubclass bInterfaceProtocol1Keyboard iInterface5EP1Interrupt HIDDeviceDescriptor: bLength9 bDescriptorType33 bcdHID1.10 bCountryCode33US bNumDescriptors1 bDescriptorType34Report wDescriptorLength64 ReportDescriptors: **UNAVAILABLE** EndpointDescriptor: bLength7 bDescriptorType5 bEndpointAddress0x81EP1IN bmAttributes3 TransferTypeInterrupt SynchTypeNone UsageTypeData wMaxPacketSize0x00081x8bytes bInterval10
我们可以看到Configuration Descriptor那一段有一个Remote Wakeup。你在自己电脑上执行lsusb -v命令,你会发现很多设备的那一段并没有这么一个Remote Wakeup,只有具有这种特性的才会在这里显示出来。很显然你会发现你的U盘是不具有这个特性的,因为USB Mass Storage协议里也没有定义这方面的特性。
Remote Wakeup这东西,是设备的特性。不是每个设备都具备的。当然有这种特性也并不意味着这种特性就是enable的,因为usb spec 2.0里9.1.1.6中有这么一句话,If a USB device is capable of remote wakeup signaling, the device must support the ability of the host to enable and disable this capability. 即从软件的角度来说,我们可以enable这种特性,也可以disable这种特性。
那么对于先天性具有这种特性的设备,如何enable这种特性?
USB Spec 2.0中9.4.5中说得很好,当我们向一个设备发送GetStatus()的请求时,其返回值中的D1就是表征此时此刻这种能力是否被enable了,默认情况D1应该是0,表示disabled,而如果D1被设置成了1,那么就表示这种特性被enable了。如何设置呢?SetFeature()请求,请求的是DEVICE_REMOTE_WAKEUP。用代码来说话,那就是咱们这里的1611行,这样就算是enable了Remote Wakeup,如果你要disable掉的话,只要把SetFeature换成ClearFeature即可。DEVICE_REMOTE_WAKEUP被称为标准的Feature选择器。如图2.27.1所示。
[TR]
[TD][I]498)this.width=498;' onmousewheel = 'javascript:return big(this)' height=146 alt="" src="http://pic.aIhUaU.com/201602/15/2233420.jpg" width=485 border=0>[/TD][/TR]
[TR]
[TD](点击查看大图)图2.27.1 Feature选择器[/TD][/TR]
而do_remote_wakeup作为struct usb_device结构体中的一个成员,其默认值为1。只是刚才在usb_suspend_device函数中,我们判断如果设备没有和driver绑定,就先把其do_remote_ wakeup设置为0,理由很简单,没有驱动的话,就没必要找麻烦了,还是那句话,男人,简单就好。
紧接着1623行, set_port_feature,这次设置的是USB_PORT_FEAT_SUSPEND,这个宏对应的USB Spec 2.0中的PORT_SUSPEND,设置了这个feature就意味着停止这个端口上的总线交通,也因此就意味着该端口连的设备进入了suspend状态。而这也正是我们的最终目标,这之后我们看到1638行就调用usb_set_device_state把设备的状态设置为USB_STATE_ SUSPENDED。当然,也要注意到,如果设置PORT_SUSPEND失败了的话,我们就将在1629行发送ClearFeature把Remote Wakeup的特性给清除掉。因为已经没有必要了,没有挂起就没有唤醒。
不小心把这个suspend的流程走了一遍,不过你一定还要问,为何drivers/usb/core/hub.c中的那个hub_suspend还没讲?于是现在来说hub driver,hub_suspend被赋值给了hub_driver中的suspend成员,而hub driver是一个interface driver,所以实际上hub_suspend将会在当初那个usb_suspend_interface中被调用,没错就是866那行,status = driver->suspend(intf,msg);
爱华网



