Linux那些事儿之我是Hub(26)支持计划生育–看代码的理由

Linux那些事儿之我是Hub(26)支持计划生育–看代码的理由北大校长马寅初先生曾斩钉截铁地跟讲:”中国人口太多是因为农村晚上没有电.”因此,为了支持计划生育这项基本国策,每一个男人都有义务认真看一下电源管理的代码.另一方面,虽然现在已经不住在农村了,但我一直坚定不移的认为,这个世界,最慢的是我家的网速,最快的是我家电表的转速.所以,为了了解如何让电表转速更慢,让我们一起来看看usb子系统里是如何支持电源管理的吧.上节说了应该从usb_sus…

大家好,又见面了,我是你们的朋友全栈君。

北大校长马寅初先生曾斩钉截铁地跟讲:”中国人口太多是因为农村晚上没有电.”

因此,为了支持计划生育这项基本国策,每一个男人都有义务认真看一下电源管理的代码.

另一方面,虽然现在已经不住在农村了,但我一直坚定不移的认为,这个世界,最慢的是我家的网速,最快的是我家电表的转速.

所以,为了了解如何让电表转速更慢,让我们一起来看看usb子系统里是如何支持电源管理的吧.

上节说了应该从usb_suspend/usb_resume开始看,那就开始吧.

usb_suspend/usb_resume这两个函数很显然是一对,但是我们不可能同时讲,只能一个一个来.倒不是故意把它们拆开,实在是没有办法.须知,形影不离并不代表相知相惜,感情在乎的是心与心的距离.两情若是久长时,又岂在朝朝暮暮. 先讲usb_suspend,再讲usb_resume.

来看usb_suspend,定义于drivers/usb/core/driver.c:

1497 static int usb_suspend(struct device *dev, pm_message_t message)

1498 {

1499 if (!is_usb_device(dev)) /* Ignore PM for interfaces */

1500 return 0;

1501 return usb_external_suspend_device(to_usb_device(dev), message);

1502 }

刚说过,usb_suspendusb子系统提供给PM core调用的,所以这里两个参数dev/message都是那边传递过来的,要不是usb device当然就不用做什么了.直接返回.然后调用usb_external_suspend_device(),后者也是来自drivers/usb/core/driver.c.

1443 /**

1444 * usb_external_suspend_device – external suspend of a USB device and its interfaces

1445 * @udev: the usb_device to suspend

1446 * @msg: Power Management message describing this state transition

1447 *

1448 * This routine handles external suspend requests: ones not generated

1449 * internally by a USB driver (autosuspend) but rather coming from the user

1450 * (via sysfs) or the PM core (system sleep). The suspend will be carried

1451 * out regardless of @udev’s usage counter or those of its interfaces,

1452 * and regardless of whether or not remote wakeup is enabled. Of course,

1453 * interface drivers still have the option of failing the suspend (if

1454 * there are unsuspended children, for example).

1455 *

1456 * The caller must hold @udev’s device lock.

1457 */

1458 int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)

1459 {

1460 int status;

1461

1462 usb_pm_lock(udev);

1463 udev->auto_pm = 0;

1464 status = usb_suspend_both(udev, msg);

1465 usb_pm_unlock(udev);

1466 return status;

1467 }

1462行和1465,锁的代码暂时先一律飘过.

我们看到,这个函数就做了两件事情,第一,udevauto_pm0,第二,调用usb_suspend_both.

继续跟踪usb_suspend_both.仍然是来自于drivers/usb/core/driver.c:

