首页
关于
Search
1
开发环境搭建(FTP、VSCode、交叉编译器、SSH、NFS、TFTP)
33 阅读
2
linux内核编译报错(gcc: not found + fatal error curses.h)
26 阅读
3
安装WMware Tools选项显示灰色解决方法及WMware Tools安装笔记
17 阅读
4
ubuntu24.2下解压.tar.bz2报错(bzip2: 无法exec:没有那个文件或目录)
14 阅读
5
linux内核编译报错(usr/bin/ld: scripts/dtc/dtc-parser.tab.o:(.bss+0x50): multiple definition of 'yylloc';)
14 阅读
学习笔记
linux学习笔记
文件系统
FreeRTOS
资源分享
小作品
登录
/
注册
Search
Warren
累计撰写
37
篇文章
累计收到
1
条评论
首页
栏目
学习笔记
linux学习笔记
文件系统
FreeRTOS
资源分享
小作品
页面
关于
搜索到
37
篇与
的结果
2024-08-29
pinctrl和gpio子系统实验
Linux 驱动讲究驱动分离与分层,也就是把与soc芯片相关的一些驱动代码与具体的驱动应用给分离开。pinctrl子系统pinctrl 子系统主要完成:①获取设备树中 pin 信息②根据获取到的 pin 信息来设置 pin 的复用功能③根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。 也即是说我们只需要在设备树中描述好这个gpio的具体属性,则pinctrl子系统会根据这些信息帮我们初始化好这个pin。pinctrl在设备树中的添加/修改方法我们打开设备树文件imx6ull-alientek-emmc.dts。找到iomuxc节点。在iomuxc节点下创建一个子结点,内容如下:pinctrl_led: ledgrp{ fsl,pins = < MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 >; };pinctrl节点的节点名称必须是以pincrtl_xxxx来命名pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配置信息fsl中填写的是PIN的复用功能,pins中填写的是PIN的电气属性。MX6UL_PAD_UART1_RTS_B__GPIO1_IO19,这是一个宏定义,定义在文件arch/arm/boot/dts/imx6ul-pinfunc.h。打开这个文件我们可以看到里面有好多个宏。这些宏其实是5个一组的值。具体含义就是<mux_reg conf_reg input_reg mux_mode input_val>mux_reg:mux_reg 寄存器偏移地址conf_reg:conf_reg 寄存器偏移地址input_reg:input_reg 寄存器偏移地址mux_reg:mux_reg寄存器值input_val:input_reg 寄存器值另外:pins中填写的就是conf_reg 寄存器的值那么有个问题,iomux如何根据这些信息来初始化pin。 iomuxc 节点中 compatible 属性的值为“fsl,imx6ul-iomuxc”,在 Linux 内核中全局搜索“fsl,imx6ul-iomuxc”字符串就会找到对应的驱动文件 第326行有一个 of_device_id 结构体数组,保存着这个驱动文件的兼容性值。那也就说 pinctrl-imx6ul.c 会完成 I.MX6ULL 的 PIN 配置工作。第347-355行有一个platform_driver结构体类型变量。它里面有个 probe 成员变量,当设备和驱动匹配成功以后 platform_driver 的 probe 成员变量所代表的函数就会执行。也就是说imx6ul_pinctrl_probe是PIN配置的入口函数。调用路径如下:imx_pinctrl_parse_groups 负责获取设备树中关于 PIN 的配置信息。 pinctrl_register此函数用于向Linux内核注册一个PIN控制器,函数原型如下余下的没有深究{lamp/}gpio子系统gpio子系统用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO我们在设备树根节点添加gpioled节点gpioled{ #address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-gpioled"; pinctrl-name = "default"; pinctrl-0 = <&pinctrl_led>; //指定使用的PIN对应的pinctrl节点 led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;//指定所使用的GPIO为GPIO1-IO03且低电平有效 status = "okay"; };gpio子系统API函数gpio_request函数,申请一个GPIO管脚 //要申请的 gpio 标号 //给 gpio 设置个名字 int gpio_request(unsigned gpio, const char *label)gpio_free函数,释放一个GPIO管脚 //要释放的 gpio 标号 void gpio_free(unsigned gpio)gpio_direction_input函数 //设置某个 GPIO 为输入 int gpio_direction_input(unsigned gpio)gpio_direction_output函数 //要设置为输出的 GPIO 标号 //GPIO 默认输出值 int gpio_direction_output(unsigned gpio, int value)gpio_get_value函数 //要获取的 GPIO 标号 int __gpio_get_value(unsigned gpio)gpio_set_value函数 //gpio:要设置的 GPIO 标号 //value: 要设置的值 __gpio_set_value(unsigned gpio, int value)与gpio相关的of函数1.of_gpio_named_count 函数(获取设备树某个属性里面定义了几个 GPIO 信息,包括空信息) //np:设备节点 //propname:要统计的 GPIO 属性 int of_gpio_named_count(struct device_node *np, const char *propname)2.of_gpio_count函数(获取设备树“gpios”这个属性的 GPIO 数量,包括空信息) //np:设备节点 int of_gpio_count(struct device_node *np)3.of_get_named_gpio(获取 GPIO 编号) //np:设备节点 //propname:包含要获取 GPIO 信息的属性名 //GPIO 索引(因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。) int of_get_named_gpio(struct device_node *np,const char *propname,int index) /*补充:此函数在驱动中使用很频繁*/
2024年08月29日
2 阅读
0 评论
0 点赞
2024-08-28
设备树下的LED驱动实验
修改设备树文件开发板对应的设备树文件为imx6ull-alientek-emmc.dts对应的linux源码的/arch/arm/boot/dts在根节点下创建一个alphaled的子节点,创建的内容如下。alphaled{ #address-cells = <1>; //表示 reg 属性中起始地址占用一个字长 #size-cells = <1>; //表示 reg 属性中地址长度占用一个字长 compatible = "atkalpha-led"; //设置节点兼容性为“atkalpha-led” status = "okay"; //状态为“okay” reg = < 0X020C406C 0X04 //寄存器CCM_CCGR1_BASE物理地址 0X020E0068 0X04 //寄存器SW_MUX_GPIO1_IO03_BASE物理地址 0X020E02F4 0X04 //寄存器SW_PAD_GPIO1_IO03_BASE物理地址 0X0209C000 0X04 //寄存器GPIO1_DR_BASE物理地址 0X0209C004 0X04 >; //寄存器GPIO1_GDIR_BASE物理地址 };编译设备树 make dtbs编译完成后可以在 源码/arch/arm/boot/dts下看到新的imx6ull-alientek-emmc.dtb。将新的设备树文件烧录在板子上,启动板子可以在/proc/device-tree/目录下查看到alphaled这个节点驱动程序编写#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> #define DTSLED_CNT 1 /*设备号个数*/ #define DTSLED_NAME "dtsled" /*设备名称*/ #define LEDOFF 0 /*开灯*/ #define LEDON 1 /*关灯*/ /*映射后的寄存器虚拟地址*/ static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO03; static void __iomem *SW_PAD_GPIO1_IO03; static void __iomem *GPIO1_DR; static void __iomem *GPIO1_GDIR; /*dtsled设备结构体*/ struct dtsled_dev{ dev_t devid; /*设备号*/ struct cdev cdev; /*cdev*/ struct class *class; /*类*/ struct device *device; /*设备*/ int major; /*主设备号*/ int minor; /*次设备号*/ struct device_node *nd; /*设备节点*/ }; struct dtsled_dev dtsled; /*dtsled设备*/ /*LED开关函数*/ void led_switch(u8 sta) { u32 val = 0; if(sta == LEDON){ val = readl(GPIO1_DR); val &= ~(1<<3); writel(val, GPIO1_DR); }else if(sta == LEDOFF){ val = readl(GPIO1_DR); val |= (1<<3); writel(val, GPIO1_DR); } } /*打开涉笔*/ static int led_open(struct inode *inode, struct file *filp) { filp->private_data = &dtsled;/*设置私有数据*/ return 0; } /*从设备读取数据*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { return 0; } /*向设备写数据*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int retvalue = 0; u8 databuf[4] = {0}; u8 ledstat = 0; retvalue = copy_from_user(databuf, buf, cnt); if(retvalue < 0){ printk("Kernel write failed!\r\n"); return -EFAULT; }else{ printk("Kernel write %d success!\r\n", databuf[0]); } ledstat = databuf[0]; if(ledstat == LEDOFF){ led_switch(LEDOFF); }else if(ledstat == LEDON){ led_switch(LEDON); } return 0; } /*关闭设备*/ static int led_release(struct inode *inode, struct file *filp) { return 0; } /*设备操作函数*/ static struct file_operations dtsled_fops = { .owner = THIS_MODULE, .open = led_open, .read = led_read, .write = led_write, .release = led_release, }; /*LED硬件初始化*/ static void led_hw_init(u32 *regdata, struct device_node *nd) { u32 val = 0; /*寄存器地址映射*/ #if 0 IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]); SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]); SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]); GPIO1_DR = ioremap(regdata[6], regdata[7]); GPIO1_GDIR = ioremap(regdata[8], regdata[9]); #else IMX6U_CCM_CCGR1 = of_iomap(nd, 0); SW_MUX_GPIO1_IO03 = of_iomap(nd, 1); SW_PAD_GPIO1_IO03 = of_iomap(nd, 2); GPIO1_DR = of_iomap(nd, 3); GPIO1_GDIR = of_iomap(nd, 4); #endif /*使能GPIO1时钟*/ val = readl(IMX6U_CCM_CCGR1); val &= ~(3<<26); val |= (3<<26); writel(val, IMX6U_CCM_CCGR1); /*设置GPIO1_IO03的复用功能*/ writel(5, SW_MUX_GPIO1_IO03); /*设置IO属性*/ writel(0x10B0, SW_PAD_GPIO1_IO03); /*设置IO为输出功能*/ val = readl(GPIO1_GDIR); val &= ~(1<<3); val |= (1<<3); writel(val, GPIO1_GDIR); /*关闭LED*/ val = readl(GPIO1_DR); val |= (1<<3); writel(val, GPIO1_DR); printk("led hw init done\r\n"); } /*LED硬件反初始化*/ static void led_hw_deinit(void) { /*取消映射*/ iounmap(IMX6U_CCM_CCGR1); iounmap(SW_MUX_GPIO1_IO03); iounmap(SW_PAD_GPIO1_IO03); iounmap(GPIO1_DR); iounmap(GPIO1_GDIR); } /*驱动入口函数*/ static int __init led_init(void) { int i = 0; int ret = 0; u32 regdata[10] = {0}; struct property *proper = NULL; const char *str; /*获取设备树中的属性数据*/ /*1.获取设备节点:alphaled*/ dtsled.nd = of_find_node_by_path("/alphaled"); //通过路径来查找指定的节点 if(dtsled.nd == NULL){ printk("alphaled node can not found!\r\n"); return -EINVAL; }else{ printk("alphaled node has been found!\r\n"); } /*2.获取compatible属性内容*/ proper = of_find_property(dtsled.nd, "compatible", NULL);//查找指定的属性 if(proper == NULL){ printk("compatible property find failed\r\n"); }else{ printk("compatible = %s\r\n", (char*)proper->value); } /*3.获取status属性内容*/ ret = of_property_read_string(dtsled.nd, "status", &str);//用于读取属性中字符串值 if(ret < 0){ printk("status read failed!\r\n"); }else{ printk("status = %s\r\n", str); } /*获取reg属性内容*/ ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);//读取属性中u32类型的数组数据 if(ret < 0){ printk("reg property read failed!\r\n"); }else{ printk("reg data:\r\n"); for(i=0; i<10; i++){ printk("%#X ", regdata[i]); } printk("\r\n"); } /*初始化LED*/ led_hw_init(regdata, dtsled.nd); /*1.创建设备号*/ if(dtsled.major){/*定义了设备号*/ dtsled.devid = MKDEV(dtsled.major, 0); register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME); }else{/*内有定义了设备号*/ alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);/*申请设备号*/ dtsled.major = MAJOR(dtsled.devid);/*获取分配号的主设备号*/ dtsled.minor = MINOR(dtsled.devid);/*获取分配号的次设备号*/ } printk("dtsled major = %d, minor = %d\r\n", dtsled.major, dtsled.minor); /*初始化cdev*/ dtsled.cdev.owner = THIS_MODULE; cdev_init(&dtsled.cdev, &dtsled_fops); /*向内核中添加一个cdev*/ cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT); /*创建类*/ dtsled.class = class_create(THIS_MODULE, DTSLED_NAME); if(IS_ERR(dtsled.class)){ return PTR_ERR(dtsled.class); } /*创建设备*/ dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME); if(IS_ERR(dtsled.device)){ return PTR_ERR(dtsled.device); } return 0; } /*驱动出口函数*/ static void __exit led_exit(void) { led_hw_deinit(); /*注销字符设备驱动*/ cdev_del(&dtsled.cdev);/*删除cdev*/ unregister_chrdev_region(dtsled.devid, DTSLED_CNT);/*注销设备号*/ device_destroy(dtsled.class, dtsled.devid);/*设备的销毁*/ class_destroy(dtsled.class);/*类的销毁*/ } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("warren");测试程序编写#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" #define LEDOFF 0 #define LED_ON 1 int main(int argc, int **argv) { int fd = 0; int retvalue = 0; char *filename = NULL; 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("faile %s open failed!\r\n", filename); 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; } retvalue = close(fd); if(retvalue < 0){ printf("file %s close failed!\r\n", filename); return -1; } return 0; }程序编译KERNELDIR := /home/warren/linux/linux_source_code/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd) obj-m := dtsled.o build: kernel_modules kernel_modules: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules clean: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) cleanarm-linux-gnueabihf-gcc ledApp.c -o ledApp程序测试将编译完成后的dtsled.ko文件放在/lib/modules/4.1.15-g06f53e4下使用depmod命令扫描设备中的驱动使用modprobe dtsled命令加载命令可以看到如下打印到/dev下可以查看到驱动文件将编译完成后的ledApp文件放在home下。调用ledApp可以实现开灯与关灯./ledApp /dev/dtsled 1 /*开灯*/ ./ledApp /dev/dtsled 0 /*关灯*/
2024年08月28日
2 阅读
0 评论
0 点赞
2024-08-21
Linux编译报错[lzop: not found]
报错现象使用脚本编译linux的时候,报如下错误/bin/sh: 1: lzop: not found报错原因lzop库未找到解决方案命令行在线安装lzop库sudo apt-get install lzop重新编译即可
2024年08月21日
0 阅读
0 评论
0 点赞
2024-08-19
模块加载报错[ 972.900509] dtsled: version magic '4.1.15 SMP preempt mod_unload modversions ARMv6 p2v8 ' should be '4.1.15-g06f53e4 SMP preempt mod_unload m
故障现象加载模块时候,出现报错[ 972.900509] dtsled: version magic '4.1.15 SMP preempt mod_unload modversions ARMv6 p2v8 ' should be '4.1.15-g06f53e4 SMP preempt mod_unload modversions ARMv7 p2v8 '故障原因内核版本信息不一样不一样的地方就是EXTRAVERSION以及 ARMv7 p2v8这个解决方法修改EXTRAVERSION进入到开发板的/lib/modules/文件夹下,可以看到有一个文件夹名称叫做4.1.15-xxxxxxxx进入到源码的根目录下,对Makefile文件进行修改。将-xxxxxxxx填写到EXTRAVERSION后面。修改后的Makefile如下所示:修改ARMv7 p2v8进入到linux图形化配置界面进入到System Type进入到Multiple platform selection,并取消掉ARMv6 based platformsSystem Type ---> Multiple platform selection ---> [ ] ARMv6 based platforms (ARM11) [*] ARMv7 based platforms (Cortex-A, PJ4, Scorpion, Krait)然后报错退出,正常编译即可。结果测试
2024年08月19日
1 阅读
0 评论
0 点赞
2024-08-08
Linux设备树(2)
创建小型模板设备树本创建一个设备树,没有啥意义,只是手抄练习!!!I.MX6ULL 这个 Cortex-A7 架构的 32 位 CPU。I.MX6ULL 内部 ocram,起始地址 0x00900000,大小为 128KB(0x20000)。I.MX6ULL 内部 aips1 域下的 ecspi1 外设控制器,寄存器起始地址为 0x02008000,大小为 0x4000。I.MX6ULL 内部 aips2 域下的 usbotg1 外设控制器,寄存器起始地址为 0x02184000,大小为 0x4000。I.MX6ULL 内部 aips3 域下的 rngb 外设控制器,寄存器起始地址为 0x02284000,大小为 0x4000。/ { compatible = "fsl,imx6ull-alientek-evkl", "fsl,imx6ull"; cpus{ #address-cells = <1>; #size-cells = <1>; cpu0: cpu@0{ compatible = "arm, cortex-a7"; device_type = "cpu"; reg = <0>; }; }; soc{ #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges; ocram: sram@00900000{ compatible = "fsl, lpm-sram" reg = <0x00900000, 0x20000>; }; aips1: aips-bus@02000000{ compatible = "fsl,apis-bus", "simple-bus"; #address-cells = <1>; #size-cells = <1>; reg = <0x02000000 0x100000>; rangs; escpi1: ecspi@02008000{ #address-cells = <1>; #size-cells = <1>; compatible = "fsl, imx6ul-ecspi", "fsl,imx51-ecspi"; reg = <0x02000800 0x4000>; status = "disabled"; }; }; apis2: aips-bus@02100000{ compatible = "fsl,apis-bus", "simple-bus"; #address-cells = <1>; #size-cells = <1>; reg = <0x02100000 0x100000>; rangs; usbotg1: usb@02184000{ compatible = "fsl,imx6ul-usb", "fsl,imx27-usb" reg = <0x02184000 0x4000>; status = "disable"; }; }; apis3: aips-bus@02200000{ compatible = "fsl,apis-bus", "simple-bus"; #address-cells = <1>; #size-cells = <1>; reg = <0x02200000 0x100000>; rangs; rngb: rngb@02284000{ compatible = "fsl,imx6sl-rng", "fsl,imx-rng", "imx-rng", "imx-rng"; reg = <0x02284000 0x4000>; }; }; }; };设备树在系统中的体现Linux 内核启动的时候会解析设备树中各个节点的信息,并且在根文件系统的/proc/device-tree 目录下根据节点名字创建不同文件夹。我们可以看到里面有5个文件还有若干个文件夹。“#address-cells”、“#size-cells”、“compatible”、“model”和“name”这 5 个文件,它们在设备树中就是根节点的 5个属性。我们可以通过cat命令来查看文件的内容。这个正是设备树文件根节点的属性。对于文件夹我们可以进入查看和根节点一样,里面的内容为SOC节点的属性以及它的子节点。特殊节点在根节点“/”中有两个特殊的子节点: aliases 和 chosen。aliases 子节点打开/arch/arm/boot/tds/imx6ull.dtsi文件,aliases节点的内容如下所示:aliases翻译过来就是别名。他的作用就是为了方便访问节点。作用和label作用一样。我们进入到aliases文件夹中可以看到很多个文件。我们可以通过cat命令查看这些文件,可以通过别名信息来查看到节点的详细信息。chosen 子节点chosen 并不是一个真实的设备, chosen 节点主要是为了 uboot 向 Linux 内核传递数据。一般.dts 文件中 chosen 节点通常为空或者内容很少。打开/arch/arm/boot/tds/imx6ull-alientek-emmc.dts文件。里面只是表示标准输出使用串口1。我们进入到chosen文件夹下查看可以看到有3个文件文件,为“bootargs”,“name”,“stdout-path”。通过cat命令来查看name属性内容。name属性值就为chosen节点的名称。通过cat命令来查看stdout-path属性内容,stdout-path属性值就为chosen在设备树文件指定的串口0的设备节点信息重点来到了bootargs!通过cat命令来查看bootargs这个文件的内容。可以看到bootargs的文件内容就是我们在uboot下设置的bootargs环境变量的值。现在有两个疑问点?1.我们并没有在设备树中设置 chosen 节点的 bootargs 属性,那么图bootargs这个属性是怎么产生的?uboot 在启动 Linux 内核的时候会将 bootargs 的值传递给 Linux内核。Linux 内核启动的时候会打印出命令行参数。2.为何 bootargs 文件的内容和 uboot 中 bootargs 环境变量的值一样?它们之间有什么关系?①uboot在启动时,调用函数 fdt_find_or_add_subnode 从设备树(.dtb)中找到 chosen 节点,如果没有找到的话就会自己创建一个 chosen 节点。②读取 uboot 中 bootargs 环境变量的内容。③调用函数 fdt_setprop 向 chosen 节点添加 bootargs 属性,并且 bootargs 属性的值就是环境变量 bootargs 的内容。总结一下:uboot从设备树中加载设备树文件,然后将自己的bootargs属性加载到设备树的chosen节点(如果chosen节点就创建一个)。Linux内核解析DTB文件(知识储备不足,一句带过,没有细究。)在 start_kernel 函数中完成了设备树节点解析的工作,最终实际工作的函数为 unflatten_dt_node。绑定信息文档我们在设备树中添加一个硬件对应的节点的时候,应该在Linux 源码目录/Documentation/devicetree/bindings路径下查看相应的文档。比如我们向给板子添加一个IIC设备。那么就可以查看Documentation/devicetree/bindings/i2c/i2c-imx.txt参照下面给出的例子模板来添加或修改设备节点。设备树常用 OF 操作函数我们在设备树中配置的那些属性信息,在驱动开发时候要使用,那么如何获取这个信息那?Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_”,所以在很多资料里面也被叫做 OF 函数。这些 OF 函数原型都定义在 include/linux/of.h 文件中Linux 内核使用 device_node 结构体来描述一个节点,此结构体定义在文件 include/linux/of.h 中struct device_node { const char *name; const char *type; phandle phandle; const char *full_name; struct fwnode_handle fwnode; struct property *properties; struct property *deadprops; /* removed properties */ struct device_node *parent; struct device_node *child; struct device_node *sibling; struct kobject kobj; unsigned long _flags; void *data; #if defined(CONFIG_SPARC) const char *path_component_name; unsigned int unique_id; struct of_irq_controller *irq_trans; #endif };将常用的OF函数分为4类(查找节点的 OF 函数、查找父/子节点的 OF 函数、提取属性值的 OF 函数、其他常用的 OF 函数);查找节点的 OF 函数(共5个)1.of_find_node_by_name(通过节点名字查找指定的节点)//from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 //name:要查找的节点名字。 //返回值: 找到的节点,如果为 NULL 表示查找失败。 struct device_node *of_find_node_by_name(struct device_node *from, const char *name);2.of_find_node_by_type(通过 device_type 属性查找指定的节点)//from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 //type:要查找的节点对应的 type 字符串,也就是 device_type 属性值。 //返回值: 找到的节点,如果为 NULL 表示查找失败。 struct device_node *of_find_node_by_type(struct device_node *from, const char *type)3.of_find_compatible_node(根据 device_type 和 compatible 这两个属性查找指定的节点)//from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 //type:要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以为 NULL,表示忽略掉 device_type 属性。 //compatible: 要查找的节点所对应的 compatible 属性列表。 //返回值: 找到的节点,如果为 NULL 表示查找失败 struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible)4.of_find_matching_node_and_match(通过 of_device_id 匹配表来查找指定的节点)//from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 //matches: of_device_id 匹配表,也就是在此匹配表里面查找节点。 //match: 找到的匹配的 of_device_id。 //返回值: 找到的节点,如果为 NULL 表示查找失败 struct device_node *of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)5.of_find_node_by_path(通过路径来查找指定的节点)//path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。 //返回值: 找到的节点,如果为 NULL 表示查找失败 inline struct device_node *of_find_node_by_path(const char *path)查找父/子节点的 OF 函数(2个)1.of_get_parent(获取指定节点的父节点)//node:要查找的父节点的节点。 // 返回值: 找到的父节点。 struct device_node *of_get_parent(const struct device_node *node)2.of_get_next_child(用迭代的方式查找子节点)// node:父节点。 // prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为NULL,表示从第一个子节点开始。 // 返回值: 找到的下一个子节点。 struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)提取属性值的 OF 函数(8项)Linux 内核中使用结构体 property 表示属性,此结构体在文件 include/linux/of.h 中struct property { char *name; /*属性名称*/ int length; /*属性长度*/ void *value; /*属性值*/ struct property *next; /*下一个属性*/ unsigned long _flags; unsigned int unique_id; struct bin_attribute attr; };1.of_find_property(查找指定的属性)// np:设备节点。 // name: 属性名字。 // lenp:属性值的字节数 // 返回值: 找到的属性。 property *of_find_property(const struct device_node *np, const char *name, int *lenp)2.of_property_count_elems_of_size(获取属性中元素的数量,比如获取数组大小)//np:设备节点。 // proname: 需要统计元素数量的属性名字。 // elem_size:元素长度。 // 返回值: 得到的属性元素数量。 int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)3.of_property_read_u32_index(从属性中获取指定标号的 u32 类型数据值)// np:设备节点。 // proname: 要读取的属性名字。 // index:要读取的值标号。 // out_value:读取到的值 // 返回值: 0 读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小。 int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)4.of_property_read_u8_array(读取属性中u8类型的数组数据) of_property_read_u16_array(读取属性中u16类型的数组数据) of_property_read_u32_array(读取属性中u32类型的数组数据) of_property_read_u64_array(读取属性中u64 类型的数组数据) 这 4 个函数分别是读取属性中 u8、 u16、 u32 和 u64 类型的数组数据,比如大多数的 reg 属性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据。// np:设备节点。 // proname: 要读取的属性名字。 // out_value:读取到的数组值,分别为 u8、 u16、 u32 和 u64。 // sz: 要读取的数组元素数量。 // 返回值: 0,读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小 int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz) int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz) int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz) int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)5.of_property_read_u8 函数(读取只有一个u8类型的属性) of_property_read_u16 函数(读取只有一个u16类型的属性) of_property_read_u32 函数(读取只有一个u32类型的属性) of_property_read_u64 函数(读取只有一个u64类型的属性)有些属性只有一个整形值,这四个函数就是用于读取这种只有一个整形值的属性。//np:设备节点。 // proname: 要读取的属性名字。 // out_value:读取到的数值。 返回值: 0,读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没 有要读取的数据。 int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value) int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value) int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value) int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value)6.of_property_read_string (用于读取属性中字符串值)//np:设备节点。 // proname: 要读取的属性名字。 // out_string:读取到的字符串值。 // 返回值: 0,读取成功,负值,读取失败。 int of_property_read_string(struct device_node *np, const char *propname, const char **out_string)7.of_n_addr_cells(用于获取#address-cells 属性值)//np:设备节点。 // 返回值: 获取到的#address-cells 属性值 int of_n_addr_cells(struct device_node *np)8.of_n_size_cells(用于获取#size-cells 属性值)//np:设备节点。 // 返回值: 获取到的#size-cells 属性值。 int of_n_size_cells(struct device_node *np)其他常用的 OF 函数(5个)1.of_device_is_compatible(用于查看节点的 compatible 属性是否有包含 compat 指定的字符串,也就是检查设备节点的兼容性)//device:设备节点。 // compat:要查看的字符串。 // 返回值: 0,节点的 compatible 属性中不包含 compat 指定的字符串; 正数,节点的 compatible属性中包含 compat 指定的字符串。 int of_device_is_compatible(const struct device_node *device, const char *compat)2.of_get_address(用于获取地址相关属性,主要是“reg”或者“assigned-addresses”属性值)//dev:设备节点。 // index:要读取的地址标号。 // size:地址长度。 // flags:参数,比如 IORESOURCE_IO、 IORESOURCE_MEM 等 // 返回值: 读取到的地址数据首地址,为 NULL 的话表示读取失败。 const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags)3.of_translate_address(将从设备树读取到的地址转换为物理地址)// dev:设备节点。 // in_addr:要转换的地址。 // 返回值: 得到的物理地址,如果为 OF_BAD_ADDR 的话表示转换失败。 u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)4.of_address_to_resource(从设备树里面提取资源值)IIC、 SPI、 GPIO 等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间, Linux内核使用 resource 结构体来描述一段内存空间。用 resource结构体描述的都是设备资源信息, resource 结构体定义在文件 include/linux/ioport.h 中。struct resource { resource_size_t start; //32bit起始地址 resource_size_t end; //32bit终止地址 const char *name; //资源名称 unsigned long flags; //资源标志位,一般表示资源类型(可选的资源标志定义在文件 include/linux/ioport.h 中) struct resource *parent, *sibling, *child; };最 常 见 的 资 源 标 志 就 是 IORESOURCE_MEM 、 IORESOURCE_REG 和IORESOURCE_IRQ 等。回到 of_address_to_resource 函数,此函数看名字像是从设备树里面提取资源值,但是本质上就是将 reg 属性值,然后将其转换为 resource 结构体类型。// dev:设备节点。 // index:地址资源标号。 // r:得到的 resource 类型的资源值。 // 返回值: 0,成功;负值,失败。 int of_address_to_resource(struct device_node *dev, int index, struct resource *r)5.of_iomap 函数of_iomap 函数用于直接内存映射,以前我们会通过 ioremap 函数来完成物理地址到虚拟地址的映射,采用设备树以后就可以直接通过 of_iomap 函数来获取内存地址所对应的虚拟址,不需要使用 ioremap 函数了of_iomap 函数本质上也是将 reg 属性中地址信息转换为虚拟地址,如果 reg 属性有多段的话,可以通过 index 参数指定要完成内存映射的是哪一段// np:设备节点。 // index: reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置为 0。 // 返回值: 经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败。 void __iomem *of_iomap(struct device_node *np, int index){lamp/}
2024年08月08日
3 阅读
0 评论
0 点赞
1
...
4
5
6
...
8