`
dato0123
  • 浏览: 913665 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

接下来剩下两个重要的函数,uhci_suspenduhci_resume,不过孤立的看这两个函数没有意义,得结合上下文来看,调用它们的分别是usb_hcd_pci_suspendusb_hcd_pci_resume,所以我们从这两个函数看起.当然单纯的看这些函数也是没有意义的,这个世界上像灰尘一样多的,除了美女,还有Linux内核中的函数;这个世界上像细菌一样多的,除了帅哥,还有Linux内核中的函数.所以我反复强调,重要的不是我们看完了一个两个函数本身,而是去深刻理解隐藏在代码背后的哲学思想!

因此,我们还是通过实验来探索这些代码.首先进入kdbbp命令设置一下断点,包括usb_hcd_pci_suspendusb_hcd_pci_resume.然后退出来在Shell下面执行下面两条命令:

# echo test > /sys/power/disk

# echo disk > /sys/power/state

这样两条命令这么一执行,各个suspend函数,resume函数会依次被执行一次.应该说这两条命令是对设备驱动电源管理部分代码的最简便的测试.当然,你别以为这是哥们儿我发明的,虽然哥们儿一直觉得自己是风一样的男子,但是实事求是的说,我还没有帅到那种一树梨花压海棠的程度.事实上在Documentation/power/目录下面有很多关于电源管理的知识的介绍,而在basic_pm_debugging.txt这个文件中就有关于电源管理的基本测试方法介绍,以上这两条命令就是来自于这个文件.

如果你按我说的那样去做了,那么我们会发现因为usb_hcd_pci_suspend被调用而触发了kdb.下面具体来看usb_hcd_pci_suspend,来自drivers/usb/core/hcd-pci.c:

187 /**

188 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD

189 * @dev: USB Host Controller being suspended

190 * @message: semantics in flux

191 *

192 * Store this function in the HCD's struct pci_driver as suspend().

193 */

194 int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)

195 {

196 struct usb_hcd *hcd;

197 int retval = 0;

198 int has_pci_pm;

199

200 hcd = pci_get_drvdata(dev);

201

202 /* Root hub suspend should have stopped all downstream traffic,

203 * and all bus master traffic. And done so for both the interface

204 * and the stub usb_device (which we check here). But maybe it

205 * didn't; writing sysfs power/state files ignores such rules...

206 *

207 * We must ignore the FREEZE vs SUSPEND distinction here, because

208 * otherwise the swsusp will save (and restore) garbage state.

209 */

210 if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON)

211 return -EBUSY;

212

213 if (hcd->driver->suspend) {

214 retval = hcd->driver->suspend(hcd, message);

215 suspend_report_result(hcd->driver->suspend, retval);

216 if (retval)

217 goto done;

218 }

219 synchronize_irq(dev->irq);

220

221 /* FIXME until the generic PM interfaces change a lot more, this

222 * can't use PCI D1 and D2 states. For example, the confusion

223 * between messages and states will need to vanish, and messages

224 * will need to provide a target system state again.

225 *

226 * It'll be important to learn characteristics of the target state,

227 * especially on embedded hardware where the HCD will often be in

228 * charge of an external VBUS power supply and one or more clocks.

229 * Some target system states will leave them active; others won't.

230 * (With PCI, that's often handled by platform BIOS code.)

231 */

232

233 /* even when the PCI layer rejects some of the PCI calls

234 * below, HCs can try global suspend and reduce DMA traffic.

235 * PM-sensitive HCDs may already have done this.

236 */

237 has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);

238

239 /* Downstream ports from this root hub should already be quiesced, so

240 * there will be no DMA activity. Now we can shut down the upstream

241 * link (except maybe for PME# resume signaling) and enter some PCI

242 * low power state, if the hardware allows.

243 */

244 if (hcd->state == HC_STATE_SUSPENDED) {

245

246 /* no DMA or IRQs except when HC is active */

247 if (dev->current_state == PCI_D0) {

248 pci_save_state (dev);

249 pci_disable_device (dev);

250 }

251

252 if (!has_pci_pm) {

253 dev_dbg (hcd->self.controller, "--> PCI D0/legacy/n");

254 goto done;

255 }

256

257 /* NOTE: dev->current_state becomes nonzero only here, and

258 * only for devices that support PCI PM. Also, exiting

259 * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset

260 * some device state (e.g. as part of clock reinit).

261 */

262 retval = pci_set_power_state (dev, PCI_D3hot);

263 suspend_report_result(pci_set_power_state, retval);

264 if (retval == 0) {

265 int wake = device_can_wakeup(&hcd->self.root_hub->dev);

266

267 wake = wake && device_may_wakeup(hcd->self.controller);

268

269 dev_dbg (hcd->self.controller, "--> PCI D3%s/n",

270 wake ? "/wakeup" : "");

271

272 /* Ignore these return values. We rely on pci code to

273 * reject requests the hardware can't implement, rather

274 * than coding the same thing.

275 */

276 (void) pci_enable_wake (dev, PCI_D3hot, wake);

277 (void) pci_enable_wake (dev, PCI_D3cold, wake);

278 } else {

279 dev_dbg (&dev->dev, "PCI D3 suspend fail, %d/n",

280 retval);

281 (void) usb_hcd_pci_resume (dev);

282 }

283

284 } else if (hcd->state != HC_STATE_HALT) {

285 dev_dbg (hcd->self.controller, "hcd state %d; not suspended/n",

286 hcd->state);

287 WARN_ON(1);

288 retval = -EINVAL;

289 }

290

291 done:

292 if (retval == 0) {

293 dev->dev.power.power_state = PMSG_SUSPEND;

294

295 #ifdef CONFIG_PPC_PMAC

296 /* Disable ASIC clocks for USB */

297 if (machine_is(powermac)) {

298 struct device_node *of_node;

299

300 of_node = pci_device_to_OF_node (dev);

301 if (of_node)

302 pmac_call_feature(PMAC_FTR_USB_ENABLE,

303 of_node, 0, 0);

304 }

305 #endif

306 }

307

308 return retval;

309 }

鱼哭了,水知道,看代码的我哭了,谁知道?这时候鼓励我的是鲁迅先生,他说:”真的男人,敢于直面惨淡的代码,敢于正视无耻的函数.”

首先调用driver->suspend,对于uhci来说,就是uhci_suspend.来自drivers/usb/host/uhci-hcd.c:

742 static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)

743 {

744 struct uhci_hcd *uhci = hcd_to_uhci(hcd);

745 int rc = 0;

746

747 dev_dbg(uhci_dev(uhci), "%s/n", __FUNCTION__);

748

749 spin_lock_irq(&uhci->lock);

750 if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)

751 goto done_okay; /* Already suspended or dead */

752

753 if (uhci->rh_state > UHCI_RH_SUSPENDED) {

754 dev_warn(uhci_dev(uhci), "Root hub isn't suspended!/n");

755 rc = -EBUSY;

756 goto done;

757 };

758

759 /* All PCI host controllers are required to disable IRQ generation

760 * at the source, so we must turn off PIRQ.

761 */

762 pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);

763 mb();

764 hcd->poll_rh = 0;

765

766 /* FIXME: Enable non-PME# remote wakeup? */

767

768 /* make sure snapshot being resumed re-enumerates everything */

769 if (message.event == PM_EVENT_PRETHAW)

770 uhci_hc_died(uhci);

771

772 done_okay:

773 clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);

774 done:

775 spin_unlock_irq(&uhci->lock);

776 return rc;

777 }

其实这个函数和后面我们将要遇到的那个和它遥相呼应的uhci_resume一样,不做什么正经事.这里最重要的就是773,HCD_FLAG_HW_ACCESSIBLE这个flag给清掉,即挂起阶段不允许访问.

另一个,前面764,poll_rh也给设置为0.

以及在上面一点,把寄存器USBLEGSUP0.

回到usb_hcd_pci_suspend.suspend_report_resultPM core那边提供的一个汇报电源管理操作的结果的一个函数.

219, synchronize_irq,这个函数会等待,直到对应的IRQ handlers执行完,即它就像自旋锁,不停的转不停的转,最终的结果就是过了这里之后中断服务例程不会被执行.

237,pci_find_capability(),来自pci世界的函数.不是所有的人都拥有孤独,不是所有的青春都是瑰丽无比,不是所有的开始都有美丽的结局,不是所有的憧憬都有美丽的旅程,不是所有的忧伤都有无心的伤害,不是所有的沉默都有宁静的心,不是所有的PCI设备都具有电源管理的能力.所以只有按照237行这样调用pci_find_capability才能确定这个设备是否具有电源管理的能力.has_pci_pm这个变量的含义很直白,是就是,否就否.

接下来的代码就涉及到传说中的D0,D1,D2,D3的概念了.除了标准的PCI Spec以外,这个世界上还有一个叫做PCI PM Spec的家伙,这玩意儿就是专门定义一些PCI电源管理方面的规范.PCI PM Spec定义了PCI设备一共可以有四种电源状态,D0,D1,D2,D3,这个D就表示Device,因为与D相对的有一个叫做B,B表示总线,Bus,PCI PM Spec还为PCI总线也定义了四种电源状态,B0,B1,B2,B3.当然,关于PCI总线的电源状态不是我们此刻应该关心的,我们现在需要关心的是D字头的这四种状态,,确切的说是五种状态,因为D3又被分为D3HotD3Cold.这又是怎么说呢?

事实上,D0耗电最多,它代表着设备正常工作的状态,所有的PCI设备在被使用之前都必须先被设置为D0状态.D3耗电最少,D2D1耗电少,D3耗电多,D1D0耗电少,D2耗电多.实际上很多人关心的就只是D3D0.因为PCI PM Spec规定每个PCI设备如果支持电源管理那么它至少要实现D3D0.D1,D2是可选的,硬件设计者心情好就实现,心情不好你就不实现,没有人指责你.

这里我们说D3耗电最少,而从别的状态进入D3状态有两种可能,一种是通过软件来实现,比如写寄存器,一种是通过物理上断电,这种区别我想就是我远在湖南老家的祖母也知道,因为就相当于我们电视机的两种关机,一种是通过遥控器按一下,一种是通过按电视机正前方的电源开关.于是为了区分这两种情况,定义Spec的同志们就把它们分别称为D3hotD3cold.从术语上来讲,D3hotD3cold的区别就在于有没有Vcc.D3hot转入到D0可以通过写PMCSR寄存器,D3cold转入到D0可以通过加上Vcc和使RST#信号有效(assert).而为了区分这两种D0,又把从D3cold转过来的D0称之为uninitialized D0 state,即未初始化的D0,当设备在power on reset之后,也是处于这种未初始化的D0状态.而经历了软件初始化之后的D0状态被称之为D0 active state,D0活跃状态.注意,设备在每次reset之后都是进入uninitialized D0 state,每次从D3cold返回到D0也是进入这种状态,这种状态就必须重新做初始化,而每次从D3hot返回到D0都是进入的D0 active state,这种状态当然就不用再次初始化了.

D1状态属于轻微睡眠状态.当一个PCI设备处于这种状态的时候,软件可以访问它的配置空间,但是不可以访问它内存空间,I/O空间.

D2状态就是比D1省更多的电.当然它的恢复时间也更长,即从D2恢复到D0 active至少需要200微秒.而从D3D0的转变则至少需要10毫秒.

drivers/usb/core/hcd.h中定义了以下这样一些宏,

104 # define __ACTIVE 0x01

105 # define __SUSPEND 0x04

106 # define __TRANSIENT 0x80

107

108 # define HC_STATE_HALT 0

109 # define HC_STATE_RUNNING (__ACTIVE)

110 # define HC_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)

111 # define HC_STATE_RESUMING (__SUSPEND|__TRANSIENT)

112 # define HC_STATE_SUSPENDED (__SUSPEND)

113

114 #define HC_IS_RUNNING(state) ((state) & __ACTIVE)

115 #define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)

这几个宏的意思都是我们从字面上就能看出来的.这里我们在244行看到了HC_STATE_SUSPENDED,hcd->state什么时候会等于它?事实上在我们之前看到过的函数中,configure_hc()中把hcd->state设置为了HC_STATE_SUSPENDED,而之后我们在start_rh()中又把它设置为了HC_STATE_RUNNING,也就是说,正常工作的时候,hcd->state肯定是HC_STATE_RUNNING,但是在另一个地方会把hcd->state设置为HC_STATE_SUSPENDED,它就是hcd_bus_suspend,而这个函数咱们前面已经说过,hub_suspend会负责调用它.于是也就是说,hub_suspend调用过hcd_bus_suspendhcd->state设置为了HC_STATE_SUSPENDED之后,这里244if条件满足,然后判断dev->current_state,我们说了,正常工作的话,PCI设备确实应该处于D0状态,即这里的PCI_D0.

至于这里为何连续调用pci_save_state,pci_disable_device,pci_set_power_state,pci_enable_wake这四大函数,请你参考Documentation/power/pci.txt文件.里面说了PCI设备驱动应该为如何编写suspend/resume函数.看了那篇文章我们同时就知道为何在usb_hcd_pci_resume函数中我们接连调用pci_enable_wake,pci_enable_device,pci_set_master,pci_restore_state这四大函数.

当然,作为一个有责任心的男人,我不可能把自己该讲的东西推卸给别人.至少我应该多少说两句.

248,pci_save_state,保存设备在挂起之前PCI的配置空间,或者更为准确的说,pci配置空间中的前64bytes保存起来.

249,pci_disable_device,I/O,bus mastering,irq能关掉的全部给老子关掉.其实这就是PCI设备电源管理的基本要求.,挂起一个PCI设备最起码的要求就是你别跟我执行DMA,别发中断了,任何的唤醒事件通过PME#信号来发起,当然再加上保存状态以备后来恢复之用.

262,我们说过,软件上的挂起一定是D3hot,而不是D3cold,所以这里就设置为PCI_D3hot.

276行和277,先后调用,pci_enable_wake()函数,这个函数的第二个参数表示一种电源状态,咱们看到传递的一次是PCI_D3hot,一次是PCI_D3cold,这就是使得设备可以从这两种状态中产生PME#信号.(PME#就是Power Management Event Signal,即电源管理事件信号.)PME#信号是PCI Power Spec中出镜率最高的一个名词.如果一个设备希望改变它的电源状态,它就可以发送一个PME#信号.而设备是否允许发送信号也是有开关的,并且每种状态都有一个开关.所以这里的做法就是为D3hotD3cold打开开关.而这里pci_enable_wake的第三个参数是表示开还是关.即传递1进去就是enable,传递0进去就是disable.而咱们这里的wake是通过265行和267行这两个判断得到的,即能不能唤醒和愿不愿意唤醒.

278,如果挂起不成功,就执行usb_hcd_pci_resume重新恢复.这种效果就像避孕一样,不成功,则成人.

293,如果挂起成功,就设置power_statePMSG_SUSPEND.

295行至305,看到这个CONFIG_PPC_PMAC之后全国人民都激动了,这种激动之情不亚于1999年那次建国50周年的大阅兵.

于是usb_hcd_pci_suspend就结束了,下面我们来看usb_hcd_pci_resume.
分享到:
评论

相关推荐

    Linux那些事儿之我是UHCI

    写一下UHCI吧,也顺便怀念一下Intel,以及Intel的那几个女同事们,好久没联系了,你们可好? UHCI是 Intel提出来的.虽然离开 Intel一年多了,但我总觉得也许有一天我还会回到 Intel.所 以关于 Intel的东西,我多少会关注...

    Linux那些事儿

    Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 Linux那些事儿之我是Sysfs 今天本人将9个单独的文档整理出来,做成了一...

    Linux那些事儿1-9合集

    由复旦fudan_abc写的,风趣的文笔,深入浅...linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 linux那些事儿之我是Sysfs

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux那些事儿(EHCI Block SCSI Sysfs PCI USB U 盘 UHCI Hub)

    Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Block层.pdf Linux那些事儿之我是SCSI硬盘.pdf Linux那些事儿之我是Sysfs.pdf Linux那些事儿之我是PCI.pdf ...Linux那些事儿之我是UHCI.pdf

    linux的那些事儿全集

    Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI ...Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘

    linux那些事全集

    Linux那些事儿之我是UHCI Linux那些事儿之我是Sysfs Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是PCI Linux那些事儿之我是Hub Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Block层

    Linux那些事儿.rar

    Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是UHCI,...

    Linux那些事儿系列

    本人整理的fudan_abc的专栏中以完结的文章,在此向原作者表示感谢,给...内容包括:linux那些事儿之我是U盘,linux那些事儿之我是USB,linux那些事儿之我是HUB,linux那些事儿之我是UHCI,linux那些事儿之我是Sysfs。

    Linux那些事儿之我是XXX全集.rar

    Linux那些事儿之我是XXX全集 包含USB core U盘 UHCI PCI SCSI硬盘 Block Hub EHCI 。 想学驱动的童鞋,不妨看看。该书主要是进行源代码的分析

    Linux那些事儿 系列之2 Block+EHCI+PCI+SCSI

    Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是PCI.pdf Linux那些事儿之我是SCSI硬盘.pdf 注: 之前有人已经上传了《Linux那些事儿 系列》,其已经包含了:hub,sysfs...

    linux那些事儿之我是USB.zip

    里面包含Linux那些事的九个文档,Block层,ECHI主机控制,HUB,PCI,SCSI硬盘,Sysfs,UHCI,USB+core,U盘等九个文档,内容详细,而且全面都有书签,适合系统学习!

    Linux那些事系列

    Linux那些事儿之我是Hub Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘 Linux那些事之我是HUB1

    微信生活缴费商业项目标准版

    Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是UHCI,...

    usb那些事的全集

    usb那些事的全集,包含: 1 Linux那些事儿之我是Block层 2 Linux那些事儿之我是EHCI主机控制器 3 Linux那些事儿之我是HUB ...7 Linux那些事儿之我是UHCI 8 Linux那些事儿之我是USB+core 9 Linux那些事儿之我是U盘

    Linux那些事儿[完整版]自己整理

    我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内核 戏说USB 说的是2.6.22的内核 我是Hub/UHCI/EHCI 说的是2.6.22.1的内核

    LINUX那些事儿 linux经典之作

    我是Hub/UHCI/EHCI 说的是2.6.22.1的内核 其中我是U盘属于基础性的.这一阶段会遇到一些问题.比如urb提交之后究竟怎么处理的?用户空间究竟是如何访问U盘的?DMA究竟怎么回事. 这之后可以开始看Hub.这一阶段你会明白一...

    linux那些事儿.rar

    linux那些事儿,Block层,EHCI主机控制器,Hub,PCI,SCSI硬盘,Sysfs,UHCI,USB core,U盘等文档。

    Linux那些事儿(包括Hub, Sysfs, UHCI, USB, U盘5个部分)

    一个很经典的东东,里面的很多东西都很有启发性,值得看看

Global site tag (gtag.js) - Google Analytics