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

前言

DTS文件和内核驱动的联系通常是通过设备树机制实现的。设备树机制是一种描述系统硬件的数据结构,它以树形结构组织设备节点,并提供设备节点的属性信息,包括设备地址、中断号、寄存器地址等等。

在Linux内核启动时,内核会使用设备树机制自动加载设备驱动程序,并将设备节点和驱动程序进行匹配,从而实现设备驱动的自动加载和初始化。

什么是DTS?

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

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

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

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

驱动是如何读取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。

👇点击下方公众号卡片获取资料👇
  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核设备驱动模型采用了统一的设备模型,即设备树(Device Tree)模型。 设备树是一种描述硬件设备和资源的数据结构,它以一种可移植的方式描述了系统设备断、地址空间等信息。设备树使用一种类似于树形结构的语法,通过节点和属性来表示不同的设备和资源。 Linux内核设备驱动模型使用设备树来描述系统的硬件设备,并在运行时通过解析设备树来注册和管理设备驱动程序设备树允许系统在不同的硬件平台上使用相同的内核镜像,只需通过不同的设备树文件来描述硬件配置即可。 设备树模型提供了一种灵活和可扩展的方式来管理设备驱动程序。它可以动态地加载和卸载驱动程序,自动探测和配置硬件设备,并提供了一套标准的接口供设备驱动程序与硬件设备进行交互。 通过设备树模型,Linux内核可以实现对各种硬件设备的支持,包括处理器、总线控制器、外设等。它为不同的硬件平台提供了一致的接口,简化了设备驱动程序的开发和维护工作。 总结起来Linux内核设备驱动模型采用了设备树模型,通过解析设备树来注册和管理设备驱动程序设备树提供了一种可移植、灵活和可扩展的方式来描述和配置系统的硬件设备。它简化了设备驱动程序的开发和维护,并实现了对各种硬件设备的统一支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值