当前位置: 首页 > 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.dtcms.com/a/357092.html

相关文章:

  • shell学习(二)
  • Sharding-JDBC 使用方法
  • 为什么不能创建泛型数组?
  • C++并发编程-17. 线程安全的链表
  • Unity EventTrigger 动态添加事件
  • flume事务机制详解:保障数据可靠性的核心逻辑
  • 项目中为什么使用SpringBoot?
  • 晨控CK-FR102ANS与欧姆龙NX系列PLC配置EtherNet/IP通讯连接手册
  • 如何规划一年、三年、五年的IP发展路线图?
  • Android 端 QGroundControl 控制 PC 端Gazebo Sim 仿真无人机
  • 龙迅#LT7642GX适用于4路HDMI2.1/DP/TPYE-C转HDMI+LVDS/MIPI混合开关应用,分辨率高达8K30HZ !
  • ADFS 和 OAuth 的区别
  • 第三届机械工程与先进制造智能化技术研讨会(MEAMIT2025)
  • 打造企业内部的“技术桥梁”:超级用户机制如何助力制造企业高效运维
  • “聚势同行・创赢未来”淮南高新区科技型企业沙龙——2025大数据企业专场成功举办
  • 解决RTX3070魔改16G在UBUNTU中黑屏问题
  • AI模型库哪个好?2025年主流AI模型选型指南与API成本对比推荐
  • 在现场把“数据”变成可用的力量 —— 谈EG8200Lite的实战价值
  • 七牛云灵矽AI实践:构建可扩展智能体的开放协议与架构
  • C++实现快速反转一个数的算法
  • “上门做饭”平台的核心技术栈与运营壁垒是什么?
  • linux系统学习(13.系统管理)
  • 【混合开发】Android+webview模拟crash崩溃补充说明
  • Electron 项目来实现文件下载和上传功能(AI)
  • Martin Fowler分享了他对大语言模型(LLM)与软件开发现状的一些思考
  • 【机器学习深度学习】Embedding 与 RAG:让 AI 更“聪明”的秘密
  • AC上网行为安全管理
  • 【完整源码+数据集+部署教程】停车位状态检测系统源码和数据集:改进yolo11-DCNV2-Dynamic
  • 深入理解会话状态管理:多轮对话与API最佳实践
  • 【AI】常见8大LLM大语言模型地址