今天终于将按键驱动改写成input驱动并测试成功了。下面记录下过程吧。 1、建立文件夹InputKey 2、在文件夹InputKey下建立驱动程序inputDev.c。 #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/irq.h> #include <asm/uaccess.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/platform_device.h> #include <linux/cdev.h> #include <linux/miscdevice.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/input.h> MODULE_AUTHOR("wjb"); MODULE_LICENSE("GPL"); struct input_dev* button_devp; struct button_irq_desc{ int irq; int pin; int number; char *name; }; static struct button_irq_desc button_irqs[] = { {IRQ_EINT8, S3C2410_GPG(0), 0, "KEY0"}, {IRQ_EINT11, S3C2410_GPG(3), 1, "KEY1"}, {IRQ_EINT13, S3C2410_GPG(5), 2, "KEY2"}, {IRQ_EINT15, S3C2410_GPG(6), 3, "KEY3"}, {IRQ_EINT14, S3C2410_GPG(7), 4, "KEY4"}, {IRQ_EINT19, S3C2410_GPG(11), 5, "KEY5"}, }; static irqreturn_t button_interrupt(int irq, void* dev_id) { struct button_irq_desc *button_irqs = (struct button_irq_desc*)dev_id; int down; down = !s3c2410_gpio_getpin(button_irqs->pin); switch(button_irqs->irq) { case IRQ_EINT8: input_report_key(button_devp, BTN_0, down); input_sync(button_devp); break; case IRQ_EINT11: input_report_key(button_devp, BTN_1, down); input_sync(button_devp); break; case IRQ_EINT13: input_report_key(button_devp, BTN_2, down); input_sync(button_devp); break; case IRQ_EINT15: input_report_key(button_devp, BTN_3, down); input_sync(button_devp); break; case IRQ_EINT14: input_report_key(button_devp, BTN_4, down); input_sync(button_devp); break; case IRQ_EINT19: input_report_key(button_devp, BTN_5, down); input_sync(button_devp); break; default: break; } return IRQ_RETVAL(IRQ_HANDLED); } static int __init button_init(void) { int i; int err; button_devp = input_allocate_device(); for(i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) { if(button_irqs[i].irq < 0) { continue; } err = request_irq(button_irqs[i].irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]); if(err) { break; } } if(err) { i--; for(; i >= 0; i--) { if(button_irqs[i].irq < 0) { continue; } disable_irq(button_irqs[i].irq); free_irq(button_irqs[i].irq, (void*)&button_irqs[i]); } return -EBUSY; } set_bit(EV_KEY, button_devp->evbit); set_bit(BTN_0, button_devp->keybit); set_bit(BTN_1, button_devp->keybit); set_bit(BTN_2, button_devp->keybit); set_bit(BTN_3, button_devp->keybit); set_bit(BTN_4, button_devp->keybit); set_bit(BTN_5, button_devp->keybit); input_register_device(button_devp); return 0; } static void __exit button_exit(void) { int i = 5; input_unregister_device(button_devp); for(i = 5; i >= 0; i--) { if(button_irqs[i].irq < 0) { continue; } disable_irq(button_irqs[i].irq); free_irq(button_irqs[i].irq, (void*)&button_irqs[i]); } } module_init(button_init); module_exit(button_exit); 这里有两点要注意: 1)button_init函数一定要返回一个值。不然虽然能编译通过,但是运行会有问题。 2)中断处理函数返回值不能是void型。据说这个是2.6内核和2.4内核的区别。同样,如果返回值为void,编译也没问题,运行会有bad_irq字样的错误。 3、Makefile ifneq ($(KERNELRELEASE), ) obj-m := inputDev.o else KDIR := /usr/src/linux-2.6.32.2 all: make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers endif 4、make 运行make命令,会生成inputDev.ko。 5、编写应用程序appInputDev.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdint.h> #include <sys/ioctl.h> #include <sys/fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <linux/input.h> /* struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; };*/ int main(void) { struct input_event ev_key; int fd = open("/dev/input/event1", O_RDWR); while(1) { int count = read(fd, &ev_key, sizeof(struct input_event)); if(EV_KEY == ev_key.type&&ev_key.code) { printf("time : %ld, %d", ev_key.time.tv_sec, ev_key.time.tv_usec); printf(" type:%d code : %d value : %d \n", ev_key.type, ev_key.code, ev_key.value); } } return 0; } 注意:此处打开的文件是/dev/input/event1,之所以是event1,而不是event0,或者event2等等,主要是我发现系统之前已经有一个/dev/input/event0,而添加了这个设备模块后,/dev/input/event1就多出来了。但是如果我们有多个input设备同时注册,那么具体该打开的文件是哪个,我还有点不懂。 6、生成应用程序 使用命令arm-linux-gcc -o appInput appInputDev.c,将会生成应用程序appInput。 7、测试 将inputDev.ko和appInput拷贝到开发板。 1)使用insmod inputDev.ko将设备注册。 这时候,会发现/dev/input/下多出一个event1,用cat /dev/bus/input/devices,会发现一个新的虚拟设备/devices/virtual/input/input1已经存在了,其handler正是event1。(如果使用rmmod inputDev将inputDev设备模块移掉,然后再insmod inputDev.ko,会发现新的虚拟设备变成/devices/virtual/input/input2了,但是handler仍旧是event1;继续多删除模块添加模块几次,/devices/virtual/input/input*的数值会继续增长。) 2)执行./appInput,运行appInput。然后按下任意一个按键,会发现有输出: time : 1293632357, 846429 type:1 code : 257 value : 1
浙公网安备 33010602011771号