platform_driver与platform_device

本文详细介绍平台设备驱动的注册过程及其实现原理,包括platform_driver结构的定义与初始化、与platform_device之间的关联方式以及如何实现probe函数等内容。

网上查了很多相关的资料,但写的都不是很易懂,这篇感觉描述的还比较清晰,从百度文库里找到的,参考一下。

首先介绍一下注册一个驱动的步骤:

 1、定义一个platform_driver结构 

2、初始化这个结构,指定其probe、remove等函数,并初始化其中的driver变量 

3、实现其probe、remove等函数 

看platform_driver结构,定义于include/linux/platform_device.h文件中: 
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 (*suspend_late)(struct platform_device *, pm_message_t state); 

    int (*resume_early)(struct platform_device *);    

   int (*resume)(struct platform_device *);    

   struct device_driver driver; }; 

    可见,它包含了设备操作的几个功能函数,同样重要的是,它还包含了一个device_driver结构。刚才提到了驱动程序中需要初始化这个变量。下面看一下这个变量的定义,位于include/linux/device.h中:   
struct device_driver { 

    const char        * name;   

    struct bus_type        * bus; 

    struct kobject        kobj; 

    struct klist        klist_devices;     

    struct klist_node    knode_bus; 

    struct module        * owner; 

    const char         * mod_name;  /* used for built-in modules */  

    struct module_kobject    * mkobj; 

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

    需要注意这两个变量:name和owner。那么的作用主要是为了和相关的platform_device关联起来,owner的作用是说明模块的所有者,驱动程序中一般初始化为THIS_MODULE。     下面是一个platform_driver的初始化实例: 

static struct platform_driver s3c2410iis_driver = {  

  .probe = s3c2410iis_probe,  

  .remove = s3c2410iis_remove,  

 .driver = { 

      .name = "s3c2410-iis",    

       .owner = THIS_MODULE,   }, };  

    上面的初始化是一个音频驱动的实例。注意其中的driver这个结构体,只初始化了其name和owner两个量。接着看一下和driver相关的另一个结构,定义如下: 

struct platform_device {     

const char    * name;   

     int        id; 

    struct device    dev;   

    u32        num_resources;    

    struct resource    * resource; }; 

   该结构中也有一个name变量。platform_driver从字面上来看就知道是设备驱动。设备驱动是为谁服务的呢?当然是设备了。platform_device就描述了设备对象。下面是一个具体的实例:  

struct platform_device s3c_device_iis = {   

     .name         = "s3c2410-iis",     

     .id         = -1, 

    .num_resources     = ARRAY_SIZE(s3c_iis_resource),   

    .resource     = s3c_iis_resource,     .dev = { 

    .dma_mask = &s3c_device_iis_dmamask,     

    .coherent_dma_mask = 0xffffffffUL     } }; 

    它的name变量和刚才上面的platform_driver的name变量是一致的,内核正是通过这个一致性来为驱动程序找到资源,即platform_device中的resource。这个结构的定义如下,位于include/linux/ioport.h中:  
struct resource { 

    resource_size_t start;   

    resource_size_t end;     

    const char *name;     

    unsigned long flags; 

    truct resource *parent, *sibling, *child; }; 
    下面是一个具体的实例: 

static struct resource s3c_iis_resource[] = {   

  [0] = { 

        .start = S3C24XX_PA_IIS, 
        .end = S3C24XX_PA_IIS + S3C24XX_SZ_IIS -1,
        .flags = IORESOURCE_MEM,     } }; 
    这个结构的作用就是告诉驱动程序设备的起始地址和终止地址和设备的端口类型。这里的地址指的是物理地址。 
    另外还需要注意platform_device中的device结构,它详细描述了设备的情况,定义如下: 
struct device { 
    struct klist        klist_children; 
    struct klist_node    knode_parent;        /* node in sibling list */ 

    struct klist_node    knode_driver;     

struct klist_node    knode_bus;    

