Linux中设备树是怎么和驱动程序联系起来的?

科技   2024-11-18 12:01   北京  
DTS文件和内核驱动的联系通常是通过设备树机制实现的。设备树机制是一种描述系统硬件的数据结构,它以树形结构组织设备节点,并提供设备节点的属性信息,包括设备地址、中断号、寄存器地址等等。
在Linux内核启动时,内核会使用设备树机制自动加载设备驱动程序,并将设备节点和驱动程序进行匹配,从而实现设备驱动的自动加载和初始化。

1、什么是DTS?

设备树源码(Device Tree SourceDTS)是用来描述硬件设备信息的一种语言。它是一种中立的表示方式,用于描述硬件设备的物理特性、接口信息和驱动程序的相关信息等,与处理器架构和操作系统无关。设备树被广泛应用于嵌入式系统领域,特别是在使用Linux内核的嵌入式系统中。

设备树是一种树形结构的数据结构,通常使用.dts文件来描述。.dts文件包含了设备树节点和属性的定义,其中节点代表硬件设备,属性则包含了硬件设备的相关信息。设备树文件通常被编译成二进制格式(Device Tree BlobDTB),并通过bootloader加载到内存中,供内核驱动程序解析使用。

设备树的作用是将硬件信息与软件驱动程序分离,可以使驱动程序更加灵活,适应多种硬件平台。使用设备树可以使内核代码更具可移植性,简化了内核的开发和维护,同时也方便了硬件厂商和系统集成商进行硬件设计和系统开发。

在Linux内核中,设备树被广泛应用于各种设备的驱动程序开发。在编写驱动程序时,我们通常需要通过解析设备树文件来获取硬件设备的相关信息,并使用内核提供的接口来控制硬件设备。

2、驱动是如何读取DTS文件的?

驱动程序可以通过内核提供的API来读取DTS文件中的节点和属性信息,以获取硬件设备的相关信息。在Linux内核中,驱动程序通常使用以下API来读取DTS文件:

of_find_node_by_name:通过节点名字查找节点,返回设备树节点的指针。

of_property_read_u32:读取DTS文件中的一个32位整数类型的属性值,并返回读取结果。

of_property_read_string:读取DTS文件中的一个字符串类型的属性值,并返回读取结果。

of_irq_get:获取设备的中断号。

of_clk_get:获取设备的时钟信息。

of_address_to_resource:获取设备的物理地址信息。

of_device_is_available:检查设备是否可用。

以上API是设备树节点和属性的读取接口,驱动程序可以通过这些接口来获取设备树中的信息,并根据需要进行操作。

在驱动程序中,通常会在设备初始化的过程中读取设备树中的节点和属性信息,根据这些信息初始化设备并设置相关参数。例如,一个LED驱动程序可以通过读取设备树中的GPIO节点来获取LED所连接的GPIO引脚信息,从而控制LED的亮灭。

这里以i.MX6ULL为例讲解一下驱动读取DTS的过程。

首先,我们需要在设备树中定义一个设备节点,该节点包含了LED的相关信息,如下所示:

&iomuxc {
    pinctrl_leds: ledsgrp {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x4001b0b0
        >;
    };

    leds {
        compatible = "gpio-leds";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_leds>;
        led1 {
            gpios = <&gpio1 3 0>;
            label = "led1";
            default-state = "off";
        };
    };
};

在这个设备树节点中,我们首先定义了一个pinctrl组,该组用于定义LED的GPIO引脚,然后在设备树中定义了一个leds节点,该节点指定了与LED相关的设备信息,包括设备类型(compatible属性)、GPIO引脚配置(pinctrl属性)、设备节点名称(led1)、LED的GPIO编号(gpios属性)、LED的名称(label属性)和默认状态(default-state属性)。

接下来,我们需要在内核驱动程序中使用设备树中的设备节点信息来控制LED。在i.MX6ULL中,LED控制器驱动程序是通过LED框架来实现的,该驱动程序需要在probe函数中获取LED的GPIO配置信息,然后使用LED框架提供的接口来控制LED。

以下是一个简单的LED控制器驱动程序的示例代码,该驱动程序使用了设备树中定义的led1设备节点:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/leds.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>

static struct gpio_desc *led_gpio_desc;

static int myled_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    struct led_classdev *led_cdev;
    int ret;

    // 从设备树中获取LED的GPIO配置
    led_gpio_desc = of_get_named_gpio_desc(np, "gpios"0);
    if (!led_gpio_desc) {
        dev_err(&pdev->dev, "Failed to get GPIO\n");
        return -EINVAL;
    }

    // 将GPIO引脚设置为输出模式
    ret = gpio_direction_output(led_gpio_desc->gpio, 0);
    if (ret) {
        dev_err(&pdev->dev, "Failed to set GPIO direction\n");
        return ret;
    }

    // 注册LED设备
    led_cdev = devm_kzalloc(&pdev->dev, sizeof(*led_cdev), GFP_KERNEL);
    if (!led_cdev) {
        dev_err(&pdev->dev, "Failed to allocate memory\n");
        return -ENOMEM;
    }

    led_cdev->name = "myled";
    led_cdev->brightness_set = led_brightness_set;
    led_cdev->max_brightness = 1;

    ret = devm_led_classdev_register(&pdev->dev, led_cdev);
    if (ret) {
        dev_err(&pdev->dev, "Failed to register LED device\n");
        return ret;
    }

    return 0;
}

static int myled_remove(struct platform_device *pdev)
{
    devm_led_classdev_unregister(&pdev->dev, &led_cdev);

    return 0;
}

static const struct of_device_id myled_of_match[] = {
    { .compatible = "gpio-leds" },
    {},
};
MODULE_DEVICE_TABLE(of, myled_of_match);

static struct platform_driver myled_driver = {
    .driver = {
        .name = "myled",
        .of_match_table = myled_of_match,
    },
    .probe = myled_probe,
    .remove = myled_remove,
};

module_platform_driver(myled_driver);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Driver for myled");
MODULE_LICENSE("GPL");

probe函数中,我们首先从设备树中获取了LED的GPIO配置信息,然后将GPIO引脚设置为输出模式,接着注册了一个LED设备,并设置了该设备的名称(name属性)、亮度设置函数(brightness_set属性)和最大亮度值(max_brightness属性)。最后,我们通过调用devm_led_classdev_register函数将该LED设备注册到LED框架中。

remove函数中,我们调用devm_led_classdev_unregister函数来注销已经注册的LED设备。

设备树文件通过定义设备节点来描述硬件设备信息,内核驱动程序可以通过解析设备树文件来获取硬件设备信息,并通过LED框架提供的接口来控制LED。

END

来源:嵌入式悦翔园


版权归原作者所有,如有侵权,请联系删除


推荐阅读

为什么大厂都在用Yocto?

培养一个优秀的嵌入式工程师有多难?

C/C++大限将至,美国强硬要求2026年前全面剔除!


→点关注,不迷路←

嵌入式微处理器
关注嵌入式相关技术和资讯,你想知道的都在这里。
 最新文章