Linux那些事儿之我是Hub(29)梦醒时分

爱情就像拔河比赛,如果一方先放手,另一方就会受伤.

只可惜说出这句话的梁咏琪,最终还是放开了与郑伊健相牵的手.

suspendresume也是这样,如果你不调用suspend,那么你永远也不需要调用resume,它们就这样青梅竹马的存在于这个世界上,过着世外桃源般的日子.但是如果你不小心调用了suspend让设备睡眠,那么你就必然需要在将来某个时刻调用resume来唤醒设备.

看完了suspend我们来看resume,变量usb_bus_type中的成员resume被赋值为usb_resume,usb_suspend对应.来自drivers/usb/core/driver.c:

   1504 static int usb_resume(struct device *dev)

   1505 {

   1506         struct usb_device       *udev;

   1507

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

   1509                 return 0;

   1510         udev = to_usb_device(dev);

   1511         if (udev->autoresume_disabled)

   1512                 return -EPERM;

   1513         return usb_external_resume_device(udev);

   1514 }

看过了usb_suspend再来看这个usb_resume就显得很简单了,两个函数基本能体现一种对称美.autoresume_disabledstruct usb_device中的一个成员,,我们提供给用户一种选择,让用户可以自己来disable掉设备的autoresume,一旦disable掉了,就意味着设备是不会唤醒了,所以这里直接返回错误码.

接着,usb_external_resume_device,

   1469 /**

   1470  * usb_external_resume_device - external resume of a USB device and its interfaces

   1471  * @udev: the usb_device to resume

   1472  *

   1473  * This routine handles external resume requests: ones not generated

   1474  * internally by a USB driver (autoresume) but rather coming from the user

   1475  * (via sysfs), the PM core (system resume), or the device itself (remote

   1476  * wakeup).  @udev's usage counter is unaffected.

   1477  *

   1478  * The caller must hold @udev's device lock.

   1479  */

   1480 int usb_external_resume_device(struct usb_device *udev)

   1481 {

   1482         int     status;

   1483

   1484         usb_pm_lock(udev);

   1485         udev->auto_pm = 0;

   1486         status = usb_resume_both(udev);

   1487         udev->last_busy = jiffies;

   1488         usb_pm_unlock(udev);

   1489

   1490         /* Now that the device is awake, we can start trying to autosuspend

   1491          * it again. */

   1492         if (status == 0)

   1493                 usb_try_autosuspend_device(udev);

   1494         return status;

   1495 }

也不干别的,设置好udev->auto_pm0,然后调用usb_resume_both.再然后调用usb_try_autosuspend_device,关于autosuspend/autoresume的部分我们稍候会单独讲.现在先来看usb_resume_both.

   1083 /**

   1084  * usb_resume_both - resume a USB device and its interfaces

   1085  * @udev: the usb_device to resume

   1086  *

   1087  * This is the central routine for resuming USB devices.  It calls the

   1088  * the resume method for @udev and then calls the resume methods for all

   1089  * the interface drivers in @udev.

   1090  *

   1091  * Before starting the resume, the routine calls itself recursively for

   1092  * the parent device of @udev, thereby propagating the change up the device

   1093  * tree and assuring that @udev will be able to resume.  If the parent is

   1094  * unable to resume successfully, the routine fails.

   1095  *

   1096  * The resume method calls are subject to mutual exclusion under control

   1097  * of @udev's pm_mutex.  Many of these calls are also under the protection

   1098  * of @udev's device lock (including all requests originating outside the

   1099  * USB subsystem), but autoresume requests generated by a child device or

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

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

   1102  * must be prepared to handle resume calls arriving at unpredictable times.

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

   1104  * other autoresumes) while holding @udev's device lock (preventing outside

   1105  * resumes).

   1106  *

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

   1108  *

   1109  * This routine can run only in process context.

   1110  */

   1111 static int usb_resume_both(struct usb_device *udev)

   1112 {

   1113         int                     status = 0;

   1114         int                     i;

   1115         struct usb_interface    *intf;

   1116         struct usb_device       *parent = udev->parent;

   1117

   1118         cancel_delayed_work(&udev->autosuspend);

   1119         if (udev->state == USB_STATE_NOTATTACHED) {

   1120                 status = -ENODEV;

   1121                 goto done;

   1122         }

   1123

   1124         /* Propagate the resume up the tree, if necessary */

   1125         if (udev->state == USB_STATE_SUSPENDED) {

   1126                 if (udev->auto_pm && udev->autoresume_disabled) {

   1127                         status = -EPERM;

   1128                         goto done;

   1129                 }

   1130                 if (parent) {

   1131                         status = usb_autoresume_device(parent);

   1132                         if (status == 0) {

   1133                                 status = usb_resume_device(udev);

   1134                                 if (status) {

   1135                                         usb_autosuspend_device(parent);

   1136

   1137                                         /* It's possible usb_resume_device()

   1138                                          * failed after the port was

   1139                                          * unsuspended, causing udev to be

   1140                                          * logically disconnected.  We don't

   1141                                          * want usb_disconnect() to autosuspend

   1142                                          * the parent again, so tell it that

   1143                                          * udev disconnected while still

   1144                                          * suspended. */

   1145                                         if (udev->state ==

   1146                                                         USB_STATE_NOTATTACHED)

   1147                                                 udev->discon_suspended = 1;

   1148                                 }

   1149                         }

   1150                 } else {

   1151

   1152                         /* We can't progagate beyond the USB subsystem,

   1153                          * so if a root hub's controller is suspended

   1154                          * then we're stuck. */

   1155                         if (udev->dev.parent->power.power_state.event !=

   1156                                         PM_EVENT_ON)

   1157                                 status = -EHOSTUNREACH;

   1158                         else

   1159                                 status = usb_resume_device(udev);

   1160                 }

   1161         } else {

   1162

   1163                 /* Needed only for setting udev->dev.power.power_state.event

   1164                  * and for possible debugging message. */

   1165                 status = usb_resume_device(udev);

   1166         }

   1167

   1168         if (status == 0 && udev->actconfig) {

   1169                 for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {

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

   1171                         usb_resume_interface(intf);

   1172                 }

   1173         }

   1174

   1175  done:

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

   1177         return status;

   1178 }

