嵌入式Linux驱动开发:设备树与平台设备驱动

嵌入式Linux驱动开发:设备树与平台设备驱动

引言

本笔记旨在详细记录嵌入式Linux驱动开发中设备树(Device Tree)和平台设备驱动(Platform Driver)的核心概念与实现。通过分析提供的代码与设备树文件,我们将深入理解如何在i.MX6ULL平台上使用设备树来描述硬件,并通过平台设备驱动来管理这些硬件资源。本文档将结合代码实例,详细解释每个关键部分的作用与实现细节。

设备树(Device Tree)

设备树是一种数据结构,用于描述硬件的配置和连接方式,使得操作系统可以在不硬编码硬件信息的情况下初始化和使用硬件。设备树文件通常以.dts为扩展名,编译后生成.dtb文件,由引导加载程序传递给内核。

设备树文件结构

设备树文件由一系列节点(node)和属性(property)组成。每个节点代表一个硬件设备或子系统,属性则描述该设备的具体配置。

根节点

设备树的根节点用/表示,包含全局属性和子节点。

dts 复制代码
/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:指定设备的兼容性,用于匹配驱动程序。
子节点

子节点描述具体的硬件设备,如内存、外设等。

dts 复制代码
memory {
    reg = <0x80000000 0x20000000>;
};
  • memory节点描述系统的物理内存布局,reg属性指定内存的起始地址和大小。

设备树源文件(DTSI)

设备树源文件(.dtsi)类似于C语言的头文件,包含多个.dts文件共享的通用定义。通过#include指令引入。

dts 复制代码
#include "imx6ull.dtsi"

设备树编译

设备树文件需要编译成二进制格式(.dtb),通常使用dtc(Device Tree Compiler)工具完成。

bash 复制代码
dtc -I dts -O dtb -o imx6ull-alientek-emmc.dtb imx6ull-alientek-emmc.dts

平台设备驱动

平台设备驱动是Linux内核中用于管理平台特定设备的一种机制。它通过设备树描述硬件资源,并在驱动程序中解析这些资源。

平台设备(Platform Device)

平台设备由platform_device结构体表示,包含设备的名称、资源和私有数据。

资源定义

资源定义了设备所需的内存、中断等硬件资源。

c 复制代码
static struct resource led_resources[] = {
    [0] = {
        .start = CCM_CCGR1_BASE,
        .end = CCM_CCGR1_BASE + REGISTER_LNE - 1,
        .flags = IORESOURCE_MEM,
    },
    ...
};
  • startend:资源的起始和结束地址。
  • flags:资源类型,如IORESOURCE_MEM表示内存资源。
平台设备注册

平台设备通过platform_device_register函数注册到内核。

c 复制代码
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结构体表示,包含驱动的名称、匹配表、探测和移除函数。

匹配表

匹配表用于将驱动与设备树中的节点关联。

c 复制代码
struct of_device_id led_of_match[] = {
    {.compatible = "alientek,gpioled"},
    {/* sentinel */}
};
  • compatible:与设备树中节点的compatible属性匹配。
探测函数

探测函数在设备与驱动匹配成功后调用,用于初始化设备。

c 复制代码
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为输出模式。
移除函数

移除函数在设备卸载时调用,用于释放资源。

c 复制代码
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结构体定义设备的操作函数。

c 复制代码
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:关闭设备。

应用程序

应用程序通过系统调用与驱动程序交互,控制硬件设备。

c 复制代码
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:关闭设备文件。

参考资料

相关推荐
工藤新一¹8 小时前
Linux 进程状态 — 僵尸进程
linux·僵尸进程·c/c++·进程状态
jianqiang.xue8 小时前
Proteus 仿真 + STM32CubeMX 协同开发全教程:从配置到仿真一步到位
stm32·单片机·嵌入式硬件·proteus·adc·仿真
Sadsvit8 小时前
Ansible: YAML语法与 Playbook写法解析
linux·运维·服务器·centos·自动化·ansible
滴滴滴嘟嘟嘟.8 小时前
嵌入式Linux驱动开发:ICM20608六轴传感器SPI驱动
linux·运维·驱动开发
逼子格9 小时前
【Proteus仿真】蜂鸣器控制系列仿真——蜂鸣器控制/蜂鸣器播放音乐/蜂鸣器播放多种音乐/蜂鸣器和LED组成报警装置
单片机·嵌入式硬件·proteus·硬件工程·蜂鸣器·流水灯
Sadsvit9 小时前
Ansible变量的定义与使用
linux·运维·centos·自动化·ansible
特种加菲猫9 小时前
深入Linux内核:IPC资源管理揭秘
linux·笔记
武晓兵9 小时前
MCU启动过程简介
嵌入式硬件