Linux设备模型的目的:为内核建立一个统一的设备模型,从而有一个对系统结构的一般性抽象描述。换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。

对于整个设备总线驱动模型的样子,如下图。简单来说,bus 负责维护注册进来的devcie 与 driver,每注册进来一个device 或者 driver 都会调用 Bus->match 函数 将device 与 driver 进行配对,并将它们加入链表,如果配对成功,调用Bus->probe或者driver->probe函数。注意:一个device 只能配对一个driver;而一个driver可以对应多个device。其它诸如devices_kset、kobject_uevent这里不关心,这个涉及的内容比较深入,暂时不去分析。

platform平台设备驱动是基于设备总线驱动模型的,如下图,这篇主要是记录平台设备的驱动与设备的注册匹配过程

以上参考自

platform_bus提供platform_device_register、platform_driver_register等函数供platform_device层与platform_driver调用。

platform_driver属于驱动层,会在里面提供file_operations结构体供应用层调用、创建设备节点文件对应相应的驱动

platform_device属于设备层,会在里面提供resource资源文件供驱动层调用

下面是一个例子,分别编写了Led_dev.c设备文件和Led_drv.c驱动文件。列出程序源码,再根据源码分析Led_dev与Led_drv注册与匹配过程

Led_dev.c的程序源码:

#include <linux/module.h>
#include <linux/version.h> #include <linux/init.h> #include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h> /* 分配/设置/注册一个platform_device */ static struct resource led_resource[] = {
[] = {
.start = 0x56000050,
.end = 0x56000050 + - ,
.flags = IORESOURCE_MEM,
},
[] = {
.start = ,
.end = ,
.flags = IORESOURCE_IRQ,
} }; static void led_release(struct device * dev)
{
} static struct platform_device led_dev = {
.name = "myled",
.id = -,
.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 ;
} static void led_dev_exit(void)
{
platform_device_unregister(&led_dev);
} module_init(led_dev_init);
module_exit(led_dev_exit); MODULE_LICENSE("GPL");

Led_drv.c的程序源码:

/* 分配/设置/注册一个platform_driver */

#include <linux/module.h>
#include <linux/version.h> #include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h> 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)
{
//printk("first_drv_open\n");
/* 配置为输出 */
*gpio_con &= ~(0x3<<(pin*));
*gpio_con |= (0x1<<(pin*));
return ;
} static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val; //printk("first_drv_write\n"); copy_from_user(&val, buf, count); // copy_to_user(); if (val == )
{
// 点灯
*gpio_dat &= ~(<<pin);
}
else
{
// 灭灯
*gpio_dat |= (<<pin);
} return ;
} static struct file_operations led_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = led_open,
.write = led_write,
}; static int led_probe(struct platform_device *pdev)
{
struct resource *res; /* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, );
gpio_con = ioremap(res->start, res->end - res->start + );
gpio_dat = gpio_con + ; res = platform_get_resource(pdev, IORESOURCE_IRQ, );
pin = res->start; /* 注册字符设备驱动程序 */ printk("led_probe, found led\n"); major = register_chrdev(, "myled", &led_fops); cls = class_create(THIS_MODULE, "myled"); class_device_create(cls, NULL, MKDEV(major, ), NULL, "led"); /* /dev/led */ return ;
} static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
/* iounmap */
printk("led_remove, remove led\n"); class_device_destroy(cls, MKDEV(major, ));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con); return ;
} 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 ;
} static void led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
} module_init(led_drv_init);
module_exit(led_drv_exit); MODULE_LICENSE("GPL");

将上面两个程序编译后得到Led_dev.ko和Led_drv.ko两个模块,假设先加载Led_dev.ko文件:insmod Led_drv.ko。

列出执行加载操作后的程序流程:加载后会先调用led_drv_init函数,这个函数只是调用platform_driver_register函数。

