platform 驱动
这是 linux 设备驱动分离与分层思想的一种架构产物,由总线(bus)、驱动(driver)、设备(device),这样我们只需要根据不同驱动和设备,采用统一的 API 来提高程序的重用率
platform 总线
需要使用 bus_type 结构体 include/linux/device.h
//实例: driver/base/platform.c 定义实例
struct bus_type {
......
int (*match)(struct device *dev, struct device_driver *drv); //用于匹配驱动和设备
......
};
驱动与设备的匹配方式
驱动与设备有 4 种方式来进行,驱动和设备的匹配
//实例:drivers/base/platform.c
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
//比较一下两者是否相同,如果相同 strcmp 返回的是 0,有所差别返回的就是非0 A<B => <0, A>B => >0, A=B => =0
if (pdev->driver_override)
return !strcmp(pdev->driver_override, dev->name);
//方式1: 采用 of 匹配(设备树)
if (of_driver_match_device(dev, drv))
return 1;
//方式2: 采用 acpi 方式匹配
if (acpi_driver_match_device(dev, drv))
return 1;
//方式3: 采用 id 表
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
//方式4: 直接比较驱动和设备的 name 字段看看是否匹配
return (strcmp(pdev->name, drv->name) == 0);
}
ACPI:由国外开发的电源管理标准,主要用于控制电脑开、关、低功耗等状态,以及对于即插即用设备控制并为其供电
ID table: 在 linux 存在很长时间,基于 struct device_id(include/linux/mod_devicetable.h)
方式3与方式4都属于适配无设备树情况下的驱动设备驱动设备匹配方式,一般使用方式4多一点,比较简单
platform 驱动
基于 platform_driver 结构体(include/linux/platform_device.h)
struct platform_driver {
......
int (*probe)(struct platform_device *);//设备匹配成功之后,会执行该函数
struct device_driver driver; //基础驱动框架
const struct platfrom_device_id *id_table;
......
};
struct platform_device_id {
char name[PLATFORM_NAME_SIZE];
kernel_ulong_t driver_data;
};
//include/linux/device.h
struct device_driver {
......
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
......
};
//include/linux/mod_devicetable.h
struct of_device_id {
char name[32];
char type[32];
char compatible[128];
const void *data;
};
函数 | 描述 |
---|---|
int platform_driver_register(struct platform_driver *driver) | 向内核注册驱动,0成功;<0 失败 |
void platform_driver_unregister(struct platform_driver *drv) | 卸载驱动 |
//platform 驱动框架
//设备结构体
struct xxx_dev {
struct cdev cdev;
......
};
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_dev(&xxxdev.cdev); //删除 dev
//具体内容
return 0;
}
//platform 匹配列表,必须最后留空
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx-gpio" },
{ }
};
//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);
}
//卸载
static void __exit xxxdriver_exit(void)
{
platform_driver_unregister(&xxx_driver);
}
module_init(xxxdriver_init);
module_exit(xxxdriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
platform 设备
若使用设备树,直接在设备树中定义即可
以下内容属于非设备树使用
//include/linux/platform_device.h
struct platform_device {
const char *name;
struct resource *resource;
......
};
//设备信息
struct resource {
......
resource_size_t start;//对于内存类资源
resource_size_t end;
const char *name;
unsigned long flags; //资源类型
struct resource *parent, *sibling, *child;
};
//资源类型 include/linux/ioport.h
#define IORESOURCE_BITS 0xff
#define IORESOURCE_TYPE_BITS 0x1f00
.......
函数 | 描述 |
---|---|
int platform_device_register(struct platform_device *pdev) | 注册设备信息向内核 |
void platform_device_unregister(struct platform_device *pdev) | 卸载设备 |
//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,
.end = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1);
.flags = IORESOURCE_MEN,
},
[1] = {
.start = PERIPH2_REGISTER_BASE,
.end = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEN,
},
};
//设备结构体
static struct platform_device xxxdevice = {
.name = "xxx-gpio",
.id = -1,
.num_resources = ARRAY_SIZE(xxx_resources),
.resource = xxx_resources,
};
//加载设备
static int __init xxxdevice_init(void)
{
return platform_device_register(&xxxdevice);
}
//卸载设备
static void __exit xxxdevice_exit(void)
{
platform_device_unregister(&xxxdevice);
}
module_init(xxxdevice_init);
moduel_exit(xxxdevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");