在此之前的代码,我们都是应用程序主动去read()按键值
这章所要涉及的异步通知是一旦设备就绪,则驱动主动通知应用程序,这样应用程序不需要查询设备状态。这一点类似于“中断”
一、应用程序异步通知signal()
这章的异步通知使用的是UNIX环境高级编程第十章的信号。由于是异步通知,因此我们还需要使用fcntl(fd, F_SETFL, oflag | FASYNC)将应用程序的文件描述符设置为异步模式
fcntl()函数最终会调用驱动程序中的fasync() -> fasync_helper(),因此在驱动程序中我们只需要调用fasync_helper()函数即可
fasync_helper()函数的作用就是初始化fasync_struct,包括分配内存和设置属性,最后需要在驱动的release()函数里把fasync_helper()初始化的东西free掉
应用程序的示例代码如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <string.h> 7 #include <signal.h> 8 9 int fd; 10 11 static void sig_handler(int signo) 12 { 13 unsigned char key_val; 14 read(fd, &key_val, 1); 15 printf("key_val = 0x%x\n", key_val); 16 } 17 18 int main(int argc, char** argv) 19 { 20 int oflags = 0; 21 22 signal(SIGIO, sig_handler); 23 24 fd = open("/dev/key", O_RDWR); 25 if (fd < 0) { 26 printf("can't open /dev/key\n"); 27 return -1; 28 } 29 30 fcntl(fd, F_SETOWN, getpid()); 31 oflags = fcntl(fd, F_GETFL); 32 fcntl(fd, F_SETFL, oflags | FASYNC); 33 34 while (1) { 35 sleep(1000); 36 } 37 38 close(fd); 39 40 return 0; 41 }
应用程序完成的有以下几个任务:
1. fcntl(fd, F_SETOWN, getpid())设置文件拥有者为本进程,这样驱动程序发出的信号才能被本进程接到
2. fcntl(fd, F_SETFL, oflags | FASYNC)设置应用程序支持异步通知
3. 通过signal()连接信号和信号处理函数
二、驱动程序异步通知fasync()
在分析完应用程序之后,我们需要确定驱动程序需要做什么,如下图:
由图,可得出驱动需要做的有以下几点:
1. 设置file->f_owner,这个已经由内核帮我们完成了
2. 编写fasync()函数,里面调用fasync_helper()函数
3. 在资源可获得时,调用kill_fasync()函数释放信号
我们现在来看一下fasync()系统调用过程:
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) -> do_fcntl(fd, cmd, arg, f.file); -> case F_SETFL: -> setfl(fd, filp, arg); -> filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); /* 驱动程序的fasync() */ -> case F_SETOWN: -> f_setown(filp, arg, 1);
异步通知所使用保存属性的结构体是fasync_struct
fasync()、fasync_helper()和kill_fasync()函数声明如下:
int (*fasync) (int fd, struct file *filp, int mode); int fasync_helper(int fd, struct file * filp, int mode, struct fasync_struct **fapp) void kill_fasync(struct fasync_struct **fp, int sig, int band)
驱动程序模版如下:
struct key_device { ... struct fasync_struct *fasync; }; static int key_fasync(int fd, struct file *filp, int mode) { struct key_device *dev = filp->private_data; // 初始化fasync return fasync_helper(fd, filp, mode, &dev->fasync); } static struct file_operations key_fops = { ... .fasync = key_fasync, }; static ssize_t key_read(struct file *filp, char __user *buf, size_t len, loff_t * loff) { ... schedule(); // 休眠 // 有数据,释放信号 kill_fasync(&dev->fasync, SIGIO, POLL_IN); mutex_lock(&dev->lock); copy_to_user(buf, &key_val, 1); mutex_unlock(&dev->lock); ... } static int key_release(struct inode *nodep, struct file *filp) { key_fasync(-1, filp, 0); // 将文件从异步通知列表中删除 ... }
若需要完整驱动程序代码,可查看:
key源代码:
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/init.h> 4 #include <linux/cdev.h> 5 #include <linux/slab.h> 6 #include <linux/device.h> 7 #include <linux/irq.h> 8 #include <linux/interrupt.h> 9 #include <linux/wait.h> 10 #include <linux/timer.h> 11 #include <linux/gpio.h> 12 #include <linux/sched.h> 13 #include <linux/mutex.h> 14 15 #include <asm/uaccess.h> 16 #include <asm/irq.h> 17 #include <asm/io.h> 18 19 #include <mach/gpio.h> 20 21 #define KEY_MAJOR 255 22 23 struct pin_desc { 24 int gpio; 25 int val; 26 char *name; 27 }; 28 29 struct key_device { 30 struct cdev cdev; 31 wait_queue_head_t r_head; 32 wait_queue_head_t w_head; 33 struct mutex lock; 34 struct fasync_struct *fasync; 35 }; 36 37 static struct pin_desc desc[4] = { 38 { EXYNOS4_GPX3(2), 0x01, "KEY0" }, 39 { EXYNOS4_GPX3(3), 0x02, "KEY1" }, 40 { EXYNOS4_GPX3(4), 0x03, "KEY2" }, 41 { EXYNOS4_GPX3(5), 0x04, "KEY3" }, 42 }; 43 44 static int g_major = KEY_MAJOR; 45 module_param(g_major, int, S_IRUGO); 46 47 static struct key_device* dev; 48 static struct class* scls; 49 static struct device* sdev; 50 static unsigned char key_val; 51 52 static irqreturn_t key_interrupt(int irq, void *dev_id) 53 { 54 struct pin_desc *pindesc = (struct pin_desc *)dev_id; 55 unsigned int tmp; 56 57 tmp = gpio_get_value(pindesc->gpio); 58 59 /* active low */ 60 printk(KERN_DEBUG "KEY %d: %08x\n", pindesc->val, tmp); 61 62 if (tmp) 63 key_val = pindesc->val; 64 else 65 key_val = pindesc->val | 0x80; 66 67 set_current_state(TASK_RUNNING); 68 69 return IRQ_HANDLED; 70 } 71 72 static ssize_t key_read(struct file *filp, char __user *buf, size_t len, loff_t * loff) 73 { 74 struct key_device *dev = filp->private_data; 75 76 // 声明等待队列 77 DECLARE_WAITQUEUE(rwait, current); 78 mutex_lock(&dev->lock); // 上锁 79 add_wait_queue(&dev->r_head, &rwait); 80 81 // 休眠 82 __set_current_state(TASK_INTERRUPTIBLE); 83 mutex_unlock(&dev->lock); // 解锁 84 schedule(); 85 86 // 有数据 87 kill_fasync(&dev->fasync, SIGIO, POLL_IN); 88 89 mutex_lock(&dev->lock); // 上锁 90 copy_to_user(buf, &key_val, 1); 91 mutex_unlock(&dev->lock); // 解锁 92 93 remove_wait_queue(&dev->r_head, &rwait); 94 set_current_state(TASK_RUNNING); 95 96 return 1; 97 } 98 99 static int key_open(struct inode *nodep, struct file *filp) 100 { 101 struct key_device *dev = container_of(nodep->i_cdev, struct key_device, cdev); 102 // 放入私有数据中 103 filp->private_data = dev; 104 105 int irq; 106 int i, err = 0; 107 108 for (i = 0; i < ARRAY_SIZE(desc); i++) { 109 if (!desc[i].gpio) 110 continue; 111 112 irq = gpio_to_irq(desc[i].gpio); 113 err = request_irq(irq, key_interrupt, IRQ_TYPE_EDGE_BOTH, 114 desc[i].name, (void *)&desc[i]); 115 if (err) 116 break; 117 } 118 119 if (err) { 120 i--; 121 for (; i >= 0; i--) { 122 if (!desc[i].gpio) 123 continue; 124 125 irq = gpio_to_irq(desc[i].gpio); 126 free_irq(irq, (void *)&desc[i]); 127 } 128 return -EBUSY; 129 } 130 131 init_waitqueue_head(&dev->r_head); 132 mutex_init(&dev->lock); // 初始化 133 134 return 0; 135 } 136 137 static int key_fasync(int fd, struct file *filp, int mode) 138 { 139 struct key_device *dev = filp->private_data; 140 141 return fasync_helper(fd, filp, mode, &dev->fasync); 142 } 143 144 static int key_release(struct inode *nodep, struct file *filp) 145 { 146 key_fasync(-1, filp, 0); 147 148 // 释放中断 149 int irq, i; 150 151 for (i = 0; i < ARRAY_SIZE(desc); i++) { 152 if (!desc[i].gpio) 153 continue; 154 155 irq = gpio_to_irq(desc[i].gpio); 156 free_irq(irq, (void *)&desc[i]); 157 } 158 159 return 0; 160 } 161 162 static struct file_operations key_fops = { 163 .owner = THIS_MODULE, 164 .read = key_read, 165 .open = key_open, 166 .fasync = key_fasync, 167 .release = key_release, 168 }; 169 170 static int keys_init(void) 171 { 172 int ret; 173 int devt; 174 if (g_major) { 175 devt = MKDEV(g_major, 0); 176 ret = register_chrdev_region(devt, 1, "key"); 177 } 178 else { 179 ret = alloc_chrdev_region(&devt, 0, 1, "key"); 180 g_major = MAJOR(devt); 181 } 182 183 if (ret) 184 return ret; 185 186 dev = kzalloc(sizeof(struct key_device), GFP_KERNEL); 187 if (!dev) { 188 ret = -ENOMEM; 189 goto fail_alloc; 190 } 191 192 cdev_init(&dev->cdev, &key_fops); 193 ret = cdev_add(&dev->cdev, devt, 1); 194 if (ret) 195 return ret; 196 197 scls = class_create(THIS_MODULE, "key"); 198 sdev = device_create(scls, NULL, devt, NULL, "key"); 199 200 return 0; 201 202 fail_alloc: 203 unregister_chrdev_region(devt, 1); 204 205 return ret; 206 } 207 208 static void keys_exit(void) 209 { 210 dev_t devt = MKDEV(g_major, 0); 211 212 device_destroy(scls, devt); 213 class_destroy(scls); 214 215 cdev_del(&(dev->cdev)); 216 kfree(dev); 217 218 unregister_chrdev_region(devt, 1); 219 } 220 221 module_init(keys_init); 222 module_exit(keys_exit); 223 224 MODULE_LICENSE("GPL");
Makefile:
1 KERN_DIR = /work/tiny4412/tools/linux-3.5 2 3 all: 4 make -C $(KERN_DIR) M=`pwd` modules 5 6 clean: 7 make -C $(KERN_DIR) M=`pwd` modules clean 8 rm -rf modules.order 9 10 obj-m += key.o
测试文件:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <string.h> 7 #include <signal.h> 8 9 int fd; 10 11 static void sig_handler(int signo) 12 { 13 unsigned char key_val; 14 read(fd, &key_val, 1); 15 printf("key_val = 0x%x\n", key_val); 16 } 17 18 int main(int argc, char** argv) 19 { 20 int oflags = 0; 21 22 signal(SIGIO, sig_handler); 23 24 fd = open("/dev/key", O_RDWR); 25 if (fd < 0) { 26 printf("can't open /dev/key\n"); 27 return -1; 28 } 29 30 fcntl(fd, F_SETOWN, getpid()); 31 oflags = fcntl(fd, F_GETFL); 32 fcntl(fd, F_SETFL, oflags | FASYNC); 33 34 while (1) { 35 sleep(1000); 36 } 37 38 close(fd); 39 40 return 0; 41 }
下一章 七、内核定时器