前言
在linux内核中我们找不到类似之前demo类型的代码,对于linux来说,代码重用性很重要,否则内核中就会出现很多垃圾代码导致内核文件数相当的大。就比如相同的模块在不同的平台就有不同的驱动程序,那么以此来说的话一个硬件就对应多个驱动文件,显然在linux内核中是不允许存在的。所以在linux内核中提出了,总线,驱动,设备的概念。
当我们向系统注册一个设备(驱动)总线就去看有没有与之匹配的驱动(设备)如果有的话就把彼此建立起来联系。
linux内核中大量的驱动都采用这个方式,驱动以标准途径拿到board_info,这样芯片原厂做了驱动工作,我们拿到芯片只需要按照原厂标准去写设备驱动。这种方式达到了驱动的分层隔离思想。
Platform设备驱动
基于上述方式,linux提供了一种虚拟总线Platform,相应设备为platform_device,相应驱动为platform_driver
总线
struct bus_type {
const char *name; //总线名字
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
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);/*匹配函数*/
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);
/*
现在一般休眠唤醒都在dev_pm_ops 中执行
*/
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(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;
};
总线就是通过match函数来根据注册的设备或驱动进行相应匹配,match函数指针的两个参数分别为 device 和 device_driver 类型,也就是设备和驱动。
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
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)
return !strcmp(pdev->driver_override, drv->name);
/*设备树匹配方式*/
if (of_driver_match_device(dev, drv))
return 1;
/*ACPI匹配方式*/
if (acpi_driver_match_device(dev, drv))
return 1;
/*id_table匹配方式*/
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/*name字段匹配*/
return (strcmp(pdev->name, drv->name) == 0);
}
device_driver 中有of_match_table成员,它保存着驱动的compatible匹配表,设备树中的每个设备节点的 compatible 属性会和 of_match_table 表中的所有成员比较,查看是否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe 函数就会执行。
驱动
struct device_driver {
const char *name;
struct bus_type *bus;
...
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
...
};
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 *);
/*
device_driver中有成员of_device_id,acpi_device_id,name匹配方式
*/
struct device_driver driver;
/*
id_table匹配方式
*/
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
int platform_driver_register (struct platform_driver *driver);//注册platform 驱动
void platform_driver_unregister(struct platform_driver *driver);//卸载platform 驱动
模板
/*
* platform 驱动的 probe 函数
* 驱动与设备匹配成功以后此函数就会执行
*/
static int xxx_probe(struct platform_device *dev){
return 0;
}
static int xxx_remove(struct platform_device *dev){
return 0;
}
/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx_xxx"},
{ /* Sentinel */ }
};
static struct platform_driver xxx_driver = {
.driver = {
.name = "xxx",
.of_match_table = xxx_of_match,
},
.probe = xxx_probe,
.remove = xxx_remove,
};
static int __init xxx_init(void){
return platform_driver_register(&xxx_driver);
}
static void __exit xxx_exit(void){
platform_driver_unregister(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin);
设备
struct platform_device {
const char *name;//设备名字,用于name匹配
int id;
bool id_auto;
struct device dev;
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;
};
struct resource {
resource_size_t start;//起始信息
resource_size_t end;//终止信息
const char *name;
unsigned long flags;//资源类型
struct resource *parent, *sibling, *child;
};
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);
模板
/* 寄存器地址定义*/
#define REGI1_BASE (0X20000000) /* 外设 1 寄存器首地址 */
#define REGI2_BASE (0X02000000) /* 外设 2 寄存器首地址 */
#define REGISTER_LENGTH 4
/* 资源 */
static struct resource xxx_resources[] = {
[0] = {
.start = REGI1_BASE ,
.end = (REGI1_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,//内存类型
},
[1] = {
.start = REGI2_BASE ,
.end = (REGI2_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
};
static struct platform_device xxx_device= {
.name = "xxx",
.id = -1,
.num_resources = ARRAY_SIZE(xxx_resources),
.resource = xxx_resources,
};
static int __init xxx_init(void){
return platform_device_register(&xxx_device);
}
static void __exit xxx_exit(void){
platform_device_unregister(&xxx_device);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");
demo
platform_device.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
/* 寄存器地址定义*/
#define REGI1_BASE (0X20000000) /* 外设 1 寄存器首地址 */
#define REGI2_BASE (0XC2000000) /* 外设 2 寄存器首地址 */
#define REGISTER_LENGTH 4
/* 资源 */
static struct resource demo_device_resources[] = {
[0] = {
.start = REGI1_BASE ,
.end = (REGI1_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,//内存类型
},
[1] = {
.start = REGI2_BASE ,
.end = (REGI2_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
};
static void demo_release(struct device *dev)
{
printk("demo device released!\r\n");
}
static struct platform_device demo_device= {
.name = "demo-test",
.id = -1,
.dev = {
.release = demo_release,
},
.num_resources = ARRAY_SIZE(demo_device_resources),
.resource = demo_device_resources,
};
static int __init demo_device_init(void){
return platform_device_register(&demo_device);
}
static void __exit demo_device_exit(void){
platform_device_unregister(&demo_device);
}
module_init(demo_device_init);
module_exit(demo_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");
platform_driver.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#define DEMO_CNT 1 /* 设备号个数 */
#define DEMO_NAME "demo" /* 名字 */
#define MEM_BUFFER_SIZE 1000
/* demo设备结构体 */
struct demo_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
unsigned char mem[MEM_BUFFER_SIZE];
};
struct demo_dev demo; /* demo设备 */
static struct file_operations demo_fops = {
.owner = THIS_MODULE,
};
char* strcpy(char* dest, const char* src) {
char* tmp = dest;
while ((*dest++ = *src++) != '0');
return tmp;
}
static ssize_t std_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n",demo.mem);
}
static ssize_t std_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
strcpy(demo.mem,buf);
return len;
}
static DEVICE_ATTR_RW(std);
static struct attribute *demo_class_attrs[] = {
&dev_attr_std.attr,
NULL,
};
static const struct attribute_group demo_group = {
.attrs = demo_class_attrs,
};
static const struct attribute_group *demo_groups[] = {
&demo_group,
NULL,
};
static int demo_probe(struct platform_device *dev)
{
struct resource *source[2];
int ressize[2];
int i = 0;
printk(" driver match device is success !!!\n");
/* 1、获取资源 */
for (i = 0; i < 2; i++) {
source[i] = platform_get_resource(dev, IORESOURCE_MEM, i); /* 依次MEM类型资源 */
if (!source[i]) {
dev_err(&dev->dev, "No MEM resource for always on\n");
/*
正常这里是要返回的
*/
//return -ENXIO;
}else{
ressize[i] = resource_size(source[i]);
printk(" souce [%#x] ressize [%d]\n",source[i]->start,ressize[i]);
}
}
/* 1、创建设备号 */
if (demo.major) { /* 定义了设备号 */
demo.devid = MKDEV(demo.major, 0);
register_chrdev_region(demo.devid, DEMO_CNT, DEMO_NAME);
} else { /* 没有定义设备号 */
alloc_chrdev_region(&demo.devid, 0, DEMO_CNT, DEMO_NAME); /* 申请设备号 */
demo.major = MAJOR(demo.devid); /* 获取分配号的主设备号 */
demo.minor = MINOR(demo.devid); /* 获取分配号的次设备号 */
}
printk("major=%d,minor=%d\r\n",demo.major, demo.minor);
/* 2、初始化cdev */
demo.cdev.owner = THIS_MODULE;
cdev_init(&demo.cdev, &demo_fops);
/* 3、添加一个cdev */
cdev_add(&demo.cdev, demo.devid, DEMO_CNT);
/* 4、创建类 */
demo.class.owner = THIS_MODULE;
demo.class.name = DEMO_NAME;
demo.class.dev_groups = demo_groups;
class_register(&demo.class);
/* 5、创建设备 */
demo.device = device_create(&demo.class, NULL, demo.devid, NULL, DEMO_NAME);
if (IS_ERR(demo.device)) {
return PTR_ERR(demo.device);
}
return 0;
}
static int demo_remove(struct platform_device *dev)
{
/* 注销字符设备驱动 */
cdev_del(&demo.cdev);/* 删除cdev */
unregister_chrdev_region(demo.devid, DEMO_CNT); /* 注销设备号 */
device_destroy(&demo.class, demo.devid);
class_unregister(&demo.class);
return 0;
}
/* 设备树匹配列表 */
static const struct of_device_id demo_of_match[] = {
{ .compatible = "demo-trees" },
{ /* Sentinel */ }
};
/* platform驱动结构体 */
static struct platform_driver demo_driver= {
.driver = {
.name = "demo-test", /* 驱动名字,用于和设备匹配 */
.of_match_table = demo_of_match, /* 设备树匹配表,用于和设备树匹配 */
},
.probe = demo_probe,
.remove = demo_remove,
};
static int __init demo_init(void)
{
return platform_driver_register(&demo_driver);
}
static void __exit demo_exit(void)
{
platform_driver_unregister(&demo_driver);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");
dts文件添加
demo-tree {
#address-cells = <1>;
#size-cells = <1>;
compatible = "demo-trees";
status = "okay";
};
此实例先不在改设备树情况下,加载device 和driver 模块,用 device 和driver 的name进行匹配 匹配成功后probe函数可以读取device资源
在加上设备树代码后 可以只加载driver模块 ,也可以匹配成功,并运行driver模块的probe函数
在不修改设备树的情况下 只加载driver模块,probe函数不运行。