Linux设备模型_platform设备

Linux设备模型(8)_platform设备


存档,转载于:http://www.wowotech.NET/linux_kenrel/platform_device.html


1. 前言

Linux设备模型的抽象中,存在着一类称作“Platform Device”的设备,内核是这样描述它们的(Documentation/driver-model/platform.txt):


Platform devices are devices that typically appear as autonomous entities in the system. This includes legacy port-based devices and host bridges to peripheral buses, and most controllers integrated into system-on-chip platforms.  What they usually have in common is direct addressing from a CPU bus.  Rarely, a platform_device will be connected through a segment of some other kind of bus; but its registers will still be directly addressable.



概括来说,Platform设备包括:基于端口的设备(已不推荐使用,保留下来只为兼容旧设备,legacy);连接物理总线的桥设备;集成在SOC平台上面的控制器;连接在其它bus上的设备(很少见)。等等。

这些设备有一个基本的特征:可以通过CPU bus直接寻址(例如在嵌入式系统常见的“寄存器”)。因此,由于这个共性,内核在设备模型的基础上(device和device_driver),对这些设备进行了更进一步的封装,抽象出paltform bus、platform device和platform driver,以便驱动开发人员可以方便的开发这类设备的驱动。

可以说,paltform设备对Linux驱动工程师是非常重要的,因为我们编写的大多数设备驱动,都是为了驱动plaftom设备。本文我们就来看看Platform设备在内核中的实现。


2. Platform模块的软件架构

内核中Platform设备有关的实现位于include/linux/platform_device.h和drivers/base/platform.c两个文件中,它的软件架构如下:



由图片可知,Platform设备在内核中的实现主要包括三个部分:

Platform Bus,基于底层bus模块,抽象出一个虚拟的Platform bus,用于挂载Platform设备;
Platform Device,基于底层device模块,抽象出Platform Device,用于表示Platform设备;
Platform Driver,基于底层device_driver模块,抽象出Platform Driver,用于驱动Platform设备。

其中Platform Device和Platform Driver会会其它Driver提供封装好的API,具体可参考后面的描述。


3. Platform模块向其它模块提供的API汇整

Platform提供的接口包括:Platform Device和Platform Driver两个数据结构,以及它们的操作函数。

3.1 数据结构

1. 用于抽象Platform设备的数据结构—-“struct platform_device”:

反反复复非

  1. /* include include/linux/platform_device.h, line 22 */  
  2. struct platform_device {  
  3.     const char  *name;  
  4.     int     id;   
  5.     bool        id_auto;  
  6.     struct device   dev;  
  7.     u32     num_resources;  
  8.     struct resource *resource;  
  9.           
  10.     const struct platform_device_id *id_entry;  
  11.           
  12.     /* MFD cell pointer */  
  13.     struct mfd_cell *mfd_cell;  
  14.           
  15.     /* arch specific additions */  
  16.     struct pdev_archdata    archdata;  
  17. };  
/* include include/linux/platform_device.h, line 22 */
struct platform_device {
    const char  *name;
    int     id; 
    bool        id_auto;
    struct device   dev;
    u32     num_resources;
    struct resource *resource;
        
    const struct platform_device_id *id_entry;
        
    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;
        
    /* arch specific additions */
    struct pdev_archdata    archdata;
};



该结构的解释如下:

dev,真正的设备(Platform设备只是一个特殊的设备,因此其核心逻辑还是由底层的模块实现)。

name,设备的名称,和struct device结构中的init_name(”Linux设备模型(5)_device和device driver”)意义相同。实际上,该名称在设备注册时,会拷贝到dev.init_name中。

id,用于标识该设备的ID。在“Linux设备模型(6)_Bus”中有提过,内核允许存在多个名称相同的设备。而设备驱动的probe,依赖于名称,Linux采取的策略是:在bus的设备链表中查找device,和对应的device_driver比对name,如果相同,则查看该设备是否已经绑定了driver(查看其dev->driver指针是否为空),如果已绑定,则不会执行probe动作,如果没有绑定,则以该device的指针为参数,调用driver的probe接口。因此,在driver的probe接口中,通过判断设备的ID,可以知道此次驱动的设备是哪个。

id_auto,指示在注册设备时,是否自动赋予ID值(不需要人为指定啦,可以懒一点啦)。

num_resources、resource,该设备的资源描述,由struct resource(include/linux/ioport.h)结构抽象。
在Linux中,系统资源包括I/O、Memory、Register、IRQ、DMA、Bus等多种类型。这些资源大多具有独占性,不允许多个设备同时使用,因此Linux内核提供了一些API,用于分配、管理这些资源。当某个设备需要使用某些资源时,只需利用struct resource组织这些资源(如名称、类型、起始、结束地址等),并保存在该设备的resource指针中即可。然后在设备probe时,设备需求会调用资源管理接口,分配、使用这些资源。而内核的资源管理逻辑,可以判断这些资源是否已被使用、是否可被使用等等。

id_entry,和内核模块相关的内容,暂不说明。

mfd_cell,和MFD设备相关的内容,暂不说明。

archdata,一个奇葩的存在!!它的目的是为了保存一些architecture相关的数据,去看看arch/arm/include/asm/device.h中struct pdev_archdata结构的定义,就知道这种放纵的设计有多么垃圾了。不管它了!!


2. 用于抽象Platform设备驱动的数据结构—-“struct platform_driver”:


  1. /* include include/linux/platform_device.h, line 173 */  
  2. struct platform_driver {  
  3.     int (*probe)(struct platform_device *);   
  4.     int (*remove)(struct platform_device *);   
  5.     void (*shutdown)(struct platform_device *);   
  6.     int (*suspend)(struct platform_device *, pm_message_t state);  
  7.     int (*resume)(struct platform_device *);   
  8.     struct device_driver driver;     
  9.     const struct platform_device_id *id_table;  
  10. };  
/* include include/linux/platform_device.h, line 173 */
struct platform_driver {
    int (*probe)(struct platform_device *); 
    int (*remove)(struct platform_device *); 
    void (*shutdown)(struct platform_device *); 
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *); 
    struct device_driver driver;   
    const struct platform_device_id *id_table;
};



struct platform_driver结构和struct device_driver非常类似,无非就是提供probe、remove、suspend、resume等回调函数,这里不再细说。

另外这里有一个id_table的指针,该指针和”Linux设备模型(5)_device和device driver”所描述的of_match_table、acpi_match_table的功能类似:提供其它方式的设备probe。我们在”Linux设备模型(5)_device和device driver”讲过,内核会在合适的时机检查device和device_driver的名字,如果匹配,则执行probe。其实除了名称之外,还有一些宽泛的匹配方式,例如这里提到的各种match table,具体原理就先不罗嗦了,徒添烦恼!就当没看见,呵呵。

3.2 Platform Device提供的API

Platform Device主要提供设备的分配、注册等接口,供其它driver使用,具体包括:

  1. /* include/linux/platform_device.h */  
  2. extern int platform_device_register(struct platform_device *);   
  3. extern void platform_device_unregister(struct platform_device *);   
  4.   
  5. extern void arch_setup_pdev_archdata(struct platform_device *);   
  6. extern struct resource *platform_get_resource(struct platform_device *,  
  7.                                               unsigned int, unsigned int);  
  8. extern int platform_get_irq(struct platform_device *, unsigned int);  
  9. extern struct resource *platform_get_resource_byname(struct platform_device *,  
  10.                                                      unsigned int,  
  11.                                                      const char *);   
  12. extern int platform_get_irq_byname(struct platform_device *, const char *);   
  13. extern int platform_add_devices(struct platform_device **, int);  
  14.   
  15. extern struct platform_device *platform_device_register_full(  
  16.                 const struct platform_device_info *pdevinfo);  
  17.                       
  18. static inline struct platform_device *platform_device_register_resndata(  
  19.                 struct device *parent, const char *name, int id,   
  20.                 const struct resource *res, unsigned int num,  
  21.                 const void *data, size_t size)  
  22.                       
  23. static inline struct platform_device *platform_device_register_simple(  
  24.                 const char *name, int id,   
  25.                 const struct resource *res, unsigned int num)  
  26.                       
  27. static inline struct platform_device *platform_device_register_data(  
  28.                 struct device *parent, const char *name, int id,   
  29.                 const void *data, size_t size)  
  30.                       
  31. extern struct platform_device *platform_device_alloc(const char *name, int id);  
  32. extern int platform_device_add_resources(struct platform_device *pdev,  
  33.                                          const struct resource *res,  
  34.                                          unsigned int num);  
  35. extern int platform_device_add_data(struct platform_device *pdev,  
  36.                                     const void *data, size_t size);  
  37.   
  38. extern int platform_device_add(struct platform_device *pdev);  
  39. extern void platform_device_del(struct platform_device *pdev);  
  40. extern void platform_device_put(struct platform_device *pdev);  
/* include/linux/platform_device.h */
extern int platform_device_register(struct platform_device *); 
extern void platform_device_unregister(struct platform_device *); 

extern void arch_setup_pdev_archdata(struct platform_device *); 
extern struct resource *platform_get_resource(struct platform_device *,
                                              unsigned int, unsigned int);
extern int platform_get_irq(struct platform_device *, unsigned int);
extern struct resource *platform_get_resource_byname(struct platform_device *,
                                                     unsigned int,
                                                     const char *); 
extern int platform_get_irq_byname(struct platform_device *, const char *); 
extern int platform_add_devices(struct platform_device **, int);

extern struct platform_device *platform_device_register_full(
                const struct platform_device_info *pdevinfo);
                    
static inline struct platform_device *platform_device_register_resndata(
                struct device *parent, const char *name, int id, 
                const struct resource *res, unsigned int num,
                const void *data, size_t size)
                    
static inline struct platform_device *platform_device_register_simple(
                const char *name, int id, 
                const struct resource *res, unsigned int num)
                    
static inline struct platform_device *platform_device_register_data(
                struct device *parent, const char *name, int id, 
                const void *data, size_t size)
                    
extern struct platform_device *platform_device_alloc(const char *name, int id);
extern int platform_device_add_resources(struct platform_device *pdev,
                                         const struct resource *res,
                                         unsigned int num);
extern int platform_device_add_data(struct platform_device *pdev,
                                    const void *data, size_t size);

extern int platform_device_add(struct platform_device *pdev);
extern void platform_device_del(struct platform_device *pdev);
extern void platform_device_put(struct platform_device *pdev);




platform_device_register、platform_device_unregister,Platform设备的注册/注销接口,和底层的device_register等接口类似。

arch_setup_pdev_archdata,设置platform_device变量中的archdata指针。

platform_get_resource、platform_get_irq、platform_get_resource_byname、platform_get_irq_byname,通过这些接口,可以获取platform_device变量中的resource信息,以及直接获取IRQ的number等等。

platform_device_register_full、platform_device_register_resndata、platform_device_register_simple、platform_device_register_data,其它形式的设备注册。调用者只需要提供一些必要的信息,如name、ID、resource等,Platform模块就会自动分配一个struct platform_device变量,填充内容后,注册到内核中。

platform_device_alloc,以name和id为参数,动态分配一个struct platform_device变量。

platform_device_add_resources,向platform device中增加资源描述。

platform_device_add_data,向platform device中添加自定义的数据(保存在pdev->dev.platform_data指针中)。

platform_device_add、platform_device_del、platform_device_put,其它操作接口。


3.3 Platform Driver提供的API

