Linux混杂设备驱动 - 按键设备驱动

之前的一篇博客概括了混杂设备驱动模型(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");

 

你可能感兴趣的:(Linux混杂设备驱动 - 按键设备驱动)