//----------key_drv.c------------- //驱动代码 #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/uaccess.h> #include <linux/input-event-codes.h> #include <linux/wait.h> #include <asm/io.h> #define GPX1CON_ADDR 0x11000c20 #define GPX1_SIZE 8 //设计一个全局设备对象--描述按键信息 struct key_irq_str{ int irqno; unsigned int dev_major; unsigned int dev_minor; struct class* pkey_class; struct device* pkey_device; void* reg_virt_base; wait_queue_head_t wq; int key_state; //表示是否有数据 }; struct key_irq_str *pkey_irq_str; //设计一个描述按键的数据的对象 struct key_value_dsc{ int code; //表示按键的类型:home, esc, Q, W, E, R, T... int date; //表示按下还是抬起 1/0 }; struct key_value_dsc key3_value_dsc = { .code = 0, .date = 0, }; irqreturn_t key_irq_handler(int irqno, void *devid) { unsigned int GPX1DAT; printk("--------------%s------------------\n",__FUNCTION__); GPX1DAT = readl(pkey_irq_str->reg_virt_base + 4); if( GPX1DAT &= 0x1<<2 ){ printk("__KERN__:Enter up\n"); key3_value_dsc.code = KEY_ENTER; key3_value_dsc.date = 0; } else{ printk("__KERN__:Enter down\n"); key3_value_dsc.code = KEY_ENTER; key3_value_dsc.date = 1; } //表示有数据,需要唤醒整个等待队列 wake_up_interruptible(&(pkey_irq_str->wq)); //同时设置标志位 pkey_irq_str->key_state = 1; return IRQ_HANDLED; } int key_drv_open (struct inode* inode, struct file *filp){ printk("__KERN__:open\n"); return 0; } ssize_t key_drv_read (struct file *filp, char __user *buf, size_t count, loff_t *fpos){ int ret; wait_event_interruptible(pkey_irq_str->wq, pkey_irq_str->key_state); //表示有数据 ret = copy_to_user(buf, &key3_value_dsc, count); if(ret == 0) { //printk("__KERN__:read\n"); } else { printk("__KERN__:Err to read\n"); } key3_value_dsc.code = 0; key3_value_dsc.date = 0; pkey_irq_str->key_state = 0; return count; } ssize_t key_drv_write (struct file *filp, const char __user *buf, size_t count, loff_t *fpos){ return 0; } int key_drv_release (struct inode *inod, struct file *filp){ printk("__KERN__:release\n"); return 0; } const struct file_operations key_drv_fops = { .open = key_drv_open, .read = key_drv_read, .write = key_drv_write, .release = key_drv_release, }; int get_irqno_from_node(void) { //获取到设备树中的节点 struct device_node *np = of_find_node_by_path("/key_int_node"); if(np){ printk("find node ok\n"); } else{ printk("find node failed\n"); } //通过节点去获取到中断号码 pkey_irq_str->irqno = irq_of_parse_and_map(np,0); printk("irqno = %d\n",pkey_irq_str->irqno); return pkey_irq_str->irqno; } static int __init key_drv_init(void) { int ret; //1.设定一个全局的设备对象 pkey_irq_str = kzalloc(sizeof(struct key_irq_str), 1); //2.申请主设备号,设置次设备号 pkey_irq_str->dev_major = register_chrdev(0, "key_drv_test", &key_drv_fops); pkey_irq_str->dev_minor = 0; //3.创建设备节点 pkey_irq_str->pkey_class = class_create(THIS_MODULE, "key_interrupt_dev"); pkey_irq_str->pkey_device = device_create(pkey_irq_str->pkey_class,NULL,MKDEV(pkey_irq_str->dev_major, pkey_irq_str->dev_minor),NULL,"INTRRUPT KEY%d",3); //4.硬件初始化--地址映射或中断申请 ret = get_irqno_from_node(); ret = request_irq(pkey_irq_str->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "IRQ_EINT10", NULL); if(ret != 0) { printk("request_irq ERROR!\n"); return ret; } pkey_irq_str->reg_virt_base = ioremap(GPX1CON_ADDR, GPX1_SIZE); //5. 初始化等待队列头 init_waitqueue_head(&(pkey_irq_str->wq)); pkey_irq_str->key_state = 0; return 0; } static void __exit key_drv_exit(void) { iounmap(pkey_irq_str->reg_virt_base); free_irq(pkey_irq_str->irqno,NULL); device_destroy(pkey_irq_str->pkey_class, MKDEV(pkey_irq_str->dev_major, pkey_irq_str->dev_minor)); class_destroy(pkey_irq_str->pkey_class); unregister_chrdev(pkey_irq_str->dev_major, "key_drv_test"); kfree(pkey_irq_str); } module_init(key_drv_init); module_exit(key_drv_exit); MODULE_LICENSE("GPL");
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define KEY_ENTER 28 struct key_value_dsc{ int code; //表示按键的类型:home, esc, Q, W, E, R, T... int date; //表示按下还是抬起 1/0 }; int main(int argc, char **argv) { int ret, fd; struct key_value_dsc key_value; fd = open("/dev/INTRRUPT KEY3",O_RDWR); if(fd < 0){ perror("open:"); exit(1); } while(1) { ret = read(fd, &key_value, sizeof(struct key_value_dsc)); if(ret < 0){ perror("read:"); exit(1); } if(key_value.code == KEY_ENTER) { if(key_value.date == 1) { printf("__APP__key enter pressed\n",key_value.date); } else if(key_value.date == 0) { printf("__APP__key enter up\n",key_value.date); } } } close(fd); return 0; }
ROOTFS_DIR = /home/linux/Level11 APP_NAME = key_test CROSS_COMPILE = arm-none-linux-gnueabi- CC = $(CROSS_COMPILE)gcc ifeq ($(KERNELRELEASE), ) KERNEL_DIR = /home/linux/Level10/day6/linux-5.4.79 CUR_DIR = $(shell pwd) all : make -C $(KERNEL_DIR) M=$(CUR_DIR) modules -j6 ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- $(CC) $(APP_NAME).c -o $(APP_NAME) clean : make -C $(KERNEL_DIR) M=$(CUR_DIR) clean clear install: cp -raf *.ko $(APP_NAME) ./1/ else obj-m += key_drv.o endif
[root@farsight ]# ./key_test [ 453.414405] __KERN__:open [ 456.354621] --------------key_irq_handler------------------ [ 456.358723] __KERN__:Enter down __APP__key enter pressed [ 456.518079] --------------key_irq_handler------------------ [ 456.522176] __KERN__:Enter up __APP__key enter up [ 457.595005] __KERN__:release [root@farsight ]# jobs [1]+ Stopped ./key_test [root@farsight ]# bg %1 [1] ./key_test [root@farsight ]# top Mem: 46196K used, 926464K free, 0K shrd, 0K buff, 11316K cached CPU: 0.0% usr 4.2% sys 0.0% nic 95.7% idle 0.0% io 0.0% irq 0.0% sirq Load average: 0.00 0.00 0.00 1/67 112 PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND 112 102 0 R 2288 0.2 0 2.1 top 102 1 0 S 2292 0.2 2 0.0 -/bin/sh 1 0 0 S 2288 0.2 1 0.0 {linuxrc} init 111 102 0 S 1504 0.1 3 0.0 ./key_test 9 2 0 SW 0 0.0 0 0.0 [ksoftirqd/0] 88 2 0 IW< 0 0.0 3 0.0 [kworker/u9:1-xp] 92 2 0 IW 0 0.0 2 0.0 [kworker/u8:4-rp] 94 2 0 IW 0 0.0 3 0.0 [kworker/u8:6-rp] 95 2 0 IW< 0 0.0 1 0.0 [kworker/u9:3-xp] 5 2 0 IW 0 0.0 0 0.0 [kworker/0:0-eve] 90 2 0 IW 0 0.0 2 0.0 [kworker/u8:3-rp] 10 2 0 IW 0 0.0 2 0.0 [rcu_preempt] 83 2 0 IW 0 0.0 3 0.0 [kworker/u8:1-rp] 93 2 0 IW 0 0.0 2 0.0 [kworker/u8:5-rp] 29 2 0 SW 0 0.0 1 0.0 [kdevtmpfs] 82 2 0 IW 0 0.0 2 0.0 [kworker/2:3-mm_] 67 2 0 IW 0 0.0 1 0.0 [kworker/1:1-mm_] 59 2 0 IW 0 0.0 3 0.0 [kworker/3:1-nfs] 101 2 0 IW 0 0.0 3 0.0 [kworker/u8:7-rp] 107 2 0 IW< 0 0.0 2 0.0 [kworker/u9:0-xp]
结论:./key_test 进程cpu使用率几乎为0,按键阻塞实验成功。
//----------------key_drv.c------------------------ #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/uaccess.h> #include <linux/input-event-codes.h> #include <linux/wait.h> #include <asm/io.h> #define GPX1CON_ADDR 0x11000c20 #define GPX1_SIZE 8 //设计一个全局设备对象--描述按键信息 struct key_irq_str{ int irqno; unsigned int dev_major; unsigned int dev_minor; struct class* pkey_class; struct device* pkey_device; void* reg_virt_base; wait_queue_head_t wq; int key_state; //表示是否有数据 }; struct key_irq_str *pkey_irq_str; //设计一个描述按键的数据的对象 struct key_value_dsc{ int code; //表示按键的类型:home, esc, Q, W, E, R, T... int date; //表示按下还是抬起 1/0 }; struct key_value_dsc key3_value_dsc = { .code = 0, .date = 0, }; irqreturn_t key_irq_handler(int irqno, void *devid) { unsigned int GPX1DAT; printk("--------------%s------------------\n",__FUNCTION__); GPX1DAT = readl(pkey_irq_str->reg_virt_base + 4); if( GPX1DAT &= 0x1<<2 ){ printk("__KERN__:Enter up\n"); key3_value_dsc.code = KEY_ENTER; key3_value_dsc.date = 0; } else{ printk("__KERN__:Enter down\n"); key3_value_dsc.code = KEY_ENTER; key3_value_dsc.date = 1; } //表示有数据,需要唤醒整个等待队列 wake_up_interruptible(&(pkey_irq_str->wq)); //同时设置标志位 pkey_irq_str->key_state = 1; return IRQ_HANDLED; } int key_drv_open (struct inode* inode, struct file *filp){ printk("__KERN__:open\n"); return 0; } ssize_t key_drv_read (struct file *filp, char __user *buf, size_t count, loff_t *fpos){ int ret; //兼容阻塞和非阻塞模式。如果当前是非阻塞模式,并且没有数据,立马返回一个出错码 if(filp->f_flags & O_NONBLOCK && !pkey_irq_str->key_state) { return EAGAIN; } wait_event_interruptible(pkey_irq_str->wq, pkey_irq_str->key_state); //表示有数据 ret = copy_to_user(buf, &key3_value_dsc, count); if(ret == 0) { //printk("__KERN__:read\n"); } else { printk("__KERN__:Err to read\n"); } key3_value_dsc.code = 0; key3_value_dsc.date = 0; pkey_irq_str->key_state = 0; return count; } ssize_t key_drv_write (struct file *filp, const char __user *buf, size_t count, loff_t *fpos){ return 0; } int key_drv_release (struct inode *inod, struct file *filp){ printk("__KERN__:release\n"); return 0; } const struct file_operations key_drv_fops = { .open = key_drv_open, .read = key_drv_read, .write = key_drv_write, .release = key_drv_release, }; int get_irqno_from_node(void) { //获取到设备树中的节点 struct device_node *np = of_find_node_by_path("/key_int_node"); if(np){ printk("find node ok\n"); } else{ printk("find node failed\n"); } //通过节点去获取到中断号码 pkey_irq_str->irqno = irq_of_parse_and_map(np,0); printk("irqno = %d\n",pkey_irq_str->irqno); return pkey_irq_str->irqno; } static int __init key_drv_init(void) { int ret; //1.设定一个全局的设备对象 pkey_irq_str = kzalloc(sizeof(struct key_irq_str), 1); //2.申请主设备号,设置次设备号 pkey_irq_str->dev_major = register_chrdev(0, "key_drv_test", &key_drv_fops); pkey_irq_str->dev_minor = 0; //3.创建设备节点 pkey_irq_str->pkey_class = class_create(THIS_MODULE, "key_interrupt_dev"); pkey_irq_str->pkey_device = device_create(pkey_irq_str->pkey_class,NULL,MKDEV(pkey_irq_str->dev_major, pkey_irq_str->dev_minor),NULL,"INTRRUPT KEY%d",3); //4.硬件初始化--地址映射或中断申请 ret = get_irqno_from_node(); ret = request_irq(pkey_irq_str->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "IRQ_EINT10", NULL); if(ret != 0) { printk("request_irq ERROR!\n"); return ret; } pkey_irq_str->reg_virt_base = ioremap(GPX1CON_ADDR, GPX1_SIZE); //5. 初始化等待队列头 init_waitqueue_head(&(pkey_irq_str->wq)); pkey_irq_str->key_state = 0; return 0; } static void __exit key_drv_exit(void) { iounmap(pkey_irq_str->reg_virt_base); free_irq(pkey_irq_str->irqno,NULL); device_destroy(pkey_irq_str->pkey_class, MKDEV(pkey_irq_str->dev_major, pkey_irq_str->dev_minor)); class_destroy(pkey_irq_str->pkey_class); unregister_chrdev(pkey_irq_str->dev_major, "key_drv_test"); kfree(pkey_irq_str); } module_init(key_drv_init); module_exit(key_drv_exit); MODULE_LICENSE("GPL");
//----------------key_test.c----------------- #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define KEY_ENTER 28 #define EAGAIN 35 struct key_value_dsc{ int code; //表示按键的类型:home, esc, Q, W, E, R, T... int date; //表示按下还是抬起 1/0 }; int main(int argc, char **argv) { int ret, fd; struct key_value_dsc key_value; fd = open("/dev/INTRRUPT KEY3",O_RDWR | O_NONBLOCK); if(fd < 0){ perror("open:"); exit(1); } while(1) { ret = read(fd, &key_value, sizeof(struct key_value_dsc)); if(ret == EAGAIN) { printf("Error code: %d\n",ret); continue; } else if(ret < 0){ perror("read:"); exit(1); } if(key_value.code == KEY_ENTER) { if(key_value.date == 1) { printf("__APP__key enter pressed\n",key_value.date); } else if(key_value.date == 0) { printf("__APP__key enter up\n",key_value.date); } } } close(fd); return 0; }
ROOTFS_DIR = /home/linux/Level11 APP_NAME = key_test CROSS_COMPILE = arm-none-linux-gnueabi- CC = $(CROSS_COMPILE)gcc ifeq ($(KERNELRELEASE), ) KERNEL_DIR = /home/linux/Level10/day6/linux-5.4.79 CUR_DIR = $(shell pwd) all : make -C $(KERNEL_DIR) M=$(CUR_DIR) modules -j6 ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- $(CC) $(APP_NAME).c -o $(APP_NAME) clean : make -C $(KERNEL_DIR) M=$(CUR_DIR) clean rm $(APP_NAME) clear install: cp -raf *.ko $(APP_NAME) ./1/ else obj-m += key_drv.o endif
[root@farsight ]# ./key_test [ 453.414405] __KERN__:open [ 456.354621] --------------key_irq_handler------------------ [ 456.358723] __KERN__:Enter down __APP__key enter pressed [ 456.518079] --------------key_irq_handler------------------ [ 456.522176] __KERN__:Enter up __APP__key enter up [ 457.595005] __KERN__:release [root@farsight ]# jobs [1]+ Stopped ./key_test [root@farsight ]# bg %1 [1] ./key_test [root@farsight ]# top Mem: 47708K used, 924952K free, 0K shrd, 0K buff, 12604K cached CPU: 5.5% usr 19.5% sys 0.0% nic 74.9% idle 0.0% io 0.0% irq 0.0% sirq Load average: 0.45 0.16 0.06 2/72 106 PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND 105 101 0 R 1508 0.1 1 24.9 ./key_test 106 101 0 R 2292 0.2 2 0.1 top 101 1 0 S 2292 0.2 3 0.0 -/bin/sh 1 0 0 S 2288 0.2 1 0.0 {linuxrc} init 9 2 0 SW 0 0.0 0 0.0 [ksoftirqd/0] 93 2 0 IW< 0 0.0 2 0.0 [kworker/u9:3-xp] 90 2 0 IW 0 0.0 2 0.0 [kworker/u8:3-rp] 87 2 0 IW< 0 0.0 1 0.0 [kworker/u9:1-xp] 61 2 0 IW< 0 0.0 3 0.0 [kworker/u9:0-xp] 82 2 0 IW 0 0.0 3 0.0 [kworker/u8:1-rp] 96 2 0 IW 0 0.0 2 0.0 [kworker/u8:7-rp] 91 2 0 IW 0 0.0 1 0.0 [kworker/u8:4-rp] 103 2 0 IW 0 0.0 1 0.0 [kworker/u8:8-fl] 89 2 0 IW 0 0.0 1 0.0 [kworker/u8:2-rp] 7 2 0 IW 0 0.0 0 0.0 [kworker/u8:0-rp] 5 2 0 IW 0 0.0 0 0.0 [kworker/0:0-eve] 92 2 0 IW 0 0.0 1 0.0 [kworker/u8:5-rp] 94 2 0 IW 0 0.0 1 0.0 [kworker/u8:6-rp] 29 2 0 SW 0 0.0 1 0.0 [kdevtmpfs] 81 2 0 IW 0 0.0 2 0.0 [kworker/2:2-mm_]