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

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

沿着上面的线索我们继续玩.现在我们设置断点wakeup_rh.然后我们插入U盘.不出所料,我们又一次进入了kdb.用bt命令看一下调用堆栈,发现调用wakeup_rh的uhci_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->state为HC_STATE_RESUMING之外,就是调用hcd driver的bus_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_state是UHCI_RH_SUSPENDED,所以353行这个if条件是满足的.于是354行设置uhci->rh_state为UHCI_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.因为我们知道中断寄存器就是一个16个bits的寄存器,而其bit4到bit15是保留位,而bit0到bit3则对应咱们这里这四个flag.

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

敏而好学,不耻下问。

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

相关文章:

你感兴趣的文章:

标签云: