文章目录

  • 一、设备树下的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驱动编写相关推荐

  1. 【正点原子MP157连载】第三十五章 设备树下的platform驱动编写-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  2. 设备树下的platform 驱动编写

    目录 设备树下的platform 驱动简介 硬件原理图分析 实验程序编写 修改设备树文件 platform 驱动程序编写 编写测试APP 运行测试 编译驱动程序和测试APP 运行测试 上一章我们详细的 ...

  3. 设备树下的 platform 驱动开发框架

    1. 设备树下的platform驱动开发 platform驱动框架分为总线.设备和驱动,其中总线是由Linux内核提供,在编写驱动时只要关注于设备和驱动的具体实现即可.Linux下的platform驱 ...

  4. Linux 设备树下的 platform 驱动实验基于正点原子IMX6ULL开发板

    1 设备树下的 platform 驱动简介 platform 驱动框架分为总线.设备和驱动,其中总线不需要我们这些驱动程序员去管理,这个是 Linux 内核提供的,我们在编写驱动的时候只要关注于设备和 ...

  5. Linux 设备树下的 platform 驱动示例

    1.简介 基于总线.设备和驱动这样的驱动框架,Linux 内核提出来 platform 这个虚拟总线,相应的也有 platform 设备和 platform 驱动. Linux 总线设备和驱动模式 2 ...

  6. 设备树下的 platform 驱动

    platform 设备驱动 Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,在这个思路下诞生了我们将来最常打交道的platform 设备驱动,也叫做平台设备驱动. L ...

  7. 嵌入式实践教程--设备树下的I2C驱动开发

    Linux I2C 驱动框架简介 回想一下我们在裸机篇中是怎么编写 AP3216C 驱动的,我们编写了四个文件:bsp_i2c.c.bsp_i2c.h.bsp_ap3216c.c 和 bsp_ap32 ...

  8. 【正点原子Linux连载】第四十四章 设备树下的LED驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  9. firefly-rk3288j开发板--设备树下的 LED 驱动

    firefly-rk3288j开发板–设备树下的 LED 驱动 1 准备工作 开发板:aio-rk3288j SDK版本:rk3288_linux_release_20210304 下载工具:Linu ...

最新文章

  1. 当AI实现多任务学习,它究竟能做什么?
  2. Android Weekly Notes Issue #220
  3. 操作系统(1) -- 计算机系统概述
  4. 无监督学习与监督学习的区别
  5. 深入理解Spark 2.1 Core (十):Shuffle Map 端的原理与源码分析
  6. Hiberate--one to many
  7. SO_REUSEADDR
  8. mysql 配置自动截断_MySql超长自动截断实例详解
  9. ubuntu pycharm设置快捷图标_这些Ubuntu中的小技巧,你知道吗?
  10. 宏定义超过字长的一些问题
  11. MP3解码算法原理解析
  12. iphone邮件服务器 263,在iphone上怎么设置263邮箱
  13. [中科]寒武纪,不愁钱路无客户
  14. 编译原理——自下而上语法分析
  15. 学大伟业:化学竞赛学习规划与推荐书目
  16. python遍历指定文件夹下所有文件夹和文件
  17. 单片机加减法计算器_十进制加减法计算器单片机设计.doc
  18. python3实现maxent 最大熵模型
  19. B2B商业模式以及交易模式
  20. 《从你的全世界路过》

热门文章

  1. 那些年啊,那些事——一个程序员的奋斗史 ——113
  2. 创建react项目 Linux,idea2018 快速搭建react项目指南
  3. 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 LNK2038 检测到“RuntimeLibrary”的不匹配项: 值“MD_DynamicRelease”不匹配值“MDd_DynamicDe
  4. Steam游戏的爬取与分析
  5. Google Earth选项中的重要设置
  6. PPT结束语有哪些?
  7. Source Insight免费下载(含秘钥+教程)
  8. 一款基于Latex语法和MathJax渲染的零基础公式编辑器,数学公式插件
  9. 服务器工业tnt配置文件,各服务器配置(实验步骤整理版).docx
  10. 微软 Win10 这么用才最顺手,电脑必做的 16 项设置