首页
关于
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
资源分享
小作品
页面
关于
搜索到
17
篇与
的结果
2024-11-02
Linux并发与竞争-原子操作
并发与竞争并发与竞争简介因为Linux系统是个多任务操作系统,会存在多个任务同时使用一个资源的问题。就会导致系统奔溃。原因有如下几点:① 多线程并发访问② 抢占式并发访问③ 中断程序并发访问④ 多核见并发访问总的来说就是多个程序并发访问同一个资源的问题,解决办法就是阻止多个程序同时访问同一个资源。就是说A程序在访问这个资源时候,B程序访问不了这个资源。需要用到下面4个东东(原子操作、自旋锁、信号量、互斥体)原子操作简介原子操作就是指不能再进一步分割的操作。就是CPU只需要一条指向就可以执行完的操作原子整形操作API函数Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作。此结构体定义在 include/linux/types.h 文件中,定义如下: /*32bit*/ typedef struct { int counter; } atomic_t; /*64bit*/ typedef struct { long long counter; } atomic64_t;如果要使用原子操作 API 函数,首先要先定义一个 atomic_t 的变量 atomic_t a;也可以在定义原子变量的时候给原子变量赋初值 atomic_t b = ATOMIC_INIT(0);关于原子操作API函数如下相应的也提供了 64 位原子变量的操作 API 函数; API 函数用法一样,只是将“atomic_”前缀换为“atomic64_”,将 int 换为 long long。关于原子位操作API函数如下原子位操作是直接对内存进行操作,不像原子整形变量那样有个 atomic_t 的数据结构。API 函数如下;原子操作实验设备树文件修改在iomuxc节点下的imx6ul-evk节点下创建pinctrl_led节点 pinctrl_led : ledgrp{ fsl,pins< IMX6UL_PAD_GPIO01_IO03__GPIO1_IO03 0x10B0 >; };在根节点/下创建LED节点 gpioled : { #address-cells = <1>; #size-cells = <1>; compatible = "warren-gpioled"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; };驱动程序#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 "linux/of_gpio.h" #include "asm/mach/map.h" #include "asm/uaccess.h" #include "asm/io.h" #define GPIOLED_CNT 1 #define GPIOLED_NAME "led" #define LEDON 1 #define LEDOFF 0 struct gpioled_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; struct device_node *nd; int led_gpio; atomic_t lock; }; struct gpioled_dev gpioled; static int led_open(struct inode *inode, struct file *filp) { if(!atomic_dec_and_test(&gpioled.lock)){ atomic_inc(&gpioled.lock); return -EBUSY; } filp->private_data = &gpioled; return 0; } static int led_release(struct inode *inode, struct file *filp) { struct gpioled_dev *dev = filp->private_data; atomic_inc(&dev->lock); 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; unsigned char databuf[1]; unsigned char ledstat; struct gpioled_dev *dev = filp->private_data; retvalue = copy_from_user(databuf, buf, cnt); if(retvalue < 0){ printk("kernel write failed!\r\n"); return -EFAULT; } ledstat = databuf[0]; if(ledstat == LEDON){ gpio_set_value(dev->led_gpio, 0); }else if(ledstat == LEDOFF){ gpio_set_value(dev->led_gpio, 1); } return 0; } static struct file_operations gpioled_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .write = led_write, .read = led_read, }; static int __init led_init(void) { int ret = 0; atomic_set(&gpioled.lock, 1); gpioled.nd = of_find_node_by_path("/led"); if(gpioled.nd == NULL){ printk("gpioled node find failed!\r\n"); return -EINVAL; }else{ printk("gpioled node find success!\r\n"); } gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0); if(gpioled.led_gpio < 0){ printk("can't get led-gpio\r\n"); return -EINVAL; } printk("led-gpio num = %d\r\n", gpioled.led_gpio); ret = gpio_direction_output(gpioled.led_gpio, 1); if(ret < 0){ printk("can't set gpio!\r\n"); } if(gpioled.major){ gpioled.devid = MKDEV(gpioled.major, 0); register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME); }else{ alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); gpioled.major = MAJOR(gpioled.devid); gpioled.minor = MINOR(gpioled.devid); } printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor); gpioled.cdev.owner = THIS_MODULE; cdev_init(&gpioled.cdev, &gpioled_fops); cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT); gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME); if(IS_ERR(gpioled.class)){ return PTR_ERR(gpioled.class); } gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME); if(IS_ERR(gpioled.device)){ return PTR_ERR(gpioled.device); } return 0; } static void __exit led_exit(void) { cdev_del(&gpioled.cdev); unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); device_destroy(gpioled.class, gpioled.devid); class_destroy(gpioled.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" int main(int argc, int *argv[]) { int fd; char *filename = NULL; char data = 0; int retvalue = 0; int cnt = 0; if(argc != 3){ printf("error usage!\r\n"); return -1; } filename = (char*)argv[1]; fd = open(filename, O_RDWR); if(fd < 0){ printf("file %s open failed\r\n", filename); return -1; } data = atoi(argv[2]); retvalue = write(fd, &data, 1); if(retvalue < 0){ printf("write %s failed\r\n", filename); close(fd); return -1; } while(1){ sleep (1); cnt++; printf("App runing times : %d\r\n", cnt); if(cnt > 5){ break; } } printf("App runing finished\r\n"); retvalue = close(fd); if(retvalue < 0){ printf("file %s close failed\r\n", filename); return -1; } return 0; }自旋锁简介原子操作只能保护一个变量的值,并不能保护结构体变量。或者说原子操作的进一步使用就是自旋锁。自旋锁的缺点:那就等待自旋锁的线程会一直处于自旋状态,这样会浪费处理器时间,降低系统性能,所以自旋锁的持有时间不能太长。Linux 内核使用结构体 spinlock_t 表示自旋锁,结构体定义如下所示:typedef struct spinlock { union { struct raw_spinlock rlock; #ifdef CONFIG_DEBUG_LOCK_ALLOC # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) struct { u8 __padding[LOCK_PADSIZE]; struct lockdep_map dep_map; }; #endif }; } spinlock_t;在使用自旋锁之前,肯定要先定义一个自旋锁变量,定义方法如下spinlock_t lock;自旋锁API函数 被自旋锁保护的临界区一定不能调用任何能够引起睡眠和阻塞的API 函数,否则的话会可能会导致死锁现象的发生。 比如有两个线程AB。A线程加锁后进入睡眠了。B线程因为获取不到锁,导致B线程一直死等。就会导致锁死现象。 还有种情况就是在中断里面加锁,假设在中断A中加锁,还没来得及解锁就被新中断打断或者被抢占。导致锁死。所以说在中断中使用自旋锁时候需要在获取锁之前先禁止本地中断。在释放锁之后使能本地中断。Linux内核提供了相应的API函数,如下。 不推荐使用spin_lock_irq/spin_unlock_irq。建议使用 spin_lock_irqsave/ spin_unlock_irqrestore,因为这一组函数会保存中断状态,在释放锁的时候会恢复中断状态。一般在线程中使用 spin_lock_irqsave/spin_unlock_irqrestore,在中断中使用 spin_lock/spin_unlock。 在Linux中,中断处理被分为两个主要部分:顶半部(上半部,Top Half)和下半部(Bottom Half)。这种设计是为了解决中断处理程序执行时间过长和中断丢失的问题,同时确保关键任务能够迅速得到响应。 顶半部的主要任务是快速处理中断,并暂时关闭中断请求。它主要处理与硬件紧密相关或时间敏感的事情,比如对硬件的读取寄存器中的中断状态并清除中断标志。当一个中断发生时,顶半部会进行相应的硬件操作,并将中断例程的下半部挂到该设备的下半部执行队列中。由于顶半部的执行速度很快,它可以服务更多的中断请求 下半部用于延迟处理顶半部未完成的工作。与顶半部不同,下半部是可以被中断的,这意味着它可以在执行过程中被新的中断打断。下半部几乎执行了中断处理程序的所有任务,但它并不是非常紧急的,通常比较耗时。因此,它的执行时机由系统自行安排,并不在中断服务上下文中执行。 下半部(BH)也会竞争共享资源,如果要在下半部里面使用自旋锁,可以使用下列API函数自旋锁实验设备树文件修改{alert type="info"}与原子锁一致{/alert}驱动程序编写#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 "linux/of_gpio.h" #include "asm/mach/map.h" #include "asm/uaccess.h" #include "asm/io.h" #define GPIOLED_CNT 1 #define GPIOLED_NAME "led" #define LEDON 1 #define LEDOFF 0 struct gpioled_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; struct device_node *nd; int led_gpio; int dev_stats; spinlock_t lock; }; struct gpioled_dev gpioled; static int led_open(struct inode *inode, struct file *filp) { unsigned long flags; filp->private_data = &gpioled; spin_lock_irqsave(&gpioled.lock, flags); //加锁 if(gpioled.dev_stats){ //设备正在使用 spin_unlock_irqrestore(&gpioled.lock, flags); //解锁 return -EBUSY; } gpioled.dev_stats++; spin_unlock_irqrestore(&gpioled.lock, flags); //解锁 return 0; } static int led_release(struct inode *inode, struct file *filp) { struct gpioled_dev *dev = filp->private_data; unsigned long flags; spin_lock_irqsave(&dev->lock, flags); //加锁 if(gpioled.dev_stats){ gpioled.dev_stats--; } spin_unlock_irqrestore(&dev->lock, flags); //解锁 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; unsigned char databuf[1]; unsigned char ledstat; struct gpioled_dev *dev = filp->private_data; retvalue = copy_from_user(databuf, buf, cnt); if(retvalue < 0){ printk("kernel write failed!\r\n"); return -EFAULT; } ledstat = databuf[0]; if(ledstat == LEDON){ gpio_set_value(dev->led_gpio, 0); }else if(ledstat == LEDOFF){ gpio_set_value(dev->led_gpio, 1); } return 0; } static struct file_operations gpioled_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .write = led_write, .read = led_read, }; static int __init led_init(void) { int ret = 0; spin_lock_init(&gpioled.lock); gpioled.nd = of_find_node_by_path("/led"); if(gpioled.nd == NULL){ printk("gpioled node find failed!\r\n"); return -EINVAL; }else{ printk("gpioled node find success!\r\n"); } gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0); if(gpioled.led_gpio < 0){ printk("can't get led-gpio\r\n"); return -EINVAL; } printk("led-gpio num = %d\r\n", gpioled.led_gpio); ret = gpio_direction_output(gpioled.led_gpio, 1); if(ret < 0){ printk("can't set gpio!\r\n"); } if(gpioled.major){ gpioled.devid = MKDEV(gpioled.major, 0); register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME); }else{ alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); gpioled.major = MAJOR(gpioled.devid); gpioled.minor = MINOR(gpioled.devid); } printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor); gpioled.cdev.owner = THIS_MODULE; cdev_init(&gpioled.cdev, &gpioled_fops); cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT); gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME); if(IS_ERR(gpioled.class)){ return PTR_ERR(gpioled.class); } gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME); if(IS_ERR(gpioled.device)){ return PTR_ERR(gpioled.device); } return 0; } static void __exit led_exit(void) { cdev_del(&gpioled.cdev); unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); device_destroy(gpioled.class, gpioled.devid); class_destroy(gpioled.class); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("warren");实验程序编写{alert type="info"}与原子锁一致{/alert}实验现象{alert type="info"}与原子锁一致{/alert}信号量信号量简介信号量就是自旋锁的升级版本,自旋锁在解锁失败时候会死等,直到解锁成功为止。信号量则在获取不到时候进入到休眠状态。可以看到信号量的开销要比自旋锁大,因为信号量使线程进入休眠状态以后会切换线程,切换线程就会有开销。 信号量的特点:.因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场合因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势 信号量就是生产者与消费者的关系,因为它允许多个线程同时访问共享资源。 Linux 内核使用 semaphore 结构体表示信号量,结构体内容如下所示 struct semaphore { raw_spinlock_t lock; unsigned int count; struct list_head wait_list; };信号量API函数信号量的使用如下: struct semaphore sem; /*定义信号量*/ sema_init(&sem, 1); /*初始化信号量*/ down(&sem); /*申请信号量, 申请不到会进入到睡眠但睡眠可以被信号打断*/ down_trylock(&sem);/*申请信号量, 申请不到立即返回非0,不会进入到睡眠*/ dowm_interruptible(&sem);/*申请信号量, 申请不到会进入到睡眠, 但睡眠可以被信号打断*/ up(&sem); /*释放信号量*/驱动程序编写#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 "linux/of_gpio.h" #include "asm/mach/map.h" #include "asm/uaccess.h" #include "asm/io.h" #include "linux/semaphore.h" #define GPIOLED_CNT 1 #define GPIOLED_NAME "led" #define LEDON 1 #define LEDOFF 0 struct gpioled_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; struct device_node *nd; int led_gpio; struct semaphore sem; }; struct gpioled_dev gpioled; static int led_open(struct inode *inode, struct file *filp) { filp->private_data = &gpioled; if(down_interruptible(&gpioled.sem)){ return -ERESTARTSYS; } return 0; } static int led_release(struct inode *inode, struct file *filp) { struct gpioled_dev *dev = filp->private_data; up(&dev->sem); 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; unsigned char databuf[1]; unsigned char ledstat; struct gpioled_dev *dev = filp->private_data; retvalue = copy_from_user(databuf, buf, cnt); if(retvalue < 0){ printk("kernel write failed!\r\n"); return -EFAULT; } ledstat = databuf[0]; if(ledstat == LEDON){ gpio_set_value(dev->led_gpio, 0); }else if(ledstat == LEDOFF){ gpio_set_value(dev->led_gpio, 1); } return 0; } static struct file_operations gpioled_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .write = led_write, .read = led_read, }; static int __init led_init(void) { int ret = 0; sema_init(&gpioled.sem, 1); gpioled.nd = of_find_node_by_path("/led"); if(gpioled.nd == NULL){ printk("gpioled node find failed!\r\n"); return -EINVAL; }else{ printk("gpioled node find success!\r\n"); } gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0); if(gpioled.led_gpio < 0){ printk("can't get led-gpio\r\n"); return -EINVAL; } printk("led-gpio num = %d\r\n", gpioled.led_gpio); ret = gpio_direction_output(gpioled.led_gpio, 1); if(ret < 0){ printk("can't set gpio!\r\n"); } if(gpioled.major){ gpioled.devid = MKDEV(gpioled.major, 0); register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME); }else{ alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); gpioled.major = MAJOR(gpioled.devid); gpioled.minor = MINOR(gpioled.devid); } printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor); gpioled.cdev.owner = THIS_MODULE; cdev_init(&gpioled.cdev, &gpioled_fops); cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT); gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME); if(IS_ERR(gpioled.class)){ return PTR_ERR(gpioled.class); } gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME); if(IS_ERR(gpioled.device)){ return PTR_ERR(gpioled.device); } return 0; } static void __exit led_exit(void) { cdev_del(&gpioled.cdev); unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); device_destroy(gpioled.class, gpioled.devid); class_destroy(gpioled.class); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("warren");应用程序编写{alert type="info"}与原子锁一致{/alert}实验现象互斥体互斥体简介互斥体就是信号量的特殊化,一次只允许一个线程拥有资源的所有权。将信号量的值设置为 1 就可以使用信号量进行互斥访问了。Linux 内核使用 mutex 结构体表示互斥体,定义如下。struct mutex { /* 1: unlocked, 0: locked, negative: locked, possible waiters */ atomic_t count; spinlock_t wait_lock; };mutex 之前要先定义一个 mutex 变量 struct mutex lock;使用 mutex 的时候要注意如下几点 {mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁} {和信号量一样, mutex 保护的临界区可以调用引起阻塞的 API 函数} {因为一次只有一个线程可以持有 mutex,因此,必须由 mutex 的持有者释放 mutex。并且 mutex 不能递归上锁和解锁}互斥体API函数互斥体的使用如下 struct mutex lock; /*互斥体定义*/ mutex_init(&lock); /*互斥体初始化*/ mutex_lock(&lock); /*获取互斥锁, 获取不到就睡觉且不能被打断*/ mutex_trylock(&lock);/*获取互斥锁,获取不到直接返回0*/ mutex_is_locked(&lock);/*查看互斥锁是否被获取,是返回1 否返回0*/ mutex_lock_interruptible(&lock);/*获取互斥锁,获取不到就睡觉,且可以被信号打断*/ mutex_unlock(&lock);驱动程序编写#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 "linux/of_gpio.h" #include "asm/mach/map.h" #include "asm/uaccess.h" #include "asm/io.h" #include "linux/mutex.h" #define GPIOLED_CNT 1 #define GPIOLED_NAME "led" #define LEDON 1 #define LEDOFF 0 struct gpioled_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; struct device_node *nd; int led_gpio; struct mutex lock; }; struct gpioled_dev gpioled; static int led_open(struct inode *inode, struct file *filp) { filp->private_data = &gpioled; if(mutex_lock_interruptible(&gpioled.lock)){ return -ERESTARTSYS; } return 0; } static int led_release(struct inode *inode, struct file *filp) { struct gpioled_dev *dev = filp->private_data; mutex_unlock(&dev->lock); 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; unsigned char databuf[1]; unsigned char ledstat; struct gpioled_dev *dev = filp->private_data; retvalue = copy_from_user(databuf, buf, cnt); if(retvalue < 0){ printk("kernel write failed!\r\n"); return -EFAULT; } ledstat = databuf[0]; if(ledstat == LEDON){ gpio_set_value(dev->led_gpio, 0); }else if(ledstat == LEDOFF){ gpio_set_value(dev->led_gpio, 1); } return 0; } static struct file_operations gpioled_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .write = led_write, .read = led_read, }; static int __init led_init(void) { int ret = 0; mutex_init(&gpioled.lock); gpioled.nd = of_find_node_by_path("/led"); if(gpioled.nd == NULL){ printk("gpioled node find failed!\r\n"); return -EINVAL; }else{ printk("gpioled node find success!\r\n"); } gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0); if(gpioled.led_gpio < 0){ printk("can't get led-gpio\r\n"); return -EINVAL; } printk("led-gpio num = %d\r\n", gpioled.led_gpio); ret = gpio_direction_output(gpioled.led_gpio, 1); if(ret < 0){ printk("can't set gpio!\r\n"); } if(gpioled.major){ gpioled.devid = MKDEV(gpioled.major, 0); register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME); }else{ alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); gpioled.major = MAJOR(gpioled.devid); gpioled.minor = MINOR(gpioled.devid); } printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor); gpioled.cdev.owner = THIS_MODULE; cdev_init(&gpioled.cdev, &gpioled_fops); cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT); gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME); if(IS_ERR(gpioled.class)){ return PTR_ERR(gpioled.class); } gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME); if(IS_ERR(gpioled.device)){ return PTR_ERR(gpioled.device); } return 0; } static void __exit led_exit(void) { cdev_del(&gpioled.cdev); unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); device_destroy(gpioled.class, gpioled.devid); class_destroy(gpioled.class); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("warren");应用程序编写{alert type="info"}与原子锁一致{/alert}实验现象{alert type="info"}与信号量一致{/alert}{lamp/}完结 ::(狗头)
2024年11月02日
2 阅读
0 评论
1 点赞
2024-11-02
蜂鸣器实验
蜂鸣器实验是对pinctrl和gpio的复习。设备树修改添加pinctrl节点在设备树文件的iomuxc节点的imx6ul-evk子节点下创建一个pinctrl_beep子节点 pinctrl_beep: beepgrp{ fsl,pins = < MUX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0 >; };添加beep节点在设备树文件的/节点下创建一个beep节点 beep :{ #address-cells = <1>; #size-cells = <1>; compatible = "warren-beep"; pinctrl-name = "default";/*添加pinctrl信息*/ pinctrl-0 = <&pinctrl_beep>; beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGHT>; /*添加GPIO属性信息*/ status = "okay"; };驱动程序编写#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/errno.h" #include "linux/gpio.h" #include "linux/cdev.h" #include "linux/device.h" #include "linux/of.h" #include "linux/of_address.h" #include "linux/of_gpio.h" #include "asm/mach/map.h" #include "asm/uaccess.h" #include "asm/io.h" #define BEEP_CNT 1 /* 设备号个数 */ #define BEEP_NAME "beep" /* 名字 */ #define BEEPOFF 0 /* 关蜂鸣器 */ #define BEEPON 1 /* 开蜂鸣器 */ struct beep_dev{ dev_t devid; /*设备号*/ struct cdev cdev; /*字符设备cdev*/ struct class *class; /*类*/ struct device *device; /*设备*/ int major; /*主设备号*/ int minor; /*次设备号*/ struct device_node *nd; /*设备节点*/ int beep_gpio; /*beep所使用的GPIO编号*/ }; struct beep_dev beep; static int beep_open(struct inode *inode, struct file *filp) { filp->private_data = &beep; return 0; } static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int retvalue; unsigned char databuf[4]; unsigned char beepstat; struct beep_dev *dev = filp->private_data; retvalue = copy_from_user(databuf, buf, cnt); if(retvalue < 0){ printk("kernel write failed!\r\n"); return -EFAULT; } beepstat = databuf[0]; if(beepstat == BEEPON){ gpio_set_value(dev->beep_gpio, 0); }else{ gpio_set_value(dev->beep_gpio, 1); } return 0; } static int beep_release(struct inode *inode, struct file *filp) { return 0; } static struct file_operations beep_fops = { .owner = THIS_MODULE, .write = beep_write, .open = beep_open, .release = beep_release, }; static int __init beep_init(void) { int ret = 0; /*1. 从设备数文件中获取beep节点信息*/ beep.nd = of_find_node_by_path("/beep"); if(beep.nd == NULL){ printk("beep node not find!\r\n"); return -EINVAL; }else{ printk("beep node find success!\r\n"); } /*根据beep节点信息来获取BEEP所使用的GPIO编号*/ beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0); if(beep.beep_gpio < 0){ printk("can't get beep-gpio"); return -EINVAL; } printk("led-gpio num = %d\r\n", beep.beep_gpio); /*配置beep-gpio为输出高电平,默认关闭BEEP*/ ret = gpio_direction_output(beep.beep_gpio, 1); if(ret < 0){ printk("can't set beep gpio!\r\n"); } /*创建设备号*/ if(beep.major){ beep.devid = MKDEV(beep.major, 0); register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME); }else{ alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME); beep.major = MAJOR(beep.devid); beep.minor = MINOR(beep.devid); } printk("beep major=%d,minor=%d\r\n", beep.major, beep.minor); /*初始化cdev*/ beep.cdev.owner = THIS_MODULE; cdev_init(&beep.cdev, &beep_fops); /*添加cdev*/ cdev_add(&beep.cdev, beep.devid, BEEP_CNT); /*创建类*/ beep.class = class_create(THIS_MODULE, BEEP_NAME); if(IS_ERR(beep.class)){ return PTR_ERR(beep.class); } /*创建设备*/ beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME); if(IS_ERR(beep.device)){ return PTR_ERR(beep.device); } return 0; } static void __exit beep_exit(void) { cdev_del(&beep.cdev); unregister_chrdev_region(beep.devid, BEEP_CNT); device_destroy(beep.class, beep.devid); class_destroy(beep.class); } module_init(beep_init); module_exit(beep_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" int main(int argc, int *argv[]) { int fd = 0; int retvalue = 0; char *filename=NULL; unsigned char buff[4]; if(argc != 3){ printf("Error Usage!\r\n"); return -1; } filename = (char *)argv[1]; fd = open(filename, O_RDWR); if(fd < 0){ printf("file %s open failed!\r\n", filename); return -1; }else{ printf("file %s open success!\r\n", filename); } buff[0] = atoi((char *)argv[2]); retvalue = write(fd, buff, 1); if(retvalue < 0){ printf("beep control failed!\r\n"); } retvalue = close(fd); if(retvalue < 0){ printf("file close failed!\r\n"); return -1; } return 0; }实验现象
2024年11月02日
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-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
2
...
4