当前位置: 首页 > news >正文

嵌入式Linux设备树驱动开发 - dtsof驱动

嵌入式Linux设备树驱动开发 - dtsof驱动

一、项目概述

本项目实现了一个基于设备树(Device Tree)的驱动程序,用于解析IMX6ULL开发板的设备树信息。该驱动程序主要演示了如何在Linux内核模块中访问和解析设备树中的属性信息。

二、驱动程序分析 (dtsof.c)

1. 头文件与宏定义

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>          // 设备树相关头文件
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>       // kmalloc/kfree相关// 模块初始化和退出函数声明
static int __init dtsof_init(void);
static void __exit dtsof_exit(void);

2. 设备树解析函数

模块初始化函数
static int __init dtsof_init(void)
{int ret = 0;struct device_node *bl_nd = NULL;   // 设备节点指针struct property *compprop = NULL;   // 属性指针const char *status;                // 状态属性值u32 def_value = 0;                 // 默认亮度值u32 elemsize = 0;                  // 数组元素个数u32 *brival;                        // 亮度值数组指针/* 查找backlight节点 */bl_nd = of_find_node_by_path("/backlight");if (bl_nd == NULL){ret = -ENODEV;goto fail_findnode;}/* 查找compatible属性 */compprop = of_find_property(bl_nd, "compatible", NULL);if (compprop == NULL){ret = -ENOENT;goto fail_findprop;}else{printk(KERN_INFO "Compatible property found: %s\r\n", (char *)compprop->value);}/* 读取status属性 */ret = of_property_read_string(bl_nd, "status", &status);if (ret < 0) {goto fail_findprop;} else {printk(KERN_INFO "Status property value: %s\n", status);}/* 读取default-brightness-level属性 */ret = of_property_read_u32(bl_nd, "default-brightness-level", &def_value);if (ret < 0) {goto fail_findprop;} else {printk(KERN_INFO "defult-brightness-level property value: %u\n", def_value);}/* 获取brightness-levels属性元素个数 */elemsize = of_property_count_elems_of_size(bl_nd, "brightness-levels", sizeof(u32));if (elemsize < 0) {goto fail_findprop;} else {printk(KERN_INFO "Number of brightness-level elements: %d\n", elemsize);}/* 分配内存用于存储亮度值数组 */brival = kmalloc(elemsize * sizeof(u32), GFP_KERNEL);if (!brival) {ret = -ENOMEM;goto fail_kmalloc;}/* 读取brightness-levels属性数组 */ret = of_property_read_u32_array(bl_nd, "brightness-levels", brival, elemsize);if (ret < 0) {goto fail_findprop;} else {int i;printk(KERN_INFO "Brightness levels: ");for (i = 0; i < elemsize; i++) {printk(KERN_CONT "%u ", brival[i]);}printk(KERN_CONT "\n");}kfree(brival);  // 释放内存return 0;fail_kmalloc:kfree(brival);fail_findprop:fail_findnode:return ret;
}
模块退出函数
static void __exit dtsof_exit(void)
{printk(KERN_INFO "dtsof module exited\n");
}

3. 模块注册

module_init(dtsof_init);
module_exit(dtsof_exit);

4. 模块信息

MODULE_LICENSE("GPL");
MODULE_AUTHOR("alientek");

三、设备树文件分析 (imx6ull-alientek-emmc.dts)

1. 设备树基本结构

/dts-v1/;#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"/ {model = "Freescale i.MX6 ULL 14x14 EVK Board";compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";/* 标准输出配置 */chosen {stdout-path = &uart1;};/* 内存配置 */memory {reg = <0x80000000 0x20000000>;};/* 保留内存区域 */reserved-memory {#address-cells = <1>;#size-cells = <1>;ranges;linux,cma {compatible = "shared-dma-pool";reusable;size = <0x14000000>;linux,cma-default;};};/* 背光配置节点 */backlight {compatible = "pwm-backlight";pwms = <&pwm1 0 5000000>;brightness-levels = <0 4 8 16 32 64 128 255>;default-brightness-level = <6>;status = "okay";};/* 其他外设配置... */
};

2. 重要配置节点

backlight节点
backlight {compatible = "pwm-backlight";pwms = <&pwm1 0 5000000>;  // 使用PWM1,周期5000000nsbrightness-levels = <0 4 8 16 32 64 128 255>;  // 亮度等级default-brightness-level = <6>;  // 默认亮度等级status = "okay";  // 状态启用
};
pwm1配置
&pwm1 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_pwm1>;status = "okay";
};
pinctrl_pwm1配置
pinctrl_pwm1: pwm1grp {fsl,pins = <MX6UL_PAD_GPIO1_IO08__PWM1_OUT   0x110b0>;
};

四、Makefile分析

KERNERDIR := /home/ubuntu2004/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENTDIR := $(shell pwd)obj-m := dtsof.o  # 编译成内核模块build : kernel_moduleskernel_modules:$(MAKE) -C $(KERNERDIR) M=$(CURRENTDIR) modules  # 进入内核目录编译模块clean:$(MAKE) -C $(KERNERDIR) M=$(CURRENTDIR) clean  # 清理编译结果

五、设备树基础理论

1. 设备树基本概念

设备树(Device Tree)是一个描述硬件配置的数据结构,它将硬件信息从内核代码中分离出来,使得同一个内核可以支持多种硬件平台。

2. 设备树基本格式

  • 节点: 用斜杠斜杠包裹的路径表示,如/backlight
  • 属性: 每个节点可以包含多个属性,属性值可以是字符串、32位整数或它们的数组
  • 兼容性: compatible属性用于匹配驱动程序
  • 状态: status属性表示设备状态,"okay"表示启用

