转自:http://blog.csdn.net/aaronychen/article/details/3928479

LinuxUSB suspend/resume源码分析

Author:aaron

 

本文主要从自己开发的一个USB驱动的例子来深入讲解linux内核是如何支持USB设备的休眠和唤醒的,

最近我在为我们公司的一个模块写linux下的驱动, 其中之一就是要支持USB的休眠唤醒问题, 实际上linux内核对USB的这个功能的支持还是比较新的, 也就是最近几年的事.

一  打开/关闭USB suspend/resuem功能

要让linux支持usb suspend/resuem, 当然先要把内核中这个功能的代码编译进去咯, 即要在make menuconfig时打开对这项功能的支持.

第一个打开的项是CONFIG_PM, 即整个系统的电源管理, USB suspend/resuem只是整个电源管理的一个自系统. 只有打开了这个功能才能让USB的这个特性能用.

第二个要打开的当让是USB自己的开关了CONFIG_USB_SUSPEND. 即打开了这个功能后我们只要在我们自己的驱动里简单调用下USB core提供的函数接口就能使我们的设备休眠了.

二 源码分析

在2.6.19之前的代码中不支持USB自动休眠的功能, 它只能是在host休眠情况下才会让USB设备也休眠. 所以如果我们要让自己的设备在不使用的情况下就休眠就得自己添加相应的代码, 幸运的是我们不需要添加复杂的代码就能达到这个目的, 因为USB core里提供了几个接口可以直接让我们的驱动调用以把我们的设备置入休眠状态.

下面我们以2.6.16的代码为例来分析下USB设备是如何进入休眠的.

Drivers/usr/core/hub.c:

int usb_suspend_device(struct usb_device *udev)

{

#ifdef      CONFIG_USB_SUSPEND

if (udev->state == USB_STATE_NOTATTACHED)

return -ENODEV;

return __usb_suspend_device(udev, udev->portnum);

#else

/* NOTE:  udev->state unchanged, it's not lying ... */

udev->dev.power.power_state = PMSG_SUSPEND;

return 0;

#endif

}

没错, 在我们的驱动里只要在适当的地方调用这个函数就可以使我们的设备休眠了. 但是需要注意的是,  内核没有EXPORT这个函数, 因此如果我们的驱动要编译成模块的话, 我们只有修改内核以EXPORT这个函数了.

实际上真正干正事的函数是__usb_suspend_device

Drivers/usr/core/hub.c:

static int __usb_suspend_device (struct usb_device *udev, int port1)

{

int   status = 0;

/* caller owns the udev device lock */

if (port1 < 0)

return port1;

/*如果设备已休眠或还没attach上则直接返回*/

if (udev->state == USB_STATE_SUSPENDED

|| udev->state == USB_STATE_NOTATTACHED) {

return 0;

}

/* all interfaces must already be suspended */

/*要休眠设备, 首先必须要设备下的每个interface都可以休眠才行*/

if (udev->actconfig) {

int   i;

for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {

struct usb_interface       *intf;

intf = udev->actconfig->interface[i];

if (is_active(intf)) {   /*如果某个interface处于活动状态则不能休眠*/

dev_dbg(&intf->dev, "nyet suspended/n");

return -EBUSY;

}

}

}

/* we only change a device's upstream USB link.

* root hubs have no upstream USB link.

*/

/*干正事的一个函数*/

if (udev->parent)

status = hub_port_suspend(hdev_to_hub(udev->parent), port1,

udev);

if (status == 0)

udev->dev.power.power_state = PMSG_SUSPEND;   /*保存设备状态*/

return status;

}

第一个参数是要休眠的USB设备, 第二个参数是该USB设备所连接到的hub的某个端口.

从这个函数我们可以大概猜测到, 要一个设备休眠原理就是要把这个设备attach到的那个端口休眠掉.

没错USB spec规定了只要把设备所attach上的那个端口disable掉, 那么这条路径上就没有任何传输了, 在过了一段时间后设备端应该会产生一个suspend的中断, 以让设备进入休眠状态.

Drivers/usr/core/hub.c:

static int hub_port_suspend(struct usb_hub *hub, int port1,

struct usb_device *udev)

{

int   status;

// dev_dbg(hub->intfdev, "suspend port %d/n", port1);

/* enable remote wakeup when appropriate; this lets the device

* wake up the upstream hub (including maybe the root hub).

*

* NOTE:  OTG devices may issue remote wakeup (or SRP) even when

* we don't explicitly enable it here.

*/

/*如果设备支持远程唤醒功能, 则打开此功能*/

if (device_may_wakeup(&udev->dev)) {

status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),

USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,

USB_DEVICE_REMOTE_WAKEUP, 0,

NULL, 0,

USB_CTRL_SET_TIMEOUT);

if (status)

dev_dbg(&udev->dev,

"won't remote wakeup, status %d/n",

status);

}

/* see 7.1.7.6 */

/*^_^看usb spec 7.1.7.6吧, 这个命令就是把hub的这个port disable掉, 这样就达到休眠的目的了*/

status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);

if (status) {

dev_dbg(hub->intfdev,

"can't suspend port %d, status %d/n",

port1, status);

/* paranoia:  "should not happen" */

(void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),

USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,

USB_DEVICE_REMOTE_WAKEUP, 0,

NULL, 0,

USB_CTRL_SET_TIMEOUT);

} else {

/* device has up to 10 msec to fully suspend */

dev_dbg(&udev->dev, "usb suspend/n");

usb_set_device_state(udev, USB_STATE_SUSPENDED);  /*设置设备状态*/

msleep(10);

}

return status;

}

