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