Linux下USB suspend/resume源码分析【转】
转自:http://blog.csdn.net/aaronychen/article/details/3928479
Linux下USB 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源码分析【转】的更多相关文章
- linux下MySQL 5.6源码安装
linux下MySQL 5.6源码安装 1.下载:当前mysql版本到了5.6.20 http://dev.mysql.com/downloads/mysql 选择Source Code 2.必要软件 ...
- linux下hadoop2.6.1源码64位的编译
linux下hadoop2.6.1源码64位的编译 一. 前言 Apache官网上提供的hadoop本地库是32位的,如果我们的Linux服务器是64位的话,就会现问题.我们在64位服务器执行Hado ...
- Linux内核2.6.14源码分析-双向循环链表代码分析(巨详细)
Linux内核源码分析-链表代码分析 分析人:余旭 分析时间:2005年11月17日星期四 11:40:10 AM 雨 温度:10-11度 编号:1-4 类别:准备工作 Email:yuxu97101 ...
- 如何找到Linux下常用命令的源码
Linux系统,常用命令的来源很多,有些命令是shell自带的,比如cd,通过执行help命令,可以查看当前系统所有的内置命令. 用type <cmd_name>来查看一个命令是否为内置命 ...
- linux下PostgreSQL数据库的源码安装
实验环境>>>>>>>>>>>>>>>>>>操作系统:CentOS release 6.3 ...
- 【SystemTap】 Linux下安装使用SystemTap源码安装SystemTap
转自 http://blog.csdn.net/zklth/article/details/6248558 文章 http://blog.csdn.net/zklth/archive/2010/09/ ...
- linux信号的处理--部分源码分析
基于linux master v4.9版本 信号是异步的, 一.信号何时来 信号是异步的,对于一个进程随时都会接收到信号. 二.选择线程(task)来处理 那么一个进程接收到信号时,需要选择一个tas ...
- linux下cmake安装mysql 源码
1.假设已经有mysql-5.6.21.tar.gz以及cmake-2.8.4.tar.gz两个源文件 (1)先安装cmake(mysql5.5以后是通过cmake来编译的) [root@ rhel5 ...
- angular源码分析:angular中脏活累活的承担者之$interpolate
一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...
随机推荐
- spring-boot随笔
配置了spring-boot-starter-web的依赖后,会自动添加tomcat和spring mvc的依赖,那么spring boot 会对tomcat和spring mvc进行自动配置 < ...
- Leetcode题库——40.组合总和II
@author: ZZQ @software: PyCharm @file: combinationSum2.py @time: 2018/11/15 18:38 要求:给定一个数组 candidat ...
- 第二个spring冲刺第8天
今天我们团队分别安排了不同的任务,分别是1人程序编写,1人检查bug,1人负责客户体验,还有我们的总负责人王俊凯同学负责各个部分的协调.今天程序有了新的调整,但是功能还是没有完全做出来,不过还在开发途 ...
- Linux基础六(网络管理)
目录 一.网络配置 1. IP 地址配置 2. 网络配置文件 3. 虚拟机网络配置参数 二.网络命令 1. 网络环境查看命令 2. 网络测试命令 三.远程会话安全协议-SSH原理 1. SSH 原理 ...
- Linux基础二(挂载、关机重启与系统等级)
一.Linux 基础之挂载 1. 挂载和查询 1.1 挂载 什么叫挂载?装系统的时候要给硬盘分区,在 Windows 中要分 C 盘 D 盘 DEF 盘,这个操作我们叫做分配盘符,分配盘符之后我们就可 ...
- 设置macbook休眠模式
前言: macbook默认合上盖默认是进入混合休眠模式模式(mode 3),此时电脑还会供电.不想耗电的话关机的话当前的工作状态就丢失了. macbook实际上是可以进入休眠模式的,只是没开放出来,我 ...
- send和sendmsg性能测试
1,摘要:测试send和sendmsg的性能,影响这两个函数性能主要有发送的字节大小,增加循环次数,从100到10000000(千万)2,基本信息cat /proc/cpuinfo查看CPU信息,如下 ...
- Vue-router的基本用法
刚学习vue不久,就接触了路由这个好东西.下面简单聊聊vue-router的基本用法. 一.路由的概念 路由,其实就是指向的意思,当我点击页面上的home按钮时,页面中就要显示home的内容,如果点击 ...
- Jamie's Contact Groups POJ - 2289(多重匹配 最大值最小化 最大流)
Jamie's Contact Groups Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 8567 Accepted: ...
- hdwiki 前后台版权信息在哪修改
hdwiki 前台copyright 信息在 view/default/footer.htm 搜索footer-phdwiki 后台copyright 信息在 view/default/admin_m ...