Linux那些事儿之我是Hub(28)将suspend分析到底 – fudan

伫倚危楼风细细望极春愁黯黯生天际草色烟光残照里无言谁会凭栏意拟把疏狂图一醉对酒当歌强乐还无味衣带渐宽终不悔为伊消得人憔悴

北宋词人柳永曾用这首蝶恋花来抒发对Linux内核中电源管理部分代码的无奈.当年柳永痛苦的看这代码看得想跳楼自尽.这首词,上片写登楼伫望情景.以细风,草色,烟光,残阳几个关合着忧愁的意象,组成一幅黄昏春望图,多层次地描摹写词人愁之景,愁之态,笔意婉约.下片抒情,直抒胸臆,写词人情深志坚.“拟把”、“强乐”三句辞意顿折,写词人欲借疏狂之歌呼,陶然之酣醉,谋求醉而忘忧,歌而暂欢,以摆脱读不懂代码的压抑,却落得个“还无味”的无聊和空虚,可见其愁之浓深、刻骨,竟无法排遣.最后揭明词人对待这种愁的果决态度:“终不悔”.“为伊”,方始画龙点晴地道破憔悴无悔的隐秘:为了看懂这代码,我亦值得憔悴、瘦损,以生命相托!语直情切,挟带着市民式的激情,真是荡气回肠.全词成功地刻画出一个志诚男子的形象,描写心理充分细腻,尤其是词的最后两句,直抒胸臆,画龙点睛般地揭示出主人公的精神境界,被王国维称为“专作情语而绝妙者”.

我知道你也会觉得电源管理这部分的代码显得很复杂,调用关系一层又一层.但我们只能继续往下看.如果没有问题,usb_suspend_interface函数就这么返回了.返回值为status,当然就是0.然后我们回到usb_suspend_both,接着看下一个函数usb_suspend_device().同样来自drivers/usb/core/driver.c:

795 /* Caller has locked udev’s pm_mutex */

796 static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)

797 {

798 struct usb_device_driver *udriver;

799 int status = 0;

800

801 if (udev->state == USB_STATE_NOTATTACHED ||

802 udev->state == USB_STATE_SUSPENDED)

803 goto done;

804

805 /* For devices that don’t have a driver, we do a standard suspend. */

806 if (udev->dev.driver == NULL) {

807 udev->do_remote_wakeup = 0;

808 status = usb_port_suspend(udev);

809 goto done;

810 }

811

812 udriver = to_usb_device_driver(udev->dev.driver);

813 status = udriver->suspend(udev, msg);

814

815 done:

816 // dev_dbg(&udev->dev, "%s: status %d/n", __FUNCTION__, status);

817 if (status == 0)

818 udev->dev.power.power_state.event = msg.event;

819 return status;

820 }

这里有一个新鲜的东西,过去我们知道usb设备驱动程序都是针对interface的,不是针对device的,所以很早我们就见到过一个叫做struct usb_driver的结构体,但是敏感的你是否注意到2.6.22.1的内核中还有另一个结构体,struct usb_device_driver呢?它定义于include/linux/usb.h中:

859 /**

860 * struct usb_device_driver – identifies USB device driver to usbcore

861 * @name: The driver name should be unique among USB drivers,

862 * and should normally be the same as the module name.

863 * @probe: Called to see if the driver is willing to manage a particular

864 * device. If it is, probe returns zero and uses dev_set_drvdata()

865 * to associate driver-specific data with the device. If unwilling

866 * to manage the device, return a negative errno value.

867 * @disconnect: Called when the device is no longer accessible, usually

868 * because it has been (or is being) disconnected or the driver’s

869 * module is being unloaded.

870 * @suspend: Called when the device is going to be suspended by the system.

871 * @resume: Called when the device is being resumed by the system.

872 * @drvwrap: Driver-model core structure wrapper.

873 * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend

874 * for devices bound to this driver.

875 *

876 * USB drivers must provide all the fields listed above except drvwrap.

877 */

