之前写的按键驱动中发现一个不足之处,还有一个bug,先附上代码
key.c
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/miscdevice.h> 4 #include <linux/fs.h> 5 #include <linux/interrupt.h> 6 #include <linux/io.h> 7 #include <linux/wait.h> 8 #include <linux/uaccess.h> 9 #include <linux/workqueue.h> 10 #include <asm/ioctls.h> 11 12 #define GPNCON 0x7F008830 13 14 struct my_key_struct { 15 int irq; 16 int id; 17 char name[16]; 18 }; 19 20 struct my_key_struct my_key_config[6] = { 21 {S3C_EINT(0), 0, "key0"}, 22 {S3C_EINT(1), 1, "key1"}, 23 {S3C_EINT(2), 2, "key2"}, 24 {S3C_EINT(3), 3, "key3"}, 25 {S3C_EINT(4), 4, "key4"}, 26 {S3C_EINT(5), 5, "key5"}, 27 }; 28 29 static int __g_key_value; 30 31 int volatile condition = 0; 32 DECLARE_WAIT_QUEUE_HEAD(key_wqh); 33 34 //struct work_struct my_work; 35 struct delayed_work my_delayed_work; 36 struct workqueue_struct *p_my_workqueue; 37 38 void my_work_func (struct work_struct *p_work) 39 { 40 condition = 1; 41 wake_up_interruptible(&key_wqh); 42 } 43 44 irqreturn_t key_irqhandler (int irq, void *p_arg) 45 { 46 __g_key_value = *((int *)p_arg); 47 48 //printk("irq\n"); 49 50 //schedule_work(&my_work); 51 //schedule_delayed_work(&my_delayed_work, 2); 52 //queue_work(p_my_workqueue, &my_work); 53 queue_delayed_work(p_my_workqueue, &my_delayed_work, 20); 54 55 return 0; 56 } 57 58 static int key_open (struct inode *p_inode, struct file *p_file) 59 { 60 int ret, i; 61 int *p_gpncon; 62 int temp; 63 64 p_gpncon = ioremap(GPNCON, 4); 65 temp = readl(p_gpncon); 66 temp &= ~(0xFFF); 67 temp |= 0xAAA; 68 writel(temp, p_gpncon); 69 70 p_my_workqueue = create_singlethread_workqueue("my_workqueue"); 71 72 //INIT_WORK(&my_work, my_work_func); 73 INIT_DELAYED_WORK(&my_delayed_work, my_work_func); 74 75 for (i = 0; i < 6; i++) { 76 ret = request_irq(my_key_config[i].irq, 77 key_irqhandler, 78 IRQF_TRIGGER_FALLING, 79 my_key_config[i].name, 80 (void*)&my_key_config[i].id); 81 82 if (ret != 0) { 83 84 i--; 85 while (i >= 0) { 86 free_irq(my_key_config[i].irq, (void*)&my_key_config[i].id); 87 } 88 return -1; 89 } 90 } 91 92 return 0; 93 } 94 95 static int key_close (struct inode *p_inode, struct file *p_file) 96 { 97 int i; 98 99 for (i = 0; i < 6; i++) { 100 free_irq(my_key_config[i].irq, (void*)&my_key_config[i].id); 101 } 102 103 destroy_workqueue(p_my_workqueue); 104 105 return 0; 106 } 107 108 static ssize_t key_read (struct file *p_file, char __user *p_dat, size_t size, loff_t *p_loff) 109 { 110 unsigned long err; 111 112 if (p_file->f_flags & O_NONBLOCK) { 113 if (condition == 0) { 114 return -EAGAIN; 115 } else { 116 condition = 0; 117 if (!copy_to_user(p_dat, &__g_key_value, 4)) { 118 return 4; 119 } 120 } 121 } else { 122 wait_event_interruptible(key_wqh, condition); 123 condition = 0; 124 } 125 126 if (!copy_to_user(p_dat, &__g_key_value, 4)) { 127 __g_key_value = -1; 128 129 return 4; 130 } 131 132 return 0; 133 } 134 135 static struct file_operations fops = { 136 .owner = THIS_MODULE, 137 .open = key_open, 138 .release = key_close, 139 .read = key_read, 140 }; 141 142 static struct miscdevice key_dev = { 143 .minor = MISC_DYNAMIC_MINOR, 144 .name = "mykey", 145 .fops = &fops, 146 }; 147 148 static int __init key_init (void) 149 { 150 misc_register(&key_dev); 151 152 return 0; 153 } 154 155 static void __exit key_exit (void) 156 { 157 misc_deregister(&key_dev); 158 } 159 160 module_init(key_init); 161 module_exit(key_exit); 162 163 MODULE_LICENSE("GPL");
key_app.c
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/ioctl.h> 6 7 int main(int argc, const char *argv[]) 8 { 9 int fd; 10 int key_val; 11 unsigned long mode = 1; 12 int ret; 13 int flag; 14 15 fd = open("/dev/mykey", O_RDWR); 16 17 while (1) { 18 ret = read(fd, &key_val, 4); 19 if (4 == ret) { 20 printf("\nkey_val = %d\n", key_val); 21 22 if (key_val == 5) { 23 flag = fcntl(fd, F_GETFL); 24 flag |= O_NONBLOCK; 25 fcntl(fd, F_SETFL, flag); 26 } else if (key_val == 3) { 27 flag = fcntl(fd, F_GETFL); 28 flag &= ~O_NONBLOCK; 29 fcntl(fd, F_SETFL, flag); 30 } else if (key_val == 4) { 31 close(fd); 32 exit(1); 33 } 34 } else { 35 printf("f"); 36 } 37 } 38 39 return 0; 40 }
在调用read函数读取按键的值时应该有阻塞和非阻塞两种方式,用open("/dev/mykey", O_RDWR);方式打开默认是阻塞的方式,如果要用非阻塞的方式打开则可以用open("/dev/mykey", O_RDWR | O_NONBLOCK);或者用fcntl设置(见key_app.c第23~25行),第27~29行则是设置成阻塞模式。在驱动代码key.c中的read函数中要检测O_NONBLOCK这个标志来实现阻塞或非阻塞。
另外open函数中将工作队列相关的两个函数放到了申请中断的前面,这样做是为了解决一个BUG。我们用insmod key.ko将按键驱动加载到内核中去之后不执行key_app,也就是说没有执行open函数中的创建工作队列和申请中断的操作,这时按下按键,虽然不会有任何反应,但是中断会挂起,之后再执行原先的key_app后在open函数里面首先就是注册中断,而注册中断之后由于已经有挂起的中断,因此会立马执行到注册的中断函数里面,在中断函数里面调用queue_delayed_work(p_my_workqueue, &my_delayed_work, 20);很遗憾的是此时工作队列都还没有,因此程序崩溃了。解决方法就是将工作队列相关函数放到申请中断之前。