您的位置:首页 > 运维架构 > Linux

Linux那些事儿之我是UHCI(28)实战电源管理(四)

2007-11-22 17:31 453 查看
这个usb_hcd_pci_resume来自drivers/usb/core/hcd-pci.c:
312 /**
313 * usb_hcd_pci_resume - power management resume of a PCI-based HCD
314 * @dev: USB Host Controller being resumed
315 *
316 * Store this function in the HCD's struct pci_driver as resume().
317 */
318 int usb_hcd_pci_resume (struct pci_dev *dev)
319 {
320 struct usb_hcd *hcd;
321 int retval;
322
323 hcd = pci_get_drvdata(dev);
324 if (hcd->state != HC_STATE_SUSPENDED) {
325 dev_dbg (hcd->self.controller,
326 "can't resume, not suspended!/n");
327 return 0;
328 }
329
330 #ifdef CONFIG_PPC_PMAC
331 /* Reenable ASIC clocks for USB */
332 if (machine_is(powermac)) {
333 struct device_node *of_node;
334
335 of_node = pci_device_to_OF_node (dev);
336 if (of_node)
337 pmac_call_feature (PMAC_FTR_USB_ENABLE,
338 of_node, 0, 1);
339 }
340 #endif
341
342 /* NOTE: chip docs cover clean "real suspend" cases (what Linux
343 * calls "standby", "suspend to RAM", and so on). There are also
344 * dirty cases when swsusp fakes a suspend in "shutdown" mode.
345 */
346 if (dev->current_state != PCI_D0) {
347 #ifdef DEBUG
348 int pci_pm;
349 u16 pmcr;
350
351 pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
352 pci_read_config_word(dev, pci_pm + PCI_PM_CTRL, &pmcr);
353 pmcr &= PCI_PM_CTRL_STATE_MASK;
354 if (pmcr) {
355 /* Clean case: power to USB and to HC registers was
356 * maintained; remote wakeup is easy.
357 */
358 dev_dbg(hcd->self.controller, "resume from PCI D%d/n",
359 pmcr);
360 } else {
361 /* Clean: HC lost Vcc power, D0 uninitialized
362 * + Vaux may have preserved port and transceiver
363 * state ... for remote wakeup from D3cold
364 * + or not; HCD must reinit + re-enumerate
365 *
366 * Dirty: D0 semi-initialized cases with swsusp
367 * + after BIOS init
368 * + after Linux init (HCD statically linked)
369 */
370 dev_dbg(hcd->self.controller,
371 "PCI D0, from previous PCI D%d/n",
372 dev->current_state);
373 }
374 #endif
375 /* yes, ignore these results too... */
376 (void) pci_enable_wake (dev, dev->current_state, 0);
377 (void) pci_enable_wake (dev, PCI_D3cold, 0);
378 } else {
379 /* Same basic cases: clean (powered/not), dirty */
380 dev_dbg(hcd->self.controller, "PCI legacy resume/n");
381 }
382
383 /* NOTE: the PCI API itself is asymmetric here. We don't need to
384 * pci_set_power_state(PCI_D0) since that's part of re-enabling;
385 * but that won't re-enable bus mastering. Yet pci_disable_device()
386 * explicitly disables bus mastering...
387 */
388 retval = pci_enable_device (dev);
389 if (retval < 0) {
390 dev_err (hcd->self.controller,
391 "can't re-enable after resume, %d!/n", retval);
392 return retval;
393 }
394 pci_set_master (dev);
395 pci_restore_state (dev);
396
397 dev->dev.power.power_state = PMSG_ON;
398
399 clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
400
401 if (hcd->driver->resume) {
402 retval = hcd->driver->resume(hcd);
403 if (retval) {
404 dev_err (hcd->self.controller,
405 "PCI post-resume error %d!/n", retval);
406 usb_hc_died (hcd);
407 }
408 }
409
410 return retval;
411 }
330行至340行,激动的理由同usb_hcd_pci_suspend.
346行,首先判断,如果是PCI_D0,那么就不存在所谓的resume了.
376行,377行,调用pci_enable_wake,但是传递的第三个参数是0,即这次是关,而上次咱们在usb_hcd_pci_suspend中看到的传递的就不一定是0.这里关闭的是当前状态下的PME#能力,以及PCI_D3cold下的PME#能力,因为这两种状态下没有必要再发送PME#信号了.
388行,pci_enable_device,和前面那个pci_disable_device相对应.其实咱们不是头一次看到这个函数,当初在usb_hcd_pci_probe中就调用了这个函数.
394行,pci_set_master,在设备开始工作之前为设备启用总线控制.其实咱们也不是头一次看到这个函数,当初在usb_hcd_pci_probe中也调用了这个函数.
395行,pci_restore_state,很显然和前面那个pci_save_state相对应.恢复当初保存的PCI设备配置空间.
一切顺利就把power_state设置为PMSG_ON.
399行, HCD_FLAG_SAW_IRQ这个flag意义不大.用Alan Stern的话说就是,这个flag是用来汇报错误的.如果一个urb在这个flag被清掉了以后unlink,这就意味着可能中断号赋错了.就比如现在咱们这里调用clear_bit清掉了这个flag,如果这时候你取unlink一个urb,那么一定是有问题的.当然我个人觉得这个flag的用处不大,如果我是Greg,我肯定把这个flag给删掉.
401行,调用driver->resume,对于uhci,就是uchi_resume函数.来自drivers/usb/host/uhci-hcd.c:
779 static int uhci_resume(struct usb_hcd *hcd)
780 {
781 struct uhci_hcd *uhci = hcd_to_uhci(hcd);
782
783 dev_dbg(uhci_dev(uhci), "%s/n", __FUNCTION__);
784
785 /* Since we aren't in D3 any more, it's safe to set this flag
786 * even if the controller was dead.
787 */
788 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
789 mb();
790
791 spin_lock_irq(&uhci->lock);
792
793 /* FIXME: Disable non-PME# remote wakeup? */
794
795 /* The firmware or a boot kernel may have changed the controller
796 * settings during a system wakeup. Check it and reconfigure
797 * to avoid problems.
798 */
799 check_and_reset_hc(uhci);
800
801 /* If the controller was dead before, it's back alive now */
802 configure_hc(uhci);
803
804 if (uhci->rh_state == UHCI_RH_RESET) {
805
806 /* The controller had to be reset */
807 usb_root_hub_lost_power(hcd->self.root_hub);
808 suspend_rh(uhci, UHCI_RH_SUSPENDED);
809 }
810
811 spin_unlock_irq(&uhci->lock);
812
813 if (!uhci->working_RD) {
814 /* Suspended root hub needs to be polled */
815 hcd->poll_rh = 1;
816 usb_hcd_poll_rh_status(hcd);
817 }
818 return 0;
819 }
和uhci_suspend所做的事情相反,这里主要就是设置HCD_FLAG_HW_ACCESSIBLE这个flag.
而像check_and_reset_hc和configure_hc这两个函数都是一开始咱们初始化uhci的时候调用过的.resume的时候和init的时候做的事情有时候是很相似的.
而把rh_state设置为UHCI_RH_RESET的地方只有一处,即finish_reset中.这个函数咱们见过不止一次了.而且事实上在刚才说的这个check_and_reset_hc()中就会调用finish_reset().于是我提出一个问题,在咱们这个实验中,finish_reset会不会被调用?让我们再次把check_and_reset_hc()贴出来:
164 /*
165 * Initialize a controller that was newly discovered or has lost power
166 * or otherwise been reset while it was suspended. In none of these cases
167 * can we be sure of its previous state.
168 */
169 static void check_and_reset_hc(struct uhci_hcd *uhci)
170 {
171 if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr))
172 finish_reset(uhci);
173 }
实践表明,uhci_check_and_reset_hc在我们这个实验中返回值一定是0.(在kdb中我们可以使用rd命令来查看寄存器%eax,因为%eax通常就代表着函数的返回值.所以我们可以看到在这个实验中,如果把断点设置在uhci_check_and_reset_hc调用的下一行,那么%eax必然是0.)于是我们来到uhci_check_and_reset_hc中看一下为什么返回值是0.
85 /*
86 * Initialize a controller that was newly discovered or has just been
87 * resumed. In either case we can't be sure of its previous state.
88 *
89 * Returns: 1 if the controller was reset, 0 otherwise.
90 */
91 int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
92 {
93 u16 legsup;
94 unsigned int cmd, intr;
95
96 /*
97 * When restarting a suspended controller, we expect all the
98 * settings to be the same as we left them:
99 *
100 * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
101 * Controller is stopped and configured with EGSM set;
102 * No interrupts enabled except possibly Resume Detect.
103 *
104 * If any of these conditions are violated we do a complete reset.
105 */
106 pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup);
107 if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) {
108 dev_dbg(&pdev->dev, "%s: legsup = 0x%04x/n",
109 __FUNCTION__, legsup);
110 goto reset_needed;
111 }
112
113 cmd = inw(base + UHCI_USBCMD);
114 if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) ||
115 !(cmd & UHCI_USBCMD_EGSM)) {
116 dev_dbg(&pdev->dev, "%s: cmd = 0x%04x/n",
117 __FUNCTION__, cmd);
118 goto reset_needed;
119 }
120
121 intr = inw(base + UHCI_USBINTR);
122 if (intr & (~UHCI_USBINTR_RESUME)) {
123 dev_dbg(&pdev->dev, "%s: intr = 0x%04x/n",
124 __FUNCTION__, intr);
125 goto reset_needed;
126 }
127 return 0;
128
129 reset_needed:
130 dev_dbg(&pdev->dev, "Performing full reset/n");
131 uhci_reset_hc(pdev, base);
132 return 1;
133 }
其实这个函数咱们N年前就讲过,所以这里就不细讲了.但是正如注释里说的那样,这个函数就是检查看是否需要reset,如果需要,就跳到reset_needed这里,去执行uhci_reset_hc(),也就是真正的执行reset.而如果真的执行了reset,那么返回值就是1.否则就是执行127行,返回0.于是咱们就确定了,在咱们这个实验中,肯定没有reset,因为如果reset了,这个函数就返回了1,于是finish_reset就被执行了,可事实上finish_reset并没有被执行.
虽说彪悍的人生不需要解释,然而122行这里还是需要解释一下.既然没有reset,那么说明在几个中断位中,除了UHCI_USBINTR_RESUME以外的另外几个中断位没有enabled的,因为否则就会需要reset了,即,需要调用uhci_reset_hc了.而在uhci_reset_hc中会有代码把UHCI的中断寄存器彻底清零,或者说彻底disable.而在这种情况下,finish_reset会被调用,这样才会有设置rh_state为UHCI_RH_RESET,也只有设置了UHCI_RH_RESET,下面的两个函数才会被调用.它们是:
807行,usb_root_hub_lost_power则来自drivers/usb/core/hub.c:
1095 /**
1096 * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
1097 * @rhdev: struct usb_device for the root hub
1098 *
1099 * The USB host controller driver calls this function when its root hub
1100 * is resumed and Vbus power has been interrupted or the controller
1101 * has been reset. The routine marks all the children of the root hub
1102 * as NOTATTACHED and marks logical connect-change events on their ports.
1103 */
1104 void usb_root_hub_lost_power(struct usb_device *rhdev)
1105 {
1106 struct usb_hub *hub;
1107 int port1;
1108 unsigned long flags;
1109
1110 dev_warn(&rhdev->dev, "root hub lost power or was reset/n");
1111
1112 /* Make sure no potential wakeup events get lost,
1113 * by forcing the root hub to be resumed.
1114 */
1115 rhdev->dev.power.prev_state.event = PM_EVENT_ON;
1116
1117 spin_lock_irqsave(&device_state_lock, flags);
1118 hub = hdev_to_hub(rhdev);
1119 for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
1120 if (rhdev->children[port1 - 1]) {
1121 recursively_mark_NOTATTACHED(
1122 rhdev->children[port1 - 1]);
1123 set_bit(port1, hub->change_bits);
1124 }
1125 }
1126 spin_unlock_irqrestore(&device_state_lock, flags);
1127 }
1099至1102这几行注释就好比这段代码的段落大意.这个函数要做什么全在这段注释里说的清清楚楚了,我想我就没有必要多解释了,相信看了这段注释之后,即使是那几位在天安门广场自焚的哥们儿也能看懂这段代码了.
回到uhci_resume()函数,另一个函数,suspend_rh()再一次被调用.实际上在咱们整个故事中,suspend_rh被调用的地方只有三处,前两处咱们都已经看到过了,而这里就是第三处.这个函数比较重要的是第二个参数,前两次我们传递的分别是UHCI_RH_AUTO_STOPPED和UHCI_RH_SUSPENDED,而这一次我们传递的又是UHCI_RH_SUSPENDED.但是和之前那次执行suspend_rh并传递第二个参数为UHCI_RH_SUSPENDED的情形不同,此时此刻,rh->state是UHCI_RH_RESET.考虑到这个函数也不是很长,我们不妨再次贴出来.
259 static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state)
260 __releases(uhci->lock)
261 __acquires(uhci->lock)
262 {
263 int auto_stop;
264 int int_enable, egsm_enable;
265
266 auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
267 dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
268 "%s%s/n", __FUNCTION__,
269 (auto_stop ? " (auto-stop)" : ""));
270
271 /* If we get a suspend request when we're already auto-stopped
272 * then there's nothing to do.
273 */
274 if (uhci->rh_state == UHCI_RH_AUTO_STOPPED) {
275 uhci->rh_state = new_state;
276 return;
277 }
278
279 /* Enable resume-detect interrupts if they work.
280 * Then enter Global Suspend mode if _it_ works, still configured.
281 */
282 egsm_enable = USBCMD_EGSM;
283 uhci->working_RD = 1;
284 int_enable = USBINTR_RESUME;
285 if (remote_wakeup_is_broken(uhci))
286 egsm_enable = 0;
287 if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable ||
288 !device_may_wakeup(
289 &uhci_to_hcd(uhci)->self.root_hub->dev))
290 uhci->working_RD = int_enable = 0;
291
292 outw(int_enable, uhci->io_addr + USBINTR);
293 outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD);
294 mb();
295 udelay(5);
296
297 /* If we're auto-stopping then no devices have been attached
298 * for a while, so there shouldn't be any active URBs and the
299 * controller should stop after a few microseconds. Otherwise
300 * we will give the controller one frame to stop.
301 */
302 if (!auto_stop && !(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) {
303 uhci->rh_state = UHCI_RH_SUSPENDING;
304 spin_unlock_irq(&uhci->lock);
305 msleep(1);
306 spin_lock_irq(&uhci->lock);
307 if (uhci->dead)
308 return;
309 }
310 if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH))
311 dev_warn(&uhci_to_hcd(uhci)->self.root_hub->dev,
312 "Controller not stopped yet!/n");
313
314 uhci_get_current_frame_number(uhci);
315
316 uhci->rh_state = new_state;
317 uhci->is_stopped = UHCI_IS_STOPPED;
318 uhci_to_hcd(uhci)->poll_rh = !int_enable;
319
320 uhci_scan_schedule(uhci);
321 uhci_fsbr_off(uhci);
322 }
你可以重复初恋,却不能重复热情.你可以重复后悔,却重复不了最爱.你可以重复调用这个函数,却重复不了其上下文.很显然,这一次282行以下的代码会被执行.而这样做的意义就是让HC在从reset中醒来之后直接进入挂起状态.除此之外我们需要注意的是uhci->working_RD只是在这个函数中被设置过,而且正常来说应该设置的是1.这个flag被设置为1就是告诉全世界的劳动人民这个Root Hub不需要被轮询.
当然你可能也注意到290行,uhci->working_RD又被设置为了0.不过你放心,这行代码被执行是小概率事件,实际上中日甲午战争那会儿,内核代码中是没有一个叫做egsm_enable的变量的, remote_wakeup_is_broken()这个函数以前也没有,只是后来在去年春天,人们遇到了一个Bug,在华硕的A7V8X主板上,如果有设备插在usb端口上,并且uhci-hcd模块加载了,则系统没有办法STR(Suspend-to-RAM).从当时的日志文件来分析,这基本上是BIOS固件或者主板电路有问题,也就是说这本是华硕的错,然而,人生就是这样一条不归路,走上去,就回不了头,过了就过了,成了就成了,做了就做了,错了已经错了.写代码的人任劳任怨,最终在2006年10月,为了纪念十七大倒计时一周年,由Alan Stern大侠提交了一个patch,其中就包括上面说的这个变量和这个函数,即在遇到华硕的这块变态的主板的时候,设置egsm_enable为0,即disable EGSM.而正常情况下它将保持1.而resume_detect_interrupts_are_broken()则是因为另外一些硬件Bug,正常情况下它会毫不犹豫的返回0.所以,290行实际上对大多数人来说是不会执行的.换言之,working_RD基本上可以认定就是1,而int_enable也可以认定是USBINTR_RESUME.后者意味着虽然咱们挂起了主机控制器,但是打开了Resume的中断,这样有设备插入的话就会唤醒主机控制器.(友情提示一下,如何看自己的机器是不是这款主板?用dmidecode命令.)
因此回到uhci_resume ()中,813行这个if条件八成是不会被满足的.因此就不轮询.即poll_rh也不设置,usb_hcd_poll_rh_status也不被调用.用一首诗来描绘这段代码:
一番轮询端口凉,春花落尽菊花香;莫笑轮询误岁月,人生何事不空忙!
这样uhci_resume也就结束了.于是乎,usb_hcd_pci_resume也结束了.
最后补充一点,如果不轮询那么如何了解端口的状态呢?别忘了当初我们讲过的那个uhci_irq,你回去看看这个函数的421和422行.不就是说探测到USBSTS_RD的中断的时候,调用usb_hcd_poll_rh_status么?总之最终还是要通过usb_hcd_poll_rh_status这个函数才能摆平.而对于像华硕的某款垃圾主板所具有的这个问题,咱就只能轮询了,因为中断关掉了.
不过最终想说的是,这代码毕竟是那些资本主义国家的人写的,倘若由我们80后来写,首先我就会把suspend_rh中285行至290行给删掉,然后把uhci_resume中813行至817行删掉.然后把那个working_RD这个变量也给删掉.我们80后童年的梦在现实面前破碎了都没人来修补,凭什么给你们那些破厂商来修补硬件bug啊?当我们这一代人辛苦劳累面对的是动辄上万元的商品房价格,当医疗,教育,住房福利对我们这一代人来说,只是很久很久以前的传说,或者是罪恶的资本主义的表现,我们又哪里还有心情去像那些资本主义国家的人一样不断完善这代码呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: