在SOC中存在I2C、SPI、USB 等总线模型。通过总线来将驱动和设备分开。形成【驱动】-【z总线模型】-【设备】
在SOC中不存在这个外设的总线模型时,就引出了platform总线模型。同样的也是将具体的驱动和控制的设备对象分开。
include/linux/device.h中,bus_type便是这个总线模型,通过其中的match函数对驱动和设备进行匹配。
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);
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;
};
//注册和注销设备,如果是从驱动获取设备资源,就需要注册该部分
//如果是从设备树获取资源,那么使用对应的of函数从设备树获取对应的资源即可。match函数通过注册的driver和设备树中compatible比较获取资源。
static struct platform_device led_device = {
.name = "_ledxx",
.id = -1,
.dev = {
.release = &led_release,
},
.num_resources = ...,
.resource = ...,
};
platform_device_register(&led_device );
platform_device_unregister(&led_device );
//注册和注销驱动
static struct platform_driver led_driver=
{
.drver = {
.name = "_ledxx",
}
.probe = xxx_probe,//probe函数指针
.remove= xxx_remove,//remove函数指针
}
platform_driver_register(&led_driver);
platform_driver_unregister(&led_driver);
其中在注册设或则是注册驱动的时候,最终会调用到bus_type中的match函数,进行比较查找,如果找到对应的驱动或则设备,那么就会调用到driver中的probe函数,所以我们应该在probe函数中进行创建设备驱动和对应的初始化。
/* 设备结构体 */
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,
ze_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);
}
/* 驱动模块卸载 */
static void __exit xxxdriver_exit(void)
{
platform_driver_unregister(&xxx_driver);
}
module_init(xxxdriver_init);
module_exit(xxxdriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("you_name");
其中name 表示设备名字,要和所使用的 platform_driver驱动的 name 字段相同,否则的话设
备就无法匹配到对应的驱动。
num_resources 表示资源数量,一般为第 28 行 resource 资源的大小。
resource 表示资源,也就是设备信息,比如外设寄存器等。
struct platform_device {
const char *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;
};
device_driver(直接复制正点原子的教程中的部分资料)
/* 寄存器地址定义*/
#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_MEM,
},
[1] = {
.start = PERIPH2_REGISTER_BASE,
.end = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
};
/* platform 设备结构体 */
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 xxx_resourcesdevice_exit(void)
{
platform_device_unregister(&xxxdevice);
}
module_init(xxxdevice_init);
module_exit(xxxdevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yourname");
此时不需要使用device_driver。在设备树中compatible属性去描述,通过该属性去与platform进行匹配。
//设备树添加设备节点
1 gpioled {
2 #address-cells = <1>;
3 #size-cells = <1>;
4 compatible = "my-gpioled";
5 pinctrl-names = "default";
6 pinctrl-0 = <&pinctrl_led>;
7 led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
8 status = "okay";
9 };
//platform中定义设备匹配信息
1 static const struct of_device_id leds_of_match[] = {
2 { .compatible = "my-gpioled" }, /* 兼容属性 */
3 { /* Sentinel */ }
4 };
5
6 MODULE_DEVICE_TABLE(of, leds_of_match);
7
8 static struct platform_driver leds_platform_driver = {
9 .driver = {
10 .name = "imx6ul-led",
11 .of_match_table = leds_of_match,
12 },
13 .probe = leds_probe,
14 .remove = leds_remove,
15 };