878 struct usb_device_driver {

879 const char *name;

880

881 int (*probe) (struct usb_device *udev);

882 void (*disconnect) (struct usb_device *udev);

883

884 int (*suspend) (struct usb_device *udev, pm_message_t message);

885 int (*resume) (struct usb_device *udev);

886 struct usbdrv_wrap drvwrap;

887 unsigned int supports_autosuspend:1;

888 };

889 #define to_usb_device_driver(d) container_of(d, struct usb_device_driver, /

890 drvwrap.driver)

我们以前说过,usb设备驱动程序往往是针对interface的,而不是针对device的,换言之,一个interface对应一个driver,这一情况到今天来看仍然是正确的,但将来就未必了,因为有一些行为是针对整个device的,比如电源管理中的挂起,可能整个设备需要统一的行为,而不是说每个interface可以单独行动,想干嘛干嘛.一个设备多个interface,那么它们就是一个整体,一个整体对外就会有整体的表现.interface driver就是专门处理各个interface的个性的,而device driver么,就用来对付整体.而这里我们看到的两个函数usb_suspend_device和usb_suspend_interface就是这种情况的体现.而usb_suspend_device这段代码的意图更是相当的明显,如果有device driver,那就调用它的suspend函数,如果没有,就调用一个通用的函数,usb_port_suspend.我们来看一下这个通用的suspend函数.

1684 /*

1685 * usb_port_suspend – suspend a usb device’s upstream port

1686 * @udev: device that’s no longer in active use

1687 * Context: must be able to sleep; device not locked; pm locks held

1688 *

1689 * Suspends a USB device that isn’t in active use, conserving power.

1690 * Devices may wake out of a suspend, if anything important happens,

1691 * using the remote wakeup mechanism. They may also be taken out of

1692 * suspend by the host, using usb_port_resume(). It’s also routine

1693 * to disconnect devices while they are suspended.

1694 *

1695 * This only affects the USB hardware for a device; its interfaces

1696 * (and, for hubs, child devices) must already have been suspended.

1697 *

1698 * Suspending OTG devices may trigger HNP, if that’s been enabled

1699 * between a pair of dual-role devices. That will change roles, such

1700 * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.

1701 *

1702 * Returns 0 on success, else negative errno.

1703 */

1704 int usb_port_suspend(struct usb_device *udev)

1705 {

1706 return __usb_port_suspend(udev, udev->portnum);

1707 }

原来是一个幌子,真正干实事的是__usb_port_suspend(),当然从这些函数的名字我们也可以看出,实际上针对整个设备的挂起是与usb hub的某个端口有关的,确切的说就是设备所连接的那个端口,这也就是为什么这两个函数都是出现在drivers/usb/core/hub.c中,而从这里的注释我们也不难看出,这里的目标就是挂起设备所连接的那个端口.而在此之前,我们已经调用usb_suspend_interface挂起了设备的每一个interface.而如果是hub的话,会先要求挂起其所有的子设备.这一点不用我们操心,因为有设备树的存在,PM core那边自然就知道如何做这件事情了.向伟大的PM core致敬,少先队员行队礼,非少先队员行注目礼.

1644 /*

1645 * Devices on USB hub ports have only one "suspend" state, corresponding

1646 * to ACPI D2, "may cause the device to lose some context".

1647 * State transitions include:

1648 *

1649 * – suspend, resume … when the VBUS power link stays live

1650 * – suspend, disconnect … VBUS lost

1651 *

1652 * Once VBUS drop breaks the circuit, the port it’s using has to go through

1653 * normal re-enumeration procedures, starting with enabling VBUS power.

1654 * Other than re-initializing the hub (plug/unplug, except for root hubs),

1655 * Linux (2.6) currently has NO mechanisms to initiate that: no khubd

1656 * timer, no SRP, no requests through sysfs.

1657 *

1658 * If CONFIG_USB_SUSPEND isn’t enabled, devices only really suspend when

1659 * the root hub for their bus goes into global suspend … so we don’t

1660 * (falsely) update the device power state to say it suspended.

1661 */

1662 static int __usb_port_suspend (struct usb_device *udev, int port1)

