/****************************************************************************************
*Name: keyboard.c
*Author: Ma Dongpeng<madongpeng@hrbeu.edu.cn>
*Time: 2011-02-17 14:08:10
*Version: 1.0.0
*Description: keyborad driver for linux2.6.25
*****************************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#define DEVICE_NAME "keyboard" // 加载模式后,执行”cat /proc/devices”命令看到的设备名称
#define KEYBOARD_MAJOR 252 // 主设备号
#define KEY_NUM 4 //按键数目
#define KEYSTATUS_DOWNX 0
#define KEYSTATUS_UP 1
#define KEY_TIMER_DELAY 15 //以ms为单位,表示延时15ms
#define KEY_TIMER_DELAY1 100
#define KEY_TIMER_DELAY2 20
static int keyboard_major=KEYBOARD_MAJOR;
struct key_irq_desc
{
int irq; //中断号
int pin; //中断引脚
int pin_setting; //设置gpio功能
int number;
char *name;
};
// 用来指定按键所用的外部中断引脚及中断触发方式, 名字
static struct key_irq_desc key_irqs [] =
{
{IRQ_EINT1, S3C2410_GPF1, S3C2410_GPF1_EINT1, 0, "KEY1"}, /* K1 */
{IRQ_EINT4, S3C2410_GPF4, S3C2410_GPF4_EINT4, 1, "KEY2"}, /* K2 */
{IRQ_EINT2, S3C2410_GPF2, S3C2410_GPF2_EINT2, 2, "KEY3"}, /* K3 */
{IRQ_EINT0, S3C2410_GPF0, S3C2410_GPF0_EINT0, 3, "KEY4"}, /* K4 */
};
struct key_dev
{
struct cdev cdev;
char key_values[KEY_NUM]; //记录键值为1表示对应的按键被按下
unsigned int key_status[KEY_NUM]; //记录按键状态
wait_queue_head_t key_waitq; //等待队列
int key_values_flag; //记录是否有任何一个按键被按下
};
struct key_dev *key_devp;
static struct timer_list key_timer[KEY_NUM]; //按键的定时器
static char __initdata info[] = "******************keyborad Driver*****************\n";
static struct class *key_class;
/******************************************************************************************
*键盘中断处理程序
*******************************************************************************************/
static irqreturn_t key_interrupt(int irq, void *dev_id)
{
struct key_irq_desc *key_irqs = (struct key_irq_desc *)dev_id;
disable_irq(key_irqs->irq); //关中断进入查询模式
key_devp->key_status[key_irqs->number]=KEYSTATUS_DOWNX;
key_timer[key_irqs->number].expires=jiffies+KEY_TIMER_DELAY;
add_timer(&key_timer[key_irqs->number]); //启动定时器
return IRQ_RETVAL(IRQ_HANDLED);
}
/******************************************************************************************
*处理键盘事件,在key_timer_handler中被调用,记录键值唤醒等待队列
*******************************************************************************************/
static void key_event(int num)
{
//printk(KERN_ALERT "num=%d\n",num);
key_devp->key_values[num]=1;
key_devp->key_values_flag=1;
wake_up_interruptible(&key_devp->key_waitq); //唤醒等待队列
}
/******************************************************************************************
*定时器中断处理程序
*******************************************************************************************/
static void key_timer_handler(unsigned long data)
{
struct key_irq_desc *key_irqs = (struct key_irq_desc *)data;
if(s3c2410_gpio_getpin(key_irqs->pin)==KEYSTATUS_DOWNX) //键盘仍然属于按下状态
{
if(key_devp->key_status[key_irqs->number]==KEYSTATUS_DOWNX)
{
key_devp->key_status[key_irqs->number]=KEYSTATUS_UP;
key_timer[key_irqs->number].expires=jiffies+KEY_TIMER_DELAY1;//准备进入连按模式,延时较长
key_event(key_irqs->number); //记录键值唤醒等待队列
add_timer(&key_timer[key_irqs->number]); //启动定时器
}
else
{
key_timer[key_irqs->number].expires=jiffies+KEY_TIMER_DELAY2;//连按模式,延时较短
key_event(key_irqs->number); //记录键值唤醒等待队列
add_timer(&key_timer[key_irqs->number]); //启动定时器
}
}
else //键盘已经抬起
{
key_devp->key_status[key_irqs->number]=KEYSTATUS_UP;
enable_irq(key_irqs->irq);
}
}