对于linux这样一个成熟,庞大,复杂的操作系统,代码的重用性非常重要,否则的话会在linux内核中存在大量无意义的重复代码。尤其是驱动程序,因为驱动程序占用了 Linux内核代码量的大头,如果不对驱动程序加以管理,任由重复的代码肆意增加,那么用不了多久Linux 内核的文件数量就庞大到无法接受的地步。

分隔和分离:也就是将主机驱动和设备驱动分隔开来,比如I2C,SPI等等都会采样驱动分隔的方法来简化驱动的开发。在实际的驱动开发中,一般I2C主机控制器驱动已经由半导体厂商编写好了,而设备驱动一般也由设备区间的厂家编写好了,我们只需要提供设备信息(设备树中)即可。然后根据获取到的设备信息来初始化设备。这样就相当于驱动只负责驱动,设备只负责设备,想办法将两者匹配即可。

当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之相匹配的设备,如果有就将两则匹配起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。

  分层:一个公司有很多层,每一层有着处理不同事物的员工,比如第一层负责后端开发,第二层负责前段开发,第三层负责销售,每一层都有自己的职责。在linux驱动往往也是分层的,分层的目的也是为了在不同层处理不同的内容,例如input(输入子系统),input子系统负责管理所有跟输入有关的驱动,包括键盘、鼠标、触摸等,最底层的就是设备原始驱动,负责获取输入设 备的原始值,获取到的输入事件上报给 input 核心层。input 核心层会处理各种 IO 模型,并且提供 file_operations 操作集合。我们在编写输入设备驱动的时候只需要处理好输入事件的上报即 可,至于如何处理这些上报的输入事件那是上层去考虑的,我们不用管。可以看出借助分层模型可以极大的简化我们的驱动编写,对于驱动编写来说非常的友好。

platform总线

        linux系统内核使用bus_type结构体表示总线(虚拟总线),结构体如下

第10行,match函数,这个函数很重要,单词match就是“匹配”的意思。因此此函数就是完成设备与驱动之间匹配的。两个参数:dev 和 drv,这两个参数分别为 device 和 device_driver 类型,也就是设备和驱动。

platform总线就是bus_type的一个具体实例。

platform_bus_type就是platform平台总线,其中platform_match就是匹配函数,下面的图片可以看出是如何进行匹配的。

四个if代表了四种匹配方式:

第一种匹配方式,OF类型的匹配,也就是设备树的匹配方式,device_driver结构体中有个名为of_match_table的成员变量,此变量保存着驱动的compatible匹配表,设备树中的每个设备节点的compatible属性会和of_match_table表中的所有成员进行比较,有相同的话匹配成功,匹配成功就会执行probe函数。

第二种匹配方式,ACPI匹配方式。

第三种匹配方式,id_table匹配,每个platform-driver结构体有一个id_table成员变量,顾名思义,保存了很多id信息,这些id信息存放着这个platform驱动所支持的驱动类型。

第四种匹配方式,如果id_table不存在的话可以直接比较驱动和设备的name字段,看看是不是相等,相等匹配成功。

platform驱动

        platform_driver结构体表示platform驱动,结构图定义如下:

第2行,probe函数,当驱动与设备匹配成功以后 probe 函数就会执行,非常重要的函数!!一般驱动的提供者会编写,如果自己要编写一个全新的驱动,那么 probe 就需要自行实现。

第7行,driver成员,为device_drver结构体变量,name字段和of_match_table都在里面,所以这个就是我们匹配时所注重的。

第8行,id_table 表,也就是platform 总线匹配驱动和设备的时候采用的第三种方法。

当我们定义并初始化好platform_driver结构体变量以后,我们需要在驱动入口函数里面调用platform_driver_register函数向linux内核注册一个platform驱动,函数原型如下: 

driver:要注册的platform驱动

返回值:负数代表失败,0代表成功

还需要在驱动卸载函数里面通过platform_diver_unregister函数卸载platform驱动。

下面就是有设备树的匹配方式