usb_suspend_both的结构比较类似,不过这里是先针对device,再针对interface,usb_suspend_both那儿是先针对interface,再针对device.我们先来看一下udev->autosuspend.我们刚刚才看到过cancel_delayed_work,这里又出现了一次.

struct usb_device结构体有一个成员,struct delayed_work autosuspend,要明白它,必须先明白另一个家伙,ksuspend_usb_wq.drivers/usb/core/usb.c:

     51 /* Workqueue for autosuspend and for remote wakeup of root hubs */

     52 struct workqueue_struct *ksuspend_usb_wq;

之前咱们在讲hub->leds的时候提到过struct workqueue_struct代表一个工作队列,不过作为hub->leds,咱们没有单独建立一个工作队列,而是使用默认的公共队列,但是这里咱们需要单独建立自己的队列.很显然,hub->leds代表着与指示灯相关的代码,其地位是很低下的,不可能受到足够的重视,而这里这个工作队列代表着整个usb子系统里与电源管理非常相关的一部分代码的利益,当然会被受到重视.

不信的话,咱们可以再一次看一下usb子系统初始化的代码,usb_init函数,其第一个重要的函数就是ksuspend_usb_init(),drivers/usb/core/usb.c:

    200 #ifdef  CONFIG_PM

    201

    202 static int ksuspend_usb_init(void)

    203 {

    204         /* This workqueue is supposed to be both freezable and

    205          * singlethreaded.  Its job doesn't justify running on more

    206          * than one CPU.

    207          */

    208         ksuspend_usb_wq = create_freezeable_workqueue("ksuspend_usbd");

    209         if (!ksuspend_usb_wq)

    210                 return -ENOMEM;

    211         return 0;

    212 }

    213

    214 static void ksuspend_usb_cleanup(void)

    215 {

    216         destroy_workqueue(ksuspend_usb_wq);

    217 }

    218

    219 #else

    220

    221 #define ksuspend_usb_init()     0

    222 #define ksuspend_usb_cleanup()  do {} while (0)

    223

224 #endif  /* CONFIG_PM */

如果你没有打开CONFIG_PM这个编译开关,当然就什么也不会发生,这俩函数也就是空函数,如果打开了,那么ksuspend_usb_initusb_init中被调用,ksuspend_usb_cleanup反其道而行之,usb_exit中被调用.

我们看到,这两个函数都非常短,但这并不要紧.生命的长短并不是最重要的,而是精彩与否.   利群广告词:人生就像是一场旅行,不在乎目的地,在乎的是沿途的风景和看风景的心情.怎样理解这段广告词?一个吸烟的同学说,可不可以这样认为,吸烟就像是男人的春药,不在乎浓缩生命的长河,在乎的是吸烟时的沉醉和沉醉时的快乐.