1663 {

1664 int status = 0;

1665

1666 /* caller owns the udev device lock */

1667 if (port1 < 0)

1668 return port1;

1669

1670 /* we change the device’s upstream USB link,

1671 * but root hubs have no upstream USB link.

1672 */

1673 if (udev->parent)

1674 status = hub_port_suspend(hdev_to_hub(udev->parent), port1,

1675 udev);

1676 else {

1677 dev_dbg(&udev->dev, "usb %ssuspend/n",

1678 udev->auto_pm ? "auto-" : "");

1679 usb_set_device_state(udev, USB_STATE_SUSPENDED);

1680 }

1681 return status;

1682 }

我倒,貌似还是一个幌子,真正的幕后英雄是hub_port_suspend.udev->parent为空的是Root Hub,对于Root Hub,只要设置设备状态为USB_STATE_SUSPENDED即可.因为子设备已经挂起了.而Root Hub本身不存在说接在哪个port的问题.于是我们来看hub_port_suspend.

1587 /*

1588 * Selective port suspend reduces power; most suspended devices draw

1589 * less than 500 uA. It’s also used in OTG, along with remote wakeup.

1590 * All devices below the suspended port are also suspended.

1591 *

1592 * Devices leave suspend state when the host wakes them up. Some devices

1593 * also support "remote wakeup", where the device can activate the USB

1594 * tree above them to deliver data, such as a keypress or packet. In

1595 * some cases, this wakes the USB host.

1596 */

1597 static int hub_port_suspend(struct usb_hub *hub, int port1,

1598 struct usb_device *udev)

1599 {

1600 int status;

1601

1602 // dev_dbg(hub->intfdev, "suspend port %d/n", port1);

1603

1604 /* enable remote wakeup when appropriate; this lets the device

1605 * wake up the upstream hub (including maybe the root hub).

1606 *

1607 * NOTE: OTG devices may issue remote wakeup (or SRP) even when

1608 * we don’t explicitly enable it here.

1609 */

1610 if (udev->do_remote_wakeup) {

1611 status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),

1612 USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,

1613 USB_DEVICE_REMOTE_WAKEUP, 0,

1614 NULL, 0,

1615 USB_CTRL_SET_TIMEOUT);

1616 if (status)

1617 dev_dbg(&udev->dev,

1618 "won’t remote wakeup, status %d/n",

1619 status);

1620 }

1621

1622 /* see 7.1.7.6 */

1623 status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);

1624 if (status) {

1625 dev_dbg(hub->intfdev,

1626 "can’t suspend port %d, status %d/n",

1627 port1, status);

1628 /* paranoia: "should not happen" */

1629 (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),

1630 USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,

1631 USB_DEVICE_REMOTE_WAKEUP, 0,

1632 NULL, 0,

1633 USB_CTRL_SET_TIMEOUT);

1634 } else {

1635 /* device has up to 10 msec to fully suspend */

1636 dev_dbg(&udev->dev, "usb %ssuspend/n",

1637 udev->auto_pm ? "auto-" : "");

1638 usb_set_device_state(udev, USB_STATE_SUSPENDED);

1639 msleep(10);

1640 }

1641 return status;

1642 }

其实之前我们见到过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命令看到的输出信息中的一部分,这是一个键盘/鼠标的结合体,

Bus 003 Device 002: ID 0624:0294 Avocent Corp.

Device Descriptor:

bLength 18

bDescriptorType 1

bcdUSB 1.10

bDeviceClass 0 (Defined at Interface level)

bDeviceSubClass 0

bDeviceProtocol 0

bMaxPacketSize0 8

idVendor 0x0624 Avocent Corp.

idProduct 0x0294

bcdDevice 1.00

iManufacturer 1 Avocent

iProduct 2 Dell 03R874

iSerial 0

bNumConfigurations 1

Configuration Descriptor:

bLength 9

bDescriptorType 2

wTotalLength 59

bNumInterfaces 2

bConfigurationValue 1

iConfiguration 4 HID Keyboard / Mouse

bmAttributes 0xa0

(Bus Powered)