Platform Driver提供struct platform_driver的分配、注册等功能,具体如下:

  1. /* include/linux/platform_device.h */  
  2.      
  3. extern int platform_driver_register(struct platform_driver *);   
  4.   
  5. extern void platform_driver_unregister(struct platform_driver *);   
  6.   
  7. /* non-hotpluggable platform devices may use this so that probe() and  
  8.  * its support may live in __init sections, conserving runtime memory. 
  9.  */  
  10. extern int platform_driver_probe(struct platform_driver *driver,  
  11.                 int (*probe)(struct platform_device *));  
  12.                       
  13. static inline void *platform_get_drvdata(const struct platform_device *pdev)  
  14.   
  15. static inline void platform_set_drvdata(struct platform_device *pdev,  
  16.                                         void *data)  
/* include/linux/platform_device.h */
   
extern int platform_driver_register(struct platform_driver *); 

extern void platform_driver_unregister(struct platform_driver *); 

/* non-hotpluggable platform devices may use this so that probe() and 
 * its support may live in __init sections, conserving runtime memory.
 */
extern int platform_driver_probe(struct platform_driver *driver,
                int (*probe)(struct platform_device *));
                    
static inline void *platform_get_drvdata(const struct platform_device *pdev)

static inline void platform_set_drvdata(struct platform_device *pdev,
                                        void *data)


platform_driver_registe、platform_driver_unregister,platform driver的注册、注销接口。

platform_driver_probe,主动执行probe动作。

platform_set_drvdata、platform_get_drvdata,设置或者获取driver保存在device变量中的私有数据。


3.4 懒人API

又是注册platform device,又是注册platform driver,看着挺啰嗦的。不过内核想到了这点,所以提供一个懒人API,可以同时注册platform driver,并分配一个platform device:


  1. extern struct platform_device *platform_create_bundle(  
  2.         struct platform_driver *driver, int (*probe)(struct platform_device *),   
  3.         struct resource *res, unsigned int n_res,  
  4.         const void *data, size_t size);  
extern struct platform_device *platform_create_bundle(
        struct platform_driver *driver, int (*probe)(struct platform_device *), 
        struct resource *res, unsigned int n_res,
        const void *data, size_t size);


 
 
3.5 Early platform device/driver

内核启动时,要完成一定的初始化操作之后,才会处理device和driver的注册及probe,因此在这之前,常规的platform设备是无法使用的。但是在Linux中,有些设备需要尽早使用(如在启动过程中充当console输出的serial设备),所以platform模块提供了一种称作Early platform device/driver的机制,允许驱动开发人员,在开发驱动时,向内核注册可在内核早期启动过程中使用的driver。这些机制提供了如下接口:

  1. extern int early_platform_driver_register(struct early_platform_driver *epdrv,  
  2.                                           char *buf);  
  3. extern void early_platform_add_devices(struct platform_device **devs, int num);  
  4.   
  5. static inline int is_early_platform_device(struct platform_device *pdev)  
  6. {  
  7.         return !pdev->dev.driver;  
  8. }  
  9.   
  10. extern void early_platform_driver_register_all(char *class_str);  
  11. extern int early_platform_driver_probe(char *class_str,  
  12.                                        int nr_probe, int user_only);  
  13. extern void early_platform_cleanup(void);  
extern int early_platform_driver_register(struct early_platform_driver *epdrv,
                                          char *buf);
extern void early_platform_add_devices(struct platform_device **devs, int num);

static inline int is_early_platform_device(struct platform_device *pdev)
{
        return !pdev->dev.driver;
}

extern void early_platform_driver_register_all(char *class_str);
extern int early_platform_driver_probe(char *class_str,
                                       int nr_probe, int user_only);
extern void early_platform_cleanup(void);



early_platform_driver_register,注册一个用于Early device的driver。

early_platform_add_devices,添加一个Early device。

is_early_platform_device,判断指定的device是否是Early device。

early_platform_driver_register_all,将指定class的所有driver注册为Early device driver。

early_platform_driver_probe,probe指定class的Early device。

early_platform_cleanup,清除所有的Early device/driver。


