USB2.0之Suspend/Resume

USB2.0之Suspend/Resume

最近深入学习了USB2.0的Suspend/Resume。

Suspend/Resume协议实现

USB2.0规范约定所有设备都需要支持Suspend 。

当一个设备从他的上行口(upstream facing port)看到持续超过3ms的Idle状态,那么它就应该进入Suspend状态。
对于high-speed,Idle开始时无法区分是Idle还是Reset。 USB规范约定,当检测到Idle 3ms,就要配置为full-speed,断开D+D-的端接电阻,打开D+的上拉。这时,如果bus状态为data J,则判定为IDLE,如果为SE0则判定为Reset。

Suspend又分为Global Suspend和Selective Suspend。
Global Suspend由host发起,全局3.0ms Idle,然后进入Suspend。
Selective Suspend通过给hub port发SetPortFeature使对应hub port下的设备进入Suspend。

当设备进入Suspend状态,它从vbus上抽取的电流必须在10ms之内切换到suspend电流,也就是2.5mA以下。

bus在suspend状态时,host和device都可以发送Resume信号唤醒。
Resume信号类似于Data K。
USB规范约定这个Resume信号需持续20ms以上。
USB规范约定device默认禁止remote wakeup。 host可以用SetDeviceFeature(DEVICE_REMOTE_WAKEUP)命令打开remote wakeup。

linux中对USB Suspend/Resume的支持

usb suspend/resume是通过SetPortFeature命令控制的。Linux USB驱动也是通过这种Port command控制suspend/resume。如果要让一个设备suspend,则向其所在的hub Port发SetPortFeature命令中的set port suspend。
对于roothub,它没有所在的Port,则无法set port suspeng而是执行hcd的bus_suspend。
有的时候在set port suspend前还需要enable remote wakeup。观察发现runtime suspend过程中会enable remote wakeup,而suspeng过程中不会enable remote wakeup。

linux kernel USB autosuspend

linux kernel中有两种场景会操作USB Suspend/Resume。一种是runtime pm,另一种是全系统的Suspend/Resume(即S3、Suspend to Ram)。

可以通过sysfs控制usb autosuspend

下面是关于Linux USB利用runtime pm实现Suspend/Resume的一些探索。
echo auto > /sys/bus/usb/devices/1-1/power/level
echo on > /sys/bus/usb/devices/1-1/power/level
这条命令使能了一个设备的rpm suspend。(虚拟文件level目前被标记为“不推荐”,推荐使用control。)
在sysfs.c中的level_store()用来支持这个功能。
注意,这个文件节点控制suspend是面向device的,而不是面向interface的。
当向这个文件写入on/auto时,kernel中通过usb_enable_autosuspend()/usb_disable_autosuspend()产生作用。最终调用的是pm_runtime_allow()和pm_runtime_forbid()。

usb autosuspend使用了runtime pm

runtime pm的回调函数通过dev_pm_ops结构体注册给tuntime pm框架。
USB驱动软件的这个结构体在core/usb.c中,叫做usb_device_pm_ops。
注册了usb_runtime_suspend()、usb_runtime_resume()、usb_runtime_idle()三个函数。

可以通过一些接口函数操作runtime pm的行为。USB驱动软件封装了一些操作runtime pm的函数。除了前面提到的usb_enable_autosuspend()、usb_disable_autosuspend(),还有一些,如 usb_autopm_get/put_interface_*()、以及usb_mask_last_busy()、usb_autosuspend_device()、usb_autoresume_device()。 注意这些函数的操作对象,有的是usb_device,有的是usb_interface。典型情况是get/put类函数操作的是usb_interface,其实很容易理解,因为业务总是基于interface运行的,所以“投票”动作也是从interface发起。
usb_interface是usb_device的子设备,usb_interface的投票会传递到usb_device。(usb_set_configuration()中可以看到intf->dev.parent = &dev->dev 。)

runtime pm支持设定一个自动suspend的延时时间。对于hub设备,在hub.c中的hub_probe()函数,将autosuspend delay设定为0。对于一般设备,usb.c中usb_alloc_dev()函数,设定为2秒。有一个sysfs接口可以修改autosuspend,与前文所述的level在同一个路径。

USB3.0的U3与USB2.0的Suspend同一个流程

关于USB3.0 的U1、U2、U3。 U1、U2对标USB2.0的LPM,软件只需要协商和初始化一些属性,不需要参与其进入/推出的运行。U3相当于USB2.0的Suspend。
U3的进入/推出也是利用runtime pm机制实现的。实际上他们使用的是相同的代码,区别在于hcd中的hub_control()函数。在xhci驱动中,USB2、USB3不仅是两个port,也注册了两个hcd,所以roothub也是两个,所以它们的runtime pm流程是相互独立的。

源代码分析

usb_runtime_suspend()
  |- usb_suspend_both()
    |-usb_suspend_both()
      |- driver->susend()      // 没有撤销urb的动作
    |- usb_suspend_device()
      |- udriver->suspend()
        |- generic_suspend()
          |- hcd_bus_suspend()    // root hub的时候执行此分支 
            |- hcd->driver->bus_suspend() 
              |- xhci_bus_suspend()
          |- usb_port_suspend()   // 一般设备的时候执行此分支
            |- USB3 : hub_set_port_link_state()
            |- USB2 : set_port_feature()

参考资料

USB2.0协议:
7.1.7.6
7.1.7.7
9.1.6
10.2.7
10.5.4.5
11.9
11.24.1

  • 1
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值