platform_driver_register(&led_drv);
led_drv->driver.bus = &platform_bus_type;//platform_bus_type里面含有platform_match匹配函数
led_drv->driver.probe = platform_drv_probe;
led_drv->driver.remove = platform_drv_remove;
driver_register(&led_drv->driver);
bus_add_driver(&led_drv->driver);
driver_attach(&led_drv->driver);
bus_for_each_dev(led_drv->driver->bus, NULL, &led_drv->driver, __driver_attach);
while ((dev = next_device(&i)) && !error)
{
__driver_attach(dev, led_drv->driver);
driver_probe_device(drv, dev);
drv->bus->match(&led_dev->dev, drv);//最终调用到这个函数匹配,找到这个函数其实是platform_match函数
really_probe(&led_dev->dev, drv);//匹配成功调用
                           dev->driver = drv;//匹配驱动
drv->probe(dev);//调用led_drv->driver.probe函数
}

首先platform_driver_register函数会初始化driver.bus、driver.probe、driver.remove等变量

int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}

其中platform_bus_type里含有platform_match函数,这个函数最终会被调用用来匹配Led_dev与Led_drv。

struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
static int platform_match(struct device * dev, struct device_driver * drv)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev); return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == );//根据名称匹配dev与drv
}

接着看到driver_register函数,通过层层调用,最终会从dev链表搜索,通过platform_match函数找到与Led_drv匹配的Led_dev。找到后调用really_probe函数,really_probe函数先是执行dev->driver = drv;这样就将dev与drv联系起来了,接着调用led_probe函数,初始化驱动。因为这时候还没有注册Led_dev.ko所以不会匹配成功。

接着加载Led_dev.ko文件:insmod Led_dev.ko。列出执行加载操作后的程序流程:加载后会先调用led_dev_init函数,这个函数只是调用platform_device_register函数。

platform_device_register(&led_dev);
platform_device_add(&led_dev)
device_add(&led_dev->dev);
bus_attach_device(&led_dev->dev);
device_attach(&led_dev->dev);
if (dev->driver) {//如果设备的驱动程序已经存在
{
device_bind_driver( &led_dev->dev);//试着链接驱动
}
bus_for_each_drv(led_dev->dev->bus, NULL, &led_dev->dev, __device_attach);
while ((drv = next_driver(&i)) && !error)
{
__device_attach(drv, &led_dev->dev);
driver_probe_device(drv, &led_dev->dev);
drv->bus->match(&led_dev->dev, drv);//最终调用到这个函数匹配,找到这个函数其实是platform_match函数
really_probe(&led_dev->dev, drv);//匹配成功调用
rv->bus->match(&led_dev->dev, drv);//最终调用到这个函数匹配,找到这个函数其实是platform_match函数
really_probe(&led_dev->dev, drv);//匹配成功调用
}

通过层层调用,最终定位到device_attach函数与Led_drv不同的是,Led_dev首先会确认自己是否已经有驱动存在,如果不存在才会从drv链表搜索,通过platform_match函数找到与Led_dev匹配的Led_drv。找到后调用really_probe函数,really_probe函数先是执行dev->driver = drv;这样就将dev与drv联系起来了,接着调用led_probe函数,初始化驱动。

static int led_probe(struct platform_device *pdev)
{
struct resource *res; /* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, );
gpio_con = ioremap(res->start, res->end - res->start + );
gpio_dat = gpio_con + ; res = platform_get_resource(pdev, IORESOURCE_IRQ, );
pin = res->start; /* 注册字符设备驱动程序 */ printk("led_probe, found led\n"); major = register_chrdev(, "myled", &led_fops); cls = class_create(THIS_MODULE, "myled"); class_device_create(cls, NULL, MKDEV(major, ), NULL, "led"); /* /dev/led */ return ;
}

在led_probe中,首先会从Led_dev的led_probe中获得相应的led_resource信息来配置IO端口,接着注册字符设备,然后创建设备描述符文件。这就是平台设备驱动模型整个注册匹配的过程。

与注册过程相反rmmod Led_drv最终会调用led_remove函数

static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
/* iounmap */
printk("led_remove, remove led\n"); class_device_destroy(cls, MKDEV(major, ));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con); return ;
}

而执行rmmod Led_dev最终会通过层层调用到led_release函数,所以led_release函数必不可少,即使它是空的函数,什么也没做

static void led_release(struct device * dev)
{
}

以上全部就是对驱动的分离分层的实现。通过平台设备驱动模型实现。