Remote Wakeup

MaxPower 100mA

Interface Descriptor:

bLength 9

bDescriptorType 4

bInterfaceNumber 0

bAlternateSetting 0

bNumEndpoints 1

bInterfaceClass 3 Human Interface Devices

bInterfaceSubClass 1 Boot Interface Subclass

bInterfaceProtocol 1 Keyboard

iInterface 5 EP1 Interrupt

HID Device Descriptor:

bLength 9

bDescriptorType 33

bcdHID 1.10

bCountryCode 33 US

bNumDescriptors 1

bDescriptorType 34 Report

wDescriptorLength 64

Report Descriptors:

** UNAVAILABLE **

Endpoint Descriptor:

bLength 7

bDescriptorType 5

bEndpointAddress 0x81 EP 1 IN

bmAttributes 3

Transfer Type Interrupt

Synch Type None

Usage Type Data

wMaxPacketSize 0x0008 1x 8 bytes

bInterval 10

我们可以看到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选择器.如图所示:

而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的特性给清除掉.因为已经没有必要了,没有挂起就没有唤醒,没有共产党就没有新中国,没有新中国就没有性生活.

Ok,不小心把这个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);

于是我们就来具体看看hub_suspend.

1919 static int hub_suspend(struct usb_interface *intf, pm_message_t msg)

1920 {

1921 struct usb_hub *hub = usb_get_intfdata (intf);

1922 struct usb_device *hdev = hub->hdev;

1923 unsigned port1;

1924 int status = 0;

1925

1926 /* fail if children aren’t already suspended */

1927 for (port1 = 1; port1 <= hdev->maxchild; port1++) {

1928 struct usb_device *udev;

1929

1930 udev = hdev->children [port1-1];

1931 if (udev && msg.event == PM_EVENT_SUSPEND &&

1932 #ifdef CONFIG_USB_SUSPEND

1933 udev->state != USB_STATE_SUSPENDED

1934 #else

1935 udev->dev.power.power_state.event

1936 == PM_EVENT_ON

1937 #endif

1938 ) {

1939 if (!hdev->auto_pm)

1940 dev_dbg(&intf->dev, "port %d nyet suspended/n",

1941 port1);

1942 return -EBUSY;

1943 }

1944 }

1945

1946 dev_dbg(&intf->dev, "%s/n", __FUNCTION__);

1947

1948 /* stop khubd and related activity */

1949 hub_quiesce(hub);

1950

1951 /* "global suspend" of the downstream HC-to-USB interface */

1952 if (!hdev->parent) {

1953 status = hcd_bus_suspend(hdev->bus);

1954 if (status != 0) {

1955 dev_dbg(&hdev->dev, "’global’ suspend %d/n", status);

1956 hub_activate(hub);

1957 }

1958 }

1959 return status;

1960 }

其实也没做什么.不过我们可以看到msg在整个suspend的情节里是代代相传,每个函数都把它当参数.

1927至1944这一段循环的目的就是判断是否有任何一个Hub的子设备尚未挂起,我们说过只有底层的劳苦大众能够安详的睡眠,上层的公仆们才好意思安心去休息.当然你别美,写代码的无非是描绘一种乌托邦式的世界,这是他们的理想,仅此而已.关于CONFIG_USB_SUSPEND,我们在drivers/usb/core/Kconfig中可以看到,

74 config USB_SUSPEND

75 bool "USB selective suspend/resume and wakeup (EXPERIMENTAL)"

76 depends on USB && PM && EXPERIMENTAL

77 help

78 If you say Y here, you can use driver calls or the sysfs

79 "power/state" file to suspend or resume individual USB

80 peripherals.

81

82 Also, USB "remote wakeup" signaling is supported, whereby some

83 USB devices (like keyboards and network adapters) can wake up

84 their parent hub. That wakeup cascades up the USB tree, and

85 could wake the system from states like suspend-to-RAM.

86

87 If you are unsure about this, say N here.