993 /**

994 * usb_suspend_both – suspend a USB device and its interfaces

995 * @udev: the usb_device to suspend

996 * @msg: Power Management message describing this state transition

997 *

998 * This is the central routine for suspending USB devices. It calls the

999 * suspend methods for all the interface drivers in @udev and then calls

1000 * the suspend method for @udev itself. If an error occurs at any stage,

1001 * all the interfaces which were suspended are resumed so that they remain

1002 * in the same state as the device.

1003 *

1004 * If an autosuspend is in progress (@udev->auto_pm is set), the routine

1005 * checks first to make sure that neither the device itself or any of its

1006 * active interfaces is in use (pm_usage_cnt is greater than 0). If they

1007 * are, the autosuspend fails.

1008 *

1009 * If the suspend succeeds, the routine recursively queues an autosuspend

1010 * request for @udev’s parent device, thereby propagating the change up

1011 * the device tree. If all of the parent’s children are now suspended,

1012 * the parent will autosuspend in turn.

1013 *

1014 * The suspend method calls are subject to mutual exclusion under control

1015 * of @udev’s pm_mutex. Many of these calls are also under the protection

1016 * of @udev’s device lock (including all requests originating outside the

1017 * USB subsystem), but autosuspend requests generated by a child device or

1018 * interface driver may not be. Usbcore will insure that the method calls

1019 * do not arrive during bind, unbind, or reset operations. However, drivers

1020 * must be prepared to handle suspend calls arriving at unpredictable times.

1021 * The only way to block such calls is to do an autoresume (preventing

1022 * autosuspends) while holding @udev’s device lock (preventing outside

1023 * suspends).

1024 *

1025 * The caller must hold @udev->pm_mutex.

1026 *

1027 * This routine can run only in process context.

1028 */

1029 static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)

1030 {

1031 int status = 0;

1032 int i = 0;

1033 struct usb_interface *intf;

1034 struct usb_device *parent = udev->parent;

1035

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

1037 udev->state == USB_STATE_SUSPENDED)

1038 goto done;

1039

1040 udev->do_remote_wakeup = device_may_wakeup(&udev->dev);

1041

1042 if (udev->auto_pm) {

1043 status = autosuspend_check(udev);

1044 if (status < 0)

1045 goto done;

1046 }

1047

1048 /* Suspend all the interfaces and then udev itself */

1049 if (udev->actconfig) {

1050 for (; i < udev->actconfig->desc.bNumInterfaces; i++) {

1051 intf = udev->actconfig->interface[i];

1052 status = usb_suspend_interface(intf, msg);

1053 if (status != 0)

1054 break;

1055 }

1056 }

1057 if (status == 0)

1058 status = usb_suspend_device(udev, msg);

1059

1060 /* If the suspend failed, resume interfaces that did get suspended */

1061 if (status != 0) {

1062 while (–i >= 0) {

1063 intf = udev->actconfig->interface[i];

1064 usb_resume_interface(intf);

1065 }

1066

1067 /* Try another autosuspend when the interfaces aren’t busy */

1068 if (udev->auto_pm)

1069 autosuspend_check(udev);

1070

1071 /* If the suspend succeeded, propagate it up the tree */

1072 } else {

1073 cancel_delayed_work(&udev->autosuspend);

1074 if (parent)

1075 usb_autosuspend_device(parent);

1076 }

1077

1078 done:

1079 // dev_dbg(&udev->dev, “%s: status %d/n”, __FUNCTION__, status);

1080 return status;

1081 }

这里有两个重要的概念,autosuspend/autoresume.autosuspend,即自动挂起,这是由driver自行决定,它自己进行判断,当它觉得应该挂起设备的时候,它就会去Just do it!关于autosuspend我们后面会讲.

1040, device_may_wakeup(),我们前面说过,设备有没有被唤醒的能力有一个flag可以标志,can_wakeup,那么如果有这种能力,用户仍然可以根据实际需要关掉这种能力,或者打开这种能力,这就体现在sysfs下的一个文件.比如:

localhost:~ # cat /sys/bus/usb/devices/1-5/power/wakeup

enabled

localhost:~ # cat /sys/bus/usb/devices/1-5/:1.0/power/wakeup

localhost:~ #

可以看到后者的输出值为空,这说明该设备是不支持remote wakeup,换句话说,can_wakeup也应该是设置为了0,这种情况device_may_wakeup返回值必然是false,而前者的输出值为enabled,说明该设备是支持remote wakeup,并且此刻remote wakeup的特性是打开的.别的设备也一样,用户可以通过sysfs来进行设置,你可以把wakeupenabled改为disabled.