//匹配成功后就会执行
static int led_probe(struct platform_device *dev)
{
}//驱动卸载就会执行
static int led_remove(struct platform_device *dev)
{
}/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "atkalpha-gpioled" },{ /* Sentinel */ }
};/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver        = {.name   = "imx6ul-led",          /* 驱动名字,用于和设备匹配 */.of_match_table    = led_of_match, /* 设备树匹配表       */},.probe     = led_probe,.remove        = led_remove,
};//驱动入口
static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}//驱动出口
static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}

platform设备

成熟的platform驱动需要支持有设备树和无设备树的方式,当有设备数的时候,只需要按照上面的程序将设备树中信息提取出来(匹配就行)。当无设备树的时候,我们就需要编写一个设备文件。将设备注册到内核中,这样总线就会从驱动找到与之匹配的驱动。platform_device描述设备信息,设备结构体定义如下:

第23行,name表示设备名字,与想要匹配的驱动name字段一定要一样,否则就无法匹配成功。

第27行,num_resources表示资源数量,一般为28行resouce资源的大小

第28行,resource表示资源,也就是设备信息,比如外设寄存器等,linux内核使用resource结构体表示资源,结构体内容如下:

start和 end 分别表示资源的起始和终止信息,对于内存类的资源,就表示内存起始和终止地址,name 表示资源名字,flags 表示资源类型

在以前不支持设备树的linux版本中,用户需要编写platform_device变量来描述设备,然后使用platform_device_register函数将设备信息注册到linux内核中,原型如下:

pdev:要注册的platform设备

返回值:负数代表失败,0代表成功

如果不再使用 platform 的话可以通过 platform_device_unregister 函数注销掉相应的 platform设备,platform_device_unregister 函数原型如下:

        我们根据下面的程序发现,我们不使用设备树的时候,就需要知道设备的信息(寄存器地址),在device_struct中也就是资源,当我们在驱动程序中获取到这些资源的只是物理地址,我们还需要转化为虚拟地址。platform_get_resource函数获取资源,对这些寄存器进行配置,也就完成了设备的初始化。

/* * 寄存器地址定义*/
#define CCM_CCGR1_BASE              (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE      (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE      (0X020E02F4)
#define GPIO1_DR_BASE               (0X0209C000)
#define GPIO1_GDIR_BASE             (0X0209C004)
#define REGISTER_LENGTH             4/*  * 设备资源信息,也就是LED0所使用的所有寄存器*/
static struct resource led_resources[] = {[0] = {.start   = CCM_CCGR1_BASE,.end  = (CCM_CCGR1_BASE + REGISTER_LENGTH - 1),.flags   = IORESOURCE_MEM,},    [1] = {.start  = SW_MUX_GPIO1_IO03_BASE,.end  = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),.flags   = IORESOURCE_MEM,},[2] = {.start  = SW_PAD_GPIO1_IO03_BASE,.end  = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),.flags   = IORESOURCE_MEM,},[3] = {.start  = GPIO1_DR_BASE,.end   = (GPIO1_DR_BASE + REGISTER_LENGTH - 1),.flags    = IORESOURCE_MEM,},[4] = {.start  = GPIO1_GDIR_BASE,.end = (GPIO1_GDIR_BASE + REGISTER_LENGTH - 1),.flags  = IORESOURCE_MEM,},
};/** platform设备结构体 */
static struct platform_device leddevice = {.name = "imx6ul-led",.id = -1,.dev = {.release = &led_release,},.num_resources = ARRAY_SIZE(led_resources),.resource = led_resources,
};//驱动入口
static int __init leddevice_init(void)
{return platform_device_register(&leddevice);
}//驱动出口
static void __exit leddevice_exit(void)
{platform_device_unregister(&leddevice);
}

