linux内核演进中设备驱动关于电源管理方式的变更

转自http://blog.csdn.net/lizhiguo0532/article/details/6453567

The suspend/resume will not be called if they are defined in
device_driver directly,
PM core will only use suspend/resume function in dev_pm_ops. Thus we shall
mark the old suspend/resume deprecated and make them scheduled for removal.

新版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函数方法结构体中的对应函数指针。
  再看platform_pm_suspend和platform_pm_resume。

  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函数(是struct device_driver没有做默认实现,platform_driver也不没有使用platform_drv_suspend/platform_drv_resume之类去回调i2c_driver.suspend,而是由platform_pm_suspend去调用,纳入PM core管理),其余三个函数可以在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);
  }

  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;
   };
   

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

   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;
   };


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核设备驱动模型采用了统一的设备模型,即设备树(Device Tree)模型。 设备树是一种描述硬件设备和资源的数据结构,它以一种可移植的方式描述了系统设备断、地址空间等信息。设备树使用一种类似于树形结构的语法,通过节点和属性来表示不同的设备和资源。 Linux内核设备驱动模型使用设备树来描述系统的硬件设备,并在运行时通过解析设备树来注册和管理设备驱动程序。设备树允许系统在不同的硬件平台上使用相同的内核镜像,只需通过不同的设备树文件来描述硬件配置即可。 设备树模型提供了一种灵活和可扩展的方式管理设备驱动程序。它可以动态地加载和卸载驱动程序,自动探测和配置硬件设备,并提供了一套标准的接口供设备驱动程序与硬件设备进行交互。 通过设备树模型,Linux内核可以实现对各种硬件设备的支持,包括处理器、总线控制器、外设等。它为不同的硬件平台提供了一致的接口,简化了设备驱动程序的开发和维护工作。 总结起来,Linux内核设备驱动模型采用了设备树模型,通过解析设备树来注册和管理设备驱动程序。设备树提供了一种可移植、灵活和可扩展的方式来描述和配置系统的硬件设备。它简化了设备驱动程序的开发和维护,并实现了对各种硬件设备的统一支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值