毫无疑问当我们想在usb子系统里支持suspend我们完全可以理直气壮的打开这个编译开关.前面我们说过,dev.power.power_state.event如果等于PM_EVENT_ON,就相当于向世界宣布,本设备当前并不是挂起状态.而这里的udev->state不等于USB_STATE_SUSPENDED也是表征同样的含义,不过你需要注意,USB_STATE_SUSPENDED将由usb_set_device_state来设置,而你不难发现,这个世界上一共有两个函数会调用这个函数来设置这个状态,它们就是刚才说过的__usb_port_suspend和hub_port_suspend(),其中,后者还是被前者调用的,即__usb_port_suspend调用hub_port_suspend,而__usb_port_suspend是被usb_port_suspend调用,我们继续跟踪的话就会发现,如果你没有打开CONFIG_USB_SUSPEND,usb_port_suspend只是一个空函数,啥也不做.

1886 #else /* CONFIG_USB_SUSPEND */

1887

1888 /* When CONFIG_USB_SUSPEND isn’t set, we never suspend or resume any ports. */

1889

1890 int usb_port_suspend(struct usb_device *udev)

1891 {

1892 return 0;

1893 }

所以, 这里判断USB_STATE_SUSPENDED之前要先判断编译开关.另一个问题,这里因为USB_STATE_SUSPENDED是usb这部分代码定义的宏,而PM_EVENT_ON毕竟是PM core那边定义的宏,所以,我们应该尽量使用自己的这个宏.实在不行才去使用外部的资源.

auto_pm是用来设置自动挂起的,如果它为1,表明这次挂起是自动挂起,而不是系统级的挂起. 以咱们这个上下文来看,由于咱们在usb_external_suspend_device中设置了auto_pm为0,所以这里就直接返回了.

为何auto_pm为0就返回,否则就不返回,稍后我们看到autosuspend/autoresume那边的代码就明白了,我们现在这里的思路是,没什么特别的理由,那么子设备如果没有睡眠,那么父设备的suspend就会失败,但是如果这是一次autosuspend,那么就不会失败.因为autosuspend会有专门的方法来处理这种情况.后面会看到.

Ok,千呼万唤始出来的hub_quiesce()函数.很久很久以前我们就见到过那个hub->quiesce了,我们曾多次判断过它是否为零,在hub_events()中我们判断过,在hub_irq()中我们判断过,在led_work()中我们判断过.

500 static void hub_quiesce(struct usb_hub *hub)

501 {

502 /* (nonblocking) khubd and related activity won’t re-trigger */

503 hub->quiescing = 1;

504 hub->activating = 0;

505

506 /* (blocking) stop khubd and related activity */

507 usb_kill_urb(hub->urb);

508 if (hub->has_indicators)

509 cancel_delayed_work(&hub->leds);

510 if (hub->has_indicators || hub->tt.hub)

511 flush_scheduled_work();

512 }

看到这么短小精悍的函数,不由得一阵喜悦涌上心头.这个函数是唯一一处设置hub->quiescing为1的地方.它和另一个函数针锋相对,即hub_activate(),hub_activate()中设置hub->quiescing为0,而设置hub->activating为1.而这个函数恰恰相反,仔细一看你会发现,这两个函数做的事情那几乎是完全相反.那边人家调用usb_submit_urb()提交一个urb,这边就给人拆台,调用usb_kill_urb()来撤掉该urb,那边人家调用schedule_delayed_work建立一个延时工作的函数,这边就调用cancel_delayed_work给人家拆了,flush_scheduled_work()通常在cancel_delayed_work后面被调用,这个函数会使等待队列中所有的任务都被执行.

1952到1958行是专门针对Root Hub的,因为通常Host Controller Driver也会提供自己的suspend函数,所以如果挂起操作已经上升到了Root Hub这一层,就应该调用hcd的suspend函数.即hcd_bus_suspend.1954行,如果挂起失败了,那么就别挂起,还是调用hub_activate()重新激活,苦海无涯,回头是岸.游手好闲会使人心智生锈

Linux那些事儿之我是Hub(28)将suspend分析到底 – fudan

相关文章:

你感兴趣的文章:

标签云: