嵌入式Linux驱动开发:设备树与平台设备驱动
嵌入式Linux驱动开发:设备树与平台设备驱动
引言
本笔记旨在详细记录嵌入式Linux驱动开发中设备树(Device Tree)和平台设备驱动(Platform Driver)的核心概念与实现。通过分析提供的代码与设备树文件,我们将深入理解如何在i.MX6ULL平台上使用设备树来描述硬件,并通过平台设备驱动来管理这些硬件资源。本文档将结合代码实例,详细解释每个关键部分的作用与实现细节。
设备树(Device Tree)
设备树是一种数据结构,用于描述硬件的配置和连接方式,使得操作系统可以在不硬编码硬件信息的情况下初始化和使用硬件。设备树文件通常以.dts
为扩展名,编译后生成.dtb
文件,由引导加载程序传递给内核。
设备树文件结构
设备树文件由一系列节点(node)和属性(property)组成。每个节点代表一个硬件设备或子系统,属性则描述该设备的具体配置。
根节点
设备树的根节点用/
表示,包含全局属性和子节点。
/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";...
};
model
:描述开发板的型号。compatible
:指定设备的兼容性,用于匹配驱动程序。
子节点
子节点描述具体的硬件设备,如内存、外设等。
memory {reg = <0x80000000 0x20000000>;
};
memory
节点描述系统的物理内存布局,reg
属性指定内存的起始地址和大小。
设备树源文件(DTSI)
设备树源文件(.dtsi
)类似于C语言的头文件,包含多个.dts
文件共享的通用定义。通过#include
指令引入。
#include "imx6ull.dtsi"
设备树编译
设备树文件需要编译成二进制格式(.dtb
),通常使用dtc
(Device Tree Compiler)工具完成。
dtc -I dts -O dtb -o imx6ull-alientek-emmc.dtb imx6ull-alientek-emmc.dts
平台设备驱动
平台设备驱动是Linux内核中用于管理平台特定设备的一种机制。它通过设备树描述硬件资源,并在驱动程序中解析这些资源。
平台设备(Platform Device)
平台设备由platform_device
结构体表示,包含设备的名称、资源和私有数据。
资源定义
资源定义了设备所需的内存、中断等硬件资源。
static struct resource led_resources[] = {[0] = {.start = CCM_CCGR1_BASE,.end = CCM_CCGR1_BASE + REGISTER_LNE - 1,.flags = IORESOURCE_MEM,},...
};
start
和end
:资源的起始和结束地址。flags
:资源类型,如IORESOURCE_MEM
表示内存资源。
平台设备注册
平台设备通过platform_device_register
函数注册到内核。
static struct platform_device leddevice = {.name = "imx6ull-led",.id = -1,.dev = {.release = leddevice_realease,},.num_resources = ARRAY_SIZE(led_resources),.resource = led_resources,
};static int __init leddevice_init(void)
{platform_device_register(&leddevice);return 0;
}
平台驱动(Platform Driver)
平台驱动由platform_driver
结构体表示,包含驱动的名称、匹配表、探测和移除函数。
匹配表
匹配表用于将驱动与设备树中的节点关联。
struct of_device_id led_of_match[] = {{.compatible = "alientek,gpioled"},{/* sentinel */}
};
compatible
:与设备树中节点的compatible
属性匹配。
探测函数
探测函数在设备与驱动匹配成功后调用,用于初始化设备。
static int led_probe(struct platform_device *dev)
{gpioled.nd = of_find_node_by_path("/gpioled");if (gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;}gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpios", 0);if (gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}ret = gpio_direction_output(gpioled.led_gpio, 1);if (ret < 0) {printk("can't set gpio!\r\n");}// 注册字符设备驱动...return 0;
}
of_find_node_by_path
:根据路径查找设备树节点。of_get_named_gpio
:从设备树节点中获取GPIO编号。gpio_direction_output
:设置GPIO为输出模式。
移除函数
移除函数在设备卸载时调用,用于释放资源。
static int led_remove(struct platform_device *dev)
{cdev_del(&gpioled.cdev);unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class);return 0;
}
字符设备驱动
字符设备驱动通过file_operations
结构体定义设备的操作函数。
static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release,
};
open
:打开设备。read
:读取设备数据。write
:写入设备数据。release
:关闭设备。
应用程序
应用程序通过系统调用与驱动程序交互,控制硬件设备。
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if (argc != 3) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0) {printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);retvalue = write(fd, databuf, sizeof(databuf));if (retvalue < 0) {printf("LED Control Failed!\r\n");close(fd);return -1;}close(fd);return 0;
}
open
:打开设备文件。write
:向设备写入数据,控制LED状态。close
:关闭设备文件。
参考资料
- 源码仓库