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

看了suspend自然就要看resume,在电源管理的世界里,挂起和唤醒是永远被相提并论的一对,它们就像天上的雪花,本来互不相识,一旦落在地上,化成水,结成冰,便再也分不开了!

沿着上面的线索我们继续玩.现在我们设置断点wakeup_rh.然后我们插入U.不出所料,我们又一次进入了kdb.bt命令看一下调用堆栈,发现调用wakeup_rhuhci_rh_resume,调用uhci_rh_resume的是hcd_bus_resume,调用hcd_bus_resume的是hub_resume,我们还可以继续追溯下去,最终我们可以追溯到hcd_resume_work函数.不过我们还是从hub_resume开始看起,来自drivers/usb/core/hub.c:

   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 }

很显然,我们进入了1974行这个hcd_bus_resume函数,来自drivers/usb/core/hcd.c:

   1276 int hcd_bus_resume (struct usb_bus *bus)

   1277 {

   1278         struct usb_hcd          *hcd;

   1279         int                     status;

   1280

   1281         hcd = container_of (bus, struct usb_hcd, self);

   1282         if (!hcd->driver->bus_resume)

   1283                 return -ENOENT;

   1284         if (hcd->state == HC_STATE_RUNNING)

   1285                 return 0;

   1286         hcd->state = HC_STATE_RESUMING;

   1287         status = hcd->driver->bus_resume (hcd);

   1288         if (status == 0)

   1289                 hcd->state = HC_STATE_RUNNING;

   1290         else {

   1291                 dev_dbg(&bus->root_hub->dev, "%s fail, err %d/n",

   1292                                 "resume", status);

   1293                 usb_hc_died(hcd);

   1294         }

   1295         return status;

   1296 }

这个函数除了设置hcd->stateHC_STATE_RESUMING之外,就是调用hcd driverbus_resume函数,对于uhci,就是uhci_rh_resume.来自drivers/usb/host/uhci-hcd.c:

    727 static int uhci_rh_resume(struct usb_hcd *hcd)

    728 {

    729         struct uhci_hcd *uhci = hcd_to_uhci(hcd);

    730         int rc = 0;

    731

    732         spin_lock_irq(&uhci->lock);

    733         if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {

    734                 dev_warn(&hcd->self.root_hub->dev, "HC isn't running!/n");

    735                 rc = -ESHUTDOWN;

    736         } else if (!uhci->dead)

    737                 wakeup_rh(uhci);

    738         spin_unlock_irq(&uhci->lock);

    739         return rc;

    740 }

既然执行了wakeup_rh,那么说明HCD_FLAG_HW_ACCESSIBLE仍然是设置了的,同时uhci->dead也仍然是0.其实只要你不让主机控制器停下来,它就不会无缘无故的停下来.正如呼吸,也许被忽视,却永远不会停止.wakeup_rh来自drivers/usb/host/uhci-hcd.c:

    340 static void wakeup_rh(struct uhci_hcd *uhci)

    341 __releases(uhci->lock)

    342 __acquires(uhci->lock)

    343 {

    344         dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,

    345                         "%s%s/n", __FUNCTION__,

    346                         uhci->rh_state == UHCI_RH_AUTO_STOPPED ?

    347                                 " (auto-start)" : "");

    348

    349         /* If we are auto-stopped then no devices are attached so there's

    350          * no need for wakeup signals.  Otherwise we send Global Resume

    351          * for 20 ms.

    352          */

    353         if (uhci->rh_state == UHCI_RH_SUSPENDED) {

    354                 uhci->rh_state = UHCI_RH_RESUMING;

    355                 outw(USBCMD_FGR | USBCMD_EGSM | USBCMD_CF,

    356                                 uhci->io_addr + USBCMD);

    357                 spin_unlock_irq(&uhci->lock);

    358                 msleep(20);

    359                 spin_lock_irq(&uhci->lock);

    360                 if (uhci->dead)

    361                         return;

    362

    363                 /* End Global Resume and wait for EOP to be sent */

    364                 outw(USBCMD_CF, uhci->io_addr + USBCMD);

    365                 mb();

    366                 udelay(4);

    367                 if (inw(uhci->io_addr + USBCMD) & USBCMD_FGR)

    368                         dev_warn(uhci_dev(uhci), "FGR not stopped yet!/n");

    369         }

    370

    371         start_rh(uhci);

    372

    373         /* Restart root hub polling */

    374         mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);

    375 }