 struct device        *parent; 

    struct kobject kobj; 

    char    bus_id[BUS_ID_SIZE];    /* position on parent bus */    

 struct device_type    *type;   

  unsigned        is_registered:1;     

unsigned        uevent_suppress:1; 

    struct semaphore    sem;    /* semaphore to synchronize calls to its driver.                      */ 

    struct bus_type    * bus;        /* type of bus device is on */    

 struct device_driver *driver;    /* which driver has allocated this  device */ 

    void        *driver_data;    /* data private to the driver */   

  void        *platform_data;    /* Platform specific data, device core doesn't touch it */    

 struct dev_pm_info    power; 

 
#ifdef CONFIG_NUMA 
     int        numa_node;    /* NUMA node this device is close to */ #endif 

     u64        *dma_mask;    /* dma mask (if dma'able device) */     

     u64        coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for                                                              consistent                      allocations such descriptors. */ 

     struct list_head    dma_pools;    /* dma pools (if dma'ble) */ 
    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem 
 override */     /* arch specific additions */   

    struct dev_archdata    archdata; 

    spinlock_t        devres_lock;   

    struct list_head    devres_head;     /* class_device migration path */   

    struct list_head    node;     

    struct class        *class; 
    dev_t            devt;        /* dev_t, creates the sysfs "dev" */ 
    struct attribute_group    **groups;    /* optional groups */ 
     void    (*release)(struct device * dev); }; 
  

    相信大家都知道module_init()这个宏。驱动模块加载的时候会调用这个宏。它接收一个函数为参数,作为它的参数的函数将会对上面提到的platform_driver进行处理。看一个实例:假如这里module_init要接收的参数为s3c2410_uda1341_init这个函数,下面是这个函数的定义: 

static int __init s3c2410_uda1341_init(void) {  

 memzero(&input_stream, sizeof(audio_stream_t)); 

  memzero(&output_stream, sizeof(audio_stream_t));  

 return platform_driver_register(&s3c2410iis_driver);  }  

注意函数体的最后一行,它调用的是platform_driver_register这个函数。这个函数定义于driver/base/platform.c中,原型如下: 
int platform_driver_register(struct platform_driver *drv) 
它的功能就是为上面提到的plarform_driver中的driver这个结构中的probe、remove这些变量指定功能函数。 
    到目前为止,内核就已经知道了有这么一个驱动模块。内核启动的时候,就会调用与该驱动相关的probe函数。我们来看一下probe函数实现了什么功能。     probe函数的原型为 
    int xxx_probe(struct platform_device *pdev) 
    即它的返回类型为int,接收一个platform_device类型的指针作为参数。返回类型就是我们熟悉的错误代码了,而接收的这个参数呢,我们上面已经说过,驱动程序为设备服务,就需要知道设备的信息。而这个参数,就包含了与设备相关的信息。 
    probe函数接收到plarform_device这个参数后,就需要从中提取出需要的信息。它一般会通过调用内核提供的platform_get_resource和platform_get_irq等函数来获得相关信息。如通过platform_get_resource获得设备的起始地址后,可以对其进行request_mem_region和ioremap等操作,以便应用程序对其进行操作。通过platform_get_irq得到设备的中断号以后,就可以调用request_irq函数来向系统申请中断。这些操作在设备驱动程序中一般都要完成。 
    在完成了上面这些工作和一些其他必须的初始化操作后,就可以向系统注册我们在/dev目录下能看在的设备文件了。举一个例子,在音频芯片的驱动中,就可以调用register_sound_dsp来注册一个dsp设备文件,lcd的驱动中就可以调用register_framebuffer来注册fb设备文件。这个工作完成以后,系统中就有我们需要的设备文件了。而和设备文件相关的操作都是通过一个file_operations 来实现的。在调用register_sound_dsp等函数的时候,就需要传递一个file_operations 类型的指针。这个指针就提供了可以供用户空间调用的write、read等函数。file_operations结构的定义位于include/linux/fs.h中,列出如下: 
struct file_operations {     struct module *owner; 
    loff_t (*llseek) (struct file *, loff_t, int); 
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 
    int (*readdir) (struct file *, void *, filldir_t); 
    unsigned int (*poll) (struct file *, struct poll_table_struct *); 
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned 
long); 
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long); 

    int (*mmap) (struct file *, struct vm_area_struct *);   

  int (*open) (struct inode *, struct file *);    

 int (*flush) (struct file *, fl_owner_t id);    

 int (*release) (struct inode *, struct file *); 

    int (*fsync) (struct file *, struct dentry *, int datasync);   

  int (*aio_fsync) (struct kiocb *, int datasync);    

 int (*fasync) (int, struct file *, int); 

    int (*lock) (struct file *, int, struct file_lock *); 
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); 

    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);   

    int (*check_flags)(int); 

    int (*dir_notify)(struct file *filp, unsigned long arg);    

    int (*flock) (struct file *, int, struct file_lock *); 

    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); 
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); 
    int (*setlease)(struct file *, long, struct file_lock **); }; 
    到目前为止,probe函数的功能就完成了。 
    当用户打开一个设备,并调用其read、write等函数的时候,就可以通过上面的file_operations来找到相关的函数。所以,用户驱动程序还需要实现这些函数,具体实现和相关的设备有密切的关系,这里就不再介绍了
