实验环境: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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#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设备驱动,兴奋之余,知道以上内容肯定会有不足甚至错误之处,希望有兴趣的童鞋可以不吝赐教。