Platform:平台设备总线驱动模型
将软件驱动和硬件隔离,减少移植成本。Linux使用 设备-总线-驱动 模型。
硬件部分(设备) A B C D
总线 ------------------------------------
软件部分(软件) X Y Z
在Linux内核中实现了一条虚拟总线,叫做 platform_bus_type:
#inlcude <linux/platform_device.h>
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.probe = platform_probe,
.remove = platform_remove,
.shutdown = platform_shutdown,
.dma_configure = platform_dma_configure,
.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);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
/* 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);
};
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources;
struct resource *resource; // 硬件信息,通过start, end 判断类型
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 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; // 驱动信息,name
const struct platform_device_id *id_table; // id
bool prevent_deferred_probe;
};
总线上维护两条链表:dev(设备)链表: platform_device,drv(驱动)链表:platform_driver。
匹配的动作发生在添加驱动软件或者硬件节点的时候。match函数匹配成功后,调用probe函数,进行驱动。
match过程(优先级):
编程只负责向内核添加节点就可以了,匹配过程内核已经设计好了。
// 1 配置硬件节点
struct resource hdev_res[] = {
[0] = {
.start = 0, // 硬件信息起始数据,起始IO端口号,中断号等
.end = 0, // 硬件信息结束数据,
.flags = IORESOURCE_IO, // 硬件信息的类型,
},
....
};
// 2 初始化硬件节点
struct platform_device hdev_dev = {
.name = "devName", // 用于drv匹配的
.id = -1, // 防止节点重名,没有重名可以给-1
.resource = hdev_res, // 硬件资源数据
.num_resource = ARRAY_SIZE(hdev_res); // 数组长度
.dev = {
.platform_data = "", // 传递额外的数据信息
.release = func_rel, // 删除硬件节点时,自动调用
},
};
// 3 添加注册
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
setup_pdev_dma_masks(pdev);
return platform_device_add(pdev);
}
// platform_device_unregister
// 1 初始化
struct platform_driver hdev_drv = {
.id_table = tab_list; // 名称数组,数组以null结束,用于名称匹配
.dirver = {
.name = "drvName", // 用于匹配,优先级低于id_table
}
.probe = func_pro; // 匹配成功自动调用
.remove = func_rem; // 解除匹配自动调用
};
// 2 添加注册
int platform_driver_register(struct platform_driver *drv);
test2_drv.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
// rror: array type has incomplete element type ‘struct platform_device_id’
// struct platform_device_id cdd_table[] = {
// {"cdd"}, {"cdd1"}, {"cdd2"}, {""}
// };
int cdd_probe(struct platform_device *dev){
printk("cdd_probe called");
return 0;
}
int cdd_remove(struct platform_device *dev){
printk("cdd_remove called");
return 0;
}
struct platform_driver hdev_drv = {
//.id_table = cdd_table,
.probe = cdd_probe,
.remove = cdd_remove,
.driver = {
.name = "cdd",
}
};
int plat_init(void){
platform_driver_register(&hdev_drv);
return 0;
}
void plat_exit(void){
platform_driver_unregister(&hdev_drv);
}
module_init(plat_init);
module_exit(plat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andy");
MODULE_DESCRIPTION("Fist platform driver test Module!");
MODULE_VERSION("1.0");
test2_dev.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
struct resource hdev_arr[] = {
[0] = {
.start = 0x60000000,
.end = 0x60000004,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0x60000004,
.end = 0x60000008,
.flags = IORESOURCE_MEM,
},
};
void cdd_release(struct device* dev){
printk("cdd_release in device.");
}
struct platform_device hdev_dev = {
.name = "cdd",
.id = -1,
.resource = hdev_arr,
.num_resources = ARRAY_SIZE(hdev_arr),
.dev = {
.release = cdd_release,
}
};
int plat_init(void){
platform_device_register(&hdev_dev);
return 0;
}
void plat_exit(void){
platform_device_unregister(&hdev_dev);
}
module_init(plat_init);
module_exit(plat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andy");
MODULE_DESCRIPTION("Fist platform device test Module!");
MODULE_VERSION("1.0");
Makefile
# ifneq ($(KERNELRELEASE),)
# mymodule-bojs := hello.o
PWD := $(shell pwd)
KERNELDIR = /lib/modules/$(shell uname -r)/build
obj-m := test2_drv.o test2_dev.o
build: all
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
实际上,Ubuntu有内核保护,驱动没有正确加载运行起来,不过学习的目的达到了。