实验环境:1)Ubuntu9.10 OS
2)arm-linux-gcc 4.3.2交叉编译器
3)勤研S3C2440开发板(Linux2.6.29内核)
实现的功能:键盘驱动通过input子系统将按键事件上报给上层的应用程序,应用程序将相应的按键事件打印出来
关于input子系统的相关知识可以参考博文《Linux设备模型之input子系统详解》
http://blogold.chinaunix.net/u1/51562/showart_1090628.html
废话不多说,附上源码:
1) s3c2440键盘驱动源代码,参考2.6.29内核中触摸屏驱动s3c2410_ts.c实现
/* * Copyright (c) 2011 Shen Yunlong * syl272365943@126.com * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/input.h> #include <linux/errno.h> #include <linux/irq.h> #include <asm/io.h> #include <mach/regs-gpio.h> #include <mach/irqs.h> #include <linux/interrupt.h> #include <linux/irqreturn.h> #include <mach/regs-irq.h> #include <linux/bitops.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("SYL_USST"); static struct input_dev *dev; static char *s3c2440_keyboard_name = "s3c2440_keyboard_syl"; /** * Set GPF0,GPF1,GPF2,GPF3 ports used as EINT[0],EINT[1],EINT[2],EINT[3] * Set GPG0,GPG1 used as EINT[8],EINT[9] */ static inline void s3c2440_keyboard_connect(void) { s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_EINT0); s3c2410_gpio_cfgpin(S3C2410_GPF1, S3C2410_GPF1_EINT1); s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2); s3c2410_gpio_cfgpin(S3C2410_GPF3, S3C2410_GPF3_EINT3); s3c2410_gpio_cfgpin(S3C2410_GPG0, S3C2410_GPG0_EINT8); s3c2410_gpio_cfgpin(S3C2410_GPG1, S3C2410_GPG1_EINT9); } /** * Interrupt Handlers of EINT[0]~EINT[3] & EINT[8] & EINT[9] * * Note: once the ISR has reported a key with a specified value, * then the next time it (the key with the same value) will be 'filtered'. * It means that the program in user-space can not get the input_event from the kernel. */ static irqreturn_t keyboard_sw1_action(int irq, void *dev_id) { // printk(KERN_INFO "sw1 is pressed !/n"); unsigned int updown = ioread32(S3C2410_GPFDAT) & 0x1; if(updown) /* the key BTN_0 is raised */ input_report_key(dev, BTN_0, 0); else /* the key BTN_0 is pressed*/ input_report_key(dev, BTN_0, 1); input_sync(dev); return IRQ_HANDLED; } static irqreturn_t keyboard_sw2_action(int irq, void *dev_id) { // printk(KERN_INFO "sw2 is pressed !/n"); unsigned int updown = ioread32(S3C2410_GPFDAT) & (0x1<<1); if(updown) input_report_key(dev, BTN_1, 0); else input_report_key(dev, BTN_1, 1); input_sync(dev); return IRQ_HANDLED; } static irqreturn_t keyboard_up_action(int irq, void *dev_id) { // printk(KERN_INFO "up is pressed !/n"); unsigned int updown = ioread32(S3C2410_GPFDAT) & (0x1<<2); if(updown) input_report_key(dev, BTN_2, 0); else input_report_key(dev, BTN_2, 1); input_sync(dev); return IRQ_HANDLED; } static irqreturn_t keyboard_down_action(int irq, void *dev_id) { // printk(KERN_INFO "down is pressed !/n"); unsigned int updown = ioread32(S3C2410_GPFDAT) & (0x1<<3); if(updown) input_report_key(dev, BTN_3, 0); else input_report_key(dev, BTN_3, 1); input_sync(dev); return IRQ_HANDLED; } static irqreturn_t keyboard_left_action(int irq, void *dev_id) { // printk(KERN_INFO "left is pressed !/n"); unsigned int updown = ioread32(S3C2410_GPGDAT) & 0x1; if(updown) input_report_key(dev, BTN_4, 0); else input_report_key(dev, BTN_4, 1); input_sync(dev); return IRQ_HANDLED; } static irqreturn_t keyboard_right_action(int irq, void *dev_id) { // printk(KERN_INFO "right is pressed !/n"); unsigned int updown = ioread32(S3C2410_GPGDAT) & (0x1<<1); if(updown) input_report_key(dev, BTN_5, 0); else input_report_key(dev, BTN_5, 1); input_sync(dev); return IRQ_HANDLED; } /** * s3c2440 my_keyboard module initialization */ static int __init s3c2440_keyboard_init(void) { printk("s3c2440 my_keyboard module start/n"); struct input_dev *input_dev; /* Configure S3C2440 GPIOs*/ s3c2440_keyboard_connect(); printk("GPIO has configured/n"); /* Set EINT0~EINT3 and EINT8,EINT9 rising edge triggered*/ set_irq_type(IRQ_EINT0, IRQ_TYPE_EDGE_BOTH); set_irq_type(IRQ_EINT1, IRQ_TYPE_EDGE_BOTH); set_irq_type(IRQ_EINT2, IRQ_TYPE_EDGE_BOTH); set_irq_type(IRQ_EINT3, IRQ_TYPE_EDGE_BOTH); set_irq_type(IRQ_EINT8, IRQ_TYPE_EDGE_BOTH); set_irq_type(IRQ_EINT9, IRQ_TYPE_EDGE_BOTH); input_dev = input_allocate_device(); if(!input_dev) { printk(KERN_ERR "Unable to allocate the input device!!/n"); return -ENOMEM; } dev = input_dev; dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY); /* Do Not forget to set keybit[] of BTN_0--BTN_5*/ dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0) | BIT_MASK(BTN_1) | BIT_MASK(BTN_2) | BIT_MASK(BTN_3) | BIT_MASK(BTN_4) | BIT_MASK(BTN_5); dev->name = s3c2440_keyboard_name; // dev->id.bustype // dev->id.vendor // dev->id.product // dev->id.version printk("input device has allocated/n"); /* Register an Interrupt Handler to IRQ_EINT0*/ if(request_irq(IRQ_EINT0, keyboard_sw1_action, IRQF_SAMPLE_RANDOM, "s3c2440_keyboard_action", dev)) { printk(KERN_ERR "my_keyboard.c: Could not allocate keyboard IRQ_EINT0 !/n"); return -EIO; } if(request_irq(IRQ_EINT1, keyboard_sw2_action, IRQF_SAMPLE_RANDOM, "s3c2440_keyboard_action", dev)) { printk(KERN_ERR "my_keyboard.c: Could not allocate keyboard IRQ_EINT1 !/n"); return -EIO; } if(request_irq(IRQ_EINT2, keyboard_up_action, IRQF_SAMPLE_RANDOM, "s3c2440_keyboard_action", dev)) { printk(KERN_ERR "my_keyboard.c: Could not allocate keyboard IRQ_EINT2 !/n"); return -EIO; } if(request_irq(IRQ_EINT3, keyboard_down_action, IRQF_SAMPLE_RANDOM, "s3c2440_keyboard_action", dev)) { printk(KERN_ERR "my_keyboard.c: Could not allocate keyboard IRQ_EINT3 !/n"); return -EIO; } if(request_irq(IRQ_EINT8, keyboard_left_action, IRQF_SAMPLE_RANDOM, "s3c2440_keyboard_action", dev)) { printk(KERN_ERR "my_keyboard.c: Could not allocate keyboard IRQ_EINT8 !/n"); return -EIO; } if(request_irq(IRQ_EINT9, keyboard_right_action, IRQF_SAMPLE_RANDOM, "s3c2440_keyboard_action", dev)) { printk(KERN_ERR "my_keyboard.c: Could not allocate keyboard IRQ_EINT9 !/n"); return -EIO; } printk(KERN_INFO "%s successfully loaded/n", s3c2440_keyboard_name); input_register_device(dev); return 0; } /** * s3c2440 my_keyboard module exit function */ static void __exit s3c2440_keyboard_exit(void) { disable_irq(IRQ_EINT0); disable_irq(IRQ_EINT1); disable_irq(IRQ_EINT2); disable_irq(IRQ_EINT3); disable_irq(IRQ_EINT8); disable_irq(IRQ_EINT9); free_irq(IRQ_EINT0, dev); free_irq(IRQ_EINT1, dev); free_irq(IRQ_EINT2, dev); free_irq(IRQ_EINT3, dev); free_irq(IRQ_EINT8, dev); free_irq(IRQ_EINT9, dev); input_unregister_device(dev); } module_init(s3c2440_keyboard_init); module_exit(s3c2440_keyboard_exit);
2) 一个简单的上层应用程序,通过读取键盘对应的设备文件将input子系统上报的键盘事件打印出来
/* * Copyright (c) 2011 Shen Yunlong * syl272365943@126.com * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/select.h> #include <sys/time.h> #include <linux/input.h> #define MY_DEBUG 0 int main() { char *dev = "/dev/event1"; struct input_event ievent; int retval; unsigned short type; unsigned short code; int value; int fd = open(dev, O_RDWR | O_NONBLOCK); if(fd < 0) { printf("open file %s failed/n", dev); return -1; } while(1) { retval = read(fd, &ievent, sizeof(struct input_event)); if(retval > 0) { type = ievent.type; code = ievent.code; value = ievent.value; #if MY_DEBUG printf("%d %d %d/n", type, code, value); #endif if(type == EV_KEY) { switch(code) { case BTN_0: if(value == 1) printf("sw1 is pressed/n"); break; case BTN_1: if(value == 1) printf("sw2 is pressed/n"); break; case BTN_2: if(value == 1) printf("up is pressed/n"); break; case BTN_3: if(value == 1) printf("down is pressed/n"); break; case BTN_4: if(value == 1) printf("left is pressed/n"); break; case BTN_5: if(value == 1) printf("right is pressed/n"); break; } } } } close(fd); return 0; }
以上代码在勤研2440的开发板上测试成功!
这是鄙人第一个完成的linux设备驱动,兴奋之余,知道以上内容肯定会有不足甚至错误之处,希望有兴趣的童鞋可以不吝赐教。