硬件:TQ2440
开发环境:Fedora 10
2、增加外部中断,实现按下按键后有信息打印
key_driver.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#define DEVICE_NUM_NAME "TQ2440_BUTTON"
#define DEVICE_COUNT 1
#define DEVICE_CLASS "tq2440_button_class"
#define DEVICE_NAME "tq2440_button_device"
#define MIN(A, B) (A) > (B) ? (B) : (A)
dev_t devnum;
static struct cdev *cdev_button;
static struct class *button_class;
static struct device *button_device;
static DECLARE_WAIT_QUEUE_HEAD(button_wait_queue);
static volatile int ev_press = 0;
static volatile int press_cnt[] = {0, 0, 0, 0};
static volatile int press_id[] = {0, 0, 0, 0};
struct button_irqs_desc {
int irq;
unsigned long flags;
char *name;
};
static struct button_irqs_desc button_irqs[] = {
{IRQ_EINT1, IRQ_TYPE_EDGE_FALLING, "KEY1"},
{IRQ_EINT4, IRQ_TYPE_EDGE_FALLING, "KEY2"},
{IRQ_EINT0, IRQ_TYPE_EDGE_FALLING, "KEY3"},
{IRQ_EINT2, IRQ_TYPE_EDGE_FALLING, "KEY4"},
};
static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
volatile int *press_cnt1 = (volatile int *)dev_id;
*press_cnt1 += 1;
ev_press = 1;
switch (irq) {
case IRQ_EINT1:
press_id[0] = 1;
break;
case IRQ_EINT4:
press_id[1] = 1;
break;
case IRQ_EINT0:
press_id[2] = 1;
break;
case IRQ_EINT2:
press_id[3] = 1;
break;
default:
break;
}
wake_up_interruptible(&button_wait_queue);
printk("IRQ:%d\n", irq);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int button_open(struct inode *inode,struct file *file)
{
int i;
int err;
for (i = 0; i < sizeof(button_irqs) / sizeof(button_irqs[0]); i++) {
err = request_irq(button_irqs[i].irq, buttons_interrupt, \
button_irqs[i].flags, button_irqs[i].name, (void *)&press_cnt[i]);
if (err)
break;
}
if (err) {
i--;
for (; i >= 0; i--)
free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
printk("button_open request_irq failed\n");
return -EBUSY;
}
printk("button_open succeed\n");
return 0;
}
static int button_close(struct inode *inode,struct file *file)
{
int i;
for (i = 0; i < sizeof(button_irqs) / sizeof(button_irqs[0]); i++)
free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
printk("button_close succeed\n");
return 0;
}
static int button_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
int i;
unsigned long err;
/*如果ev_press等于0,休眠*/
wait_event_interruptible(button_wait_queue, ev_press);
ev_press = 0;
for (i = 0; i < 4; i++) {
if (press_id[i] == 1) {
err = copy_to_user(buff, (const void *)press_id, sizeof(press_id));
press_id[i] = 0;
return err ? -EFAULT : 0;
}
}
//memset((void *)press_cnt,0,sizeof(press_cnt));
return -EFAULT;
}
static struct file_operations button_op = {
.owner = THIS_MODULE,
.open = button_open,
.release = button_close,
.read = button_read,
};
static int __init button_init(void)
{
int ret;
ret = alloc_chrdev_region(&devnum, 0, DEVICE_COUNT, DEVICE_NUM_NAME);
if (ret < 0) {
printk("%s\t" DEVICE_NAME "\tmajor number can't register\n", __func__);
return ret;
}
printk("%s\t" DEVICE_NAME "\tmajor number:%u:%u registered\n", \
__func__, MAJOR(devnum), MINOR(devnum));
cdev_button = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (!cdev_button) {
ret = -ENOMEM;
goto fail_kmalloc;
}
cdev_init(cdev_button, &button_op);
cdev_button->owner = THIS_MODULE;
ret = cdev_add(cdev_button, devnum, 1);
if (IS_ERR(&ret)) {
printk("%s\t" "add cdev err!\n", __func__);
goto fail_cdev_add;
}
button_class = class_create(THIS_MODULE, DEVICE_CLASS);
if (IS_ERR(button_class)) {
printk("%s\t" "class create err!\n", __func__);
goto fail_class_create;
}
button_device = device_create(button_class, NULL, devnum, NULL, DEVICE_NAME);
if (IS_ERR(button_device)) {
printk("%s\t" "device create err!\n", __func__);
goto fail_device_create;
}
printk("device_create succeed!\n");
return 0;
fail_device_create:
class_destroy(button_class);
fail_class_create:
cdev_del(cdev_button);
fail_cdev_add:
kfree(cdev_button);
fail_kmalloc:
unregister_chrdev_region(devnum, DEVICE_COUNT);
return ret;
}
static void __exit button_exit(void)
{
device_destroy(button_class, devnum);
class_destroy(button_class);
cdev_del(cdev_button);
kfree(cdev_button);
unregister_chrdev_region(devnum, DEVICE_COUNT);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
Makefile:
KERN_DIR=/opt/EmbedSky/linux-2.6.30.4
all:
make -C $(KERN_DIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko *.mod.c *.markers *.order *.symvers
obj-m += key_driver.o
编译:
[root@EmbedSky key]# make
make -C /opt/EmbedSky/linux-2.6.30.4 M=/opt/tq2440_key/TQ2440_key_zhongduan/key modules
make[1]: Entering directory `/opt/EmbedSky/linux-2.6.30.4'
CC [M] /opt/tq2440_key/TQ2440_key_zhongduan/key/key_driver.o
Building modules, stage 2.
MODPOST 1 modules
LD [M] /opt/tq2440_key/TQ2440_key_zhongduan/key/key_driver.ko
make[1]: Leaving directory `/opt/EmbedSky/linux-2.6.30.4'
[root@EmbedSky key]#
测试程序key_app.c:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
int main(int argc, char *argv)
{
int fd;
int ret;
int press_id[4] = {0, 0, 0, 0};
int i;
fd = open("/dev/tq2440_button_device", O_RDWR);
if (fd < 0) {
printf("can't open!\n");
return 0;
} else {
printf("open successed!\n");
}
while (1) {
ret = read(fd, press_id, sizeof(press_id));
if (ret < 0) {
printf("read err\n");
break;
}
for (i = 0; i < 4; i++) {
if (press_id[i] == 1) {
printf("KEY%d has pressed\n", i);
break;
}
}
}
return 0;
}
编译测试程序:
[root@EmbedSky key]# arm-linux-gcc -o key_app key_app.c
[root@EmbedSky key]#
在TQ2440开发板上测试:
[root@EmbedSky /opt]# insmod key_driver.ko
button_init tq2440_button_device major number:252:0 registered
device_create succeed!
[root@EmbedSky /opt]# chmod 777 key_app
[root@EmbedSky /opt]# ./key_app
button_open succeed
open successed!
IRQ:17
IRQ:17
IRQ:17
IRQ:17
IRQ:17
KEY0 has pressed
IRQ:17
KEY0 has pressed
IRQ:17
KEY0 has pressed
IRQ:17
KEY0 has pressed
IRQ:16
KEY2 has pressed
IRQ:16
KEY2 has pressed
IRQ:48
KEY1 has pressed
IRQ:48
KEY1 has pressed
IRQ:18
KEY3 has pressed
IRQ:18
KEY3 has pressed
IRQ:18
KEY3 has pressed
IRQ:18
KEY3 has pressed
IRQ:18
IRQ:18
KEY3 has pressed
IRQ:18