Home >Operation and Maintenance >Linux Operation and Maintenance >What does linux interrupt number mean?
Linux interrupt number is the code assigned by the system to each interrupt source for identification and processing; in the interrupt system using vector interrupt mode, the CPU must use it to find the entry address of the interrupt service program, and implement Program transfer.
#The operating environment of this tutorial: linux5.9.8 system, Dell G3 computer.
linux What does the interrupt number mean?
Interrupt number and interrupt programming:
1, interrupt number
The interrupt number is the code assigned by the system to each interrupt source for easy identification and processing. In an interrupt system that uses vector interrupts, the CPU must use it to find the entry address of the interrupt service program and realize program transfer.
To implement interrupts in ARM bare metal, you need to configure:
I/O口为中断模式,触发方式,I/O口中断使能 设置GIC中断使能,分发配置,分发总使能,CPU外部中断接口使能,中断优先级
To implement interrupts in the Linux kernel, you only need to know:
中断号是什么,怎么得到中断号 中断处理方法
#2. How to get the interrupt number:
/arm/boot/dts/exynos4412-fs4412.dts
1) Look at the schematic diagram and find it in the chip manual The interrupt number SPI Port corresponding to the interrupt source No
2) Enter the device tree and go to arch/arm/boot/dts /exynos4x12-pinctrl.dtsi
gpx1: gpx1 { gpio-controller; #gpio-cells = <2>; interrupt-controller; //中断控制器 interrupt-parent = <&gic>; //继承于gic interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>; #interrupt-cells = <2>; //子继承的interrupts的长度 };
24, 25, etc. in brackets correspond to SPI Port No. The above are the nodes that have been defined in the system
In programming, you need Define your own node to describe the button, open the editable device tree file:
arch/arm/boot/dts/exynos4412-fs4412.dts, and enter the file.
3) Define the node and describe the interrupt number used by the current device
1 key_int_node{ 2 compatible = "test_key"; 3 interrupt-parent = ; //继承于gpx1 4 interrupts = ; //2表示第几个中断号,4表示触发方式为下降沿5 }; //interrupts里长度由父母的-cell决定
As another example, set the node and interrupt number of k4 --- GPX3_2 (XEINT26)
1 key_int_node{ 2 compatible = "test_key"; 3 interrupt-parent = <&gpx3>; //继承于gpx3 4 interrupts = <2 4>; //2表示第2个中断号,4表示触发方式为下降沿 5 };
How to locate the interrupt number:
Look at the I/O pin, GPX1_2, the interrupt number is the second
# in GPX1 ## 4) Compile the device tree: make dtbs Update the device tree file: cp -raf arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/ View the defined nodes : In the proc/device-tree/ directory of the root directory
3. Implement the interrupt processing method
Obtain the interrupt number through code in the driver, and apply for an interrupt Let’s first look at the interrupt-related functions:1 a,获取到中断号码: 2 int get_irqno_from_node(void) 3 { 4 // 获取到设备树中的节点 5 struct device_node *np = of_find_node_by_path("/key_int_node"); 6 if(np){ 7 printk("find node ok\n"); 8 }else{ 9 printk("find node failed\n"); 10 } 11 12 // 通过节点去获取到中断号码 13 int irqno = irq_of_parse_and_map(np, 0); 14 printk("irqno = %d\n", irqno); 15 16 return irqno; 17 } 18 b,申请中断 19 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char * name, void * dev) 20 参数1: irq 设备对应的中断号 21 参数2: handler 中断的处理函数 22 typedef irqreturn_t (*irq_handler_t)(int, void *); 23 参数3:flags 触发方式 24 #define IRQF_TRIGGER_NONE 0x00000000 //内部控制器触发中断的时候的标志 25 #define IRQF_TRIGGER_RISING 0x00000001 //上升沿 26 #define IRQF_TRIGGER_FALLING 0x00000002 //下降沿 27 #define IRQF_TRIGGER_HIGH 0x00000004 // 高点平 28 #define IRQF_TRIGGER_LOW 0x00000008 //低电平触发 29 参数4:name 中断的描述,自定义,主要是给用户查看的 30 /proc/interrupts 31 参数5:dev 传递给参数2中函数指针的值 32 返回值: 正确为0,错误非0 33 34 35 参数2的赋值:即中断处理函数 36 irqreturn_t key_irq_handler(int irqno, void *devid) 37 { 38 return IRQ_HANDLED; 39 } 43 44 c, 释放中断: 45 void free_irq(unsigned int irq, void *dev_id) 46 参数1: 设备对应的中断号 47 参数2:与request_irq中第5个参数保持一致The code implements obtaining the interrupt number and registering it Interrupt, press the key to trigger the interrupt, print information
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/fs.h> 4 #include <linux/device.h> 5 #include <asm/uaccess.h> 6 #include <asm/io.h> 7 #include <linux/slab.h> 8 #include <linux/of.h> 9 #include <linux/of_irq.h> 10 #include <linux/interrupt.h> 11 12 int irqno; //中断号 13 14 15 irqreturn_t key_irq_handler(int irqno, void *devid) 16 { 17 printk("----------%s---------",__FUNCTION__); 18 return IRQ_HANDLED; 19 } 20 21 22 //获取中断号 23 int get_irqno_from_node(void) 24 { 25 //获取设备树中的节点 26 struct device_node *np = of_find_node_by_path("/key_int_node"); 27 if(np){ 28 printk("find node success\n"); 29 }else{ 30 printk("find node failed\n"); 31 } 32 33 //通过节点去获取中断号 34 int irqno = irq_of_parse_and_map(np, 0); 35 printk("iqrno = %d",irqno); 36 37 return irqno; 38 } 39 40 41 42 static int __init key_drv_init(void) 43 { 44 //演示如何获取到中断号 45 int ret; 46 47 irqno = get_irqno_from_node(); 48 49 ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 50 "key3_eint10", NULL); 51 if(ret != 0) 52 { 53 printk("request_irq error\n"); 54 return ret; 55 } 56 57 return 0; 58 } 59 60 static void __exit key_drv_exit(void) 61 { 62 free_irq(irqno, NULL); //free_irq与request_irq的最后一个参数一致 63 } 64 65 66 67 module_init(key_drv_init); 68 module_exit(key_drv_exit); 69 70 MODULE_LICENSE("GPL"); key_drv.c
##
4. Interrupt programming---Character device driver framework// 1,设定一个全局的设备对象
key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
// 2,申请主设备号
key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
// 3,创建设备节点文件
key_dev->cls = class_create(THIS_MODULE, "key_cls");
key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major,0), NULL, "key0");
// 4,硬件初始化:
a.地址映射
b.中断申请
5. The driver implements passing the data generated by the hardware to User 1) How the hardware obtains data
key: 按下和抬起: 1/0读取key对应的gpio的状态,可以判断按下还是抬起 读取key对应gpio的寄存器--数据寄存器 //读取数据寄存器int value = readl(key_dev->reg_base + 4) & (1<<2);
2) The driver passes the data to the user
在中断处理中填充数据: key_dev->event.code = KEY_ENTER; key_dev->event.value = 0; 在xxx_read中奖数据传递给用户 ret = copy_to_user(buf, &key_dev->event, count);
while(1) { read(fd, &event, sizeof(struct key_event)); if(event.code == KEY_ENTER) { if(event.value) { printf("APP__ key enter pressed\n"); }else{ printf("APP__ key enter up\n"); } } }
6. Example:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/of.h> 4 #include <linux/of_irq.h> 5 #include <linux/interrupt.h> 6 #include <linux/slab.h> 7 #include <linux/fs.h> 8 #include <linux/device.h> 9 #include <linux/kdev_t.h> 10 #include <linux/err.h> 11 #include <linux/device.h> 12 #include <asm/io.h> 13 #include <asm/uaccess.h> 14 15 16 #define GPXCON_REG 0X11000C20 //不可以从数据寄存器开始映射,要配置寄存器 17 #define KEY_ENTER 28 18 19 //0、设计一个描述按键的数据的对象 20 struct key_event{ 21 int code; //按键类型:home,esc,enter 22 int value; //表状态,按下,松开 23 }; 24 25 //1、设计一个全局对象——— 描述key的信息 26 struct key_desc{ 27 unsigned int dev_major; 28 int irqno; //中断号 29 struct class *cls; 30 struct device *dev; 31 void *reg_base; 32 struct key_event event; 33 }; 34 35 struct key_desc *key_dev; 36 37 38 irqreturn_t key_irq_handler(int irqno, void *devid) 39 { 40 printk("----------%s---------",__FUNCTION__); 41 42 int value; 43 //读取按键状态 44 value = readl(key_dev->reg_base + 4) & (0x01<<2); 45 46 if(value){ 47 printk("key3 up\n"); 48 key_dev->event.code = KEY_ENTER; 49 key_dev->event.value = 0; 50 }else{ 51 printk("key3 down\n"); 52 key_dev->event.code = KEY_ENTER; 53 key_dev->event.value = 1; 54 } 55 return IRQ_HANDLED; 56 } 57 58 59 //获取中断号 60 int get_irqno_from_node(void) 61 { 62 int irqno; 63 //获取设备树中的节点 64 struct device_node *np = of_find_node_by_path("/key_int_node"); 65 if(np){ 66 printk("find node success\n"); 67 }else{ 68 printk("find node failed\n"); 69 } 70 71 //通过节点去获取中断号 72 irqno = irq_of_parse_and_map(np, 0); 73 printk("iqrno = %d",key_dev->irqno); 74 75 return irqno; 76 } 77 78 ssize_t key_drv_read (struct file * filp, char __user * buf, size_t count, loff_t * fops) 79 { 80 //printk("----------%s---------",__FUNCTION__); 81 int ret; 82 ret = copy_to_user(buf, &key_dev->event, count); 83 if(ret > 0) 84 { 85 printk("copy_to_user error\n"); 86 return -EFAULT; 87 } 88 89 //传递给用户数据后,将数据清除,否则APP每次读都是第一次的数据 90 memset(&key_dev->event, 0, sizeof(key_dev->event)); 91 return count; 92 } 93 94 ssize_t key_drv_write (struct file *filp, const char __user * buf, size_t count, loff_t * fops) 95 { 96 printk("----------%s---------",__FUNCTION__); 97 return 0; 98 } 99 100 int key_drv_open (struct inode * inode, struct file *filp) 101 { 102 printk("----------%s---------",__FUNCTION__); 103 return 0; 104 } 105 106 int key_drv_close (struct inode *inode, struct file *filp) 107 { 108 printk("----------%s---------",__FUNCTION__); 109 return 0; 110 } 111 112 113 const struct file_operations key_fops = { 114 .open = key_drv_open, 115 .read = key_drv_read, 116 .write = key_drv_write, 117 .release = key_drv_close, 118 119 }; 120 121 122 123 static int __init key_drv_init(void) 124 { 125 //演示如何获取到中断号 126 int ret; 127 128 //1、设定全局设备对象并分配空间 129 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL); //GFP_KERNEL表正常分配内存 130 //kzalloc相比于kmalloc,不仅分配连续空间,还会将内存初始化清零 131 132 //2、动态申请设备号 133 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops); 134 135 //3、创建设备节点文件 136 key_dev->cls = class_create(THIS_MODULE, "key_cls"); 137 key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major, 0), NULL, "key0"); 138 139 //4、硬件初始化 -- 地址映射或中断申请 140 141 key_dev->reg_base = ioremap(GPXCON_REG,8); 142 143 key_dev->irqno = get_irqno_from_node(); 144 145 ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 146 "key3_eint10", NULL); 147 if(ret != 0) 148 { 149 printk("request_irq error\n"); 150 return ret; 151 } 152 153 //a. 硬件如何获取数据 154 155 156 157 return 0; 158 } 159 160 static void __exit key_drv_exit(void) 161 { 162 iounmap(GPXCON_REG); 163 free_irq(key_dev->irqno, NULL); //free_irq与request_irq的最后一个参数一致 164 device_destroy(key_dev->cls, MKDEV(key_dev->dev_major, 0)); 165 class_destroy(key_dev->cls); 166 unregister_chrdev(key_dev->dev_major, "key_drv"); 167 kfree(key_dev); 168 } 169 170 171 172 module_init(key_drv_init); 173 module_exit(key_drv_exit); 174 175 MODULE_LICENSE("GPL"); key_drv.ckey_drv .c
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <fcntl.h> 8 9 10 #define KEY_ENTER 28 11 12 //0、设计一个描述按键的数据的对象 13 struct key_event{ 14 int code; //按键类型:home,esc,enter 15 int value; //表状态,按下,松开 16 }; 17 18 19 int main(int argc, char *argv[]) 20 { 21 struct key_event event; 22 int fd; 23 fd = open("/dev/key0", O_RDWR); 24 if(fd < 0) 25 { 26 perror("open"); 27 exit(1); 28 } 29 30 while(1) 31 { 32 read(fd, &event, sizeof(struct key_event)); 33 34 if(event.code == KEY_ENTER) 35 { 36 if(event.value) 37 { 38 printf("APP__ key enter down\n"); 39 }else{ 40 41 printf("APP__ key enter up\n"); 42 } 43 } 44 } 45 46 close(fd); 47 48 return 0; 49 } key_test.c
1 ROOTFS_DIR = /home/linux/source/rootfs#根文件系统路径 2 3 APP_NAME = key_test 4 MODULE_NAME = key_drv 5 6 CROSS_COMPILE = /home/linux/toolchains/gcc-4.6.4/bin/arm-none-linux-gnueabi- 7 CC = $(CROSS_COMPILE)gcc 8 9 ifeq ($(KERNELRELEASE),) 10 11 KERNEL_DIR = /home/linux/kernel/linux-3.14-fs4412 #编译过的内核源码的路径 12 CUR_DIR = $(shell pwd) #当前路径 13 14 all: 15 make -C $(KERNEL_DIR) M=$(CUR_DIR) modules #把当前路径编成modules 16 $(CC) $(APP_NAME).c -o $(APP_NAME) 17 @#make -C 进入到内核路径 18 @#M 指定当前路径(模块位置) 19 20 clean: 21 make -C $(KERNEL_DIR) M=$(CUR_DIR) clean 22 23 install: 24 sudo cp -raf *.ko $(APP_NAME) $(ROOTFS_DIR)/drv_module #把当前的所有.ko文件考到根文件系统的drv_module目录 25 26 else 27 28 obj-m += $(MODULE_NAME).o #指定内核要把哪个文件编译成ko 29 30 endif Makefile
Execute the user program and press the button to see the information
Exit the user program and press the button and the corresponding information will be printed.
View device and interrupt node information:
## Look at the CPU Situation: You can see that the key_test application takes up a lot of CPU. What is the reason? In the application, the kernel information is always read through the while loop. When a key interrupt occurs, key_event will be assigned a value, judged in the while loop, and then printed out, so that in the user space Keep switching back and forth with the kernel space, and reading all the time will consume a lot of CPU resources. Solution: When an interrupt occurs, read is called. If no data is generated, the process schedule is jumped out and the process sleeps. Recommended learning: "linux video tutorial"
The above is the detailed content of What does linux interrupt number mean?. For more information, please follow other related articles on the PHP Chinese website!