一. 简介
- RPM全称Runtime Power Management,即运行时电源管理。主要是用于设备运行时的电源管理,由kernel统一管理。当设备处于空闲状态时,挂起设备;而当设备需要被使用时,使设备恢复进入正常工作状态。
- 在文件 /include/linux/pm.h 中,dev_pm_ops结构体下有与runtime pm相关的三个回调函数;以及使用rpm_status枚举表示设备的状态。
struct dev_pm_ops {
......
int (*runtime_suspend)(struct device *dev); //挂起
int (*runtime_resume)(struct device *dev); //恢复
int (*runtime_idle)(struct device *dev); //空闲
};
enum rpm_status {
RPM_ACTIVE = 0, //运行状态
RPM_RESUMING, //正在恢复成运行状态
RPM_SUSPENDED, //挂起状态
RPM_SUSPENDING, //正在挂起状态
};
- runtime pm的相关API接口函数在文件 /include/linux/pm_runtime.h下。
二. 运行机制
- 每个设备都有一个usage_count变量,记录设备的使用情况。
- 当该变量大于0时,表示设备正在被使用,处于active状态;当该变量等于0时,表示设备没有被使用,处于suspend状态。
- 当要使用设备时,使用pm_runtime_get_sync/pm_runtime_get将usage_count变量+1,每次+1都会去根据当前设备状态判断是否需要resume;当需要resume时,就会调用runtime_resume回调函数。
- 当使用完设备时,使用pm_runtime_put_sync/pm_runtime_put将usage_count变量-1,为0时suspend设备;suspend设备时先调用runtime_idle回调函数,然后在合适时机调用runtime_suspend回调函数。
三. 相关操作
- 查看当前设备状态
cat /sys/devices/$dev/power/runtime_status
- 增加/减少设备计数
echo on > /sys/devices/$dev/power/control
echo auto > /sys/devices/$dev/power/control
- 查看设备在active/suspend状态的时间
cat /sys/devices/$dev/power/runtime_active_time
cat /sys/devices/$dev/power/runtime_suspend_time
四. 伪代码示例
/*Test 驱动模块结构体*/
struct TestDev_t
{
dev_t dev_num; //设备号
struct cdev TestCdev; //使用Linux内核下cdev结构体,描述一个字符设备
struct class *TestClass; //创建LedClass类指针
struct device *TestDevice; //设备节点指针
struct platform_device *p_dev; //platform设备指针
};
/*字符设备操作函数*/
static int test_open(struct inode *inode, struct file *filp)
{
struct TestDev_t *p_TestDev = container_of(inode->i_cdev, struct TestDev_t, TestCdev);
filp->private_data = p_TestDev;
pm_runtime_get(&p_TestDev->p_dev->dev);
return 0;
}
static int test_release(struct inode *inode, struct file *filp)
{
struct TestDev_t *p_TestDev = (struct TestDev_t *)filp->private_data;
pm_runtime_put(&p_TestDev->p_dev->dev);
retuen 0;
}
static struct file_operations TestDevFops = {
.owner = THIS_MODULE,
.open = test_open,
.read = test_read,
.write = test_write,
.unlocked_ioctl = test_ioctl,
.release = test_release,
};
/* 当platform设备和驱动match时,就会执行prode函数 */
static int test_probe(struct platform_device *pdev)
{
struct TestDev_t* p_TestDev = (struct TestDev_t*)kmalloc(sizeof(struct TestDev_t), GFP_KERNEL);
/*注册设备号*/
alloc_chrdev_region(TestDev.dev_num, 0, 1, "TestDev");
/*注册字符设备*/
cdev_init(&TestDev.TestCdev, &TestDevFops);
cdev_add(&TestDev.TestCdev, TestDev.dev_num, 1);
/*创建设备节点文件*/
TestDev.TestClass = class_create(THIS_MODULE, "TestDev");
TestDev.TestDevice = device_create(TestDev.TestClass, NULL, TestDev.dev_num, NULL, "TestDev");
/*使能设备rpm功能*/
pm_runtime_enable(&pdev->dev);
/*保存设备驱动结构体*/
p_TestDev->p_dev = pdev;
platform_set_drvdata(pdev, p_TestDev);
return 0;
}
/*卸载platform驱动的时,remove函数会执行*/
static int test_remove(struct platform_device *pdev)
{
struct TestDev_t* p_TestDev = platform_get_drvdata(pdev);
/*注销字符设备*/
cdev_del(&TestDev.TestCdev);
/*注销设备号*/
unregister_chrdev_region(TestDev.dev_num, 1);
/*删除设备节点文件*/
device_destroy(TestDev.TestClass, TestDev.dev_num);
class_destroy(TestDev.TestClass);
/*失能设备rpm功能*/
pm_runtime_disable(&pdev->dev);
Kfree(p_TestDev);
return 0;
}
/* 运行时挂起设备操作 */
static int test_runtime_suspend(struct device *dev)
{
printk("dev runtime suspend\r\n");
/*挂起设备的操作*/
return 0;
}
/* 运行时恢复设备操作 */
static int test_runtime_resume(struct device *dev)
{
printk("dev runtime resume\r\n");
/*唤醒设备的操作*/
return 0;
}
/* 运行时设备空闲时操作 */
static int test_runtime_idle(struct device *dev)
{
printk("dev runtime idle\r\n");
return 0;
}
/* 设备runtime的 suspend/resume/idle 操作 */
static const dev_pm_ops test_rpm_ops = {
SET_RUNTIME_PM_OPS(test_runtime_suspend, test_runtime_resume, test_runtime_idle);
};
/* 匹配列表 */
static const struct of_device_id test_of_match[] = {
{ .compatible = "xxxxxx" }, /* 表示兼容的设备属性 */
{ }
};
/* platform驱动结构体 */
static struct platform_driver test_driver = {
.driver = {
.name = "xxxxxxxxx", /* 驱动名字 */
.of_match_table = test_of_match, /* 设备树匹配表 , 用于设备匹配 */
.pm = &test_rpm_ops, /* 赋值驱动中的dev_pm_ops结构体 */
},
.probe = test_probe,
.remove = test_remove,
};
*模块加载函数*/
static int __init test_init(void)
{
return platform_driver_register(&test_driver);
}
/*模块卸载函数*/
static void __exit test_exit(void)
{
platform_driver_unregister(&test_driver);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
五. 参考资料
- 《Linux设备驱动开发详解:基于最新的Linux4.0内核》
- http://www.wowotech.net
以上是我在学习过程中的总结,不当之处请在评论区指出。