为什么需要有这么一个sysfs的接口呢?我们知道usb设备有一种特性,叫做remote wakeup,这种特性不是每个usb设备都支持,而一个设备是否支持remote wakeup可以在它的配置描述符里体现出来,但问题是,以前,区里的人们总是相信设备的各种描述符,可是你知道,现实生活中,被骗比骗人容易.制造商生产出来的产品总是有着各种问题的,它的各种描述符也许只是一种假象,比如,很多案例表明,一个设备的配置描述里声称自己支持remote wakeup,但是实际上却并不支持,当它进入睡眠之后,你根本唤不醒它.所以弟兄们学乖了,决定为用户提供一种选择,,用户可以自己打开或者关闭这种特性,就为了对付这种设备掩耳盗铃自欺欺人的社会现象.不过仔细想想,其实制造商也不容易,也许他们想通过这些设备传达一种思想,那就是:人们总说快乐很难,其实快乐很容易,只要你学会欺骗自己,你天天都是快乐的.

那么我们这里用udev->do_remote_wakeup来记录下这个值,日后会用得着的,到时候再看.

1042,刚才咱们设置了auto_pm0,所以这段不会执行.如果我们设置了auto_pm1,那么就会调用.autosuspend_check(),这个函数我们以后再回过来看.现在先根据我们实际的情景走,不执行.auto_pm0就是告诉人们我们现在没有做autosuspend.以后我们会看到,auto_pm这个变量将在autosuspend的代码中被设置为1.

接下来是一段循环,按接口进行循环,,设备有几个接口,就循环几次,因为我们知道usb,驱动程序往往是针对interface,而不是针对device,所以每一个interface就可能对应一个驱动程序,进而就可能有一个单独的suspend函数.

遍历各个接口之后,usb_suspend_interface这个函数如果能够顺利的把各个接口都给挂起了,那么再调用一个usb_suspend_device函数来执行一次总的挂起.为什么要有这两个函数我们看了就知道.先看第一个,usb_suspend_interface,来自drivers/usb/core/driver.c:

850 /* Caller has locked intf’s usb_device’s pm mutex */

851 static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)

852 {

853 struct usb_driver *driver;

854 int status = 0;

855

856 /* with no hardware, USB interfaces only use FREEZE and ON states */

857 if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||

858 !is_active(intf))

859 goto done;

860

861 if (intf->condition == USB_INTERFACE_UNBOUND) /* This can’t happen */

862 goto done;

863 driver = to_usb_driver(intf->dev.driver);

864

865 if (driver->suspend && driver->resume) {

866 status = driver->suspend(intf, msg);

867 if (status == 0)

868 mark_quiesced(intf);

869 else if (!interface_to_usbdev(intf)->auto_pm)

870 dev_err(&intf->dev, “%s error %d/n”,

871 “suspend”, status);

872 } else {

873 // FIXME else if there’s no suspend method, disconnect…

874 // Not possible if auto_pm is set…

875 dev_warn(&intf->dev, “no suspend for driver %s?/n”,

876 driver->name);

877 mark_quiesced(intf);

878 }

879

880 done:

881 // dev_dbg(&intf->dev, “%s: status %d/n”, __FUNCTION__, status);

882 if (status == 0)

883 intf->dev.power.power_state.event = msg.event;

884 return status;

885 }

一路陪我们走过来的兄弟们一定不会看不懂这个函数,最关键的代码就是866那行,driver->suspend(intf,msg),这就是调用具体的interface所绑定的那个驱动程序的suspend函数.比如,对于hub来说,这里调用的就是hub_suspend()函数.具体的hub_suspend()我们倒是不用先急着看,顺着现在的情景往下过一遍,至于hub_suspend/hub_resume,咱们跟它秋后算账.

mark_quiesced是一个内联函数,咱们一次性把相关的三个内联函数都贴出来,来自drivers/usb/core/usb.h:

98 /* Interfaces and their “power state” are owned by usbcore */

99

100 static inline void mark_active(struct usb_interface *f)

101 {

102 f->is_active = 1;

103 }

104

105 static inline void mark_quiesced(struct usb_interface *f)

106 {

107 f->is_active = 0;

108 }

109

110 static inline int is_active(const struct usb_interface *f)

111 {

112 return f->is_active;

113 }

其实就是struct usb_interface中有一个成员,unsigned is_active,这位为1就标志该interface没有suspended.反之就是记录该interface已经是suspended.suspended了也被老外称作quiesced,反之就叫做active.所以呢,这里对应的两个函数就叫做mark_activemark_quiesced.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/139120.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号