没错,ksuspend_usb_init虽然超级短,但是它却做了一件相当有意义的事情,那就是调用create_freezeable_workqueue(),这其实就是创建一个工作队列,函数的参数就是这个工作队列的名字,ksuspend_usbd,而函数的返回值就是工作队列结构体指针,struct workqueue_struct指针,然后赋值给了ksuspend_usb_wq.所以我们接下来就需要和ksuspend_usb_wq打交道了.我们所要知道的是,如果我们要把一个任务加入到工作队列中来,我们可以调用queue_work或者queue_delayed_work.2.6.22.1的内核中,往这个工作队列中加工作的地方只有两处,一个是drivers/usb/core/driver.c中的autosuspend_check()函数内部,调用的是queue_delayed_work,一个是drivers/usb/core/hcd.c,调用的是queue_work.autosuspend_check我们后面会讲,现在把与ksuspend_usb_wq相关的两行贴出来,先睹为快.

    976                         queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,

    977                                         suspend_time - jiffies);

这里第三个参数suspend_time-jiffies,表明一个延时的时间,即至少经过这么多时间之后,这个任务才可以真正执行.这就和我们前面见过的那个schedule_delayed_work()函数类似.

而正是这里让我们看到了udev->autosuspendksuspend_usb_wq之间的关系,即后者代表一个工作队列,而前者代表一个工作,这里的做法就是把autosuspend给加入到了ksuspend_usb_wq这个队列里,并且在经过一段延时之后执行这个工作.工作队列中的任务由相关的工作线程执行,可能是在一个无法预期的时间(取决于负载,中断等等),或者是在一段延迟之后.

于是你该问了,udev->autosuspend是一个struct delayed_work结构体,那么它所对应的那个函数是谁?其实我们见过,不过也许你已经忘记了,还记得八大函数的第一个么,usb_alloc_dev,当时就有这么一行,INIT_DELAYED_WORK(&dev->autosuspend,usb_autosuspend_work),所以说,每一个usb设备在它刚问世的时候就已经和一个叫做usb_autosuspend_work给捆绑了起来,即它还少不更事的时候就已经和usb_autosuspend_work()函数签了这么一个卖身契,因此,不管你是usb鼠标还是usb键盘,或者是usb mass storage,总之你都和usb_autosuspend_work得发生关系.

至此,我们就很好理解usb_resume_both函数中cancel_delayed_work那行的意思了.我们且不管autosuspend和一般的suspend有什么区别,一个很简单的道理,既然要resume,那当然就不要suspend.cancel了一个设备的autosuspendwork,自然它就不会再自动挂起,而如果你以后要让它能够自动挂起,你可以再次调用queue_delayed_work,正如在autosuspend_check中做的那样.具体代码我们后面讲autosuspend了再看.

继续在usb_resume_both中往下看,刚才我们设置了auto_pm0,所以这里1126这个if内部不会执行.

1130,如果不是Root Hub,那么调用针对父设备调用usb_autoresume_device,还是我们一直强调的那个道理,挂起的时候要从下至上,而唤醒的时候要自上而下.一个自来水管系统,如果上面没水,下面开关全打开也没有任何意义.

上面醒来了,才调用usb_resume_device唤醒下面的当前这个device.如果当前设备的唤醒失败了,那么调用usb_autosuspend_device()来把刚才做的事情取消掉,道理很简单,本来咱们的目的就是为了唤醒当前设备,为此我们先唤醒了上层的设备,结果上层的设备唤醒了,但是咱们自己却没有唤醒,那咱们所作的就是无用功了,所以还是把刚刚唤醒的上层设备给催眠吧.

虽然我们还没有讲autosuspend/autoresume,但是凭一个男人的直觉,我们基本上能够感觉出,usb_autosuspend_deviceusb_autoresume_device这两个函数可以很好的处理usb设备树,即他们不会仅仅对付一个设备,而是会很自然的沿着usb设备树去往上走或者往下走,从而保证咱们刚才说的那个挂起时从下至上,唤醒时从上而下.其实,usb_suspend_both中我们还剩下一个函数没有提,它正是usb_autosuspend_device(),而且是针对父设备的,如果你有耐心,你会发现usb_autosuspend_device会调用usb_autopm_do_device(),而后者又会调用usb_suspend_both,这样一层一层往上走,其效果就像一只蜗牛,一步一步往上爬,在最高点乘着叶片往前飞,任风吹干流过的泪和汗.而这里的usb_autoresume_device的原理恰好相反,它也会调用usb_autopm_do_device(),而后者这时候又会调用usb_resume_both,于是就会一层一层往下走,效果就相当于我们以前往的那个游戏—“是男人就下一百层”.也正是因为这种你调用我我调用你的复杂关系,我们才决定先不去深入看autosuspend相关的函数,等我们看完了非autosuspend的函数再去看就会很容易理解.

