100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED模板驱动程序的改造:设备树

【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED模板驱动程序的改造:设备树

时间:2020-09-05 11:04:01

相关推荐

【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED模板驱动程序的改造:设备树

文章目录

前言1、驱动的三种编写方法2、怎么使用设备树写驱动程序2.1、设备树节点要与platform_driver能匹配2.2、修改platform_driver的源码3、实验和调试技巧3.1、实验3.2、调试技巧device_node信息platform_device信息platform_driver信息

前言

韦东山嵌入式Linux驱动开发基础知识学习笔记

文章中大多内容来自韦东山老师的文档,还有部分个人根据自己需求补充的内容

视频教程地址:/video/BV14f4y1Q7ti

1、驱动的三种编写方法

三种驱动编写方法:直接法、总线设备模型(引入面向对象、分层、分离设计思想)、设备树模型

▲三种驱动编写方法

核心永远是file_operations结构体。

上述三种方法,只是指定“硬件资源”的方式不一样。

从上图可以知道,platform_device/platform_driver只是编程的技巧,不涉及驱动的核心。

2、怎么使用设备树写驱动程序

写在前面:

资源信息由设备树提供,由内核自动生成device_node和platform_device

platform_driver由chip_demo_gpio.c提供

提供给上层接口由leddrv.c提供

device和driver通过DTS文件和驱动chip_demo_gpio.c文件中相同的compatible属性对比完成匹配

2.1、设备树节点要与platform_driver能匹配

设备树文件经过内核的处理可以生成device_node和platform_device同时设备树文件也提供一些讯息去完成platform_device和platform_driver匹配

下面生成LED 设备需要向设备树文件中添加的内容

#define GROUP_PIN(g,p) ((g<<16) | (p))/ {100ask_led@0 {compatible = "100ask,leddrv";pin = <GROUP_PIN(0, 10)>;};100ask_led@1 {compatible = "100ask,leddrv";pin = <GROUP_PIN(6, 8)>;};};

这段内容被填写在根节点下,它通知内核创建两个device_node分别为100ask_led@0100ask_led@1,因为其位于根节点且包含compatible属性所以内核会为其创建platform_device且会以100ask,leddrv字符串为依据从内核中找到对应的platform_driver建立匹配关系,每个节点还包含了一个32位的数值用于表示LED设备需要的引脚资源,platform_driver可以获取该信息并以此为线索来配置对应引脚相关GPIO的寄存器

▲stm32mp157c-100ask-512d-lcd-v1.dts

对于百问网使用STM32MP157板子

设备树文件是:内核源码目录中arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts

修改、编译后得到arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb文件。

然后使用nfs ssh等方式把新编译出来的dtb去覆盖老文件。

2.2、修改platform_driver的源码

chip_demo_gpio.c文件中和platform_driver的创建相关的部分:

/* platform_driver.driver.of_match_table */static const struct of_device_id ask100_leds[] = {{.compatible = "100ask,leddrv" },{},};static struct platform_driver chip_demo_gpio_driver = {.probe= chip_demo_gpio_probe,.remove= chip_demo_gpio_remove,.driver= {.name = "100ask_led",.of_match_table = ask100_leds,},};static int __init chip_demo_gpio_drv_init(void){int err;/* 向内核注册platform_driver */err = platform_driver_register(&chip_demo_gpio_driver); register_led_operations(&board_demo_led_opr);return 0;}/* 驱动注销,此时会调用platform_driver.remove */static void __exit lchip_demo_gpio_drv_exit(void){platform_driver_unregister(&chip_demo_gpio_driver);}

内核在完成platform_deviceplatform_driver匹配之后会调用platform_driver的probe函数

/* 内核调用probe函数时会将对应的platform_device传入,可以通过访问其读取device_node获取设备树中包含的信息 */static int chip_demo_gpio_probe(struct platform_device *pdev){struct device_node *np;int err = 0;int led_pin;np = pdev->dev.of_node;if (!np)return -1;/* 读取device_node np中的u32的值,标签为"pin",读取之后存储在led_pin中 */err = of_property_read_u32(np, "pin", &led_pin);g_ledpins[g_ledcnt] = led_pin;led_class_create_device(g_ledcnt);g_ledcnt++;return 0;}