OK, 很简单把usb设备attach上的那个port disable掉, 就可以达到休眠的目的了(当然实际上它只是把这个port所在的那条链路上的传输停掉, 至于设备是否真会休眠要考设备本省的, 正常情况下, 设备硬件会检测总线上是否有传输, 如没有则把设备转入suspend状态).

OK, 理解了USB设备如何休眠, 那么对于USB设备如何唤醒就很清楚了, 就是重新enable那个port就行了. 这里就不在分析了.

因此假如说我们的设备要在打开之后禁止休眠, 在关闭之后才允许休眠, 该怎么做呢? 呵呵,只要在驱动的open函数里resume设备, 在close函数里suspend设备就行了.

至于2.6.19以后的版本, 加入了自动休眠的功能, 具体实现原理可以参考由fudan_abc写的<<linux那些事儿之HUB>>

Linux下USB suspend/resume源码分析【转】的更多相关文章

  1. linux下MySQL 5.6源码安装

    linux下MySQL 5.6源码安装 1.下载:当前mysql版本到了5.6.20 http://dev.mysql.com/downloads/mysql 选择Source Code 2.必要软件 ...

  2. linux下hadoop2.6.1源码64位的编译

    linux下hadoop2.6.1源码64位的编译 一. 前言 Apache官网上提供的hadoop本地库是32位的,如果我们的Linux服务器是64位的话,就会现问题.我们在64位服务器执行Hado ...

  3. Linux内核2.6.14源码分析-双向循环链表代码分析(巨详细)

    Linux内核源码分析-链表代码分析 分析人:余旭 分析时间:2005年11月17日星期四 11:40:10 AM 雨 温度:10-11度 编号:1-4 类别:准备工作 Email:yuxu97101 ...

  4. 如何找到Linux下常用命令的源码

    Linux系统,常用命令的来源很多,有些命令是shell自带的,比如cd,通过执行help命令,可以查看当前系统所有的内置命令. 用type <cmd_name>来查看一个命令是否为内置命 ...

  5. linux下PostgreSQL数据库的源码安装

    实验环境>>>>>>>>>>>>>>>>>>操作系统:CentOS release 6.3 ...

  6. 【SystemTap】 Linux下安装使用SystemTap源码安装SystemTap

    转自 http://blog.csdn.net/zklth/article/details/6248558 文章 http://blog.csdn.net/zklth/archive/2010/09/ ...

  7. linux信号的处理--部分源码分析

    基于linux master v4.9版本 信号是异步的, 一.信号何时来 信号是异步的,对于一个进程随时都会接收到信号. 二.选择线程(task)来处理 那么一个进程接收到信号时,需要选择一个tas ...

  8. linux下cmake安装mysql 源码

    1.假设已经有mysql-5.6.21.tar.gz以及cmake-2.8.4.tar.gz两个源文件 (1)先安装cmake(mysql5.5以后是通过cmake来编译的) [root@ rhel5 ...

  9. angular源码分析:angular中脏活累活的承担者之$interpolate

    一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...

随机推荐

  1. LeetCode 633. 平方数之和

    题目: 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c.     示例1: 输入: 5 输出: True 解释: 1 * 1 + 2 * 2 = 5 示例2 ...

  2. 基于SSH框架的网上书店系统开发的质量属性

    基于SSH框架的网上书店系统开发的质量属性 对于我的基于SSH框架的网上书店系统的开发要实现的质量属性有可用性.可修改性.性能.安全性.易用性和可测试性. 1.对于可用性方面的战术: 可用性(Avai ...

  3. [JSP] c:forEach 如何输出序号

    关键在于<c:forEach>的varStatus属性,具体代码如下: <table width="500" border="0" cells ...

  4. Sprint最后一天

    界面流程:  数据库里的信息: 还存在的问题: 1:选择包车城市时:下面的界面没对应到包车城市类型 2:看不到个人订票信息

  5. FZU-SE-K 第一次累计得分排行榜

    FZU-SE-K 第一次累计得分排行榜 包含第一.二.三次作业 排行 恭喜 248 文航 同学获得本期小黄衫 原图戳 这里 明细 1 - 第一次作业映射分数 2 - 第二次作业映射分数 3 - 第三次 ...

  6. NServiceBus官方文档翻译(二)NServiceBus 入门

    在这篇教程中我们将学习如何创建一个非常简单的由客户端向服务端发送消息的订单系统.该系统包括三个项目:Client.Server 和 Messages,我们将按照以下步骤来完成这个任务. 创建 Clie ...

  7. [转帖]将改名贯彻到底,Xeon E3系列将改名为Xeon E

    将改名贯彻到底,Xeon E3系列将改名为Xeon E 导读:    E3 系列改名为 XEON E 系列entry 入门级的含义 E5 1XXX 序列改名为 XEON W 系列 workstatio ...

  8. OneZero第四周——预完成功能点统计

    本周OneZero将完成“统计”功能. 功能点统计如下: 1.主页单击记录,进入修改界面,修改记录. 2.主页长按记录,出现删除按钮,删除记录. 3.全部记录按分类进行饼图显示. 4.全部记录按分类进 ...

  9. 一本通1645Fibonacci

    1645:Fibonacci 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:POJ 3070 我们知道斐波那契数列 F0=0,F1=1,Fn=Fn ...

  10. BZOJ3193 [JLOI2013]地形生成 【dp】

    题目链接 BZOJ3193 题解 注意\(key\)是小于 第一问,显然按高度降序排序,逐个插入 如果高度各不相同,那么之前插入的都比当前插入的\(i\)大,可插入的位置个数就确定了 由于存在高度相同 ...