第04节_总线设备驱动模型
总线驱动模型是为了解决什么问题呢?
使用之前的驱动模型,编写一个led驱动程序,如果需要修改gpio引脚,则需要修改驱动源码,重新编译驱动文件,假如驱动放在内核中,则需要重新编译内核
bus总线是虚拟的概念,并非硬件,dev注册设置某个结构体,这个设备也就是平台设备
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
/*resource 里面确定使用那些资源*/
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;
};
drv那面定义platform_driver 去注册
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 *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
设备和驱动如何进行通信呢?
通过bus进行匹配 platform_match函数确定(dev,drv)若匹配则调用drv中的probe函数
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
这种模型只是一种编程技巧一种机制 并不是驱动程序的核心
platform_match是如何判断dev drv是匹配的?
判断方法是比较dev 和drv 各自的name来进行匹配
平台设备platform_device这面有name
platform_driver这面有 driver (里面含有name) 还有id_table(包含 name driver_data)
id_table里面的内容表示所支持一个或多个的设备名
static int platform_match(struct device *dev, struct device_driver *drv)
{
/*省略部分无用代码*/
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
也就是优先比较 id_table中名字,如果没有则对比driver中名字
根据二期视频led代码进行修改
/* 分配/设置/注册一个platform_device */
/*设置资源*/
static struct resource led_resource[] = {
[0] = {
/*指明了使用那个引脚*/
.start = S3C2440_GPF(5),
/*end并不重要,可以随意指定*/
.end = S3C2440_GPF(5),
.flags = IORESOURCE_MEM,
},
};static void led_release(struct device * dev)
{
}
static struct platform_device led_dev = {
.name = "myled",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
},
};/*入口函数去注册平台设备*/
static int led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
}
/*出口函数去释放这个平台设备*/
static void led_dev_exit(void)
{
platform_device_unregister(&led_dev);
}module_init(led_dev_init);
module_exit(led_dev_exit);
led_drv驱动文件
static int led_probe(struct platform_device *pdev)
{
struct resource *res; /* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
led_pin = res->start; major = register_chrdev(0, "myled", &myled_oprs); led_class = class_create(THIS_MODULE, "myled");
device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
return 0;
}
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
}
};
static int myled_init(void)
{
platform_driver_register(&led_drv);
return 0;
}static void myled_exit(void)
{
platform_driver_unregister(&led_drv);
}
Makefile文件
KERN_DIR = /work/system/linux-4.19-rc3all:
make -C $(KERN_DIR) M=`pwd` modules clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.orderobj-m += led_drv.o
obj-m += led_dev.o
执行测试程序
如果我需要更换一个led 则只需要修改 led_dev led_resource结构体中的引脚即可
static struct resource led_resource[] = {
[0] = {
.start = S3C2440_GPF(6),
.end = S3C2440_GPF(6),
.flags = IORESOURCE_MEM,
},
};
设备和驱动的匹配是如何完成的?
dev这面有设备链表
drv这面也有驱动的结构体链表
通过match函数进行对比,如果相同,则调用drv中的probe函数