本驱动程序运行于TQ2440开发板,内核版本2.6.31.6
2 #include <linux/kernel.h>
3 #include <linux/fs.h>
4 #include <linux/init.h>
5 #include <linux/delay.h>
6 #include <linux/poll.h>
7 #include <linux/irq.h>
8 #include <asm/irq.h>
9 #include <linux/io.h>
10 #include <linux/interrupt.h>
11 #include <asm/uaccess.h>
12 #include <mach/regs-clock.h>
13 #include <plat/regs-adc.h>
14 #include <mach/hardware.h>
15 #include <linux/platform_device.h>
16 #include <linux/cdev.h>
17 #include <linux/miscdevice.h>
18
19
20 #define DEVICE_NAME "tq2440-buttons"
21 #define DEV_COUNT 1
22 #define BUTTON_MAJOR 0
23 #define BUTTON_MINOR 0
24
25 #define MIN(A,B) (A)>(B)?(B):(A)
26 /*等待队列:
27 *当没有按键被按下时,如果有进程调用tq2440_buttons_read函数
28 *它将休眠*/
29
30 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
31 /*中断事件标志,中断服务程序将它置1,tq2440_buttons_read将它清0 */
32
33 static volatile int ev_press = 0;
34
35 /* 按键被按下的次数(准确地说,是发生中断的次数) */
36 static volatile int press_cnt[] = {0,0,0,0};
37
38
39 static struct class *button_class;
40 struct button_irqs_desc {
41 int irq; //中断号
42 unsigned long flags; //中断标志,用来定义中断的触发方式
43 char *name; //中断名称
44 };
45
46 /*用来指定按键所用的外部中断引脚及中断触发方式,名字*/
47 static struct button_irqs_desc button_irqs[] = {
48 {IRQ_EINT0,IRQ_TYPE_EDGE_FALLING,"KEY1"}, //K1
49 {IRQ_EINT1,IRQ_TYPE_EDGE_FALLING,"KEY2"}, //K2
50 {IRQ_EINT2,IRQ_TYPE_EDGE_FALLING,"KEY3"}, //K3
51 {IRQ_EINT4,IRQ_TYPE_EDGE_FALLING,"KEY4"}, //K4
52 };
53
54 dev_t dev_num;
55 static struct cdev * buttons_cdev_p; //cdev结构体指针
56
57 /*卸载驱动程序*/
58 static void __exit tq2440_buttons_exit(void)
59 {
60 cdev_del(buttons_cdev_p);//注销cdev
61 kfree(buttons_cdev_p);//释放设备结构体内存
62 unregister_chrdev_region(dev_num,DEV_COUNT);//释放设备号
63
64 }
65
66 static irqreturn_t buttons_interrupt(int irq,void *dev_id)
67 {
68 volatile int *press_cnt = (volatile int *)dev_id;
69
70 *press_cnt = *press_cnt + 1; /*按键计数器加1 */
71 ev_press = 1; /*表示中断发生了*/
72 wake_up_interruptible(&button_waitq); /*唤醒休眠的进程*/
73
74 printk(" IRQ:%d\n",irq);
75
76 return IRQ_RETVAL(IRQ_HANDLED);
77 }
78
79 /*应用程序执行open("/dev/buttons",...)系统调用时,tq2440_buttons_open函数将
80 *被调用,它用来注册4个按键的中断处理程序*/
81 static int tq2440_buttons_open(struct inode *inode,struct file *file)
82 {
83 int i;
84 int err;
85
86 for(i =0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++) {
87 //注册中断处理函数
88 err = request_irq(button_irqs[i].irq,buttons_interrupt,button_irqs[i].flags,
89 button_irqs[i].name,(void *)&press_cnt[i]);
90
91 if(err)
92 break;
93 }
94 if(err) {
95 //如果出错,释放已经注册的中断
96 i--;
97 for(;i>=0;i--)
98 free_irq(button_irqs[i].irq,(void *)&press_cnt[i]);
99 return -EBUSY;
100 }
101 return 0;
102 }
103
104 /*应用程序对设备文件/dev/buttons执行close(...)时,
105 *就会调用tq2440_buttons_close函数*/
106 static int tq2440_buttons_close(struct inode *inode,struct file *file)
107 {
108 int i;
109
110 for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++) {
111 //释放已经注册的中断
112 free_irq(button_irqs[i].irq,(void *)&press_cnt[i]);
113 }
114 return 0;
115 }
116
117 /*应用程序对设备文件/dev/buttons执行read(...)时,
118 *就会调用tq2440_buttons_read函数
119 */
120 static int tq2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
121 {
122 unsigned long err;
123 /*如果ev_press等于0,休眠*/
124 wait_event_interruptible(button_waitq,ev_press);
125
126 /*执行到这里时ev_press肯定等于1,将它清0 */
127 ev_press = 0;
128
129 /*将按键状态复制给用户,并请0 */
130 err = copy_to_user(buff,(const void *)press_cnt,MIN(sizeof(press_cnt),count));
131
132 memset((void *)press_cnt,0,sizeof(press_cnt));
133
134 return err? -EFAULT:0;
135 }
136
137 /*这个结构是字符驱动设备程序的核心
138 *当应用程序操作设备文件时所调用的open,read,write等函数
139 *最终会调用这个结构中的对应函数
140 */
141
142 static struct file_operations tq2440_buttons_fops = {
143 .owner = THIS_MODULE,/*这是一个宏,指向编译模块时自动创建的__this_module变量*/
144 .open = tq2440_buttons_open,
145 .release = tq2440_buttons_close,
146 .read = tq2440_buttons_read,
147 };
148
149 /* 初始化并注册cdev*/
150 static void buttons_cdev_setup(void)
151 {
152 int err;
153 cdev_init(buttons_cdev_p,&tq2440_buttons_fops);
154 buttons_cdev_p->owner = THIS_MODULE;
155 buttons_cdev_p->ops = &tq2440_buttons_fops;
156 err = cdev_add(buttons_cdev_p,dev_num,1);
157 if(IS_ERR(&err))
158 printk(KERN_NOTICE "Error %d adding buttons",err);
159 }
160 static int __init tq2440_buttons_init(void)
161 {
162 int ret;
163
164 /*注册字符设备驱动程序
165 *参数为主设备号,设备名字,file_operations结构体
166 *这样,主设备号就和具体的file_operations结构体联系起来了,
167 *操作主设备号为BUTTON_MAJOR的设备文件时,就会调用
168 *tq2440_buttons_fops中的相关成员函数,BUTTON_MAJOR可以设为0,
169 *表示由内核自动分配主设备号
170 */
171
172 if(BUTTON_MAJOR) //手动分配设备号
173 {
174 dev_num=MKDEV(BUTTON_MAJOR, BUTTON_MINOR);
175 ret=register_chrdev_region(dev_num,DEV_COUNT,DEVICE_NAME);
176 }
177 else { //动态分配设备号
178 ret = alloc_chrdev_region(&dev_num,BUTTON_MINOR,DEV_COUNT,DEVICE_NAME);
179 }
180 if(ret<0) {
181 printk(DEVICE_NAME " can't register major number \n");
182 return ret;
183 }
184 /*动态申请cdev结构体的内存*/
185 buttons_cdev_p = kmalloc(sizeof(struct cdev),GFP_KERNEL);
186 if(!buttons_cdev_p) //申请失败
187 {
188 ret = -ENOMEM;
189 goto fial_malloc;
190 }
191 memset(buttons_cdev_p,0,sizeof(struct cdev));
192 buttons_cdev_setup();
193 //注册一个类,使mdev可以在"/dev/"目录下面
194 //建立设备节点
195 button_class = class_create(THIS_MODULE,DEVICE_NAME);
196 if(IS_ERR(button_class))
197 {
198 printk("Error:Failed to creat button_class \n");
199 return -1;
200 }
201 //创建一个设备节点,节点名为DEVICE_NAME
202
203 device_create(button_class,NULL,dev_num,NULL,DEVICE_NAME);
204 printk(DEVICE_NAME " initialized \n");
205 return 0;
206 fial_malloc:unregister_chrdev_region(dev_num, 1);
207 }
208
209 /*这两行制定驱动程序的初始化函数和卸载函数*/
210 module_init(tq2440_buttons_init);
211 module_exit(tq2440_buttons_exit)
驱动测试程序
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/ioctl.h>
5
6 int main(int argc,char **argv)
7 {
8 int i;
9 int ret;
10 int fd;
11 int press_cnt[4];
12
13 fd=open("/dev/tq2440-buttons",0);
14 if(fd<0) {
15 printf("Can't open /dev/tq2440-buttons \n");
16 return -1;
17 }
18 //这是个无限循环,进程有可能在read函数中休眠,当有按键按下时,
19 //它才返回
20 while(1) {
21 ret = read(fd,press_cnt,sizeof(press_cnt));
22 if(ret<0) {
23 printf("read err !\n");
24 continue;
25 }
26 //如果被按下的次数不为0,打印出来
27 for(i=0;i<sizeof(press_cnt)/sizeof(press_cnt[0]);i++) {
28 if(press_cnt[i])
29 printf("Key%d has been pressed %d times \n",i+1,press_cnt[i]);
30 }
31
32 }
33
34 }
进入根文件系统dev目录,发现已经成功动分配设备,并自动创建文件节点
[nick@TQ2440 /dev]# ls -l tq*
crw-rw---- 1 0 0 232, 0 Jan 1 00:00 tq2440-adc
crw-rw---- 1 0 0 254, 0 Jan 1 00:00 tq2440-buttons
crw-rw---- 1 0 0 231, 0 Jan 1 00:00 tq2440-leds
crw-rw---- 1 0 0 204, 64 Jan 1 00:00 tq2440_serial0
crw-rw---- 1 0 0 204, 65 Jan 1 00:00 tq2440_serial1
crw-rw---- 1 0 0 204, 66 Jan 1 00:00 tq2440_serial2
下载内核后,运行测试程序
[nick@TQ2440 /app]# ./buttons_test &
查看中断已经注册成功
[nick@TQ2440 /proc]# cat interrupts
CPU0
16: 0 s3c-ext0 KEY1
17: 0 s3c-ext0 KEY2
18: 0 s3c-ext0 KEY3
30: 27496 s3c S3C2410 Timer Tick
32: 0 s3c s3c2410-lcd
43: 0 s3c s3c2440-i2c
48: 0 s3c-ext KEY4
51: 2587 s3c-ext eth0
70: 156 s3c-uart0 s3c2440-uart
71: 1839 s3c-uart0 s3c2440-uart
83: 0 - s3c2410-wdt
Err: 0
第一列表示中断号
第二列表示这个中断发生的次数
第三列表示这个中断的硬件访问结构“struct irq_chip *chip"的名字,它在初始化中断体系结构时指定
第四列表示中断的名称
这时按下按键,输出如下
IRQ:48
Key4 has been pressed 1 times
IRQ:16
Key1 has been pressed 1 times
IRQ:18
Key3 has been pressed 1 times
IRQ:48
Key4 has been pressed 1 times
IRQ:16
Key1 has been pressed 1 times
IRQ:17
Key2 has been pressed 1 times