之前的一篇博客概括了混杂设备驱动模型(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 miscdevn');
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 failuren');
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 failuren');
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
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 #include
19 #include
20 #include
21 #include
22 #include
23 #include
24 #include
25
26 #include
27 #include
28 #include
29
30 #include
31 #include
32 #include
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;