//以下是学习完韦东山老师视屏教程后所做学习记录
中断方式取得按键值:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <asm/irq.h> 9 #include <asm/io.h> 10 #include <asm/arch/regs-gpio.h> 11 #include <asm/hardware.h> 12 13 14 static struct class *thirddrv_class; 15 static struct class_device *thirddrv_class_dev; 16 17 volatile unsigned long *gpfcon; 18 volatile unsigned long *gpfdat; 19 20 volatile unsigned long *gpgcon; 21 volatile unsigned long *gpgdat; 22 23 24 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 25 26 /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */ 27 static volatile int ev_press = 0; 28 29 30 struct pin_desc{ 31 unsigned int pin; 32 unsigned int key_val; 33 }; 34 35 36 /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ 37 /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ 38 static unsigned char key_val; 39 40 struct pin_desc pins_desc[4] = { 41 {S3C2410_GPF0, 0x01}, 42 {S3C2410_GPF2, 0x02}, 43 {S3C2410_GPG3, 0x03}, 44 {S3C2410_GPG11, 0x04}, 45 }; 46 47 48 /* 49 * 确定按键值 50 */ 51 static irqreturn_t buttons_irq(int irq, void *dev_id) 52 { 53 struct pin_desc * pindesc = (struct pin_desc *)dev_id; 54 unsigned int pinval; 55 56 pinval = s3c2410_gpio_getpin(pindesc->pin); 57 58 if (pinval) 59 { 60 /* 松开 */ 61 key_val = 0x80 | pindesc->key_val; 62 } 63 else 64 { 65 /* 按下 */ 66 key_val = pindesc->key_val; 67 } 68 69 ev_press = 1; /* 表示中断发生了 */ 70 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ 71 72 73 return IRQ_RETVAL(IRQ_HANDLED); 74 } 75 76 static int third_drv_open(struct inode *inode, struct file *file) 77 { 78 /* 配置GPF0,2为输入引脚 */ 79 /* 配置GPG3,11为输入引脚 */ 80 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); 81 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); 82 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); 83 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); 84 85 return 0; 86 } 87 88 ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 89 { 90 if (size != 1) 91 return -EINVAL; 92 93 /* 如果没有按键动作, 休眠 */ 94 wait_event_interruptible(button_waitq, ev_press); 95 96 /* 如果有按键动作, 返回键值 */ 97 copy_to_user(buf, &key_val, 1); 98 ev_press = 0; 99 100 return 1; 101 } 102 103 104 int third_drv_close(struct inode *inode, struct file *file) 105 { 106 free_irq(IRQ_EINT0, &pins_desc[0]); 107 free_irq(IRQ_EINT2, &pins_desc[1]); 108 free_irq(IRQ_EINT11, &pins_desc[2]); 109 free_irq(IRQ_EINT19, &pins_desc[3]); 110 return 0; 111 } 112 113 114 static struct file_operations sencod_drv_fops = { 115 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 116 .open = third_drv_open, 117 .read = third_drv_read, 118 .release = third_drv_close, 119 }; 120 121 122 int major; 123 static int third_drv_init(void) 124 { 125 major = register_chrdev(0, "third_drv", &sencod_drv_fops); 126 127 thirddrv_class = class_create(THIS_MODULE, "third_drv"); 128 129 thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ 130 131 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); 132 gpfdat = gpfcon + 1; 133 134 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); 135 gpgdat = gpgcon + 1; 136 137 return 0; 138 } 139 140 static void third_drv_exit(void) 141 { 142 unregister_chrdev(major, "third_drv"); 143 class_device_unregister(thirddrv_class_dev); 144 class_destroy(thirddrv_class); 145 iounmap(gpfcon); 146 iounmap(gpgcon); 147 return 0; 148 } 149 150 151 module_init(third_drv_init); 152 153 module_exit(third_drv_exit); 154 155 MODULE_LICENSE("GPL");
测试用例:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 6 /* thirddrvtest 7 */ 8 int main(int argc, char **argv) 9 { 10 int fd; 11 unsigned char key_val; 12 13 fd = open("/dev/buttons", O_RDWR); 14 if (fd < 0) 15 { 16 printf("can't open! "); 17 } 18 19 while (1) 20 { 21 read(fd, &key_val, 1); 22 printf("key_val = 0x%x ", key_val); 23 } 24 25 return 0; 26 }
以上是采用中断方式的写法。
下面学习了poll机制后,做了改进:
测试用例如下:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 #include <poll.h> 6 7 8 /* forthdrvtest 9 */ 10 int main(int argc, char **argv) 11 { 12 int fd; 13 unsigned char key_val; 14 int ret; 15 16 struct pollfd fds[1]; 17 18 fd = open("/dev/buttons", O_RDWR); 19 if (fd < 0) 20 { 21 printf("can't open! "); 22 } 23 24 fds[0].fd = fd; 25 fds[0].events = POLLIN; 26 while (1) 27 { 28 ret = poll(fds, 1, 5000); 29 if (ret == 0) 30 { 31 printf("time out "); 32 } 33 else 34 { 35 read(fd, &key_val, 1); 36 printf("key_val = 0x%x ", key_val); 37 } 38 } 39 40 return 0; 41 }
小总结:
查询方式:消耗资源,高达90%以上
中断:read()
poll:指定超时时间
都是应用程序主动去读。
下面通过异步通知来改进。
先做一个简单的测试
1 #include <stdio.h> 2 #include <signal.h> 3 void my_signal_fun(int signum) 4 { 5 static int cnt = 0; 6 printf("signal = %d,%d times",signum,++cnt); 7 } 8 9 int main(int argc,char *argv) 10 { 11 signal(SIGUSR1,my_signal_fun); 12 while(1) 13 { 14 sleep(1000); 15 } 16 return 0; 17 }
要点: 1.注册一个信号处理函数
2.谁发?发给谁?怎么发?
目标:按下按键是,驱动程序通知应用程序,
1、应用程序要注册信号处理函数。
2、驱动程序发信号给应用程序 (应用程序要告诉驱动PID号)
3、怎么发?
用到signal、fcntl
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #include <linux/poll.h> static struct class *fifthdrv_class; static struct class_device *fifthdrv_class_dev; volatile unsigned long *gpfcon; volatile unsigned long *gpfdat; volatile unsigned long *gpgcon; volatile unsigned long *gpgdat; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,fifth_drv_read将它清0 */ static volatile int ev_press = 0; static struct fasync_struct *button_async; struct pin_desc{ unsigned int pin; unsigned int key_val; }; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; struct pin_desc pins_desc[4] = { {S3C2410_GPF0, 0x01}, {S3C2410_GPF2, 0x02}, {S3C2410_GPG3, 0x03}, {S3C2410_GPG11, 0x04}, }; /* * 确定按键值 */ static irqreturn_t buttons_irq(int irq, void *dev_id) { struct pin_desc * pindesc = (struct pin_desc *)dev_id; unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval) { /* 松开 */ key_val = 0x80 | pindesc->key_val; } else { /* 按下 */ key_val = pindesc->key_val; } ev_press = 1; /* 表示中断发生了 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync (&button_async, SIGIO, POLL_IN); return IRQ_RETVAL(IRQ_HANDLED); } static int fifth_drv_open(struct inode *inode, struct file *file) { /* 配置GPF0,2为输入引脚 */ /* 配置GPG3,11为输入引脚 */ request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); return 0; } ssize_t fifth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { if (size != 1) return -EINVAL; /* 如果没有按键动作, 休眠 */ wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */ copy_to_user(buf, &key_val, 1); ev_press = 0; return 1; } int fifth_drv_close(struct inode *inode, struct file *file) { free_irq(IRQ_EINT0, &pins_desc[0]); free_irq(IRQ_EINT2, &pins_desc[1]); free_irq(IRQ_EINT11, &pins_desc[2]); free_irq(IRQ_EINT19, &pins_desc[3]); return 0; } static unsigned fifth_drv_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } static int fifth_drv_fasync (int fd, struct file *filp, int on) { printk("driver: fifth_drv_fasync "); return fasync_helper (fd, filp, on, &button_async); } static struct file_operations sencod_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = fifth_drv_open, .read = fifth_drv_read, .release = fifth_drv_close, .poll = fifth_drv_poll, .fasync = fifth_drv_fasync, }; int major; static int fifth_drv_init(void) { major = register_chrdev(0, "fifth_drv", &sencod_drv_fops); fifthdrv_class = class_create(THIS_MODULE, "fifth_drv"); fifthdrv_class_dev = class_device_create(fifthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); gpgdat = gpgcon + 1; return 0; } static void fifth_drv_exit(void) { unregister_chrdev(major, "fifth_drv"); class_device_unregister(fifthdrv_class_dev); class_destroy(fifthdrv_class); iounmap(gpfcon); iounmap(gpgcon); return 0; } module_init(fifth_drv_init); module_exit(fifth_drv_exit); MODULE_LICENSE("GPL");
测试用例:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 #include <poll.h> 6 #include <signal.h> 7 #include <sys/types.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 11 12 /* fifthdrvtest 13 */ 14 int fd; 15 16 void my_signal_fun(int signum) 17 { 18 unsigned char key_val; 19 read(fd, &key_val, 1); 20 printf("key_val: 0x%x ", key_val); 21 } 22 23 int main(int argc, char **argv) 24 { 25 unsigned char key_val; 26 int ret; 27 int Oflags; 28 29 signal(SIGIO, my_signal_fun); 30 31 fd = open("/dev/buttons", O_RDWR); 32 if (fd < 0) 33 { 34 printf("can't open! "); 35 } 36 37 fcntl(fd, F_SETOWN, getpid()); 38 39 Oflags = fcntl(fd, F_GETFL); 40 41 fcntl(fd, F_SETFL, Oflags | FASYNC); 42 43 44 while (1) 45 { 46 sleep(1000); 47 } 48 49 return 0; 50 }
fcntl(fd, F_SETOWN, getpid()); //告诉内核,发给谁
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC); //改变fasync标记,最终会调用驱动的faync>faync_helper : 初始化/释放fasync_struct
目前,我们这个按键驱动程序已经较完善。
现在我们想让这个驱动同一时间只有一个app可以打开。
如果用一个变量来控制的话,会存在漏洞,分析:
linux是一个多任务的系统,修改一个值的时候有三个过程:1、读出;2、修改;3、写回
修改代码不是一个原子操作,引入原子变量。
笔记如下:
1. 原子操作
原子操作指的是在执行过程中不会被别的代码路径所中断的操作。
常用原子操作函数举例:
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
atomic_read(atomic_t *v); //返回原子变量的值
void atomic_inc(atomic_t *v); //原子变量增加1
void atomic_dec(atomic_t *v); //原子变量减少1
int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。
2. 信号量
信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。
当获取不到信号量时,进程进入休眠等待状态。
定义信号量
struct semaphore sem;
初始化信号量
void sema_init (struct semaphore *sem, int val);
void init_MUTEX(struct semaphore *sem);//初始化为0
static DECLARE_MUTEX(button_lock); //定义互斥锁
获得信号量
void down(struct semaphore * sem);
int down_interruptible(struct semaphore * sem);
int down_trylock(struct semaphore * sem);
释放信号量
void up(struct semaphore * sem);
3. 阻塞
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
fd = open("...", O_RDWR | O_NONBLOCK);
加上按键防抖动就完美:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <asm/irq.h> 9 #include <asm/io.h> 10 #include <asm/arch/regs-gpio.h> 11 #include <asm/hardware.h> 12 #include <linux/poll.h> 13 14 15 static struct class *sixthdrv_class; 16 static struct class_device *sixthdrv_class_dev; 17 18 volatile unsigned long *gpfcon; 19 volatile unsigned long *gpfdat; 20 21 volatile unsigned long *gpgcon; 22 volatile unsigned long *gpgdat; 23 24 static struct timer_list buttons_timer; 25 26 27 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 28 29 /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */ 30 static volatile int ev_press = 0; 31 32 static struct fasync_struct *button_async; 33 34 35 struct pin_desc{ 36 unsigned int pin; 37 unsigned int key_val; 38 }; 39 40 41 /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ 42 /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ 43 static unsigned char key_val; 44 45 struct pin_desc pins_desc[4] = { 46 {S3C2410_GPF0, 0x01}, 47 {S3C2410_GPF2, 0x02}, 48 {S3C2410_GPG3, 0x03}, 49 {S3C2410_GPG11, 0x04}, 50 }; 51 52 static struct pin_desc *irq_pd; 53 54 //static atomic_t canopen = ATOMIC_INIT(1); //定义原子变量并初始化为1 55 56 static DECLARE_MUTEX(button_lock); //定义互斥锁 57 58 /* 59 * 确定按键值 60 */ 61 static irqreturn_t buttons_irq(int irq, void *dev_id) 62 { 63 /* 10ms后启动定时器 */ 64 irq_pd = (struct pin_desc *)dev_id; 65 mod_timer(&buttons_timer, jiffies+HZ/100); 66 return IRQ_RETVAL(IRQ_HANDLED); 67 } 68 69 static int sixth_drv_open(struct inode *inode, struct file *file) 70 { 71 #if 0 72 if (!atomic_dec_and_test(&canopen)) 73 { 74 atomic_inc(&canopen); 75 return -EBUSY; 76 } 77 #endif 78 79 if (file->f_flags & O_NONBLOCK) 80 { 81 if (down_trylock(&button_lock)) 82 return -EBUSY; 83 } 84 else 85 { 86 /* 获取信号量 */ 87 down(&button_lock); 88 } 89 90 /* 配置GPF0,2为输入引脚 */ 91 /* 配置GPG3,11为输入引脚 */ 92 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); 93 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); 94 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); 95 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); 96 97 return 0; 98 } 99 100 ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 101 { 102 if (size != 1) 103 return -EINVAL; 104 105 if (file->f_flags & O_NONBLOCK) 106 { 107 if (!ev_press) 108 return -EAGAIN; 109 } 110 else 111 { 112 /* 如果没有按键动作, 休眠 */ 113 wait_event_interruptible(button_waitq, ev_press); 114 } 115 116 /* 如果有按键动作, 返回键值 */ 117 copy_to_user(buf, &key_val, 1); 118 ev_press = 0; 119 120 return 1; 121 } 122 123 124 int sixth_drv_close(struct inode *inode, struct file *file) 125 { 126 //atomic_inc(&canopen); 127 free_irq(IRQ_EINT0, &pins_desc[0]); 128 free_irq(IRQ_EINT2, &pins_desc[1]); 129 free_irq(IRQ_EINT11, &pins_desc[2]); 130 free_irq(IRQ_EINT19, &pins_desc[3]); 131 up(&button_lock); 132 return 0; 133 } 134 135 static unsigned sixth_drv_poll(struct file *file, poll_table *wait) 136 { 137 unsigned int mask = 0; 138 poll_wait(file, &button_waitq, wait); // 不会立即休眠 139 140 if (ev_press) 141 mask |= POLLIN | POLLRDNORM; 142 143 return mask; 144 } 145 146 static int sixth_drv_fasync (int fd, struct file *filp, int on) 147 { 148 printk("driver: sixth_drv_fasync "); 149 return fasync_helper (fd, filp, on, &button_async); 150 } 151 152 153 static struct file_operations sencod_drv_fops = { 154 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 155 .open = sixth_drv_open, 156 .read = sixth_drv_read, 157 .release = sixth_drv_close, 158 .poll = sixth_drv_poll, 159 .fasync = sixth_drv_fasync, 160 }; 161 162 163 int major; 164 165 static void buttons_timer_function(unsigned long data) 166 { 167 struct pin_desc * pindesc = irq_pd; 168 unsigned int pinval; 169 170 if (!pindesc) 171 return; 172 173 pinval = s3c2410_gpio_getpin(pindesc->pin); 174 175 if (pinval) 176 { 177 /* 松开 */ 178 key_val = 0x80 | pindesc->key_val; 179 } 180 else 181 { 182 /* 按下 */ 183 key_val = pindesc->key_val; 184 } 185 186 ev_press = 1; /* 表示中断发生了 */ 187 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ 188 189 kill_fasync (&button_async, SIGIO, POLL_IN); 190 } 191 192 193 static int sixth_drv_init(void) 194 { 195 init_timer(&buttons_timer); 196 buttons_timer.function = buttons_timer_function; 197 //buttons_timer.expires = 0; 198 add_timer(&buttons_timer); 199 200 major = register_chrdev(0, "sixth_drv", &sencod_drv_fops); 201 202 sixthdrv_class = class_create(THIS_MODULE, "sixth_drv"); 203 204 /* 为了让mdev根据这些信息来创建设备节点 */ 205 sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ 206 207 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); 208 gpfdat = gpfcon + 1; 209 210 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); 211 gpgdat = gpgcon + 1; 212 213 return 0; 214 } 215 216 static void sixth_drv_exit(void) 217 { 218 unregister_chrdev(major, "sixth_drv"); 219 class_device_unregister(sixthdrv_class_dev); 220 class_destroy(sixthdrv_class); 221 iounmap(gpfcon); 222 iounmap(gpgcon); 223 return 0; 224 } 225 226 227 module_init(sixth_drv_init); 228 229 module_exit(sixth_drv_exit); 230 231 MODULE_LICENSE("GPL");
测试程序:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 #include <poll.h> 6 #include <signal.h> 7 #include <sys/types.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 11 12 /* sixthdrvtest 13 */ 14 int fd; 15 16 void my_signal_fun(int signum) 17 { 18 unsigned char key_val; 19 read(fd, &key_val, 1); 20 printf("key_val: 0x%x ", key_val); 21 } 22 23 int main(int argc, char **argv) 24 { 25 unsigned char key_val; 26 int ret; 27 int Oflags; 28 29 //signal(SIGIO, my_signal_fun); 30 31 fd = open("/dev/buttons", O_RDWR); 32 if (fd < 0) 33 { 34 printf("can't open! "); 35 return -1; 36 } 37 38 //fcntl(fd, F_SETOWN, getpid()); 39 40 //Oflags = fcntl(fd, F_GETFL); 41 42 //fcntl(fd, F_SETFL, Oflags | FASYNC); 43 44 45 while (1) 46 { 47 ret = read(fd, &key_val, 1); 48 printf("key_val: 0x%x, ret = %d ", key_val, ret); 49 //sleep(5); 50 } 51 52 return 0; 53 }
驱动: