设备是设备,驱动是驱动。
如果把两个糅合写一起,当设备发生变化时,势必要改写整个文件,这是非常愚蠢的做法。如果把他们分开来,当设备发生变化时,只要改写设备文件即可,驱动文件巍然不动。
从linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用 platform_driver 进行注册。
platform将驱动分为platform_device (设备文件)和platform_driver(驱动文件),他们会通过platform总线来相配对。当设备注册到总线时,会通过总线去寻找有没有相对应的驱动文件,有的话则将他两配对。同理,当驱动注册到总线时,会通过总线去寻找有没有相对应的设备文件,有的话也将他两进行配对。
linux platform driver 机制和传统的device driver机制(即:通过 driver_register 函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中用使用这些资源时,通过platform device提供的标准接口进行申请并使用。
以kernel 4.8.17为例,驱动文件:
platform_driver_register(&led_drv);
——>__platform_driver_register
——>drv->driver.bus = &platform_bus_type;
——>.match = platform_match,
——>of_driver_match_device(dev, drv)
——>of_match_device(drv->of_match_table, dev)
——>of_match_node(matches, dev->of_node)
——>__of_match_node(matches, node)
——>__of_device_is_compatible(node, matches->compatible,matches->type, matches->name)
——>acpi_driver_match_device(dev, drv)
——>platform_match_id(pdrv->id_table, pdev)
——>strcmp(pdev->name, drv->name)
代码如上,驱动注册时,会在总线上与设备匹配,有四种匹配方法:
1)如5行,通过这个OpenFirmware的匹配方式,匹配name、type、和compatible字符串三个属性,三者要同时相同(一般name、和type为空,只比较compatible字符串),compatible这个好像是在设备树(dts)里说到,这个之后再讨论。如果不匹配,则会进行第二种匹配方式。
2)如11行,我也不知道这个acpi_driver_match_device是什么,反正也是如果不匹配,则会进行第三种匹配方式。
3)如12行,通过id_table方式匹配,比较设备的名字和id_table里的名字是否有相同的。这样在id_table可以实现一个驱动对应多个设备。如果没有,则会进行第四种匹配方式了。
4)如13行,直接比较设备名字和驱动名字。
即使匹配不成功,也会driver_register(&drv->driver)进行注册,等带设备注册时来与驱动匹配。
如果匹配成功,则会引发驱动的probe()函数执行。
设备文件:
platform_device_register(&led_dev)
platform_device_add(pdev)
pdev->dev.bus = &platform_bus_type
.match = platform_match
/*之后就一样了*/
那么他们具体是怎么操作的呢?我们来具体分析,以platform_device_register为例:
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
这里面,先初始化device,其中涉及kset,可以看看这篇文章:嵌入式Linux驱动学习笔记(十六)------设备驱动模型(kobject、kset、ktype)
然后是platform_device_add函数:
int platform_device_add(struct platform_device *pdev)
{
int i, ret;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
switch (pdev->id) {
default:
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
break;
case PLATFORM_DEVID_NONE:
dev_set_name(&pdev->dev, "%s", pdev->name);
break;
case PLATFORM_DEVID_AUTO:
ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto err_out;
pdev->id = ret;
pdev->id_auto = true;
dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
break;
}
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = dev_name(&pdev->dev);
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
dev_err(&pdev->dev, "failed to claim resource %d\n", i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
/*省略部分代码*/
}
这里面,这是了所属总线,填充好名字,就会调用device_add函数了,
这个函数也很复杂,我放在嵌入式Linux驱动笔记(十六)------设备驱动模型(kobject、kset、ktype)这里讲了
但是,复杂的那些细节我们我们先可以不看,我们看到device_add函数里调用bus_probe_device函数,这一个探测函数:
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
if (!bus)
return;
if (bus->p->drivers_autoprobe)//设置了自动匹配初始化那么就开始匹配
device_initial_probe(dev);
mutex_lock(&bus->p->mutex);
list_for_each_entry(sif, &bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
mutex_unlock(&bus->p->mutex);
}
这里面,调用了device_initial_probe函数,device_initial_probe又调用了__device_attach函数,我们继续看看:
static int __device_attach(struct device *dev, bool allow_async)
{
int ret = 0;
device_lock(dev);
if (dev->driver) {
if (device_is_bound(dev)) {
ret = 1;
goto out_unlock;
}
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
struct device_attach_data data = {
.dev = dev,
.check_async = allow_async,
.want_async = false,
};
if (dev->parent)
pm_runtime_get_sync(dev->parent);
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);
if (!ret && allow_async && data.have_async) {
dev_dbg(dev, "scheduling asynchronous probe\n");
get_device(dev);
async_schedule(__device_attach_async_helper, dev);
} else {
pm_request_idle(dev);
}
if (dev->parent)
pm_runtime_put(dev->parent);
}
out_unlock:
device_unlock(dev);
return ret;
}
函数一开始,先检查device是否绑定过了,接着调用device_bind_driver对device和driver进行绑定:
int device_bind_driver(struct device *dev)
{
int ret;
ret = driver_sysfs_add(dev);//将driver和dev使用link,链接到一起,使他们真正相关
if (!ret)
driver_bound(dev);//将私有成员的driver节点挂到了driver的设备链表
else if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DRIVER_NOT_BOUND, dev);//通知bus上所有设备bound消息
return ret;
}
static int driver_sysfs_add(struct device *dev)
{
int ret;
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BIND_DRIVER, dev);
ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
kobject_name(&dev->kobj));//驱动目录下dev->kobj目录链接到dev->kobj
if (ret == 0) {
ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
"driver");//在dev->kobj目录下的driver目录链接到其驱动目录
if (ret)
sysfs_remove_link(&dev->driver->p->kobj,
kobject_name(&dev->kobj));
}
return ret;
}
接着调用__device_attach_driver进行match:
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
struct device_attach_data *data = _data;
struct device *dev = data->dev;
bool async_allowed;
int ret;
/*
* Check if device has already been claimed. This may
* happen with driver loading, device discovery/registration,
* and deferred probe processing happens all at once with
* multiple threads.
*/
if (dev->driver)
return -EBUSY;
ret = driver_match_device(drv, dev);
if (ret == 0) {
/* no match */
return 0;
} else if (ret == -EPROBE_DEFER) {
dev_dbg(dev, "Device match requests probe deferral\n");
driver_deferred_probe_add(dev);
} else if (ret < 0) {
dev_dbg(dev, "Bus failed to match device: %d", ret);
return ret;
} /* ret > 0 means positive match */
async_allowed = driver_allows_async_probing(drv);
if (async_allowed)
data->have_async = true;
if (data->check_async && async_allowed != data->want_async)
return 0;
return driver_probe_device(drv, dev);
}
这里面,先调用driver_match_device进行各种关键字match:
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
然后就是调用driver_probe_device函数触发probe函数:
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
if (dev->parent)
pm_runtime_get_sync(dev->parent);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);//调用really_probe
pm_request_idle(dev);
if (dev->parent)
pm_runtime_put(dev->parent);
return ret;
}
函数一开始检查device是否注册过,如果注册过,直接return。
否则,则调用really_probe函数:
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = -EPROBE_DEFER;
int local_trigger_count = atomic_read(&deferred_trigger_count);
if (defer_all_probes) {
/*
* Value of defer_all_probes can be set only by
* device_defer_all_probes_enable() which, in turn, will call
* wait_for_device_probe() right after that to avoid any races.
*/
dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
driver_deferred_probe_add(dev);
return ret;
}
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
if (ret)
goto pinctrl_bind_failed;
if (driver_sysfs_add(dev)) {//驱动目录下建立一个到设备的同名链接,并且在设备目录下建立一个名为 driver.到驱动的链接
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->pm_domain && dev->pm_domain->activate) {
ret = dev->pm_domain->activate(dev);
if (ret)
goto probe_failed;
}
/*
* Ensure devices are listed in devices_kset in correct order
* It's important to move Dev to the end of devices_kset before
* calling .probe, because it could be recursive and parent Dev
* should always go first
*/
devices_kset_move_last(dev);
if (dev->bus->probe) {
ret = dev->bus->probe(dev);//如果bus的probe存在就用bus的
if (ret)
goto probe_failed;
} else if (drv->probe) {//如果bus的不存在driver的存在
ret = drv->probe(dev);//再用driver的
if (ret)
goto probe_failed;
}
pinctrl_init_done(dev);
if (dev->pm_domain && dev->pm_domain->sync)
dev->pm_domain->sync(dev);
driver_bound(dev);//调用driver_bound进行绑定
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
/*省略部分代码*/
}
这里,先调用driver_sysfs_add函数,把drivers添加到sysfs中:
static int driver_sysfs_add(struct device *dev)
{
int ret;
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BIND_DRIVER, dev);
ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
kobject_name(&dev->kobj));//驱动目录下dev->kobj目录链接到dev->kobj
if (ret == 0) {
ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
"driver");//在dev->kobj目录下的driver目录链接到其驱动目录
if (ret)
sysfs_remove_link(&dev->driver->p->kobj,
kobject_name(&dev->kobj));
}
return ret;
}
最后,也是我们期望看到的,probe函数的触发:
if (dev->bus->probe) {
ret = dev->bus->probe(dev);//如果bus的probe存在就用bus的
if (ret)
goto probe_failed;
} else if (drv->probe) {//如果bus的不存在driver的存在
ret = drv->probe(dev);//再用driver的
if (ret)
goto probe_failed;
}
我们的platform总线是不自带probe的,所以这里对触发drv->probe,好了,分析到这里,就大功告成了!
platform_driver_register函数也是一样的分析方法,就不多累述了。
所以我们主要还是 构造好这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;
};
设备的结构体为:
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;
};
其中,有个重要的参数:resource(资源),结构体如下 :
struct resource {
resource_size_t start;/*资源的起始地址*/
resource_size_t end;/*资源的结束地址*/
const char *name;/*资源的名字*/
unsigned long flags;/*资源的类型*/
unsigned long desc;
struct resource *parent, *sibling, *child;
};
flags类型的可选参数有:
IORESOURCE_TYPE_BITS
IORESOURCE_IO/*IO地址空间*/
IORESOURCE_MEM/*属于外设或者用于和设备通讯的支持直接寻址的地址空间*/
IORESOURCE_REG/*寄存器偏移量*/
IORESOURCE_IRQ
IORESOURCE_DMA
IORESOURCE_BUS
start、end的含义会随着flags而变更,如:
当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;
当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。
对于同种类型的资源而言,可以有多份,譬如说某设备占据了2个内存区域,则可以定义2个IORESOURCE_MEM资源。
下面给出完整程序参考,摘抄自韦东山驱动视频。
驱动文件:
/* 分配/设置/注册一个platform_driver */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int major;
static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin;
static int led_open(struct inode *inode, struct file *file)
{
/* 配置为输出 */
*gpio_con &= ~(0x3<<(pin*2));
*gpio_con |= (0x1<<(pin*2));
return 0;
}
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
copy_from_user(&val, buf, count); // copy_to_user();
if (val == 1)
{
// 点灯
*gpio_dat &= ~(1<start, res->end - res->start + 1);
gpio_dat = gpio_con + 1;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
pin = res->start;
/* 注册字符设备驱动程序 */
printk("led_probe, found led\n");
major = register_chrdev(0, "myled", &led_fops);
cls = class_create(THIS_MODULE, "myled");
//class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
return 0;
}
static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
/* iounmap */
printk("led_remove, remove led\n");
//class_device_destroy(cls, MKDEV(major, 0));
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con);
return 0;
}
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
}
};
static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
}
static void led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
设备文件:
/* 分配/设置/注册一个platform_device */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct resource led_resource[] = {
[0] = {
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,/*指的是属于外设或者用于和设备通讯的支持直接寻址的地址空间*/
},
[1] = {
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ,
}
};
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);
MODULE_LICENSE("GPL");
需要注意的是:platform_driver 和 platform_device 中的 name 变量的值必须是相同的 。这样在 platform_driver_register() 注册时,会将当前注册的 platform_driver 中的 name 变量的值和已注册的所有 platform_device 中的 name 变量的值进行比较,只有找到具有相同名称的 platform_device 才能注册成功。当注册成功时,会调用 platform_driver 结构元素 probe 函数指针,运行.probe进行初始化。
最后,结合十六节文章分析口味更佳!
嵌入式Linux驱动学习笔记(十六)------设备驱动模型(kobject、kset、ktype)