3. 常用设备树属性

  • compatible: 驱动匹配字符串
  • reg: 寄存器地址范围
  • interrupts: 中断配置
  • clocks: 时钟配置
  • pwms: PWM配置
  • brightness-levels: 亮度等级数组
  • default-brightness-level: 默认亮度等级
  • gpios: GPIO配置

六、设备树操作函数详解

1. of_find_node_by_path

struct device_node *of_find_node_by_path(const char *path);

功能:通过路径查找设备树节点
参数:path - 设备树节点路径
返回值:成功返回设备节点指针,失败返回NULL

2. of_find_property

struct property *of_find_property(const struct device_node *np, const char *name, int *lenp);

功能:查找设备节点的属性
参数:

  • np - 设备节点指针
  • name - 属性名称
  • lenp - 返回属性值长度
    返回值:成功返回属性指针,失败返回NULL

3. of_property_read_string

int of_property_read_string(const struct device_node *np, const char *propname, const char **out_string);

功能:读取字符串类型的属性值
参数:

  • np - 设备节点指针
  • propname - 属性名称
  • out_string - 输出的字符串指针
    返回值:成功返回0,失败返回错误码

4. of_property_read_u32

int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value);

功能:读取u32类型的属性值
参数:

  • np - 设备节点指针
  • propname - 属性名称
  • out_value - 输出的u32值
    返回值:成功返回0,失败返回错误码

5. of_property_count_elems_of_size

int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size);

功能:计算属性值中元素的数量
参数:

  • np - 设备节点指针
  • propname - 属性名称
  • elem_size - 元素大小
    返回值:成功返回元素数量,失败返回错误码

6. of_property_read_u32_array

int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t count);

功能:读取u32数组类型的属性值
参数:

  • np - 设备节点指针
  • propname - 属性名称
  • out_values - 输出的u32数组
  • count - 要读取的元素个数
    返回值:成功返回0,失败返回错误码

七、驱动编译与测试流程

1. 驱动编译

make -C /path/to/kernel/source M=$(pwd) modules

2. 加载驱动

insmod dtsof.ko

3. 查看内核日志

dmesg | grep "dtsof"

4. 卸载驱动

rmmod dtsof.ko

八、设备树在嵌入式Linux中的作用

1. 硬件描述

设备树通过结构化的方式描述硬件平台的硬件配置,包括:

  • 外设类型和型号
  • 寄存器地址空间
  • 中断配置
  • 时钟配置
  • 外设参数配置

2. 驱动匹配

通过compatible属性,Linux内核可以将驱动程序与设备树中的设备节点进行匹配。

3. 资源分配

设备树提供了一种机制,让驱动程序可以查询硬件资源,如:

  • 内存地址
  • 中断号
  • 时钟源
  • GPIO引脚

4. 平台抽象

设备树使得Linux内核可以与硬件平台解耦,同一个内核可以支持多种不同的硬件平台。

九、设备树驱动开发要点

1. 错误处理

  • 检查设备节点和属性是否为空
  • 处理内存分配失败情况
  • 返回合适的错误码
  • 使用goto进行错误清理

2. 内存管理

  • 使用kmalloc/kfree进行动态内存分配
  • 确保所有分配的内存都被正确释放
  • 使用合适的内存分配标志(GFP_KERNEL)

3. 日志输出

  • 使用printk输出调试信息
  • 使用合适的日志级别(KERN_INFO, KERN_CONT等)
  • 提供详细的属性解析信息

4. 错误码使用

  • 使用标准Linux错误码(-ENODEV, -ENOENT, -ENOMEM等)
  • 确保错误码正确传递
  • 在模块加载时返回合适的错误码

Giee 源码仓库

http://www.xdnf.cn/news/1395163.html

相关文章:

  • Unity DateTime 相关
  • 处理器(CPU/MPU)的双发射是什么?
  • 命令扩展与重定向
  • 可解释人工智能XAI
  • 【机器学习深度学习】Embedding 与 RAG:让 AI 更“聪明”的秘密
  • leetcode 191 位1的个数
  • 【0422】SMgrRelationData 中 md_num_open_segs 和 md_seg_fds 数组为什么是 4 个元素? 第四个元素表示什么?
  • Ubuntu磁盘分区重新挂载读写指南
  • 不一样的发票管理模式-发票识别+发票查验接口
  • ContextMenuManager for Win:优化右键菜单,解决用户痛点
  • lxml库如何使用
  • ElasticSearch对比Solr
  • C语言————操作符详解
  • TypeScript的Type
  • MySQL 中如果发生死锁应该如何解决?
  • 每日算法题【二叉树】:对称二叉树、二叉树的前中后序遍历
  • 回车换行、缓冲区刷新、倒计时小程序
  • MQTT高延迟通信优化指南
  • Python的Listd 数据格式 V0.1
  • 深入解析Nginx核心模块
  • DAY 17 常见聚类算法-2025.8.29
  • 将数据赋值到多个文档里,并将多个word放入压缩包并下载
  • 非标设计 机架模板 misumi 设计组合案例
  • 小康AI家庭医生,亮相2025WteamAI创客节!
  • 【51单片机】【protues仿真】 基于51单片机智能视力保护台灯系统
  • 13 SQL进阶-InnoDB引擎(8.23)
  • Elasticsearch 9.X 使用推理 API 进行语义搜索
  • 2025年06月 Scratch 图形化(一级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • 《跳出“技术堆砌”陷阱,构建可演进的软件系统》
  • opencv基础学习与实战之轮廓分析与模板匹配(4)