设备树下的platform驱动编写
文章目录
- 一、设备树下的platform驱动简介
- 1.在设备树中创建设备节点
- 2.编写 platform 驱动的时候要注意兼容属性
- 3.编写platform驱动
- 二、硬件原理图分析
- 三、实验程序编写
- 1.修改设备树文件
- 2.platform驱动程序编写
- 3.编写测试APP
- 四、运行测试
一、设备树下的platform驱动简介
platform 驱动框架分为总线、设备和驱动,其中总线不需要我们这些驱动程序员去管理,这个是 Linux 内核提供的,我们在编写驱动的时候只要关注于设备和驱动的具体实现即可。
在没有设备树的 Linux 内核下,我们需要分别编写并注册 platform_device 和 platform_driver,分别代表设备和驱动。
在使用设备树的时候,设备的描述被放到了设备树中,因此platform_device 就不需要我们去编写了,我们只需要实现platform_driver 即可。
在编写基于设备树的 platform 驱动的时候我们需要注意一下几点:
1.在设备树中创建设备节点
要先在设备树中创建设备节点来描述设备信息,重点是要设置好 compatible属性的值,因为 platform 总线需要通过设备节点的 compatible 属性值来匹配驱动!
/* LED */gpioled{#address-cells = <1>;#size-cells = <1>;compatible = "atkalpha-gpioled";pinctrl-names = "default";pinctrl-0 = <&pinctrl_led>;/* 其他节点内容 */led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;status = "okay";};
注意 compatible 属性值为“atkalpha-gpioled”,因此一会在编写 platform
驱动的时候 of_match_table 属性表中要有“atkalpha-gpioled”。
2.编写 platform 驱动的时候要注意兼容属性
在使用设备树的时候 platform 驱动会通过 of_match_table 来保存兼容性值,也就是表明此驱动兼容哪些设备。所以,of_match_table 将会尤为重要,比如本例程的 platform 驱动中 platform_driver 就可以按照如下所示设置:
1 static const struct of_device_id leds_of_match[] = {2 { .compatible = "atkalpha-gpioled" }, /* 兼容属性 */
3 { /* Sentinel */ }
4 };
5
6 MODULE_DEVICE_TABLE(of, leds_of_match);
7
8 static struct platform_driver leds_platform_driver = {9 .driver = {10 .name = "imx6ul-led",
11 .of_match_table = leds_of_match,
12 },
13 .probe = leds_probe,
14 .remove = leds_remove,
15 };
第 1~4 行,of_device_id 表,也就是驱动的兼容表,是一个数组,每个数组元素为 of_device_id类型。每个数组元素都是一个兼容属性,表示兼容的设备,一个驱动可以跟多个设备匹配。这里我们仅仅匹配了一个设备,那就是创建的 gpioled 这个设备。
第 2 行的 compatible 值为“atkalpha-gpioled”,驱动中的 compatible 属性和设备中的 compatible 属性相匹配,因此驱动中对应的 probe 函数就会执行。
注意第 3 行是一个空元素,在编写 of_device_id 的时候最后一个元素一定要为空!
第 6 行,通过 MODULE_DEVICE_TABLE 声明一下 leds_of_match 这个设备匹配表。
第 11 行,设置 platform_driver 中的 of_match_table 匹配表为上面创建的 leds_of_match,至此我们就设置好了 platform 驱动的匹配表了。
3.编写platform驱动
基于设备树的 platform 驱动和上一章无设备树的 platform 驱动基本一样,都是当驱动和设备匹配成功以后就会执行 probe 函数。我们需要在 probe 函数里面执行字符设备驱动那一套,当注销驱动模块的时候 remove 函数就会执行,都是大同小异的。
二、硬件原理图分析
三、实验程序编写
1.修改设备树文件
使用以前的。
2.platform驱动程序编写
设备已经准备好了,接下来就要编写相应的 platform 驱动了,新建名为“18_dtsplatform”的文件夹,然后在 18_dtsplatform 文件夹里面创建 vscode 工程,工作区命名为“dtsplatform”。新建名为 leddriver.c 的驱动文件,在 leddriver.c 中输入如下所示内容:
#include <linux/types.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ide.h>#include <linux/init.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/gpio.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/of_gpio.h>#include <linux/semaphore.h>#include <linux/timer.h>#include <linux/irq.h>#include <linux/wait.h>#include <linux/poll.h>#include <linux/fs.h>#include <linux/fcntl.h>#include <linux/platform_device.h>#include <asm/mach/map.h>#include <asm/uaccess.h>#include <asm/io.h>/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.文件名 : leddriver.c作者 : 左忠凯版本 : V1.0描述 : 设备树下的platform驱动其他 : 无论坛 : www.openedv.com日志 : 初版V1.0 2019/8/13 左忠凯创建***************************************************************/#define LEDDEV_CNT 1 /* 设备号长度 */#define LEDDEV_NAME "dtsplatled" /* 设备名字 */#define LEDOFF 0#define LEDON 1/* leddev设备结构体 */struct leddev_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */ struct device_node *node; /* LED设备节点 */int led0; /* LED灯GPIO标号 */};struct leddev_dev leddev; /* led设备 *//** @description : LED打开/关闭* @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return : 无*/void led0_switch(u8 sta){if (sta == LEDON )gpio_set_value(leddev.led0, 0);else if (sta == LEDOFF)gpio_set_value(leddev.led0, 1); }/** @description : 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* @return : 0 成功;其他 失败*/static int led_open(struct inode *inode, struct file *filp){filp->private_data = &leddev; /* 设置私有数据 */return 0;}/** @description : 向设备写数据 * @param - filp : 设备文件,表示打开的文件描述符* @param - buf : 要写给设备写入的数据* @param - cnt : 要写入的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 写入的字节数,如果为负值,表示写入失败*/static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){int retvalue;unsigned char databuf[2];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];if (ledstat == LEDON) {led0_switch(LEDON);} else if (ledstat == LEDOFF) {led0_switch(LEDOFF);}return 0;}/* 设备操作函数 */static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.write = led_write,};/** @description : flatform驱动的probe函数,当驱动与* 设备匹配以后此函数就会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/static int led_probe(struct platform_device *dev){ printk("led driver and device was matched!\r\n");/* 1、设置设备号 */if (leddev.major) {leddev.devid = MKDEV(leddev.major, 0);register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);} else {alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);leddev.major = MAJOR(leddev.devid);}/* 2、注册设备 */cdev_init(&leddev.cdev, &led_fops);cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);/* 3、创建类 */leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);if (IS_ERR(leddev.class)) {return PTR_ERR(leddev.class);}/* 4、创建设备 */leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);if (IS_ERR(leddev.device)) {return PTR_ERR(leddev.device);}/* 5、初始化IO */ leddev.node = of_find_node_by_path("/gpioled");if (leddev.node == NULL){printk("gpioled node nost find!\r\n");return -EINVAL;} leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0);if (leddev.led0 < 0) {printk("can't get led-gpio\r\n");return -EINVAL;}gpio_request(leddev.led0, "led0");gpio_direction_output(leddev.led0, 1); /* led0 IO设置为输出,默认高电平 */return 0;}/** @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/static int led_remove(struct platform_device *dev){gpio_set_value(leddev.led0, 1); /* 卸载驱动的时候关闭LED */cdev_del(&leddev.cdev); /* 删除cdev */unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */device_destroy(leddev.class, leddev.devid);class_destroy(leddev.class);return 0;}/* 匹配列表 */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,};/** @description : 驱动模块加载函数* @param : 无* @return : 无*/static int __init leddriver_init(void){return platform_driver_register(&led_driver);}/** @description : 驱动模块卸载函数* @param : 无* @return : 无*/static void __exit leddriver_exit(void){platform_driver_unregister(&led_driver);}module_init(leddriver_init);module_exit(leddriver_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("zuozhongkai");
第 33~112 行,传统的字符设备驱动,没什么要说的。
第 120~164 行,platform 驱动的 probe 函数,当设备树中的设备节点与驱动之间匹配成功以后此函数就会执行,原来在驱动加载函数里面做的工作现在全部放到 probe 函数里面完成。
第 171~180 行,remobe 函数,当卸载 platform 驱动的时候此函数就会执行。在此函数里面释放内存、注销字符设备等,也就是将原来驱动卸载函数里面的工作全部都放到 remove 函数中完成。
第 183~186 行,匹配表,描述了此驱动都和什么样的设备匹配,第 184 行添加了一条值为"atkalpha-gpioled"的 compatible 属性值,当设备树中某个设备节点的 compatible 属性值也为“atkalpha-gpioled”的时候就会与此驱动匹配。
第 189~196 行,platform_driver 驱动结构体,191 行设置这个 platform 驱动的名字为“imx6ul-led”,因此,当驱动加载成功以后就会在/sys/bus/platform/drivers/目录下存在一个名为“imx6u-led”的文件。
第 192 行设置 of_match_table 为上面的 led_of_match。
第 203~206 行,驱动模块加载函数,在此函数里面通过platform_driver_register 向 Linux 内核注册 led_driver 驱动。
第 213~216 行,驱动模块卸载函数,在此函数里面通过 platform_driver_unregister 从 Linux内核卸载 led_driver 驱动。
3.编写测试APP
四、运行测试
depmod //第一次加载驱动的时候需要运行此命令
modprobe leddriver.ko //加载驱动模块
驱动模块加载完成以后到/sys/bus/platform/drivers/目录下查看驱动是否存在,我们在leddriver.c 中设置 led_driver (platform_driver 类型)的 name 字段为“imx6ul-led”,因此会在/sys/bus/platform/drivers/目录下存在名为“imx6ul-led”这个文件,结果如图所示:
同理,在/sys/bus/platform/devices/目录下也存在 led 的设备文件,也就是设备树中 gpioled 这个节点,
驱动和设备匹配成功以后就可以测试 LED 灯驱动了,输入如下命令打开 LED 灯:
./ledApp /dev/dtsplatled 1 //打开 LED 灯
设备树下的platform驱动编写相关推荐
- 【正点原子MP157连载】第三十五章 设备树下的platform驱动编写-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
- 设备树下的platform 驱动编写
目录 设备树下的platform 驱动简介 硬件原理图分析 实验程序编写 修改设备树文件 platform 驱动程序编写 编写测试APP 运行测试 编译驱动程序和测试APP 运行测试 上一章我们详细的 ...
- 设备树下的 platform 驱动开发框架
1. 设备树下的platform驱动开发 platform驱动框架分为总线.设备和驱动,其中总线是由Linux内核提供,在编写驱动时只要关注于设备和驱动的具体实现即可.Linux下的platform驱 ...
- Linux 设备树下的 platform 驱动实验基于正点原子IMX6ULL开发板
1 设备树下的 platform 驱动简介 platform 驱动框架分为总线.设备和驱动,其中总线不需要我们这些驱动程序员去管理,这个是 Linux 内核提供的,我们在编写驱动的时候只要关注于设备和 ...
- Linux 设备树下的 platform 驱动示例
1.简介 基于总线.设备和驱动这样的驱动框架,Linux 内核提出来 platform 这个虚拟总线,相应的也有 platform 设备和 platform 驱动. Linux 总线设备和驱动模式 2 ...
- 设备树下的 platform 驱动
platform 设备驱动 Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,在这个思路下诞生了我们将来最常打交道的platform 设备驱动,也叫做平台设备驱动. L ...
- 嵌入式实践教程--设备树下的I2C驱动开发
Linux I2C 驱动框架简介 回想一下我们在裸机篇中是怎么编写 AP3216C 驱动的,我们编写了四个文件:bsp_i2c.c.bsp_i2c.h.bsp_ap3216c.c 和 bsp_ap32 ...
- 【正点原子Linux连载】第四十四章 设备树下的LED驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...
- firefly-rk3288j开发板--设备树下的 LED 驱动
firefly-rk3288j开发板–设备树下的 LED 驱动 1 准备工作 开发板:aio-rk3288j SDK版本:rk3288_linux_release_20210304 下载工具:Linu ...
最新文章
- 当AI实现多任务学习,它究竟能做什么?
- Android Weekly Notes Issue #220
- 操作系统(1) -- 计算机系统概述
- 无监督学习与监督学习的区别
- 深入理解Spark 2.1 Core (十):Shuffle Map 端的原理与源码分析
- Hiberate--one to many
- SO_REUSEADDR
- mysql 配置自动截断_MySql超长自动截断实例详解
- ubuntu pycharm设置快捷图标_这些Ubuntu中的小技巧,你知道吗?
- 宏定义超过字长的一些问题
- MP3解码算法原理解析
- iphone邮件服务器 263,在iphone上怎么设置263邮箱
- [中科]寒武纪,不愁钱路无客户
- 编译原理——自下而上语法分析
- 学大伟业:化学竞赛学习规划与推荐书目
- python遍历指定文件夹下所有文件夹和文件
- 单片机加减法计算器_十进制加减法计算器单片机设计.doc
- python3实现maxent 最大熵模型
- B2B商业模式以及交易模式
- 《从你的全世界路过》
热门文章
- 那些年啊,那些事——一个程序员的奋斗史 ——113
- 创建react项目 Linux,idea2018 快速搭建react项目指南
- 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 LNK2038 检测到“RuntimeLibrary”的不匹配项: 值“MD_DynamicRelease”不匹配值“MDd_DynamicDe
- Steam游戏的爬取与分析
- Google Earth选项中的重要设置
- PPT结束语有哪些?
- Source Insight免费下载(含秘钥+教程)
- 一款基于Latex语法和MathJax渲染的零基础公式编辑器,数学公式插件
- 服务器工业tnt配置文件,各服务器配置(实验步骤整理版).docx
- 微软 Win10 这么用才最顺手,电脑必做的 16 项设置