Linux驱动之平台设备驱动模型简析(驱动分离分层概念的建立)的更多相关文章

  1. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  2. [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

    转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...

  3. Linux Platform devices 平台设备驱动

    设备总线驱动模型:http://blog.csdn.net/lizuobin2/article/details/51570196 本文主要参考:http://www.wowotech.net/devi ...

  4. Linux驱动之平台设备

    <平台设备设备驱动> a:背景: 平台总线是Linux2.6的设备驱动模型中,关心总线,设备和驱动这3个实体.一个现实的Linux设备和驱动通常需要挂接在一种总线上(比如本身依附于PCI, ...

  5. 【Linux高级驱动】平台设备驱动机制的编程流程与编译进内核

    [平台设备驱动机制的编程流程] [如何将驱动静态的编译进内核镜像] 1.添加资源(dev-led.c) 1.1:一般来说,系统习惯上将资源放在arch/arm/plat-samsung/目录中 cp ...

  6. Linux驱动之USB总线驱动程序框架简析

    通用串行总线(USB)是主机和外围设备之间的一种连接.USB总线规范有1.1版和2.0版,当然现在已经有了3.0版本.USB1.1支持两种传输速度:低速为1.5Mbps,高速为12Mbps.USB2. ...

  7. linux驱动的分离分层概念之LED

    bus_drv_dev模型:功能改写只需改dev硬件代码即可,drv不需改写. LED例子 下面用一个点亮LED的例子来说明这个分离的的例子: led_dev.c  定义这个平台设备的资源: stat ...

  8. Java内存模型简析

    1.多线程基础 线程通信,是指线程之间以何种机制来交换信息.其中通信的机制有两种:内存共享和消息传递.内存共享是指线程之间通过写-读内存中的公共状态隐式进行通讯(Java):消息传递在线程之间没有公共 ...

  9. Linux中 /proc/[pid] 目录各文件简析

    Linux 内核提供了一种通过 proc 文件系统,在运行时访问内核内部数据结构.改变内核设置的机制.proc 文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为访问系 ...

随机推荐

  1. ef6.0+mysql配合使用的问题

    折腾了很久由于所用到的各种库版本问题:后来终于组合成了一个可用的:记录下各种库的版本 ef6.0 mysql5.5 mysql-connector-net-6.9.12.msi mysql-for-v ...

  2. 问题-python3.6找不到tkinter

    问题:import tkinter失败 然后直接pip安装也不ok python3.6安装过程中会提示是否选择安装tkinter,如此只有打开原来的安装程序 勾选箭头所示

  3. app:processOfficalDebugResources报错的几种解决方法;

    Error:Execution failed for task ':app:processDebugResources'. 出现这个错误的同事,大多还会伴随的R文件的报错,对!是全部R文件都报错: 1 ...

  4. Vue 子组件调用父组件方法

    父组件内容: <template> <div> <info-wnd ref="infoWnd" @parentClick="wndClick ...

  5. mysql登录1045错误时 修改登录密码

    1.进入 mysql 的 bin 目录下,打开 cmd ,关闭 mysql 数据库. 2.输入 mysqld --skip-grant-tables 回车. 保持窗口不要更改不要关闭 (--skip- ...

  6. [leetcode]224. Basic Calculator

    Implement a basic calculator to evaluate a simple expression string. The expression string may conta ...

  7. DEDECMS 多站用一个站图片

    function replaceurl($newurl) { $newurl=str_replace('src="/uploads/allimg/','src="xxx.com/u ...

  8. sys.exit(main(sys.argv[1:]))

    sys.argv sys.argv[]说白了就是一个从程序外部获取参数的桥梁. 首先我们需要import sys,sys是python3的一个标准库,也就是一个官方的模块.封装了一些系统的信息和接口, ...

  9. 第五篇、Python之迭代器与生成器

    1.迭代和递归等概念 循环(loop):指的是在满足条件的情况下,重复执行同一段代码.比如,while语句,for循环. 迭代(iterate):指的是按照某种顺序逐个访问列表中的每一项.比如,for ...

  10. Rocket MQ 2 - Namesrv

    通过上文中使用可以看到,主要逻辑还是在NamesrvController中包含KVConfigManager负责配置相关的读写,RouteInfoManager负责路由信息的管理; 启动定时任务定时打 ...