您的位置:首页 > 运维架构 > 网站架构

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

2016-11-30 14:13 387 查看
新版linux系统设备架构中关于电源管理方式的变更

based on linux-2.6.32

 

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

  linux的设备模型通过诸多结构体来联合描述,如structdevice,struct device_type,struct class,

struct device_driver,struct bus_type等。

  

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

  

  struct device{

   ...

   structdev_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 structdev_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 structdev_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 structdev_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);

   conststruct dev_pm_ops *pm;

   ...

  };

  

  以上可以看出和电源管理相关的两个结构体是structdev_pm_info和struct dev_pm_ops,他们定义于文件

  @kernel/include/linux/pm.h

  

  struct dev_pm_info {

   pm_message_t  power_state;

   unsignedint  can_wakeup:1;

   unsignedint  should_wakeup:1;

   enumdpm_state  status;  

  #ifdef CONFIG_PM_SLEEP

   structlist_head entry;  

  #endif

  #ifdefCONFIG_PM_RUNTIME   //undef

   structtimer_list suspend_timer;

   unsignedlong  timer_expires;

   structwork_struct work;

   wait_queue_head_t wait_queue;

   spinlock_t  lock;

   atomic_t  usage_count;

   atomic_t  child_count;

   unsignedint  disable_depth:3;

   unsignedint  ignore_children:1;

   unsignedint  idle_notification:1;

   unsignedint  request_pending:1;

   unsignedint  deferred_resume:1;

   enumrpm_request request;

   enumrpm_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)

   gotoDPMError;

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

  ...

  其中dpm_sysfs_add()函数用来向sysfs文件系统中添加相应设备的power接口文件,如注册mt6516_tpdpaltform 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

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

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

  lrwxrwxrwxroot    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);

  staticstruct attribute * power_attrs[] = {

   &dev_attr_wakeup.attr,

   NULL,

  };

  static struct attribute_grouppm_attr_group = {

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

   .attrs =power_attrs,

  };

  

  int dpm_sysfs_add(struct device* dev)

  {

   returnsysfs_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(structdevice *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全局变量包含在PMtransition期间不允许注册设备

    

    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(structdevice *dev)

  {

   ...

   mutex_lock(&dpm_list_mtx);

   list_del_init(&dev->power.entry);

   mutex_unlock(&dpm_list_mtx);

   pm_runtime_remove(dev);

  }

 

  举例说明:

  

  我们熟知的platformbus在系统中也是作为一种设备注册进了系统,在sysfs文件系统中的位置是:

  /sys/devic
4000
es/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()

  structi2c_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-x", 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

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

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

  lrwxrwxrwxroot    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_opsplatform_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_typeplatform_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 intplatform_pm_prepare(struct device *dev)

  {

   structdevice_driver *drv = dev->driver;

   int ret =0;

  

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

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

  

   returnret;

  }

  static voidplatform_pm_complete(struct device *dev)

  {

   structdevice_driver *drv = dev->driver;

  

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

    drv->pm->complete(dev);

  }

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

  

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

  static intplatform_legacy_suspend(struct device *dev, pm_message_tmesg)

  {

   structplatform_driver *pdrv = to_platform_driver(dev->driver);

   structplatform_device *pdev = to_platform_device(dev);

   int ret =0;

  

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

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

  

   returnret;

  }

  

  static intplatform_legacy_resume(struct device *dev)

  {

   structplatform_driver *pdrv = to_platform_driver(dev->driver);

   structplatform_device *pdev = to_platform_device(dev);

   int ret =0;

  

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

    ret= pdrv->resume(pdev);

  

   returnret;

  }

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

  static intplatform_pm_suspend(struct device *dev)

  {

   structdevice_driver *drv = dev->driver;

   int ret =0;

  

   if(!drv)

    return0;

  

   if(drv->pm) {

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: