Linux platform driver机制和传统的device_driver机制相比,一个十分明显的优势在于platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。
这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。platform是一个虚拟的地址总线,相比pci,usb,它主要用于描述SOC上的片上资源,比如s3c2410上集成的控制器(lcd,watchdog,rtc等),platform所描述的资源有一个共同点,就是可以在cpu的总线上直接取址.
platform机制开发的并不复杂,由两部分组成:platform_device和platfrom_driver
通过Platform机制开发发底层驱动的大致流程为:
定义 platform_device
注册 platform_device
定义 platform_driver
注册 platform_driver
platform_device 一般来说是和硬件紧密相关的,而 platform_driver 是比较通用的代码。
而这两者是通过match函数进行比较的,比较方法就是通过name去看是不是有对应相同的name。
如果有匹配到的device,那么driver这边就会调用probe函数进行注册了。
(而现在的platform_device其实是从设备树里面进行的转化,所以现在我们其实不会太关心platform_device驱动,内核自从引入dts机制后, platform_device_register已经不推荐使用,而是直接通过of_platform_default_populate_init完成platform_device的注册)
相关代码和目录在这里。大家可以再内核里面自己去搜索下
driver/of/platform.c
of_platform_default_populate_init
of_platform_default_populate(NULL, NULL, NULL);/* Populate everything else. */
of_platform_populate(root, matches, lookup, parent)
of_platform_bus_probe(root, matches, parent)
for_each_child_of_node(root, child) {
of_platform_bus_create(child, matches, lookup, parent, true);
//节点必须包含compatible属性,才能创建platform_device
if (strict && (!of_get_property(bus, "compatible", NULL))) {
pr_debug("%s() - skipping %pOF, no compatible prop\n",
__func__, bus);
return 0;
}
//如果bus节点的compatile属性不吻合matches成表, 就不处理它的子节点
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
--------------------------------------------------------------------
//如果bus节点的compatile属性吻合matches成表, 就处理它的子节点
if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
pr_debug("%s() - skipping %pOF, already populated\n",
__func__, bus);
return 0;
}
//处理它的子节点, of_platform_bus_create是一个递归调用
--------------------------------------------------------------------
}
为了方便我们理解,我们可以做一个简单的测试。我们写两个驱动,一个代表 device 一个代表 driver。
一个简单得到driver函数:
driver.c
#include
#include
#include
static int hello_probe(struct platform_device *pdev)
{
printk("pdev->name is %s!\n",pdev->name);
pdev->dev.release(&pdev->dev);
return 0;
}
static int hello_remove(struct platform_device *pdev)
{
printk(KERN_EMERG"%s's driver is removed!\n",pdev->name);
return 0;
}
/*static const struct of_device_id of_device_dt_match[] = {
{.compatible = DRIVER_NAME},
{},
};
MODULE_DEVICE_TABLE(of,of_leds_dt_match);
用于设备树的匹配*/
/***************************************/
struct platform_driver platform_driver_hello = {
.probe = hello_probe,
.remove = hello_remove,
.driver = {
.name = "hello_device",
}
};
static int __init hello_init(void)
{
int ret;
ret = platform_driver_register(&platform_driver_hello);
if(ret)
{
printk(KERN_EMERG"platform driver register failed!\n");
return ret;
}
return ret;
}
static void __exit hello_exit(void)
{
platform_driver_unregister(&platform_driver_hello);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("cong.luo");
MODULE_DESCRIPTION ("A hello module");
为了直接我们同时把device驱动也写好
device.c
#include
#include
/*The header of driver register,include struct ,register function and unregister function about driver*/
#include
static void hello_release(struct device *dev)
{
printk("hello_module release\n");
}
struct platform_device platform_device_hello_module = {
.name = "hello_device",
.id = -1,
.dev = {
.release=hello_release,
},
};
static int __init hello_init(void)
{
platform_device_register(&platform_device_hello_module);
return 0;
}
static void __exit hello_exit(void)
{
platform_device_unregister(&platform_device_hello_module);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("cong.luo");
MODULE_DESCRIPTION ("A test module");
添加 Makefile
KEVN := $(shell uname -r)
PWD := $(shell pwd)
KERN_DIR := /lib/modules/$(KEVN)/build
obj-m += dirver.o
obj-m += device.o
all:
make -C $(KERN_DIR) M=$(PWD) modules
clean:
make -C $(KERN_DIR) M=$(PWD) clean
编译执行。单独加载driver.c
可以看到没有我们想要的log,这个时候我们将device驱动添加上去
说明我们采用这样方式加载驱动的时候,必须要有对应的匹配机制才可以让我们驱动加载,不然我们的驱动只是编译但是没有运行。