Ok,让我们来看usb_resume_device(),

    822 /* Caller has locked udev's pm_mutex */

    823 static int usb_resume_device(struct usb_device *udev)

    824 {

    825         struct usb_device_driver        *udriver;

    826         int                             status = 0;

    827

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

    829                         udev->state != USB_STATE_SUSPENDED)

    830                 goto done;

    831

    832         /* Can't resume it if it doesn't have a driver. */

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

    834                 status = -ENOTCONN;

    835                 goto done;

    836         }

    837

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

    839         status = udriver->resume(udev);

    840

    841 done:

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

    843         if (status == 0) {

    844                 udev->autoresume_disabled = 0;

    845                 udev->dev.power.power_state.event = PM_EVENT_ON;

    846         }

    847         return status;

    848 }

这种似曾相识的感觉是不言而喻的,usb_suspend_device那是相当的对称啊!最重要的当然是839,调用属于设备驱动的resume函数.不过,至少到2.6.22.1的内核为止,这个世界上总共只有一个struct usb_device_driver的结构体变量,它就是struct usb_device_driver usb_generic_driver,而实际上你会发现你的每个设备默认情况下都会和这个usb_generic_driver相绑定,除非你自己定义了自己的struct usb_device_driver结构体,不过至少在标准内核中,暂时还没有人这么干,当然以后也许会有.否则struct usb_device_driver这个结构体就太浪费了点.关于usb_generic_driver,你可以在sysfs下看到效果,比如:

localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # ls /sys/bus/usb/drivers

hub  usb  usb-storage  usbfs

所有的usb驱动程序都会在这里有一个对应的目录,其中与usb_generic_driver对应的就是那个usb目录.这是因为:

    210 struct usb_device_driver usb_generic_driver = {

    211         .name = "usb",

    212         .probe = generic_probe,

    213         .disconnect = generic_disconnect,

    214 #ifdef  CONFIG_PM

    215         .suspend = generic_suspend,

    216         .resume = generic_resume,

    217 #endif

    218         .supports_autosuspend = 1,

    219 };

这里的name就对应了在/sys/bus/usb/drivers/下面的那个子目录名称.现在的内核的处理方式是,凡是有一个新的设备被探测到,就先把它的struct usb_device和这个generic driver相绑定,即首先被调用的generic_probe,然后才会根据每一个具体的interface去绑定属于具体interface的驱动程序,去调用具体的那个interface对应的driverprobe函数,比如storage_probe.因此你会发现,不管你插入任何usb设备,你都会在/sys/bus/usb/drivers/usb/目录下面发现多出一个文件来,比如:

localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # ls /sys/bus/usb/drivers/usb

1-1  bind  module  unbind  usb1  usb2  usb3  usb4  usb5

而如果你插入的是usb-storage,那么接下来在/sys/bus/usb/drivers/usb-storage/下面也将多出一个对应的文件来,

localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # ls /sys/bus/usb/drivers/usb-storage/

1-1:1.0  bind  module  new_id  unbind

而在/sys/bus/usb/devices/目录下面你能看到所有的usb设备,

localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # ls /sys/bus/usb/devices/

1-0:1.0  1-1  1-1:1.0  2-0:1.0  3-0:1.0  4-0:1.0  5-0:1.0  usb1  usb2  usb3  usb4  usb5

贴出这些只是为了给你有一个直观的印象,而我们需要知道的是对于当前的设备来说,其默认情况下所对应的设备驱动级的suspend函数就是generic_suspend,resume函数就是generic_resume,所以我们来看一下这两个函数,

    192 #ifdef  CONFIG_PM

    193

    194 static int generic_suspend(struct usb_device *udev, pm_message_t msg)

    195 {

    196         /* USB devices enter SUSPEND state through their hubs, but can be

    197          * marked for FREEZE as soon as their children are already idled.

    198          * But those semantics are useless, so we equate the two (sigh).

    199          */

    200         return usb_port_suspend(udev);

    201 }

    202

    203 static int generic_resume(struct usb_device *udev)

    204 {

    205         return usb_port_resume(udev);

    206 }

    207

    208 #endif  /* CONFIG_PM */

