zoukankan      html  css  js  c++  java
  • linux驱动程序之电源管理之新版linux系统设备架构中关于电源管理方式的变更

    新版linux系统设备架构中关于电源管理方式的变更 based on linux-2.6.32

     

    一、设备模型各数据结构中电源管理的部分

      linux的设备模型通过诸多结构体来联合描述,如struct device,struct device_type,struct class, struct device_driver,struct bus_type等。

    @kernel/include/linux/devices.h中有这几中结构体的定义,这里只列出和PM有关的项,其余查看源码:      

    struct device{    

    ...    

    struct dev_pm_info power;     

    ...   

    }      

    struct device_type {    

    ...    

    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);    

    char *(*devnode)(struct device *dev, mode_t *mode);    

    void (*release)(struct device *dev);       

    const struct dev_pm_ops *pm;   

    };      

    struct class {    

    ...    

    void (*class_release)(struct class *class);   

    void (*dev_release)(struct device *dev);       

    int (*suspend)(struct device *dev, pm_message_t state);    

    int (*resume)(struct device *dev);       

    const struct dev_pm_ops *pm;    

    ...   

    };

        

    struct device_driver {    

    ...    

    int (*probe) (struct device *dev);    

    int (*remove) (struct device *dev);    

    void (*shutdown) (struct device *dev);    

    int (*suspend) (struct device *dev, pm_message_t state);    

    int (*resume) (struct device *dev);       

    const struct dev_pm_ops *pm;    

    ...   

    };

         

    struct bus_type {    

    ...    

    int (*match)(struct device *dev, struct device_driver *drv);    

    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);    

    int (*probe)(struct device *dev);    

    int (*remove)(struct device *dev);    

    void (*shutdown)(struct device *dev);       

    int (*suspend)(struct device *dev, pm_message_t state);    

    int (*resume)(struct device *dev);

    const struct dev_pm_ops *pm;    

    ...   

    };      

    以上可以看出和电源管理相关的两个结构体是struct dev_pm_info和struct dev_pm_ops,他们定义于文件   @kernel/include/linux/pm.h      

    struct dev_pm_info {    

    pm_message_t  power_state;    

    unsigned int  can_wakeup:1;    

    unsigned int  should_wakeup:1;    

    enum dpm_state  status;  /* Owned by the PM core - 表示该设备当前的PM状态*/   

    #ifdef CONFIG_PM_SLEEP    

    struct list_head entry;  /* 链接到dpm_list全局链表中的连接体 */   

    #endif   

    #ifdef CONFIG_PM_RUNTIME   // undef    

    struct timer_list suspend_timer;    

    unsigned long  timer_expires;    

    struct work_struct work;    

    wait_queue_head_t wait_queue;    

    spinlock_t  lock;    

    atomic_t  usage_count;    

    atomic_t  child_count;    

    unsigned int  disable_depth:3;    

    unsigned int  ignore_children:1;    

    unsigned int  idle_notification:1;    

    unsigned int  request_pending:1;    

    unsigned int  deferred_resume:1;   

    enum rpm_request request;    

    enum rpm_status  runtime_status;   

    int   runtime_error;   

    #endif   

    };   

      

    struct dev_pm_ops {    

    int (*prepare)(struct device *dev);    

    void (*complete)(struct device *dev);   

    int (*suspend)(struct device *dev);    

    int (*resume)(struct device *dev);    

    int (*freeze)(struct device *dev);    

    int (*thaw)(struct device *dev);    

    int (*poweroff)(struct device *dev);    

    int (*restore)(struct device *dev);    

    int (*suspend_noirq)(struct device *dev);    

    int (*resume_noirq)(struct device *dev);    

    int (*freeze_noirq)(struct device *dev);    

    int (*thaw_noirq)(struct device *dev);    

    int (*poweroff_noirq)(struct device *dev);    

    int (*restore_noirq)(struct device *dev);    

    int (*runtime_suspend)(struct device *dev);    

    int (*runtime_resume)(struct device *dev);    

    int (*runtime_idle)(struct device *dev);

      };

     

     

    二、device中的dev_pm_info结构体

      device结构体中的power项用来将该设备纳入电源管理的范围,记录电源管理的一些信息。   在注册设备的时候调用函数device_add()来向sysfs系统添加power接口和注册进电源管理系统,代码片段如下:   

    ...   

    error = dpm_sysfs_add(dev);  @kernel/drivers/base/power/sysfs.c   

    if (error)    

    goto DPMError;   

    device_pm_add(dev);      @kernel/drivers/base/power/main.c   

    ...   

    其中dpm_sysfs_add()函数用来向sysfs文件系统中添加相应设备的power接口文件,如注册mt6516_tpd paltform device的时候,会在sysfs中出现如下目录和文件:   

    #pwd   

    /sys/devices/platform/mt6516-tpd   

    #cd mt6516-tpd   

    #ls -l   

    -rw-r--r-- root     root         4096 2010-01-02 06:35 uevent   

    -r--r--r-- root     root         4096 2010-01-02 06:39 modalias   

    lrwxrwxrwx root     root              2010-01-02 06:39 subsystem -> ../../../bus/platform   

    drwxr-xr-x root     root              2010-01-02 06:35 power   

    lrwxrwxrwx root     root              2010-01-02 06:39 driver -> ../../../bus/platform/drivers/mt6516-tpd   

    #cd power   

    #ls -l   

    -rw-r--r-- root     root         4096 2010-01-02 06:39 wakeup      

    源码片段:   

    static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);

    static struct attribute * power_attrs[] = {    

    &dev_attr_wakeup.attr,    

    NULL,   

    };   

    static struct attribute_group pm_attr_group = {    

    .name = "power",  // attribute_group结构体的name域不为NULL的话,都会已name建立一个属性目录的    

    .attrs = power_attrs,   

    };      

    int dpm_sysfs_add(struct device * dev)   {    

    return sysfs_create_group(&dev->kobj, &pm_attr_group); //在当前device的kobject结构体对应的目录下建立   

    }      

    其中的device_pm_add()函数会将该设备插入到电源管理的核心链表dpm_list中统一管理。   

    值得一提的是,在函数device_initialize()会调用函数device_pm_init()来初始化该device结构体的power域:   

    dev->power.status = DPM_ON;

         

    void device_pm_add(struct device *dev)   {    

    ...    

    mutex_lock(&dpm_list_mtx);    

    if (dev->parent) {     

    if (dev->parent->power.status >= DPM_SUSPENDING)       // 如果某设备处于DPM_SUSPENDING极其之后的状态,此时不允许以该设备为父设备注册子设备      

    dev_warn(dev, "parent %s should not be sleeping/n", dev_name(dev->parent));    

    }

    else if (transition_started) { // transition_started全局变量包含在PM transition期间不允许注册设备     

    /*     

    * We refuse to register parentless devices while a PM     

    * transition is in progress in order to avoid leaving them     

    * unhandled down the road     

    */     

    dev_WARN(dev, "Parentless device registered during a PM transaction/n");    

    }       

    list_add_tail(&dev->power.entry, &dpm_list); // 将device结构体通过power.entry项链接进dpm_list    

    mutex_unlock(&dpm_list_mtx);   

    }      

    void device_pm_remove(struct device *dev)   {    

    ...    

    mutex_lock(&dpm_list_mtx);    

    list_del_init(&dev->power.entry);    

    mutex_unlock(&dpm_list_mtx);    

    pm_runtime_remove(dev);   

    }

     

      举例说明:      

    我们熟知的platform bus在系统中也是作为一种设备注册进了系统,在sysfs文件系统中的位置是:   /sys/devices/platform。使用函数device_register(&platform_bus)进行注册,调用device_add()函数,   

    注册ok之后,也会出现目录/sys/devices/platform/power。最后也会将其添加进dpm_list中。      

    i2c控制器外设代表的设备是注册在platform总线上的,也就是说它的父设备是platform。   

    最终在platform_device_add()中会调用函数device_add()函数来添加设备,最终也会在mt6516-i2c.0/   mt6516-i2c.1/mt6516-i2c.2中出现一个power目录,同时这3个platform设备会依靠   platform_device.dev.power.entry连接件链接到电源管理核心链表dpm_list中。   

    /sys/devices/platform/mt6516-i2c.2/power   

      

    每一个i2c控制器都会在系统中至少注册成一个适配器(adapter),该结构体将会间接提供给i2c设备的驱动来使用,以避免直接使用i2c控制器结构体。

    这个适配器没有对应的driver,在错综复杂的i2c架构中,相对于只起到了一个承上启下的作用,上接i2c控制器的结构体及driver,下接i2c设备的结构体i2c_client和特点的driver。

    adapter.dev.parent为i2c控制器对应的device,所以就会出现名为i2c-0/1/2的设备kobject,只是该设备的bus总线和device_type是:   adap->dev.bus = &i2c_bus_type;   

    adap->dev.type = &i2c_adapter_type;   

    然后调用函数device_register(&adap->dev);来注册这个device,所以在对应的i2c-0/1/2目录下也会出现power目录。   /sys/devices/platform/mt6516-i2c.2/i2c-2/power      

    i2c设备会通过自动检测或者事先静态描述的方式来注册进系统,不管什么方式,都会调用到函数:

    i2c_new_device()   

    struct i2c_client *client;   

    client->dev.parent = &client->adapter->dev;   

    client->dev.bus = &i2c_bus_type;   

    client->dev.type = &i2c_client_type;   

    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),client->addr);   

    status = device_register(&client->dev);   

    可以看得出来名字是什么了,例如:2-00aa   

    #ls -l /sys/devices/platform/mt6516-i2c.2/i2c-2/2-00aa   

    -rw-r--r-- root     root         4096 2010-01-02 06:35 uevent   

    -r--r--r-- root     root         4096 2010-01-02 06:38 name   -

    r--r--r-- root     root         4096 2010-01-02 06:38 modalias   

    lrwxrwxrwx root     root              2010-01-02 06:38 subsystem -> ../../../../../bus/i2c   

    drwxr-xr-x root     root              2010-01-02 06:35 power   

    lrwxrwxrwx root     root              2010-01-02 06:38 driver -> ../../../../../bus/i2c/drivers/mt6516-tpd 

      

    三、bus_type、device_driver、device_type、class中的dev_pm_ops方法结构体   

    在新的linux内核中,已不再有subsystem数据结构了,他的功能被kset代替。     

    全局变量bus_kset初始化:

    kernel_init()-->do_basic_setup()-->driver_init()-->buses_init()   

    bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);     

     

    1. 总线类型结构体:

    bus_type,以platform和i2c总线为例:   

    @kernel/drivers/base/platform.c   

    static const struct dev_pm_ops platform_dev_pm_ops = {    

    .prepare = platform_pm_prepare,   //     

    .complete = platform_pm_complete,  //    

    .suspend = platform_pm_suspend,   //    

    .resume = platform_pm_resume,    //    

    .freeze = platform_pm_freeze,    

    .thaw = platform_pm_thaw,    

    .poweroff = platform_pm_poweroff,  //    

    .restore = platform_pm_restore,    

    .suspend_noirq = platform_pm_suspend_noirq,    

    .resume_noirq = platform_pm_resume_noirq,    

    .freeze_noirq = platform_pm_freeze_noirq,    

    .thaw_noirq = platform_pm_thaw_noirq,    

    .poweroff_noirq = platform_pm_poweroff_noirq,    

    .restore_noirq = platform_pm_restore_noirq,    

    .runtime_suspend = platform_pm_runtime_suspend,    

    .runtime_resume = platform_pm_runtime_resume,    

    .runtime_idle = platform_pm_runtime_idle,   

    };      

    struct bus_type platform_bus_type = {    

    .name  = "platform",    

    .dev_attrs = platform_dev_attrs,    

    .match  = platform_match,    

    .uevent  = platform_uevent,    

    .pm  = &platform_dev_pm_ops,   

    };   

    从上面的dev_pm_ops结构体中拿出最普遍使用的函数指针来说明一下,对于bus_type它的电源管理是如何实现的。   

    static int platform_pm_prepare(struct device *dev)   {    

    struct device_driver *drv = dev->driver;    

    int ret = 0;       

    if (drv && drv->pm && drv->pm->prepare)     

    ret = drv->pm->prepare(dev);       

    return ret;   

    }   

    static void platform_pm_complete(struct device *dev)   {    

    struct device_driver *drv = dev->driver;       

    if (drv && drv->pm && drv->pm->complete)     

    drv->pm->complete(dev);   

    }   

    可以看出这两个函数都最终是利用了device_driver结构体中的dev_pm_ops函数方法结构体中的对应函数指针。  

        ////////////////////////////////////////////  

     static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)   {    

    struct platform_driver *pdrv = to_platform_driver(dev->driver);    

    struct platform_device *pdev = to_platform_device(dev);    

    int ret = 0;       

    if (dev->driver && pdrv->suspend)     

    ret = pdrv->suspend(pdev, mesg);       

    return ret;   

    }      

    static int platform_legacy_resume(struct device *dev)   {    

    struct platform_driver *pdrv = to_platform_driver(dev->driver);    

    struct platform_device *pdev = to_platform_device(dev);    

    int ret = 0;       

    if (dev->driver && pdrv->resume)     

    ret = pdrv->resume(pdev);       

    return ret;   

    }   

    ////////////////////////////////////////////   

    static int platform_pm_suspend(struct device *dev)   {    

    struct device_driver *drv = dev->driver;    

    int ret = 0;       

    if (!drv)     

    return 0;       

    if (drv->pm) {     

    if (drv->pm->suspend)      

    ret = drv->pm->suspend(dev);    

    } else {     

    ret = platform_legacy_suspend(dev, PMSG_SUSPEND);    

    }       

    return ret;   

    }      

    static int platform_pm_resume(struct device *dev)   {    

    struct device_driver *drv = dev->driver;    

    int ret = 0;       

    if (!drv)     

    return 0;      

     if (drv->pm) {     

    if (drv->pm->resume)      

    ret = drv->pm->resume(dev);    

    } else {     

    ret = platform_legacy_resume(dev);    

    }       

    return ret;   

    }

        这里suspend和resume函数也是最终都是调用了device_driver结构体的dev_pm_ops方法结构体中的对应函数指针(device_driver.pm项被初始化),否则使用老式的方法:platform_legacy_suspend(dev, PMSG_SUSPEND)和platform_legacy_resume(dev)。根据这两个函数的源码可以看出。一般地,在我们的platform device的platform driver定义中,都是实现了pdrv.suspend和pdrv.resume函数,而并没有实现pdrv.driver.suspend和pdrv.driver.resume函数,其余三个函数可以在platform_driver_register()函数中看出:   

    int platform_driver_register(struct platform_driver *drv)   {    

    drv->driver.bus = &platform_bus_type;    

    if (drv->probe)     

    drv->driver.probe = platform_drv_probe;    

    if (drv->remove)     

    drv->driver.remove = platform_drv_remove;   

     if (drv->shutdown)     

    drv->driver.shutdown = platform_drv_shutdown;       

    return driver_register(&drv->driver);   

    }      

    i2c总线注册没有使用新式的电源管理方法:dev_pm_ops,仍然使用老式的方式:   

    @kernel/drivers/i2c/i2c-core.c   

    struct bus_type i2c_bus_type = {    

    .name  = "i2c",    

    .match  = i2c_device_match,    

    .probe  = i2c_device_probe,    

    .remove  = i2c_device_remove,   

     .shutdown = i2c_device_shutdown,    

    .suspend = i2c_device_suspend,    

    .resume  = i2c_device_resume,   

    };      

    static int i2c_device_suspend(struct device *dev, pm_message_t mesg)   {    

    struct i2c_client *client = i2c_verify_client(dev);    

    struct i2c_driver *driver;       

    if (!client || !dev->driver)     

    return 0;    

    driver = to_i2c_driver(dev->driver);    

    if (!driver->suspend)     

    return 0;    

    return driver->suspend(client, mesg);   

    }      

    static int i2c_device_resume(struct device *dev)   {    

    struct i2c_client *client = i2c_verify_client(dev);    

    struct i2c_driver *driver;       

    if (!client || !dev->driver)     

    return 0;    

    driver = to_i2c_driver(dev->driver);    

    if (!driver->resume)    

     return 0;    

    return driver->resume(client);   

    }   // 实际上都是调用的i2c_driver结构体的suspend和resume函数。      

    2. device_type结构体暂时还没有找到有哪一个模块使用了新式了dev_pm_ops电源管理方法,一般都是没有实现这部分。      

    3. class结构体也没有找到使用dev_pm_ops方法结构体的地方,先暂时放一放。      

    4. device_driver    

    struct device_driver {     

    const char  *name;     

    struct bus_type  *bus;     

    ...     

    int (*probe) (struct device *dev);     

    int (*remove) (struct device *dev);     

    void (*shutdown) (struct device *dev);     

    int (*suspend) (struct device *dev, pm_message_t state);     

    int (*resume) (struct device *dev);    

     const struct attribute_group **groups;         

    const struct dev_pm_ops *pm;         

    struct driver_private *p;    

    };        

    struct i2c_driver {     

    ...     

    /* driver model interfaces that don't relate to enumeration  */     

    void (*shutdown)(struct i2c_client *);     

    int (*suspend)(struct i2c_client *, pm_message_t mesg);     

    int (*resume)(struct i2c_client *);     

    ...     

    struct device_driver driver;    

     const struct i2c_device_id *id_table;

    /* Device detection callback for automatic device creation */     

    int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);     

    const struct i2c_client_address_data *address_data;     

    struct list_head clients;    

    };    

    一般都是实现了platform driver和i2c_driver结构体的suspend和resume函数,并没有使用新式的电源管理方式。

  • 相关阅读:
    c++之类模板和函数模板的区别
    c++之模板-类模板
    c++之函数模板的局限性
    c++之普通函数和模板函数的调用规则
    c++之普通函数和模板函数的区别
    c++之函数模板案例
    c++之模板函数-模板
    c++文件操作之二进制文件-读文件
    c++文件操作之二进制文件-写文件
    FFmpeg获取DirectShow设备数据(摄像头,录屏)
  • 原文地址:https://www.cnblogs.com/0822vaj/p/3935863.html
Copyright © 2011-2022 走看看