花开,蝉鸣,叶落,雪飘,rh也终有醒来的那一刻.

刚才咱们看到了uhci->rh_stateUHCI_RH_SUSPENDED,所以353行这个if条件是满足的.于是354行设置uhci->rh_stateUHCI_RH_RESUMING.

USBCMD_FGR这个宏对应于uhci命令寄存器中的Bit4,FGR全称是Force Global Resume,

Force Global Resume (FGR). 1=Host Controller sends the Global Resume signal on the USB. Software sets this bit to 0 after 20 ms has elapsed to stop sending the Global Resume signal. At that time all USB devices should be ready for bus activity. The Host Controller sets this bit to 1 when a resume event (connect, disconnect, or K-state) is detected while in global suspend mode. Software resets this bit to 0 to end Global Resume signaling. The 1 to 0 transition causes the port to send a low speed EOP signal. This bit will remain a 1 until the EOP has completed.

显然,resume的时候这个flag是要被设置的.然后358行按照这里说的那样去延时20毫秒.

最后364行再一次设置USBCMD_CF.

然后按照上面这段话来理解,这时候USBCMD_FGR应该被清除掉了,如果没有,就警告.

然后就可以再次调用start_rh函数了.Root Hub醒来的刹那,天已经暗淡,窗外的树木早已在冬天离去,带着黄莺优美的歌声和秋季的落英缤纷,Root Hub明白自己应该开始工作了,所以在这个函数中,uhci->rh_state会被设置为UHCI_RH_RUNNING.所以当我们跳出kdb之后再次看debugfs的输出我们会知道,这时候Root Hub的状态那一栏又显示出了running.

在这之后还调用mod_timer去激发那个轮询函数usb_hcd_poll_rh_status,日子又像往常一样的开始过着.而我们的人生又何尝不是如此呢?每个人的一生也不过是一场戏,一个圈.反反复复,生生不息,有谁能真正摆脱轮回的束缚.

最后我们跳出kdb,实际看一下debugfs的输出:

localhost:~ # cat /sys/kernel/debug/uhci/0000/:00/:1d.0

Root-hub state: running   FSBR: 0

HC status

  usbcmd    =     00c1   Maxp64 CF RS

  usbstat   =     0000

  usbint    =     000f

  usbfrnum  =   (1)728

  flbaseadd = 1cac5728

  sof       =       40

  stat1     =     0095   Enabled Connected

  stat2     =     0080

Most recent frame: 50d40 (320)   Last ISO frame: 50d40 (320)

Periodic load table

        0       0       0       0       0       0       0       0

        0       0       0       0       0       0       0       0

        0       0       0       0       0       0       0       0

        0       0       0       0       0       0       0       0

Total: 0, #INT: 0, #ISO: 0

除了注意到Root hub state的变化之外,我们还可以注意到usbcmd的变化,实际上这一行显示的就是命令寄存器的值,usbint显示的就是中断寄存器的值.至于为何现在是这个值,咱们可以在start_rh中找到答案.

    324 static void start_rh(struct uhci_hcd *uhci)

    325 {

    326         uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;

    327         uhci->is_stopped = 0;

    328

    329         /* Mark it configured and running with a 64-byte max packet.

    330          * All interrupts are enabled, even though RESUME won't do anything.

    331          */

    332         outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, uhci->io_addr + USBCMD);

    333         outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,

    334                         uhci->io_addr + USBINTR);

    335         mb();

    336         uhci->rh_state = UHCI_RH_RUNNING;

    337         uhci_to_hcd(uhci)->poll_rh = 1;

    338 }

332行写的这三个flag就是我们看到的usbcmd那一行中的Maxp64,CF,RS.

333行写的这四个flag就是我们看到的usbint那一行中的000f.因为我们知道中断寄存器就是一个16bits的寄存器,而其bit4bit15是保留位,bit0bit3则对应咱们这里这四个flag.

以上所讲的就是Root Hub的挂起和恢复.实际上这属于USB层次上的电源管理.但是要知道很多USB主机控制器本身是PCI设备,他们是连在PCI总线上的,那么从PCI的角度来说,为了实现电源管理,写代码的人还需要做哪些事情呢?欲知详情,且听下回分解.

 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值