platform_driver中还有一个remove函数,在驱动被注销或者匹配的设备被注销时会被调用

static int chip_demo_gpio_remove(struct platform_device *pdev){int i = 0;int err;struct device_node *np;int led_pin;np = pdev->dev.of_node;if (!np)return -1;err = of_property_read_u32(np, "pin", &led_pin);/* 销毁LED设备 */for (i = 0; i < g_ledcnt; i++){if (g_ledpins[i] == led_pin){led_class_destroy_device(i);g_ledpins[i] = -1;break;};}/* 如果销毁了所有LED设备就将g_ledcnt置0 */for (i = 0; i < g_ledcnt; i++){if (g_ledpins[i] != -1)break;}if (i == g_ledcnt)g_ledcnt = 0;return 0;}

chip_demo_gpio.c

#include <linux/module.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/miscdevice.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/mutex.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/device.h>#include <linux/tty.h>#include <linux/kmod.h>#include <linux/gfp.h>#include <linux/platform_device.h>#include <linux/of.h>#include <asm/io.h>#include "led_opr.h"#include "leddrv.h"#include "led_resource.h"/* registers */// RCC_PLL4CR地址:0x50000000 + 0x894static volatile unsigned int *RCC_PLL4CR;// RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28static volatile unsigned int *RCC_MP_AHB4ENSETR;// GPIOA_MODER 地址:0x50002000 + 0x00static volatile unsigned int *GPIOA_MODER;// GPIOA_BSRR 地址: 0x50002000 + 0x18static volatile unsigned int *GPIOA_BSRR;// GPIOG_MODER 地址:0x50008000 + 0x00static volatile unsigned int *GPIOG_MODER;// GPIOG_BSRR 地址: 0x50008000 + 0x18static volatile unsigned int *GPIOG_BSRR;/* registers end *//*** @g_ledpins: 存放LED资源即pin * @g_ledcnt: 存放LED个数*/static int g_ledpins[100];static int g_ledcnt = 0;static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */ {//printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));/* 寄存器映射到变量 */if (!RCC_PLL4CR){// RCC_PLL4CR地址:0x50000000 + 0x894RCC_PLL4CR = ioremap(0x50000000 + 0x894, 4);// RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28RCC_MP_AHB4ENSETR = ioremap(0x50000000 + 0xA28, 4);// GPIOA_MODER 地址:0x50002000 + 0x00GPIOA_MODER = ioremap(0x50002000 + 0x00, 4);// GPIOA_BSRR 地址: 0x50002000 + 0x18GPIOA_BSRR = ioremap(0x50002000 + 0x18, 4);// GPIOG_MODER 地址:0x50008000 + 0x00GPIOG_MODER = ioremap(0x50008000 + 0x00, 4);// GPIOG_BSRR 地址: 0x50008000 + 0x18GPIOG_BSRR = ioremap(0x50008000 + 0x18, 4);}switch(GROUP(g_ledpins[which])){case 0:{printk("init pin of group A ...\n");/* enalbe PLL4, it is clock source for all gpio */*RCC_PLL4CR |= (1<<0);while ((*RCC_PLL4CR & (1<<1)) == 0);/* enable gpio */*RCC_MP_AHB4ENSETR |= (1<<GROUP(g_ledpins[which]));/** configure gpio as output */*GPIOA_MODER &= ~(3 << (PIN(g_ledpins[which])*2) );*GPIOA_MODER |= (1<< (PIN(g_ledpins[which])*2) );/* ... */break;}case 6:{printk("init pin of group G ...\n");/* enalbe PLL4, it is clock source for all gpio */*RCC_PLL4CR |= (1<<0);while ((*RCC_PLL4CR & (1<<1)) == 0);/* enable gpio */*RCC_MP_AHB4ENSETR |= (1<<GROUP(g_ledpins[which]));/** configure gpg8 as gpio* configure gpio as output */*GPIOG_MODER &= ~((PIN(g_ledpins[which])*2));*GPIOG_MODER |= ((PIN(g_ledpins[which])*2));break;}default:printk("not support %d\n", g_ledpins[which]);}return 0;}static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */{//printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));switch(GROUP(g_ledpins[which])){case 0:{printk("set pin of group A ...\n");*GPIOA_BSRR = ( 1<< (16*status + PIN(g_ledpins[which])));break;}case 6:{printk("set pin of group G ...\n");*GPIOG_BSRR = ( 1<< (16*status + PIN(g_ledpins[which])));break;}default:printk("not support %d\n", g_ledpins[which]);}return 0;}static struct led_operations board_demo_led_opr = {.init = board_demo_led_init,.ctl = board_demo_led_ctl,};struct led_operations *get_board_led_opr(void){return &board_demo_led_opr;}/* 内核调用probe函数时会将对应的platform_device传入,可以通过访问其读取device_node获取设备树中包含的信息 */static int chip_demo_gpio_probe(struct platform_device *pdev){struct device_node *np;int err = 0;int led_pin;np = pdev->dev.of_node;if (!np)return -1;/* 读取device_node np中的u32的值,标签为"pin",读取之后存储在led_pin中 */err = of_property_read_u32(np, "pin", &led_pin);g_ledpins[g_ledcnt] = led_pin;led_class_create_device(g_ledcnt);g_ledcnt++;return 0;}static int chip_demo_gpio_remove(struct platform_device *pdev){int i = 0;int err;struct device_node *np;int led_pin;np = pdev->dev.of_node;if (!np)return -1;err = of_property_read_u32(np, "pin", &led_pin);/* 销毁LED设备 */for (i = 0; i < g_ledcnt; i++){if (g_ledpins[i] == led_pin){led_class_destroy_device(i);g_ledpins[i] = -1;break;};}/* 如果销毁了所有LED设备就将g_ledcnt置0 */for (i = 0; i < g_ledcnt; i++){if (g_ledpins[i] != -1)break;}if (i == g_ledcnt)g_ledcnt = 0;return 0;}/* platform_driver.driver.of_match_table */static const struct of_device_id ask100_leds[] = {{.compatible = "100ask,leddrv" },{},};static struct platform_driver chip_demo_gpio_driver = {.probe= chip_demo_gpio_probe,.remove= chip_demo_gpio_remove,.driver= {.name = "100ask_led",.of_match_table = ask100_leds,},};static int __init chip_demo_gpio_drv_init(void){int err;/* 向内核注册platform_driver */err = platform_driver_register(&chip_demo_gpio_driver); register_led_operations(&board_demo_led_opr);return 0;}/* 驱动注销,此时会调用platform_driver.remove */static void __exit lchip_demo_gpio_drv_exit(void){platform_driver_unregister(&chip_demo_gpio_driver);}module_init(chip_demo_gpio_drv_init);module_exit(lchip_demo_gpio_drv_exit);MODULE_LICENSE("GPL");

3、实验和调试技巧

3.1、实验

查看/sys/firmware/devicetree/base有无节点查看/sys/devices/platform目录下有无对应的platform_device加载驱动:

# insmod leddrv.ko# insmod chip_demo_gpio.ko

测试驱动

# ./ledtest /dev/100ask_led0 on# ./ledtest /dev/100ask_led0 off

▲查看节点▲查看platform_device▲LED实验

3.2、调试技巧

device_node信息

以下目录对应设备树的根节点,可以从此进去找到自己定义的节点。

cd /sys/firmware/devicetree/base/

节点是目录,属性是文件。

属性值是字符串时,用cat命令可以打印出来;属性值是数值时,用hexdump命令可以打印出来。

▲device_node信息

platform_device信息

以下目录含有注册进内核的所有platform_device:

/sys/devices/platform

一个设备对应一个目录,进入某个目录后,如果它有“driver”子目录,就表示这个platform_device跟某个platform_driver配对了。

▲100ask_led@0已经配对

platform_driver信息

以下目录含有注册进内核的所有platform_driver:

/sys/bus/platform/drivers

一个driver对应一个目录,进入某个目录后,如果它有配对的设备,可以直接看到。

▲驱动目录下包含已经匹配的设备

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。