Linux下USB suspend/resume源码分析【转】

转自: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>>

时间: 2024-10-01 19:32:52

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

Linux下MySQL-5.6的源码安装

本文主要介绍centos下源码安装MySQL 5.6的方法,centos的版本为5.8. 1)首先,你需要到MySQL官网下载源码tar包,点击MySQL Community Server,选择Source Code,源码包不大,只有 34M左右. 注:以下操作没有特殊说明,都是以root账户执行. 2)先安装cmake(mysql5.5以后源码安装都得通过cmake编译) # yum install cmake 并确保以下两个包已安装最新版: ncurses ncurses-devel 3)添

linux下mysql5.5.15源码包编译安装

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://navyaijm.blog.51cto.com/4647068/809416 mysql果然是不愧是目前在linux下最火的数据库软件,自从mysql5.5.8之后,mysql的源码包编译安装都要用到cmake来进行编译了,编译的过程没有本质的区别,但是要直观很多.         1.源码包准备          (1)mysql-5.5.15.tar.gz mysqlmysq

Spark源码分析之九:内存管理模型

        Spark是现在很流行的一个基于内存的分布式计算框架,既然是基于内存,那么自然而然的,内存的管理就是Spark存储管理的重中之重了.那么,Spark究竟采用什么样的内存管理模型呢?本文就为大家揭开Spark内存管理模型的神秘面纱.         我们在<Spark源码分析之七:Task运行(一)>一文中曾经提到过,在Task被传递到Executor上去执行时,在为其分配的TaskRunner线程的run()方法内,在Task真正运行之前,我们就要构造一个任务内存管理器Task

虚拟机下Ubuntu找不到USB相关的源码文件

问题描述 虚拟机下Ubuntu找不到USB相关的源码文件 如题,在虚拟机里进入 /usr/src/linux/drivers/usb/core 文件夹中只有Kconfig和Makefile两个文件,没有其他文件.但是直接从官网下载的内核源码的这个路径下是有USB相关的源码的,想问下大神们这是怎么回事.菜鸟万分感激. 解决方案 http://article.yeeyan.org/view/208966/175140 为了能在Ubuntu主机上的虚拟机上访问你的USB设备,你需要把你的用户名加入到v

Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】

原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938390.html   在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段,      下面我就顺这代码逐个函数的解释,但是这

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html     前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并

Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938393.html 在分析start_kernel函数的时候,其中有构架相关的初始化函数setup_arch. 此函数根据构架而异,对于ARM构架的详细分析如下: void __init setup_arch(char **cmdli

Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938396.html     在基本分析完内核启动流程的之后,还有一个比较重要的初始化函数没有分析,那就是do_basic_setup.在内核init线程中调用了do_basic_setup,这个函数也做了很多内核和驱动的初始化

Spark源码分析之Spark Shell(下)

继上次的Spark-shell脚本源码分析,还剩下后面半段.由于上次涉及了不少shell的基本内容,因此就把trap和stty放在这篇来讲述. 上篇回顾:Spark源码分析之Spark Shell(上) function main() { if $cygwin; then # Workaround for issue involving JLine and Cygwin # (see http://sourceforge.net/p/jline/bugs/40/). # If you're us