现在的位置: 首页 > 综合 > 正文

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

2013年09月12日 ⁄ 综合 ⁄ 共 7370字 ⁄ 字号 评论关闭

这个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_hcconfigure_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 control

抱歉!评论已关闭.