呵呵,原来也就是调用usb_port_suspendusb_port_resume而已.前面我们已经看过usb_port_suspend函数,现在我们来看usb_port_resume,

   1838 /*

   1839  * usb_port_resume - re-activate a suspended usb device's upstream port

   1840  * @udev: device to re-activate

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

   1842  *

   1843  * This will re-activate the suspended device, increasing power usage

   1844  * while letting drivers communicate again with its endpoints.

   1845  * USB resume explicitly guarantees that the power session between

   1846  * the host and the device is the same as it was when the device

   1847  * suspended.

   1848  *

   1849  * Returns 0 on success, else negative errno.

   1850  */

   1851 int usb_port_resume(struct usb_device *udev)

   1852 {

   1853         int     status;

   1854

   1855         /* we change the device's upstream USB link,

   1856          * but root hubs have no upstream USB link.

   1857          */

   1858         if (udev->parent) {

   1859                 // NOTE this fails if parent is also suspended...

   1860                 status = hub_port_resume(hdev_to_hub(udev->parent),

   1861                                 udev->portnum, udev);

   1862         } else {

   1863                 dev_dbg(&udev->dev, "usb %sresume/n",

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

   1865                 status = finish_port_resume(udev);

   1866         }

   1867         if (status < 0)

   1868                 dev_dbg(&udev->dev, "can't resume, status %d/n", status);

   1869         return status;

   1870 }

结构很清晰,对于非Root Hub,调用hub_port_resume,对于Root Hub,调用finish_port_resume,先看前者再看后者.

   1770 static int

   1771 hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)

   1772 {

   1773         int     status;

   1774         u16     portchange, portstatus;

   1775

   1776         /* Skip the initial Clear-Suspend step for a remote wakeup */

   1777         status = hub_port_status(hub, port1, &portstatus, &portchange);

   1778         if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND))

   1779                 goto SuspendCleared;

   1780

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

   1782

   1783         set_bit(port1, hub->busy_bits);

   1784

   1785         /* see 7.1.7.7; affects power usage, but not budgeting */

   1786         status = clear_port_feature(hub->hdev,

   1787                         port1, USB_PORT_FEAT_SUSPEND);

   1788         if (status) {

   1789                 dev_dbg(hub->intfdev,

   1790                         "can't resume port %d, status %d/n",

   1791                         port1, status);

   1792         } else {

   1793                 /* drive resume for at least 20 msec */

   1794                 if (udev)

   1795                         dev_dbg(&udev->dev, "usb %sresume/n",

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

   1797                 msleep(25);

   1798

   1799 #define LIVE_FLAGS      ( USB_PORT_STAT_POWER /

   1800                         | USB_PORT_STAT_ENABLE /

   1801                         | USB_PORT_STAT_CONNECTION)

   1802

   1803                 /* Virtual root hubs can trigger on GET_PORT_STATUS to

   1804                  * stop resume signaling.  Then finish the resume

   1805                  * sequence.

   1806                  */

   1807                 status = hub_port_status(hub, port1, &portstatus, &portchange);

   1808 SuspendCleared:

   1809                 if (status < 0

   1810                                 || (portstatus & LIVE_FLAGS) != LIVE_FLAGS

   1811                                 || (portstatus & USB_PORT_STAT_SUSPEND) != 0

   1812                                 ) {

   1813                         dev_dbg(hub->intfdev,

   1814                                 "port %d status %04x.%04x after resume, %d/n",

   1815                                 port1, portchange, portstatus, status);

   1816                         if (status >= 0)

   1817                                 status = -ENODEV;

   1818                 } else {

   1819                         if (portchange & USB_PORT_STAT_C_SUSPEND)

   1820                                 clear_port_feature(hub->hdev, port1,

   1821                                                 USB_PORT_FEAT_C_SUSPEND);

   1822                         /* TRSMRCY = 10 msec */

   1823                         msleep(10);

   1824                         if (udev)

   1825                                 status = finish_port_resume(udev);

   1826                 }

   1827         }

   1828         if (status < 0)

   1829                 hub_port_logical_disconnect(hub, port1);

   1830

   1831         clear_bit(port1, hub->busy_bits);

   1832         if (!hub->hdev->parent && !hub->busy_bits[0])

   1833                 usb_enable_root_hub_irq(hub->hdev->bus);

   1834

   1835         return status;

   1836 }

看起来还挺复杂.但目的很明确,唤醒这个hub端口.首先是察看该port是否已经是resume.然后最直观最实在的代码就是1786行那个clear_port_feature,这行代码很显然是和hub_port_suspend中那行set_port_feature遥相呼应的.你给hub port设置了USB_PORT_FEAT_SUSPEND,我就给你清掉,偏要跟你对着干.

如果成功了,status0,进入1792行这个else,睡眠25ms,usb spec 2.0规定,resume信号应该维持至少20毫秒才能有效,这个时间被称为TDRSMDN.这个道理很显然,我们说技术是无国界的,今天早上在城铁上看见一个女的用小刀割伤了一个男的手,说那哥们对人家性骚扰,那么这道理是一样的,只有性骚扰持续了一段时间,那女的才能感觉到,否则你比如说那个男的技术足够好,那手从人家某部位像闪电一样过去,那估计双方都没有任何感觉.

18071826行这里是一种特殊情况,你这里想得很周到,睡眠25ms,可是Root Hub可能会把你的resume信号给stop,只要它在这期间发送了GET_PORT_STATUS请求.所以这里就再次读取端口的状态,如果这个端口对应的USB_PORT_STAT_POWER/USB_PORT_STAT_ENABLE/USB_PORT_STAT_CONNECTION中有一位为0,那么说明出错了,也就不用继续折腾了.而如果USB_PORT_STAT_SUSPEND这一位又被设置了,那么咱的猜测也就对了.这种情况咱们这里先把status设置为-ENODEV.

如果不是以上情况,那么1818行进去.如果SUSPEND位确实有变化,说明resume操作基本上达到了目的,那么先清掉这一位.睡眠10ms,这个10毫秒被称为TRSMRCY,或称为resume恢复时间(resume recovery time),这个道理也很简单,通常我睡一觉醒来之后必然不是马上很清醒,肯定还需要一段时间恢复一下,特别是前一天晚上玩游戏玩到半夜,第二天又有喜欢点名的变态老师的课,那么我基本上是匆匆从南区赶到本部教室,但意识却还未清醒,不过我和设备不同的是,设备恢复了之后就可以工作了,而我赶到教室之后就是接着睡.

   1709 /*

   1710  * If the USB "suspend" state is in use (rather than "global suspend"),

   1711  * many devices will be individually taken out of suspend state using

   1712  * special" resume" signaling.  These routines kick in shortly after

   1713  * hardware resume signaling is finished, either because of selective

   1714  * resume (by host) or remote wakeup (by device) ... now see what changed

   1715  * in the tree that's rooted at this device.

   1716  */

   1717 static int finish_port_resume(struct usb_device *udev)

   1718 {

   1719         int     status;

   1720         u16     devstatus;

   1721

   1722         /* caller owns the udev device lock */

   1723         dev_dbg(&udev->dev, "finish resume/n");

   1724

   1725         /* usb ch9 identifies four variants of SUSPENDED, based on what

   1726          * state the device resumes to.  Linux currently won't see the

   1727          * first two on the host side; they'd be inside hub_port_init()

   1728          * during many timeouts, but khubd can't suspend until later.

   1729          */

   1730         usb_set_device_state(udev, udev->actconfig

   1731                         ? USB_STATE_CONFIGURED

   1732                         : USB_STATE_ADDRESS);

   1733

   1734         /* 10.5.4.5 says be sure devices in the tree are still there.

   1735          * For now let's assume the device didn't go crazy on resume,

   1736          * and device drivers will know about any resume quirks.

   1737          */

   1738         status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);

   1739         if (status >= 0)

   1740                 status = (status == 2 ? 0 : -ENODEV);

   1741

   1742         if (status)

   1743                 dev_dbg(&udev->dev,

   1744                         "gone after usb resume? status %d/n",

   1745                         status);

   1746         else if (udev->actconfig) {

   1747                 le16_to_cpus(&devstatus);

   1748                 if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))

   1749                                 && udev->parent) {

   1750                         status = usb_control_msg(udev,

   1751                                         usb_sndctrlpipe(udev, 0),

   1752                                         USB_REQ_CLEAR_FEATURE,

   1753                                                 USB_RECIP_DEVICE,

   1754                                         USB_DEVICE_REMOTE_WAKEUP, 0,

   1755                                         NULL, 0,

   1756                                         USB_CTRL_SET_TIMEOUT);

   1757                         if (status)

   1758                                 dev_dbg(&udev->dev, "disable remote "

   1759                                         "wakeup, status %d/n", status);

   1760                 }

   1761                 status = 0;

   1762

   1763         } else if (udev->devnum <= 0) {

   1764                 dev_dbg(&udev->dev, "bogus resume!/n");

   1765                 status = -EINVAL;

   1766         }

   1767         return status;

   1768 }

这个函数其实就是做一些收尾的工作.我们刚才在usb_port_resume中看到,对于Root Hub就是直接调用这个函数,因为Root Hub不存在说接在别的Hub口上的说法.

1730行调用usb_set_device_state设置状态, USB_STATE_CONFIGURED或者是USB_STATE_ADDRESS,如果配置好了,就记录为前者,如果没有配置好,就记录为后者.

1738,如注释所言,spec 10.5.4.5节建议这么做.以确认设备还在,而不是说在suspend/resume的过程中被拔走了.于是调用usb_get_status,获取设备的状态,返回值是设备返回的数据的长度.spec规定,返回的数据应该是16,2bytes,所以1740行判断是否为2.如果为2,就将status置为0,并开始新的判断.1742行的判断,

1746,如果设备配置好了,然后设备又不是Root Hub,然后设备的Wakeup功能是enabled,那么就发送ClearFeature请求把这个功能给关掉,因为很显然,当一个设备在醒来的时候就没有必要打开这个功能,只有在将要睡去的时候才有必要打开,就好比我们起床以后就会把闹钟关掉,只有在我们将要睡觉的时候才有必要定闹钟.

然后如果一切正常就返回0.看完finish_port_resume函数,我们回到hub_port_resume中来,接下来,如果status小于0,说明出了问题, 于是调用hub_port_logical_disconnect,

   1560 /*

   1561  * Disable a port and mark a logical connnect-change event, so that some

   1562  * time later khubd will disconnect() any existing usb_device on the port

   1563  * and will re-enumerate if there actually is a device attached.

   1564  */

   1565 static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)

   1566 {

   1567         dev_dbg(hub->intfdev, "logical disconnect on port %d/n", port1);

   1568         hub_port_disable(hub, port1, 1);

   1569

   1570         /* FIXME let caller ask to power down the port:

   1571          *  - some devices won't enumerate without a VBUS power cycle

   1572          *  - SRP saves power that way

   1573          *  - ... new call, TBD ...

   1574          * That's easy if this hub can switch power per-port, and

   1575          * khubd reactivates the port later (timer, SRP, etc).

   1576          * Powerdown must be optional, because of reset/DFU.

   1577          */

   1578

   1579         set_bit(port1, hub->change_bits);

   1580         kick_khubd(hub);

   1581 }

很简单,这个函数其实就是先把该端口关了,然后重新枚举该设备.因为刚才的返回值为负值说明出了某种问题,但并不确定究竟是何种问题,所以最省事的办法就是重新初始化该端口.1579行这个set_bit设置了hub->change_bits,于是我们在hub_events()中就会根据这个来处理这个端口.(kick_khubd会触发hub_events,这我们早就知道.)

这时候我们注意到,hub_port_resume的一开始我们调用set_bit设置了该端口对应的busy_bits,而在hub_port_resume快结束的时候我们调用clear_bit清掉了这个busy_bits.唯一受此影响的函数是hub_events(),当初我们其实提过,但我们在对一个端口进行resume或者reset的时候,hub_events是不会对该端口进行任何操作的.

1832, busy_bits[0]0就意味着所有的端口都没有处于resume或者reset阶段,hub->hdev->parentNULL则意味着当前HubRoot Hub,于是还是调用usb_enable_root_hub_irq,当初我们在hub_events()的结尾阶段也调用了这个函数.这就是调用host controller driver的一个函数hub_irq_enable,某些主机控制器的端口连接是使用以电平触发的中断,这类主机控制器的驱动会提供这样一个函数,这个和具体的硬件有关,各家的产品不一样,咱们就不多说了.

至此,hub_port_resume函数就返回了.回到usb_port_resume,我们发现其实这个函数我们也已经看完了,因为finish_port_resume不小心也被我们讲完了.于是我们回到了usb_resume_device,如果一切Ok,那么dev.power.power_state.event也就设置为PM_EVENT_ON.

然后我们经过跋山涉水翻山越岭之后再次回到了usb_resume_both.1150,如果是Root Hub,进入else,我们一直说Root Hub没有parent,其实这是不严谨的.我们注意到struct usb_device结构体有一个成员struct usb_device *parent,同时我们还注意到struct device结构体本身也有一个成员struct device *parent,其实对于Root Hub来说,是没有前者的那个parent,但是却有后者这个parent,后者的这个parent就是相应的Host Controller所对应的struct device结构体指针.所以这里的意思就很明白了,如果主机控制器没醒的话,Root Hub以及其它的子设备再怎么玩也白搭.

