前言
(1)在韦东山Linux驱动入门实验班(5)LED驱动—驱动分层和分离,平台总线模型我们已经讲解了如何将驱动程序和硬件程序进行剥离。但是大佬们感觉这样还不行,他们认为要专门弄一个结构存储硬件信息,而不是用c文件存储。于是,大佬们就发明了设备树进行存储硬件信息。
(2)代码仓库:gitee仓库;github仓库;
(3)注意:大家下载我仓库里面的代码再阅读本文会跟好理解一点。我仓库里面的代码依旧加上了详细的注释,觉得本文过于冗余。可以看我仓库代码和正点原子的文档学习。
(4)我这里将会使用BCompare软件进行文件对比,方便各位理解。
不同点
不同点1 — platform_driver配置不同
(1)同样,一个驱动程序还是需要从module_init()这个宏开始看起,然后网上爬,我们会发现platform_driver结构体有一个细微的区别。
(2)platform_driver.driver里面我们增加了一个of_match_table变量,led_dtb。这个是用于和设备树进行匹配的。关于驱动程序和设备匹配规则不太明白的同学,建议看一下 Linux驱动与设备的匹配规则。
(3)我们在of_match_table变量中需要传入const struct of_device_id类型的数组。这里里面的of_device_id结构体中的compatible值要和设备树里面的compatible 名字是一模一样的。否则无法匹配成功。
/* 在dts文件下增加这几行 */
/{
test1:test_device_tree{
compatible = "test_compatible";
gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
};
}
不同点2 — 存放设备信息的结构体不同
(1)如果驱动和设备匹配上了,就会触发probe函数。在这个函数里面我们需要获得设备信息。如果是设备树,使用device_node指针可以获得。如果是c文件中的设备信息,使用resource结构体指针可以获得数据。
(2)如果匹配上了设备树np将会指向这个设备树文件。否则np将会是一个空指针。
struct device_node *np = pdev->dev.of_node; //设备树中
struct resource *led_resource; //c文件中
不同点3 — 获取gpio数量的方式不同
(1)在设备树中,直接调用of_gpio_count(np)可以获得gpio的数量。
(2)而在C文件中,有两种方式可以获得设备信息。
//设备树中统计的设备的 GPIO 数量
count = of_gpio_count(np);
//C文件中获取GPIO数量的两种方法
count = pdev->num_resources; //这个不会区分GPIO的flag
while (1) //这个可以获得指定flag的GPIO数量
{
/* 下面这9行,是用于统计有多少个GPIO的
* dev:一个指向 platform_device 结构的指针,表示要获取资源信息的设备。
* type:一个无符号整数,表示要获取的资源类型。在 Linux 内核中,资源类型使用常量来表示,
例如 IORESOURCE_MEM 表示内存资源,IORESOURCE_IRQ 表示中断资源等。你可以根据需要选择适当的资源类型。
* num:一个无符号整数,表示要获取的资源的索引号。在一个设备中可能存在多个相同类型的资源,通过索引号可以区分它们。
* 返回值:返回一个指向 resource 结构的指针,表示获取到的资源信息。
resource 结构包含了资源的起始地址、大小等信息。如果没有找到指定的资源,函数将返回 NULL。
*/
led_resource = platform_get_resource(pdev, IORESOURCE_IRQ, count);
if (led_resource)
{
count++;
}
else
{
break;
}
}
不同点4 — 获取引脚号的方式不同
(1)
<1>在设备树中,直接调用of_get_gpio()可以获得gpios的数据,然后并且将他转换为引脚号。
<2>例如本文中的<&gpio5 3 GPIO_ACTIVE_HIGH>,他会调用desc_to_gpio这个函数将gpio5 3转换为引脚号131。
(2)在c文件中,获取引脚信息需要调用platform_get_resource()函数获得platform_device.resource结构体,然后调用led_resource->start,就可以获得引脚号了。不过这个引脚号需要你本人来进行计算。所以相对来说麻烦一点。
//设备树
gpios[i].gpio = of_get_gpio(np, i); //获得gpio信息
//c文件中
led_resource = platform_get_resource(pdev, IORESOURCE_IRQ, i); //从节点里面取出第i项
if(led_resource==NULL)
{
printk("platform_get_resource is flaut\r\n");
}
printk("platform_get_resource is ok\r\n");
gpios[i].gpio = led_resource->start; //将需要操作的IO号传递给gpios[i].gpio
不同点5 — 获取引脚名字的方式不同
(1)说实话,其实也差不多。都是一个结构体指针指向name变量。为了方便新手理解,还是当成不同来处理算了。
//设备树
sprintf(gpios[i].name, "%s", np->name); //将设备树中的节点名字传递给gpios[i].name
//c文件
sprintf(gpios[i].name, "%s", pdev->name); //将platform_device.resource.name传递给gpios[i].name
总结
设备树其实就是将设备信息从原来存放在c文件中,变成一种树形结构来存储,然后是dts文件存储。这个能够很好的将驱动文件与设备文件剥离。实现真正的脱离硬件。