4. Platform模块的内部动作解析
4.1 Platform模块的初始化

Platform模块的初始化是由drivers/base/platform.c中platform_bus_init接口完成的,该接口的实现和动作如下:


  1. int __init platform_bus_init(void)  
  2. {  
  3.         int error;  
  4.   
  5.         early_platform_cleanup();  
  6.   
  7.         error = device_register(&platform_bus);  
  8.   
  9.         if (error)  
  10.                 return error;  
  11.         error =  bus_register(&platform_bus_type);  
  12.         if (error)  
  13.                 device_unregister(&platform_bus);  
  14.         return error;  
  15. }   
int __init platform_bus_init(void)
{
        int error;

        early_platform_cleanup();

        error = device_register(&platform_bus);

        if (error)
                return error;
        error =  bus_register(&platform_bus_type);
        if (error)
                device_unregister(&platform_bus);
        return error;
} 

early_platform_cleanup,清除所有和Early device/driver相关的代码。因为执行到这里的时候,证明系统已经完成了Early阶段的启动,转而进行正常的设备初始化、启动操作,所以不再需要Early Platform相关的东西。

device_register,注册一个名称为platform_bus的设备,该设备的定义非常简单,只包含init_name(为“platform”)。该步骤会在sysfs中创建“/sys/devices/platform/”目录,所有的Platform设备,都会包含在此目录下。

bus_register,注册一个名称为platform_bus_type的bus,该bus的定义如下:
struct bus_type platform_bus_type = {
        .name = “platform”,
       .dev_attrs = platform_dev_attrs,
       .match = platform_match,
       .uevent = platform_uevent,
       .pm = &platform_dev_pm_ops,
};
该步骤会在sysfs中创建“/sys/bus/platform/”目录,同时,结合“Linux设备模型(6)_Bus”的描述,会在“/sys/bus/platform/”目录下,创建uevent attribute(/sys/bus/platform/uevent)、devices目录、drivers目录、drivers_probe和drivers_autoprobe两个attribute(/sys/bus/platform/drivers_probe和/sys/bus/platform/drivers_autoprobe)。


4.2 platform device和platform driver的注册

结合第3章的描述,platform device和platform driver的注册,由platform_device_add和platform_driver_register两个接口实际实现。其内部动作分别如下。

platform_device_add的内部动作:

如果该设备没有指定父设备,将其父设备设置为platform_bus,即“/sys/devices/platform/”所代表的设备,这时该设备的sysfs目录即为“/sys/devices/platform/xxx_device”。

将该设备的bus指定为platform_bus_type(pdev->dev.bus = &platform_bus_type)。

根据设备的ID,修改或者设置设备的名称。对于多个同名的设备,可以使用ID区分,在这里将实际名称修改为“name.id”的形式。

调用resource模块的insert_resource接口,将该设备需要使用的resource统一管理起来(我们知道,在这之前,只是声明了本设备需要使用哪些resource,但resource模块并不知情,也就无从管理,因此需要告知)。

调用device_add接口,将内嵌的struct device变量添加到内核中。


platform_driver_register的内部动作:

将该driver的bus指定为platform_bus_type(drv->driver.bus = &platform_bus_type)。

如果该platform driver提供了probe、remove、shutdown等回调函数,将该它内嵌的struct driver变量的probe、remove、shutdown等指针,设置为platform模块提供函数,包括platform_drv_probe、platform_drv_remove和platform_drv_shutdown。因为probe等动作会从struct driver变量开始,经过platform_drv_xxx等接口的转接,就可以到达platform diver自身的回调函数中。

调用driver_register接口,将内嵌的struct driver变量添加到内核中。


4.3 platform设备的probe

我们在“Linux设备模型(6)_Bus”中讲过,设备的probe,都发生在向指定的bus添加device或者device_driver时,由bus模块的bus_probe_device,或者device_driver模块driver_attach接口触发。这里就不再详细描述了。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值