如果Host Controller醒来了,那么1159,Root Hub来说,也调用usb_resume_device去唤醒它.

1161行这个else的意思更直接,如果设备根本就没睡眠,那就没有什么唤醒它的意义了,调用usb_resume_device也不会做什么实事,无非就是设置dev.power_power_state.eventPM_EVENT_ON,仅此而已.

1168,满足了集体的,再来满足个人的,社会主义制度优越性再次被体现.usb_resume_interface按照interface的个数来循环调用,

    887 /* Caller has locked intf's usb_device's pm_mutex */

    888 static int usb_resume_interface(struct usb_interface *intf)

    889 {

    890         struct usb_driver       *driver;

    891         int                     status = 0;

    892

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

    894                         is_active(intf))

    895                 goto done;

    896

    897         /* Don't let autoresume interfere with unbinding */

    898         if (intf->condition == USB_INTERFACE_UNBINDING)

    899                 goto done;

    900

    901         /* Can't resume it if it doesn't have a driver. */

    902         if (intf->condition == USB_INTERFACE_UNBOUND) {

    903                 status = -ENOTCONN;

    904                 goto done;

    905         }

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

    907

    908         if (driver->resume) {

    909                 status = driver->resume(intf);

    910                 if (status)

    911                         dev_err(&intf->dev, "%s error %d/n",

    912                                         "resume", status);

    913                 else

    914                         mark_active(intf);

    915         } else {

    916                 dev_warn(&intf->dev, "no resume for driver %s?/n",

    917                                 driver->name);

    918                 mark_active(intf);

    919         }

    920

    921 done:

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

    923         if (status == 0)

    924                 intf->dev.power.power_state.event = PM_EVENT_ON;

    925         return status;

    926 }

898,关于struct usb_interface结构体的成员condition我们当初在usb_reset_composite_device中已经讲过,一共有四种状况,其含义正如其字面意义那样,无需多说.

908行至919行这一段不用解释你也该明白吧,看过了当初那个usb_suspend_interface()函数之后,我相信即便是西直门城铁站外面每天晚上等着招呼大家坐他的黑车的司机朋友也该知道现在这段代码的含义了.这里的mark_active和当初的那个mark_quiesced相对应,一个唱红脸一个唱白脸.909行那个driver->resume()就是调用属于该interface的驱动程序的resume函数,对于hub driver,调用的自然就是hub_resume,和前面那个hub_suspend相对应.

   1962 static int hub_resume(struct usb_interface *intf)

   1963 {

   1964         struct usb_hub          *hub = usb_get_intfdata (intf);

   1965         struct usb_device       *hdev = hub->hdev;

   1966         int                     status;

   1967

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

   1969

   1970         /* "global resume" of the downstream HC-to-USB interface */

   1971         if (!hdev->parent) {

   1972                 struct usb_bus  *bus = hdev->bus;

   1973                 if (bus) {

   1974                         status = hcd_bus_resume (bus);

   1975                         if (status) {

   1976                                 dev_dbg(&intf->dev, "'global' resume %d/n",

   1977                                         status);

   1978                                 return status;

   1979                         }

   1980                 } else

   1981                         return -EOPNOTSUPP;

   1982                 if (status == 0) {

   1983                         /* TRSMRCY = 10 msec */

   1984                         msleep(10);

   1985                 }

   1986         }

   1987

   1988         /* tell khubd to look for changes on this hub */

   1989         hub_activate(hub);

   1990         return 0;

   1991 }

一路走来的兄弟们现在看着个函数是不是觉得有点小儿科,相当于一个游戏机高手去玩魂斗罗,菜鸟调出30条命来还未必能通关,可是高手也许一条命就能玩过八关(魂斗罗一代).这个函数我想就没有必要讲了,我们完全可以一目十行,它和hub_suspend实在是太他妈的对称了.对于Roob Hub,需要调用hcd_bus_resume,这个host controller driver那边的resume函数.最后调用hub_activate()彻底激活hub.

至此 , 我们算是把 usb_resume_both 看完了 , 看完了 usb_resume_both, 也看完了 usb_suspend_both, 我们就算是基本上知道了整个 usb 子系统是如何支持电源管理的 , 或者说如何支持 PM core . 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值