嵌入式linux platform设备驱动相关推荐

  1. linux生成驱动编译的头文件,嵌入式Linux字符设备驱动——5生成字符设备节点

    嵌入式Linux字符设备驱动开发流程--以LED为例 前言 留空 头文件 #include 查看系统设备类 ls /sys/class 设备类结构体 文件(路径):include/linux/devi ...

  2. Linux platform 设备驱动实验-基于正点原子IMX6ULL开发板

    我们以前的设备驱动都非常的简单,都是对IO进行最简单的读写操作.像I2C. SPI.LCD 这些复杂外设的驱动就不能这么去写了,Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的 ...

  3. linux platform 设备驱动

    1.像I2C.SPI. LCD 等这些复杂外设的驱动就不能这么去写了, Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,在这个思路下诞生了我们将来最常打交道的 pla ...

  4. 嵌入式linux MISC设备驱动

    misc 的意思是混合.杂项的,因此 MISC 驱动也叫做杂项驱动,也就是当我们板子上的某 些外设无法进行分类的时候就可以使用 MISC 驱动.MISC 驱动其实就是最简单的字符设备驱 动,通常嵌套在 ...

  5. 嵌入式linux字符设备驱动

    1. 我们需要先调用register_chrdev_region()或 alloc_chrdev_region()来向系统申请设备号 int register_chrdev_region( dev_t ...

  6. Linux 设备驱动开发 —— platform 设备驱动

    一.platform总线.设备与驱动         在Linux 2.6 的设备驱动模型中,关心总线.设备和驱动3个实体,总线将设备和驱动绑定.在系统每注册一个设备的时候,会寻找与之匹配的驱动:相反 ...

  7. linux设备驱动归纳总结(九):1.platform设备驱动

    http://blog.chinaunix.net/uid-25014876-id-111745.html 这一节可以理解是第八章的延伸,从这节开始介绍platform设备驱动. 一.什么是paltf ...

  8. Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用

    关与设备树的概念,我们在Exynos4412 内核移植(六)-- 设备树解析 里面已经学习过,下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构,设备树源(De ...

  9. Linux驱动之platform设备驱动

    当我们在一块开发板上写好了驱动,但换一块不同芯片的开发板,我们就需要重新写一个驱动.其中主要是硬件连接也就是接口发生了改变,而软件框架几乎不用通用的.所以为了更加方便地移植,能够仅修改很小的内容就达到 ...

最新文章

  1. ubuntu 18.04.4 - 显示文件路径
  2. 分享一个WM上绘制饼图、柱形图、折线图的控件类
  3. springmvc 音频流输出_音频管理模块AudioDeviceModule解读
  4. Laravel中的Blade模版
  5. 逻辑综合——优化电路
  6. SQL SERVER备份脚本
  7. pythonpyqt5线程暂停重启时间_PyQT5 停止死循环线程(监控文件是否修改)
  8. Leetcode——1. Two Sum
  9. 变的不只有外观!iPhone 14 Pro更多细节曝光:相机、快充大升级
  10. 筛选法建立初始堆_学术简报|基于库仑效率的退役锂离子动力电池储能梯次利用筛选...
  11. 在java中获取当前系统时间 插入数据库中的时间值没有时间只有日期的原因...
  12. Learn RxJava
  13. C语言表上作业法运输问题,表上作业法解运输问题
  14. ZZULIOJ:1047: 对数表
  15. 三线摆法测刚体转动惯量实验结论_关于刚体转动的前概念研究
  16. Python贴吧灌水脚本
  17. 新浪微博登陆页面html代码,仿新浪微博登陆邮箱提示效果...-页面右下角弹出提示框示例代...-js获取IP和PcName(IE)在vs中可用_169IT.COM...
  18. ubuntu vscode use clang-format google style
  19. 海底捞月战法实战讲解
  20. sonarqube+sonar-scanner+jenkins安装配置及使用

热门文章

  1. 电脑重装系统一启动就黑屏了该怎么办
  2. GDScript:协程(Coroutine)(二)简单粗暴实用至上的语法设计
  3. kali liunx使用心得之命令行模式下如何配置连接无线网络
  4. 数据库 之带子查询的操作--插入子查询结果、带子查询的修改语句、带子查询的删除语句
  5. com.netflix.client.ClientException: Load balancer does not have available server for client:XX 异常解决
  6. 俄罗斯方块游戏的消行实现
  7. 分享两个在线制图网站
  8. mac 卸载php版本,mac osx 更改自带php版本
  9. 华为百度导航Sdk黑屏
  10. uniapp:使用百度API提取身份证信息(微信小程序适用)