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 ...
//以下是學習完韋東山老師視屏教程後所做學習記錄
中斷方式取得按鍵值:

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");driver Code
測試用例:

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!\n"); 17 } 18 19 while (1) 20 { 21 read(fd, &key_val, 1); 22 printf("key_val = 0x%x\n", key_val); 23 } 24 25 return 0; 26 }drvtest Code
以上是採用中斷方式的寫法。
下麵學習了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!\n"); 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\n"); 32 } 33 else 34 { 35 read(fd, &key_val, 1); 36 printf("key_val = 0x%x\n", key_val); 37 } 38 } 39 40 return 0; 41 }drvtest view
小總結:
查詢方式:消耗資源,高達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 }signal Code
要點: 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\n"); 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");driver Code
測試用例:

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\n", 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!\n"); 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 }drvtest code
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 }