之前的一篇博客概括了混杂设备驱动模型(http://www.cnblogs.com/ape-ming/p/5101322.html),现在就根据那篇博客所列出来的模板写一个按键设备驱动程序。
根据模板首先要写一个设备加载函数:
1 /* 2 * 函数名 : button_init 3 * 函数功能: 设备加载 4 */ 5 static int __init button_init(void) 6 { 7 int ret = 0; 8 9 /* 注册混杂设备驱动 */ 10 ret = misc_register(&misc); 11 if(ret) 12 { 13 printk("can't register miscdev\n"); 14 return ret; 15 } 16 return ret; 17 }
button_init()函数完成设备加载,在button_init()里面主要是调用了misc_register()对混杂设备进行注册,这个时候就需要给定一个混杂设备驱动结构体miscdevice:
1 static struct miscdevice misc = 2 { 3 .minor = MISC_DYNAMIC_MINOR, 4 .name = "buttons6410", 5 .fops = &fops, 6 };
misc里面的minor赋值MISC_DYNAMIC_MINOR表示动态分配设备号,name随便取,还需要一个file_operations结构体:
1 static struct file_operations fops = 2 { 3 .owner = THIS_MODULE, 4 .open = button_open, 5 .read = button_read, 6 .release = button_close, 7 };
在这个结构体里面赋值了三个函数open、read、和release:
1 /* 2 * 函数名 : button_open 3 * 函数功能: 文件打开 4 */ 5 static int button_open(struct inode *node, struct file *filp) 6 { 7 int ret = 0; 8 int i = 0; 9 10 /* 申请外部中断 */ 11 for(i = 0;i < sizeof(button_irq)/sizeof(button_irq[0]);i++) 12 { 13 ret = request_irq(button_irq[i].irq,button_interrupt,IRQF_TRIGGER_FALLING,button_irq[i].name,(void*)&button_irq[i]); 14 if(ret != 0) 15 { 16 printk("request_irq failure\n"); 17 } 18 } 19 20 /* 初始化工作队列 */ 21 INIT_WORK(&button_work,do_buttons); 22 23 /* 初始化内核定时器 */ 24 init_timer(&button_time); 25 button_time.expires = jiffies + HZ/10; //100ms 26 button_time.function = button_do_time; 27 add_timer(&button_time); 28 29 return ret; 30 }
在open函数里面申请了四个按键的外部中断,这里调用了内核定时器对按键进行延时消抖(其实没必要这么做的,纯粹练手^_^)所以初始化了一个工作队列把中断提交给底半部进行处理,之后初始化内核定时器。
1 /* 2 * 函数名 : button_read 3 * 函数功能: 文件读 4 */ 5 static ssize_t button_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 6 { 7 ssize_t ret = 0; 8 size_t size = min(count,sizeof(button_press)); 9 10 /* 等待队列唤醒 */ 11 wait_event_interruptible(q_buttons,isKey_Pressed); 12 isKey_Pressed = 0; 13 14 if(copy_to_user((void*)buffer,(const void*)&button_press,size)) 15 { 16 ret = -1; 17 printk("copy to user failure\n"); 18 } 19 else 20 { 21 memset((void*)&button_press,0,sizeof(button_press)); 22 ret = size; 23 } 24 return ret; 25 }
在read函数里面采用阻塞的办法等待等待队列唤醒,之后调用copytouser()函数把按键的状态传递给用户程序。
当按键按下即触发了外部中断,进入外部中断处理程序:
1 /* 2 * 函数名 : button_interrupt 3 * 函数功能: 外部中断服务程序 4 */ 5 static irqreturn_t button_interrupt(int irq, void *dev_id) 6 { 7 struct button_irqs *button_id = (struct button_irqs*)dev_id; 8 switch(button_id->id) 9 { 10 case 0: 11 case 1: 12 case 2: 13 case 3: 14 schedule_work(&button_work); 15 break; 16 default: break; 17 } 18 return IRQ_HANDLED; 19 }
外部中断程序调用schedule_work()提交到底半部进行处理:
1 /* 2 * 函数名 : do_buttons 3 * 函数功能: 工作队列处理函数,处理按键工作 4 */ 5 static void do_buttons(struct work_struct *work) 6 { 7 mod_timer(&button_time,jiffies + HZ/10); 8 }
在这个函数中修改定时器的值使定时器开始定时达到延时消抖的目的,这里延时100ms,延时时间到即进入内核定时器中断服务程序:
1 /* 2 * 函数名 : button_do_time 3 * 函数功能: 内核定时器服务程序 4 */ 5 6 static void button_do_time(unsigned long arg) 7 { 8 int i = 0; 9 unsigned short tmp = 0; 10 tmp = readw(S3C64XX_GPNDAT) & 0x000F; 11 switch(tmp) 12 { 13 case 0x0E: 14 button_press[0] = 1; 15 break; 16 case 0x0D: 17 button_press[1] = 1; 18 break; 19 case 0x0B: 20 button_press[2] = 1; 21 break; 22 case 0x07: 23 button_press[3] = 1; 24 break; 25 } 26 for(i = 0;i < sizeof(button_press)/sizeof(button_press[0]);i++) 27 { 28 if(button_press[i] == 0) 29 continue; 30 31 isKey_Pressed = 1; 32 33 /* 唤醒等待队列 */ 34 wake_up_interruptible(&q_buttons); 35 break; 36 } 37 }
在这个函数里面判断是哪个按键按下了并唤醒等待队列。
最后编写设备驱动卸载函数:
1 /* 2 * 函数名 : button_exit 3 * 函数功能: 设备卸载 4 */ 5 static void __exit button_exit(void) 6 { 7 /* 卸载混杂设备驱动 */ 8 misc_deregister(&misc); 9 }
这个函数调用misc_deregister()卸载misc驱动。
完整代码:
1 /* 2 * 文件名 : buttons.c 3 * 功能描述: 通过外部中断实现按键驱动程序 4 * 驱动模型: misc 5 * 设备节点: /dev/buttons6410 6 * MCU : S3C6410 7 * 端口连接: KEY0-GPN0 KEY1-GPN1 KEY2-GPN2 KEY3-GPN3 8 */ 9 10 #include <linux/module.h> 11 #include <linux/kernel.h> 12 #include <linux/fs.h> 13 #include <linux/init.h> 14 #include <linux/delay.h> 15 #include <linux/poll.h> 16 #include <linux/irq.h> 17 #include <asm/irq.h> 18 #include <asm/io.h> 19 #include <linux/interrupt.h> 20 #include <asm/uaccess.h> 21 #include <mach/hardware.h> 22 #include <linux/platform_device.h> 23 #include <linux/cdev.h> 24 #include <linux/miscdevice.h> 25 26 #include <mach/map.h> 27 #include <mach/regs-clock.h> 28 #include <mach/regs-gpio.h> 29 30 #include <plat/gpio-cfg.h> 31 #include <mach/gpio-bank-n.h> 32 #include <mach/gpio-bank-l.h> 33 34 35 volatile int isKey_Pressed = 0; // 按键按下标志 36 struct work_struct button_work; //定义工作队列 37 struct timer_list button_time; 38 struct button_irqs 39 { 40 unsigned int irq; 41 int id; 42 char* name; 43 }; 44 45 static struct button_irqs button_irq[] = 46 { 47 {IRQ_EINT(0),0,"KEY0"}, 48 {IRQ_EINT(1),1,"KEY1"}, 49 {IRQ_EINT(2),2,"KEY2"}, 50 {IRQ_EINT(3),3,"KEY3"}, 51 }; 52 53 /* 初始化等待队列 */ 54 DECLARE_WAIT_QUEUE_HEAD(q_buttons); 55 56 static volatile int button_press[4] = {0}; 57 58 /* 59 * 函数名 : do_buttons 60 * 函数功能: 工作队列处理函数,处理按键工作 61 */ 62 static void do_buttons(struct work_struct *work) 63 { 64 mod_timer(&button_time,jiffies + HZ/10); 65 } 66 67 /* 68 * 函数名 : button_interrupt 69 * 函数功能: 外部中断服务程序 70 */ 71 static irqreturn_t button_interrupt(int irq, void *dev_id) 72 { 73 struct button_irqs *button_id = (struct button_irqs*)dev_id; 74 switch(button_id->id) 75 { 76 case 0: 77 case 1: 78 case 2: 79 case 3: 80 schedule_work(&button_work); 81 break; 82 default: break; 83 } 84 return IRQ_HANDLED; 85 } 86 87 /* 88 * 函数名 : button_do_time 89 * 函数功能: 内核定时器服务程序 90 */ 91 92 static void button_do_time(unsigned long arg) 93 { 94 int i = 0; 95 unsigned short tmp = 0; 96 tmp = readw(S3C64XX_GPNDAT) & 0x000F; 97 switch(tmp) 98 { 99 case 0x0E: 100 button_press[0] = 1; 101 break; 102 case 0x0D: 103 button_press[1] = 1; 104 break; 105 case 0x0B: 106 button_press[2] = 1; 107 break; 108 case 0x07: 109 button_press[3] = 1; 110 break; 111 } 112 for(i = 0;i < sizeof(button_press)/sizeof(button_press[0]);i++) 113 { 114 if(button_press[i] == 0) 115 continue; 116 117 isKey_Pressed = 1; 118 119 /* 唤醒等待队列 */ 120 wake_up_interruptible(&q_buttons); 121 break; 122 } 123 124 } 125 126 /* 127 * 函数名 : button_open 128 * 函数功能: 文件打开 129 */ 130 static int button_open(struct inode *node, struct file *filp) 131 { 132 int ret = 0; 133 int i = 0; 134 135 /* 申请外部中断 */ 136 for(i = 0;i < sizeof(button_irq)/sizeof(button_irq[0]);i++) 137 { 138 ret = request_irq(button_irq[i].irq,button_interrupt,IRQF_TRIGGER_FALLING,button_irq[i].name,(void*)&button_irq[i]); 139 if(ret != 0) 140 { 141 printk("request_irq failure\n"); 142 } 143 } 144 145 /* 初始化工作队列 */ 146 INIT_WORK(&button_work,do_buttons); 147 148 /* 初始化内核定时器 */ 149 init_timer(&button_time); 150 button_time.expires = jiffies + HZ/10; //100ms 151 button_time.function = button_do_time; 152 add_timer(&button_time); 153 154 return ret; 155 } 156 157 /* 158 * 函数名 : button_read 159 * 函数功能: 文件读 160 */ 161 static ssize_t button_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 162 { 163 ssize_t ret = 0; 164 size_t size = min(count,sizeof(button_press)); 165 166 /* 等待队列唤醒 */ 167 wait_event_interruptible(q_buttons,isKey_Pressed); 168 isKey_Pressed = 0; 169 170 if(copy_to_user((void*)buffer,(const void*)&button_press,size)) 171 { 172 ret = -1; 173 printk("copy to user failure\n"); 174 } 175 else 176 { 177 memset((void*)&button_press,0,sizeof(button_press)); 178 ret = size; 179 } 180 return ret; 181 } 182 183 /* 184 * 函数名 : button_close 185 * 函数功能: 文件关闭 186 */ 187 static int button_close(struct inode *node, struct file *filp) 188 { 189 int ret = 0; 190 int i = 0; 191 192 /* 释放中断 */ 193 for(i = 0;i < sizeof(button_irq)/sizeof(button_irq[0]);i++) 194 { 195 free_irq(button_irq[i].irq,(void*)&button_irq[i]); 196 } 197 198 /* 释放内核定时器 */ 199 del_timer(&button_time); 200 return ret; 201 } 202 203 204 static struct file_operations fops = 205 { 206 .owner = THIS_MODULE, 207 .open = button_open, 208 .read = button_read, 209 .release = button_close, 210 }; 211 212 static struct miscdevice misc = 213 { 214 .minor = MISC_DYNAMIC_MINOR, 215 .name = "buttons6410", 216 .fops = &fops, 217 }; 218 219 220 /* 221 * 函数名 : button_init 222 * 函数功能: 设备加载 223 */ 224 static int __init button_init(void) 225 { 226 int ret = 0; 227 228 /* 注册混杂设备驱动 */ 229 ret = misc_register(&misc); 230 if(ret) 231 { 232 printk("can't register miscdev\n"); 233 return ret; 234 } 235 return ret; 236 } 237 238 /* 239 * 函数名 : button_exit 240 * 函数功能: 设备卸载 241 */ 242 static void __exit button_exit(void) 243 { 244 /* 卸载混杂设备驱动 */ 245 ret = misc_deregister(&misc); 246 } 247 248 module_init(button_init); 249 module_exit(button_exit); 250 MODULE_LICENSE("GPL"); 251 MODULE_AUTHOR("Liu Zhiming");