<think>我们正在讨论Linux设备模型中的三个关键结构体:platform_deviceplatform_driverdevice_driver。 它们之间的关系是: 1. platform_device 代表一个具体的平台设备,通常描述SoC上的外设(如I2C控制器、SPI控制器等),它包含设备的资源(如内存地址、中断号等)。 2. device_driver 是一个通用的设备驱动结构体,包含驱动的基本操作(如probe、remove)和设备模型需要的字段。 3. platform_driver 是平台设备驱动,它内嵌了一个device_driver结构体,并扩展了平台设备特有的操作(如平台设备匹配和电源管理)。 具体关系如下: - platform_driver 结构体中包含一个 device_driver 结构体成员(通常命名为driver)。 - platform_driver 通过其内部的 device_driver 成员内核设备模型关联。 - 当注册一个 platform_driver 时,实际上是通过其内部的 device_driver 注册到总线上(platform_bus_type)。 - platform_deviceplatform_driver 通过名称进行匹配(即platform_device的name字段和platform_driver的id_table或者driver.name匹配)。 - 匹配成功后,会调用 platform_driver 中的 probe 函数。 结构体定义(简化): ```c struct platform_device { const char *name; // 设备名称,用于匹配驱动 int id; struct device dev; // 内嵌的通用设备结构体 // ... 资源信息等 }; struct device_driver { const char *name; // 驱动名称 struct bus_type *bus; // 所属总线 int (*probe)(struct device *dev); // 探测设备 int (*remove)(struct device *dev); // 移除设备 // ... 其他操作 }; struct platform_driver { int (*probe)(struct platform_device *); // 平台设备的探测函数 int (*remove)(struct platform_device *); // 移除函数 struct device_driver driver; // 内嵌的通用设备驱动 const struct platform_device_id *id_table; // 设备匹配表 }; ``` 工作流程: 1. 系统启动时,平台设备(platform_device)通常通过板级文件或设备树被注册到内核。 2. 驱动程序通过调用`platform_driver_register`注册一个platform_driver。 3. 内核的platform总线(platform_bus_type)会尝试将已注册的设备驱动进行匹配。 4. 匹配依据:platform_driverdriver.nameplatform_device的name匹配,或者platform_driver的id_table中的任一条目platform_device的name匹配。 5. 匹配成功则调用platform_driver的probe函数(注意:实际调用的是platform_driver->driver.probe,但通常这个函数指针被设置为一个内部函数,该内部函数会调用platform_driver->probe,并将device转换为platform_device)。 注意:在platform_driver的初始化中,通常会将device_driver的probe函数设置为一个通用的平台驱动probe函数(例如platform_drv_probe),这个函数会调用platform_driver自己的probe函数。 总结: - platform_device 描述硬件设备。 - platform_driver 包含驱动该硬件设备的函数,并内嵌一个device_driver。 - device_driver 是设备驱动的通用部分,由platform_driver中的driver成员实现。 关系图: platform_driver -> 包含 device_driver platform_device -> 包含 device 在总线上,device_driverdevice进行匹配,而实际上匹配的是platform_deviceplatform_driver。 §§相关问题§§ 1. 在内核中,platform_deviceplatform_driver是如何通过名称进行匹配的? 2. 当platform_driver注册时,其内嵌的device_driver的probe函数是如何被设置并最终调用到platform_driver的probe函数的? 3. 设备树(Device Tree)机制下,platform_device是如何被创建和注册的? 4. platform_device结构体中的struct device dev成员device_driver结构体有何关联? 5. 在platform驱动模型中,为什么需要将device_driver内嵌在platform_driver中,而不是直接使用device_driver?</think>### Linux设备驱动模型核心结构体关系解析 #### 1. **基础层级关系** ```c struct device_driver { // 通用驱动抽象 const char *name; // 驱动名称 struct bus_type *bus; // 所属总线 int (*probe)(struct device *dev); // 探测设备 ... }; struct platform_driver { // 平台设备驱动 int (*probe)(struct platform_device *); struct device_driver driver; // 内嵌通用驱动结构 const struct platform_device_id *id_table; ... }; struct platform_device { // 平台设备实例 const char *name; // 设备名称 struct device dev; // 内嵌设备基类 struct resource *resource; // 设备资源 ... }; ``` #### 2. **关键交互机制** 1. **注册绑定流程** ```mermaid graph LR A[platform_device_register] --> B[注册到platform_bus] C[platform_driver_register] --> D[注册driverplatform_bus] B & D --> E[总线匹配driver.namedevice.name] E --> F[调用platform_driver.probe] ``` 2. **结构体内嵌关系** - `platform_driver` 包含 `device_driver` 成员 - `platform_device` 包含 `device` 成员 - 通过`container_of`宏实现父子结构转换: ```c struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct platform_driver *pdrv = container_of(drv, struct platform_driver, driver); ``` #### 3. **功能对比表** | 结构体 | 作用域 | 关键成员 | 生命周期管理 | |------------------|-------------|----------------------------|-------------------| | `device_driver` | 通用驱动模型 | name, bus, probe | 所有设备类型通用 | | `platform_driver`| 平台设备驱动 | driver, probe, id_table | 封装platform特有操作 | | `platform_device`| 平台设备实例 | name, dev, resource | 描述SoC固定资源 | #### 4. **典型驱动开发流程** ```c // 定义平台驱动 static struct platform_driver my_driver = { .probe = my_probe, .driver = { .name = "my_device", // 匹配设备名称 .owner = THIS_MODULE, }, }; // 设备探测函数 static int my_probe(struct platform_device *pdev) { // 1. 获取设备资源 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 2. 映射寄存器 void __iomem *base = devm_ioremap_resource(&pdev->dev, res); // 3. 注册字符设备等操作 ... } // 注册驱动 module_platform_driver(my_driver); ``` #### 5. **设备树(Device Tree)整合** 现代Linux使用设备树替代硬编码`platform_device`: ```dts my_device: my_device@0x12340000 { compatible = "vendor,my-device"; // 匹配驱动 reg = <0x12340000 0x1000>; // 寄存器空间 interrupts = <0 45 4>; // 中断号 }; ``` 驱动通过`of_match_table`匹配: ```c static const struct of_device_id my_of_ids[] = { { .compatible = "vendor,my-device" }, {} }; struct platform_driver my_driver = { .driver = { .of_match_table = my_of_ids, }, }; ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值