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

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

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

suspend和resume也是这样,如果你不调用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_disabled是struct 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_pm为0,然后调用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_init在usb_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->autosuspend和ksuspend_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了一个设备的autosuspend的work,自然它就不会再自动挂起,而如果你以后要让它能够自动挂起,你可以再次调用queue_delayed_work,正如在autosuspend_check中做的那样.具体代码我们后面讲autosuspend了再看.

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

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

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

虽然我们还没有讲autosuspend/autoresume,但是凭一个男人的直觉,我们基本上能够感觉出,usb_autosuspend_device和usb_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对应的driver的probe函数,比如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_suspend和usb_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,我就给你清掉,偏要跟你对着干.

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

1807至1826行这里是一种特殊情况,你这里想得很周到,睡眠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位,即2个bytes,所以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->parent为NULL则意味着当前Hub是Root 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.event为PM_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的.人生如果错了方向,停止就是进步”。

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

相关文章:

你感兴趣的文章:

标签云: