转自: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. git学习心得(1)

    这次是第一次用git这个平台,还非常不熟练,尤其是在上传代码的步骤上,有好几次都出了问题,多次询问了同学才将代码上传到平台上,git是一个分布式版本控制系统,相对于集中式的来说具有很多优点,但是中文资 ...

  2. ns3的输入输出奥秘(一) LOGGING系统

    1.LOGGING系统 (1)在我们之前对C++的理解,输出好像就是cout,然而 以myfirst.cc为例子 在我们前面的编写的代码中并没有出现cout,那他是如何输出. 可以回忆一下 LogCo ...

  3. VS2013新建web项目时出错,系统找不到指定文件

    好不容易找了一个VS2013_RTM_ULT_CHS来安装好,Key:BWG7X-J98B3-W34RT-33B3R-JVYW9 兴高采烈的打开vs2013,新建web项目,结果弹出一个错误: 傻眼了 ...

  4. Activiti源码:ActivitiEventSupport类中eventListeners的设计

    ActivitiEventSupport类成员eventListeners是使用CopyOnWriteArrayList实现的. public ActivitiEventSupport() { eve ...

  5. Gerrit 配置同步到多个仓库

    1.修改replication.config文件 [remote "xxx"] projects = Yilule.Core.Service #aliyun仓库 url = git ...

  6. asp、asp.net、.aspx、.ascx、.ashx的简单说明

    ASP是动态server页面(Active Server Page)的英文缩写.[1]是微软公司开发的取代CGI脚本程序的一种应用.它能够与数据库和其他程序进行交互,是一种简单.方便的编程工具.ASP ...

  7. MySQL : 数据库和表的基本操作总结

    针对database和table的操作大致可分为三类:创建,查询,修改,删除 1. 创建 create ①创建数据库 create database db_name; ②创建表 create tabl ...

  8. 一本通1649【例 2】2^k 进制数

    1649:[例 2]2^k 进制数 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:NOIP 2006 提高组 设 r 是个 2k 进制数,并满足以 ...

  9. poj 2482 Stars in Your Window + 51Nod1208(扫描线+离散化+线段树)

    Stars in Your Window Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 13196   Accepted:  ...

  10. Codeforces Round #442 (Div. 2)A,B,C,D,E(STL,dp,贪心,bfs,dfs序+线段树)

    A. Alex and broken contest time limit per test 2 seconds memory limit per test 256 megabytes input s ...