Bootstrap

Linux第91步_了解“platform总线,platform驱动和platform设备”,以及驱动框架和设备框架

plattorm是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动

对于一个完整的驱动程序,必须提供“有设备树”和“无设备树”两种匹配方法。

1、总线

Linux系统内核使用bus_type结构体表示总线,需要包含“#include <linux/device.h>”,结构体如下:

struct bus_type {

    const char    *name;

    const char    *dev_name;

    struct device     *dev_root;

    const struct attribute_group **bus_groups;

    const struct attribute_group **dev_groups;

    const struct attribute_group **drv_groups;

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

//dev是设备,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 (*online)(struct device *dev);

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

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

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

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

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

    const struct dev_pm_ops *pm;

    const struct iommu_ops *iommu_ops;

    struct subsys_private *p;

    struct lock_class_key lock_key;

    bool need_parent_lock;

};

2、platform总线

platform_bus_type是platform平台总线,它是bus_type的一个具体实例,需要包含“#include <linux/platform_device.h>”,platform总线定义如下:

struct bus_type  platform_bus_type = {

    .name      = "platform",

    .dev_groups   = platform_dev_groups,

    .match     = platform_match,

    .uevent       = platform_uevent,

    .dma_configure    = platform_dma_configure,

    .pm    = &platform_dev_pm_ops,

};

#include <linux/of_device.h>

static int platform_match(struct device *dev,struct device_driver *drv)

{

struct platform_device *pdev = to_platform_device(dev);

struct platform_driver *pdrv = to_platform_driver(drv);

/*When driver_override is set,only bind to the matching driver*/

if (pdev->driver_override)//当设置driver_override时,只绑定到匹配的驱动程序

return !strcmp(pdev->driver_override, drv->name);

/* Attempt an OF style match first */

if (of_driver_match_device(dev, drv))

//第1种匹配方式:OF类型的匹配,就是设备树采用的匹配方式

return 1;

/* Then try ACPI style match */

if (acpi_driver_match_device(dev, drv)) //第2种匹配方式:ACPI匹配方式

return 1;

/* Then try to match against the id table */

if (pdrv->id_table)//第3种匹配方式:id_table匹配

return platform_match_id(pdrv->id_table, pdev) != NULL;

/* fall-back to driver name match */

return (strcmp(pdev->name, drv->name) == 0);

/*

第4种匹配方式:如果第3种匹配方式的id_table不存在,就直接比较驱动和设备的name 字段,看看是不是相等,如果相等的话就匹配成功。

*/

}

3、platform驱动

对于一个完整的驱动程序,必须提供有设备树和无设备树两种匹配方法。

platform_driver结构体表示“platform驱动”,需要包含“#include <linux/platform_device.h>”,结构体如下:

struct platform_driver {

    int (*probe)(struct platform_device *);

/*probe()函数,当“驱动”与“设备”匹配成功以后,probe()函数就会执行*/

    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;

/*id_table是个表(也就是数组),每个元素的类型为platfomm_device_id*/

    bool prevent_deferred_probe;

};

platfomm_device_id结构体如下:

struct platform_device_id {

    char name[PLATFORM_NAME_SIZE];

    kernel_ulong_t driver_data;

};

device_driver结构体,需要包含“#include <linux/device.h>”如下:

struct device_driver {

    const char    *name;

    struct bus_type      *bus;

    struct module     *owner;

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

    bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */

    enum probe_type probe_type;

    const struct of_device_id   *of_match_table;

/*of_match_table是采用设备树时,驱动使用的匹配表,同样是数组,每个匹配项都为of_device_id结构体类型*/

    const struct acpi_device_id *acpi_match_table;

    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 attribute_group **dev_groups;

    const struct dev_pm_ops *pm;

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

    struct driver_private *p;

};

使用of_device_id结构体,需要包含“#include <linux/mod_devicetable.h>”,结构体如下:

struct of_device_id {

    char   name[32];

    char   type[32];

    char   compatible[128];

/*

对于设备树而言,就是通过设备节点的 compatible 属性值和of_match_table中每个项目的 compatible成员变量进行比较,如果有相等的就表示设备和此驱动匹配成功;

*/

    const void *data;

};

int platform_driver_register (struct platform_driver *driver)

//向Linux内核注册一个platform驱动

//driver:要注册的 platform 驱动;

//返回值:负数,失败;0,成功;

void platform_driver_unregister(struct platform_driver *drv)

//卸载一个platform驱动

//drv:要卸载的platform驱动;

4、platform驱动框架举例

/* 设备结构体 */

struct xxx_dev{

dev_t devid; /*声明32位变量devid用来给保存设备号*/

int major;   /*主设备号*/

int minor;   /*次设备号*/

struct cdev  cdev; /*字符设备结构变量cdev */

struct class *class;     /*类*/

struct device *device;  /*设备*/

atomic_t lock;  /*原子变量*/

struct fasync_struct *async_queue;

/* fasync_struct结构体,即“异步通知”结构体 */

};

struct xxx_dev xxxdev; /* 定义个设备结构体变量 */

static int xxx_open(struct inode *inode, struct file *filp)

{

  /* 函数具体内容 */

  return 0;

}

static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)

{

  /* 函数具体内容 */

  return 0;

}

/*字符设备驱动操作集*/

static struct file_operations xxx_fops = {

   .owner = THIS_MODULE,

   .open = xxx_open,

   .write = xxx_write,

};

/*platform驱动的probe函数

驱动与设备匹配成功以后此函数就会执行

*/

static int xxx_probe(struct platform_device *dev)

{

  ......

  cdev_init(&xxxdev.cdev, &xxx_fops); /* 注册字符设备驱动 */

  /* 函数具体内容 */

  return 0;

}

static int xxx_remove(struct platform_device *dev)

{

  .....

  cdev_del(&xxxdev.cdev);/* 删除cdev */

  /* 函数具体内容 */

  return 0;

}

/*对于一个完整的驱动程序,必须提供有设备树和无设备树两种匹配方法*/

/* 匹配列表 */

static const struct of_device_id  xxx_of_match[] = {

  { .compatible = "xxx-gpio" },

  { /* Sentinel */ }

};

/*platform平台驱动结构体*/

static struct platform_driver xxx_driver = {

  .driver = {

  .name = "xxx",//用于传统的驱动与设备匹配

  .of_match_table = xxx_of_match, //用于设备树下的驱动与设备检査

  },

  .probe = xxx_probe,

  .remove = xxx_remove,

};

/* 驱动模块加载 */

static int __init xxxdriver_init(void)

{

   return platform_driver_register(&xxx_driver);

   //向Linux内核注册一个platform驱动

}

/* 驱动模块卸载 */

static void __exit xxxdriver_exit(void)

{

  platform_driver_unregister(&xxx_driver);

  //卸载一个platform驱动

}

module_init(xxxdriver_init);

module_exit(xxxdriver_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zgq");

5、platform设备

platform_device结构体,需要包含“#include <linux/device.h>”如下:

struct platform_device {

    const char *name;

//设备名字,要和platform驱动的name字段相同,这样,设备和驱动才能匹配

    int    id;

    bool       id_auto;

    struct device dev;

    u64    platform_dma_mask;

    u32    num_resources; //资源数量

    struct resource   *resource;//表示资源,也是设备信息

    const struct platform_device_id *id_entry;

    char *driver_override; /* Driver name to force a match */

    /* MFD cell pointer */

    struct mfd_cell *mfd_cell;

    /* arch specific additions */

    struct pdev_archdata archdata;

};

resource结构体,需要包含“#include <linux/ioport.h>”如下:

struct resource {

    resource_size_t start;   //资源的起始信息,若为内存资源,则表示内存的起始地址

    resource_size_t end;    //资源的终止信息,若为内存资源,则表示内存的终止地址

    const char *name;      //资源的名字

    unsigned long flags;     //资源的內型

    unsigned long desc;     //

    struct resource *parent, *sibling, *child;

};

资源內型

#define IORESOURCE_BITS     0x000000ff   /* Bus-specific bits */

#define IORESOURCE_TYPE_BITS    0x00001f00  /* Resource type */

#define IORESOURCE_IO          0x00000100

   /*IO口的资源,PCI/ISA I/O ports */

#define IORESOURCE_MEM      0x00000200  //内存地址

#define IORESOURCE_REG      0x00000300 /* Register offsets */

#define IORESOURCE_IRQ      0x00000400  //中断号

#define IORESOURCE_DMA      0x00000800  //DMA通道号

#define IORESOURCE_BUS      0x00001000  //总线号

#define IORESOURCE_PREFETCH   0x00002000 /* No side effects */

#define IORESOURCE_READONLY   0x00004000

#define IORESOURCE_CACHEABLE    0x00008000

#define IORESOURCE_RANGELENGTH  0x00010000

#define IORESOURCE_SHADOWABLE   0x00020000

int platform_device_register(struct platform_device *pdev)

/*在linux不支持设备树时,需要采用手动方式将“platform设备信息”注册到Linux内核*/

//pdev:要注册的platform设备。

//返回值:负数,失败;0,成功。

void platform_device_unregister(struct platform_device *pdev)

//在linux不支持设备树时,需要手动注销掉相应的“platform设备”

//pdev:要注销的platform设备。

6、platform设备框架举例:

/* 寄存器地址定义*/

#define PERIPH1_REGISTER_BASE (0X20000000)   /* 外设1寄存器首地址 */

#define PERIPH2_REGISTER_BASE (0X020E0068)   /* 外设2寄存器首地址 */

#define REGISTER_LENGTH    4                 /* 设备资源 */

static struct resource xxx_resources[] = {

   [0] = { .start = PERIPH1_REGISTER_BASE,//设备外设1起始地址

           .end = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1),

           //设备外设1终止地址

           .flags = IORESOURCE_MEM, //资源內型:内存地址

         },

   [1] = { .start = PERIPH2_REGISTER_BASE, //设备外设2起始地址

           .end = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1),

           //设备外设2终止地址

           .flags = IORESOURCE_MEM, //资源內型:内存地址

         },

};

/* platform设备结构体 */

static struct platform_device xxxdevice = {

   .name = "xxx-gpio",  //必须保证和驱动中的名字一致

   .id = -1,

   .num_resources = ARRAY_SIZE(xxx_resources),

   //数组xxx_resources[]的大小,ARRAY_SIZE()用来计算数组元素的个数

   .resource = xxx_resources,//数组xxx_resources[]

};

/* 设备模块加载 */

static int __init xxxdevice_init(void)

{

  return platform_device_register(&xxxdevice);

//在linux不支持设备树时,需要将“platform设备信息”注册到Linux内核

}

/* 设备模块注销 */

static void __exit xxx_resourcesdevice_exit(void)

{

   platform_device_unregister(&xxxdevice);

   //注销掉相应的“platform设备”

   //pdev=&xxxdevice:要注销的platform设备。

}

module_init(xxxdevice_init);

module_exit(xxxdevice_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zgq");

;