按键驱动(浏览按键)

来源:互联网 发布:游戏公司防止源码外泄 编辑:程序博客网 时间:2024/05/20 12:47
 

//#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/spinlock.h>
#include <linux/irq.h>

#include <asm/hardware.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/regs-gpio.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/irqs.h>
#include <asm-arm/irq.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <asm/hardware.h>
#include <asm/arch/regs-gpio.h>
#define DEVICE_NAME "button"
#define MAX_KEY_COUNT  32         
#define EXTINT0 *(volatile unsigned int *)S3C2410_EXTINT0
#define EXTINT1 *(volatile unsigned int *)S3C2410_EXTINT1
#define EXTINT2 *(volatile unsigned int *)S3C2410_EXTINT2


MODULE_LICENSE("GPL");//模块应该指定代码所使用的许可证

typedef struct
{
 unsigned long jiffy[MAX_KEY_COUNT];  //按键时间, 如果读键时, 5秒钟以前的铵键作废
 unsigned char buf[MAX_KEY_COUNT];  //按键缓冲区
 unsigned int head,tail;     //按键缓冲区头和尾
}KEY_BUFFER;

static KEY_BUFFER g_keyBuffer;  //键盘缓冲区
static spinlock_t buffer_lock;  //缓冲区锁

static int   button_major = 255;    //Define device major add by yoyo

static void *gpecon;
static void *gpedat;
static void *gpfcon;
static void *gpfdat;
static void *gpgcon;
static void *gpgdat;

/*
 *功能: 获取当前的毫秒数(从系统启动开始)
 *入口: 
 */
static unsigned long GetTickCount(void)
{
 struct timeval currTick;
 unsigned long ulRet;

 do_gettimeofday(&currTick);
 ulRet = currTick.tv_sec;
 ulRet *= 1000;
 ulRet += (currTick.tv_usec + 500) / 1000;
 return ulRet;
}

/*
 *功能: 初始化键盘缓冲区
 *入口: 
 */
static void init_keybuffer(void)
{
 int i;
 spin_lock_irq(&buffer_lock); //获得一个自旋锁具有不会受中断的干扰
 g_keyBuffer.head = 0;
 g_keyBuffer.tail = 0;
 for(i = 0; i < MAX_KEY_COUNT; i++)
 {
  g_keyBuffer.buf[i] = 0;
  g_keyBuffer.jiffy[i] = 0;
 }
 spin_unlock_irq(&buffer_lock);//释放自旋锁
}

/*
 *功能: 删除过时(5秒前的按键值)
 *入口: 
 */
static void remove_timeoutkey(void)
{
 unsigned long ulTick;

 spin_lock_irq(&buffer_lock); //获得一个自旋锁具有不会受中断的干扰
 while(g_keyBuffer.head != g_keyBuffer.tail)
 {
  ulTick = GetTickCount() - g_keyBuffer.jiffy[g_keyBuffer.head];
  if (ulTick  < 5000) //5秒
   break;
  g_keyBuffer.buf[g_keyBuffer.head] = 0;
  g_keyBuffer.jiffy[g_keyBuffer.head] = 0;
  g_keyBuffer.head ++;
  g_keyBuffer.head &= (MAX_KEY_COUNT -1);
 }
 spin_unlock_irq(&buffer_lock);//释放自旋锁
}

/*
 *功能: 初始化GPIO, 设置中断0, 2, 11, 19为下降沿中断
 *入口: 
 */
static void init_gpio(void)
{
 //将GPE13 11 设置低位
 writel((readl(gpecon) | ((3<<26)|(3<<22))) & (~((1<<27)|(1<<23))), gpecon); //GPE13,11 设置为输出
 writel(readl(gpedat) & 0xffffd7ff, gpedat);         //GPE13,11 输出为0

 //将GPG6, 2 设置低位
 writel((readl(gpgcon) | 0x3030) & 0xffffdfdf, gpgcon); //GPG6,2 设置为输出
 writel(readl(gpgdat) & 0xffffffbb, gpgdat);    //GPG6,2 输出为0

 writel((readl(gpfcon) | 0x33) & 0xffffffee, gpfcon);      //GPF2, 0 设置为中断
 writel((readl(gpgcon) | (3<<22) | (3<<6)) & (~((1<<22) | (1<<6))), gpgcon); //GPG11,3 设置为中断

 set_irq_type(IRQ_EINT0, IRQT_FALLING);  //下降沿触发(设置中断类型)
        EXTINT0=(EXTINT0&(~0x07))+0x02;
       
 set_irq_type(IRQ_EINT2, IRQT_FALLING);
         EXTINT0=(EXTINT0&(~(0x07<<8)))+(0x02<<8);
 
 set_irq_type(IRQ_EINT11, IRQT_FALLING);
         EXTINT1=(EXTINT1&(~(0x07<<12)))+(0x02<<12);
        
 set_irq_type(IRQ_EINT19, IRQT_FALLING);
         EXTINT2=(EXTINT2&(~(0x07<<12)))+(0x02<<12);

}

/*
 *功能: 激活中断
 *入口: 
 */
static __inline void enable_irqs(void)
{
 enable_irq(IRQ_EINT0);
 enable_irq(IRQ_EINT2);
 enable_irq(IRQ_EINT11);
 enable_irq(IRQ_EINT19);
}

/*
 *功能: 屏蔽中断
 *入口: 
 */
static __inline void disable_irqs(void)
{
 disable_irq(IRQ_EINT0);
 disable_irq(IRQ_EINT2);
 disable_irq(IRQ_EINT11);
 disable_irq(IRQ_EINT19);
}

/*
 *功能: 进入中断后, 扫描铵键码
 *入口: 
 *返回: 按键码(1-16), 0xff表示错误
 */
static __inline unsigned char button_scan(int irq)
{
 long lGPF, lGPG;      
  writel((readl(gpfcon) | 0x33) & 0xffffffcc, gpfcon);                            //GPF2,0 input
  writel((readl(gpgcon) | (3<<22) | (3<<6)) & (~((3<<22) | (3<<6))), gpgcon);     //GPG11,3 input

  //不利用irq号, 直接扫描键盘
 //设置G2低位, G6, E11, E13高位
 writel((readl(gpgdat) | (1<<6)) & (~(1<<2)), gpgdat);
 writel(readl(gpedat) | (1<<11) | (1<<13), gpedat);
 //取GPF0, GPF2, GPG3, GPG11的值
 lGPF = readl(gpfdat);
 lGPG = readl(gpgdat);
 //判断按键
 if ((lGPF & (1<<0)) == 0)  return 16;
 else if((lGPF & (1<<2)) == 0) return 15;
 else if((lGPG & (1<<3)) == 0) return 14;
 else if((lGPG & (1<<11)) == 0) return 13;

 //设置G6低位, G2, E11, E13高位
 writel((readl(gpgdat) | (1<<2)) & (~(1<<6)), gpgdat);
 lGPF = readl(gpfdat);
 lGPG = readl(gpgdat);
 if ((lGPF & (1<<0)) == 0)  return 11;
 else if((lGPF & (1<<2)) == 0) return 8;
 else if((lGPG & (1<<3)) == 0) return 5;
 else if((lGPG & (1<<11)) == 0) return 2;

 //设置E11低位, G2, G6, E13高位
 writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);
 writel((readl(gpedat) | (1<<13)) & (~(1<<11)), gpedat);
 lGPF = readl(gpfdat);
 lGPG = readl(gpgdat);
 if ((lGPF & (1<<0)) == 0)  return 10;
 else if((lGPF & (1<<2)) == 0) return 7;
 else if((lGPG & (1<<3)) == 0) return 4;
 else if((lGPG & (1<<11)) == 0) return 1;

 //设置E13低位, G2, G6, E11高位
 //writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);
 writel((readl(gpedat) | (1<<11)) & (~(1<<13)), gpedat);
 lGPF = readl(gpfdat);
 lGPG = readl(gpgdat);
 if ((lGPF & (1<<0)) == 0)  return 12;
 else if((lGPF & (1<<2)) == 0) return 9;
 else if((lGPG & (1<<3)) == 0) return 6;
 else if((lGPG & (1<<11)) == 0) return 3;

 
 return 0xff ;
}

/*
 *功能: 中断函数,
 *入口: irq 中断号
 *
 */
 
 //struct pt_regs:This struct defines the way the registers are stored on the
 //stack during a system call
static irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
{
 unsigned char ucKey;
 disable_irqs();
        printk("in irq\n");
 //延迟50毫秒, 屏蔽按键毛刺
 __udelay(50000);
 ucKey = button_scan(irq);
 if ((ucKey >= 1) && (ucKey <= 16))
 {
  //如果缓冲区已满, 则不添加
  if (((g_keyBuffer.head + 1) & (MAX_KEY_COUNT - 1)) != g_keyBuffer.tail)
  {
   spin_lock_irq(&buffer_lock);
   g_keyBuffer.buf[g_keyBuffer.tail] = ucKey;
   g_keyBuffer.jiffy[g_keyBuffer.tail] = GetTickCount();
   g_keyBuffer.tail ++;
   g_keyBuffer.tail &= (MAX_KEY_COUNT -1);
   spin_unlock_irq(&buffer_lock);
  }
 }
 init_gpio();
 enable_irqs();
 //printk("in irq! %x\n",EXTINT0);

 return IRQ_HANDLED;//2.6内核返回值一般是这个宏。
}

/*
 *功能: 申请中断
 *入口: 
 *
 */
static  int request_irqs()
{
 int ret;
 ret = request_irq(IRQ_EINT0, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
 if (ret < 0)
  return ret;
 ret = request_irq(IRQ_EINT2, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
    if (ret >= 0)
 {
     ret = request_irq(IRQ_EINT11, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
  if (ret >= 0)
  {
      ret = request_irq(IRQ_EINT19, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
   if (ret >= 0)
    return ret;
   free_irq(IRQ_EINT11, button_irq);
  }
  free_irq(IRQ_EINT2, button_irq);
 }
 free_irq(IRQ_EINT0, button_irq);
 return ret;
}

/*
 *功能: 释放中断
 *入口: 
 *
 */
static __inline void free_irqs()
{
 free_irq(IRQ_EINT0, NULL);//button_irq);
 free_irq(IRQ_EINT2, NULL);//button_irq);
 free_irq(IRQ_EINT11, NULL);//button_irq);
 free_irq(IRQ_EINT19, NULL);//button_irq);
}

/*
 *功能: 打开文件, 开始中断
 *入口: 
 *
 */
static int button_open(struct inode *inode,struct file *filp)
{
 int ret = nonseekable_open(inode, filp);
 if (ret >= 0)
 {
  init_keybuffer();
  enable_irqs();
 }
 return ret;
}

/*
 *功能: 关闭文件, 屏蔽中断
 *入口: 
 *
 */
static int button_release(struct inode *inode,struct file *filp)
{
    disable_irqs();
    return 0;
}

/*
 *功能: 读键盘
 *入口: 
 *
 */
static ssize_t button_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
 ssize_t ret = 0;

 remove_timeoutkey();
 spin_lock_irq(&buffer_lock);
 while((g_keyBuffer.head != g_keyBuffer.tail) && (((size_t)ret) < count) )
 {
  buffer[ret] = (char)(g_keyBuffer.buf[g_keyBuffer.head]);
  g_keyBuffer.buf[g_keyBuffer.head] = 0;
  g_keyBuffer.jiffy[g_keyBuffer.head] = 0;
  g_keyBuffer.head ++;
  g_keyBuffer.head &= (MAX_KEY_COUNT -1);
  ret ++;
 }
 spin_unlock_irq(&buffer_lock);
 return ret;
}

/*
 *功能: 清空键盘缓冲区
 *入口: 
 *
 */
static int button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
 init_keybuffer();
 return 1;
}
//add by yoyo
/*
 *初始化并添加结构提struct cdev到系统之中
 */
static void led_setup_cdev(struct cdev *dev,int minor,struct file_operations *fops)
{
 int err;
 int devno=MKDEV(button_major,minor);
 cdev_init(dev,fops);//初始化结构体struct cdev
 dev->owner=THIS_MODULE;
 dev->ops=fops;//给结构体里的ops成员赋初值,这里是对设备操作的具体的实现函数
 err=cdev_add(dev,devno,1);//将结构提struct cdev添加到系统之中
 if(err)
 printk(KERN_INFO"Error %d adding button %d\n",err,minor);
}
/*
 *定义一个file_operations结构体,来实现对设备的具体操作的功能
 */
static struct file_operations button_fops =
{
 .owner = THIS_MODULE,
 .ioctl = button_ioctl,
 .open = button_open,
 .read = button_read,
 .release = button_release,
};

static struct cdev SimpleDevs;  //add by yoyo

/*
 *功能: 驱动初始化
 *入口: 
 *
 */

static int  button_init(void)
{
 int ret;
  int result; //add by yoyo
 gpecon = ioremap(0x56000040, 0x04);//得到相应IO口的虚拟地址,下同
 gpedat = ioremap(0x56000044, 0x04);
 gpfcon = ioremap(0x56000050, 0x04);
 gpfdat = ioremap(0x56000054, 0x04);
 gpgcon = ioremap(0x56000060, 0x04);
 gpgdat = ioremap(0x56000064, 0x04);

 init_gpio();
 ret = request_irqs();
 if (ret < 0) return ret;

 disable_irqs();
 
 //add by yoyo
 
 dev_t dev=MKDEV(button_major,0);//将主设备号和次设备号定义到一个dev_t数据类型的结构体之中
 if(button_major)
  result=register_chrdev_region(dev,1,"button");//静态注册一个设备,设备号先前指定好,并得到一个设备名,cat /proc/device来查看信息
 else
  {
  result=alloc_chrdev_region(&dev,0,1,"button");//如果主设备号被占用,则由系统提供一个主设备号给设备驱动程序
  button_major=MAJOR(dev);//得到主设备号
  }
 if(result<0)
  {
   printk(KERN_WARNING"button:unable to get major %d\n",button_major);  
   return result;
  }
 if(button_major==0)
 button_major=result;//如果静态分配失败。把动态非配的设备号给设备驱动程序
 printk(KERN_INFO"button register ok!!!!!!!!!!\n");
 
 led_setup_cdev(&SimpleDevs,0,&button_fops);//初始化和添加结构体struct cdev到系统之中
 //return 0;
 printk("button initialized.\n");
 return 0;
}

/*
 *功能: 驱动释放
 *入口: 
 *
 */
static void __exit button_exit(void)
{
 disable_irqs();
 free_irqs();

 iounmap(gpecon);
 iounmap( gpedat);
 iounmap(gpfcon);
 iounmap(gpfdat);
 iounmap(gpgcon);
        iounmap(gpgdat);

 

 cdev_del(&SimpleDevs);//删除结构体struct cdev
          printk("button_major=%d\n",button_major);
 unregister_chrdev_region(MKDEV(button_major,0),1);//卸载设备驱动所占有的资源
 printk("button device uninstalled\n");

}

module_init(button_init);//初始化设备驱动程序的入口
module_exit(button_exit);//卸载设备驱动程序的入口

 

 应用编程:

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

main()
{
 int fd;
 char key=0;
 fd = open("/dev/button_scan", O_RDWR);//打开设备
 if (fd == -1)
 {
  printf("open device button errr!\n");
  return 0;
 }

 ioctl(fd,0,0); //清空键盘缓冲区, 后面两个参数没有意义,
 while(key != 16)
 { 
 
  if (read(fd, &key, 1) > 0)//读键盘设备,得到相应的键值
  {
   printf("*********************Key Value = %d*****************************\n", key);
  }
 }
 close(fd);//     //关闭设备
 return 0;
}

 

 

网上资料:

/*
1、阻塞读取,队列概念
2、睡眠等待中断产生
3、定时器产生及相应函数
疑问:去除按键缓冲 
*/

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-irq.h>
#define KEYSTATUS_DOWNX 2                   // 定义按键不确定状态
#define KEYSTATUS_DOWN 0                    // 定义按键按下后电平
#define KEYSTATUS_UP 1                      // 定义按键抬起后电平
#define BUF_CLEAR _IO(0xFF, 0)              // 清除键盘缓冲区命令

#define DEVICE_NAME "utukey"
#define MAX_KEY_BUF 16      // 按键缓冲区大小
#define KEY_NUM  6          // 按键个数
#define BUF_HEAD (utukey_dev.buf[utukey_dev.head]) //缓冲头
#define BUF_TAIL (utukey_dev.buf[utukey_dev.tail]) //缓冲尾
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(key_info_tab[key].gpio_port) == KEYSTATUS_DOWN)
#define INCBUF(x,mod) ((++(x))&((mod)-1))
#define KEY_TIME_DELAY (HZ/10)      // 100ms
#define KEY_TIME_DELAY1 (HZ/100)    // 10ms

#define UTUKEY_MAJOR 0                      // 定义0使用自动分配设备号
unsigned int utukey_major = UTUKEY_MAJOR;
/* 定义按键设备结构体 */
struct utukey_dev
{
    struct cdev cdev;                       // cdev结构体
    unsigned int key_status[KEY_NUM];       // 记录按键状态
    unsigned int buf[MAX_KEY_BUF];          // 按键环形缓冲区
    unsigned int head,  tail;               // 按键缓冲区头和尾
    wait_queue_head_t wq;                   // 等待队列
};
struct utukey_dev utukey_dev;               // 定义设备结构体
struct utukey_dev *utukey_devp;             // 定义设备结构体指针
struct timer_list key_timer[KEY_NUM];       // 定义6个按键去抖动定时器
static struct key_info                      // 定义按键所用资源结构体
{
    int irq_no;                             // 占用的中断号
    int irq_type;                           // 中断类型
    unsigned int gpio_port;                 // 占用的引脚
    unsigned int gpio_setting;              // 引脚设置值
    int key_code;                           // 按键值
    char *name;                             // 按键的对应字符串
}key_info_tab[] =
{
    {
        IRQ_EINT11, IRQT_FALLING, S3C2410_GPG3, S3C2410_GPG3_INP, 1, "Key Up" //下降沿触发
    },
    {
        IRQ_EINT0, IRQT_FALLING, S3C2410_GPF0, S3C2410_GPF0_INP, 2, "Key Down"
    },
    {
        IRQ_EINT19, IRQT_FALLING, S3C2410_GPG11,S3C2410_GPG11_INP, 3, "Key Left"
    },
    {
        IRQ_EINT2, IRQT_FALLING, S3C2410_GPF2,S3C2410_GPF2_INP, 4, "Key Right"
    },
    {
        IRQ_EINT6, IRQT_FALLING, S3C2410_GPF6,S3C2410_GPF6_INP, 5, "Key Enter"
    },
    {
        IRQ_EINT5, IRQT_FALLING, S3C2410_GPF5,S3C2410_GPF5_INP, 6, "Key Exit"
    },
};

/* 按键中断服务程序 */
static irqreturn_t utukey_irq(int irq, void *dev_id)
{
    int key = (int)dev_id;      // 传递key_info_tab[]索引
    int i;
    int found = 0;
   
    for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)   // 查找产生中断的按键
    {
        if (key_info_tab[i].irq_no == irq)
  {
            found = 1;
            break;
        }
    }
    if (!found)                                     // 没找到
    {
        printk(KERN_NOTICE"bad irq %d in button\n", irq);
        return IRQ_NONE;   //错误的中断
    }
    disable_irq(key_info_tab[key].irq_no);          // 找到,关闭对应中断
    utukey_dev.key_status[key] = KEYSTATUS_DOWNX;   // 按键处于不确定状态
    key_timer[key].expires = jiffies + KEY_TIME_DELAY1;     // 去抖动延时
    add_timer(&key_timer[key]);    //add_timer
    return IRQ_HANDLED;   //正确的中断
}

/* 申请irq中断 */
static int request_irqs(void)
{
    int i;
    for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)
    {
        s3c2410_gpio_cfgpin(key_info_tab[i].gpio_port, key_info_tab[i].gpio_setting);   // 设置按键引脚的模式(输入)
        set_irq_type(key_info_tab[i].irq_no, key_info_tab[i].irq_type);      // 设置中断类型
        if (request_irq(key_info_tab[i].irq_no, utukey_irq, SA_INTERRUPT, DEVICE_NAME, (void *)i)) // 向内核注册中断
        {
            printk(KERN_WARNING "buttons:can't get irq no.%d\n", key_info_tab[i].irq_no);           
            return 1;
        }
    }
    return 0;
}

/* 释放irq中断 */
static void free_irqs(void)
{
    int i;
    for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)
    {
        free_irq(key_info_tab[i].irq_no, (void *)i);
    }
}

/* 记录键值并唤醒等待队列 */
static void keyEvent(unsigned key)
{
    BUF_HEAD = key_info_tab[key].key_code;                  // 记录键值
    utukey_dev.head = INCBUF(utukey_dev.head, MAX_KEY_BUF); // 调整缓冲区头指针
    wake_up_interruptible(&(utukey_dev.wq));                // 唤醒等待队列
}

/* 驱动读函数 */
static ssize_t utukey_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos)
{
    unsigned int key_ret;
    unsigned long flags;
retry:
    if (utukey_dev.head != utukey_dev.tail)                         // 缓冲区有数据?
    {
        local_irq_save(flags);                                      // 进入临界区 ,关中断      
        key_ret = BUF_TAIL;                                         // 读出键值
        utukey_dev.tail = INCBUF(utukey_dev.tail, MAX_KEY_BUF);     // 调整缓冲区尾指针
        local_irq_restore(flags);                                   // 退出临界区,开中断       
        copy_to_user(buffer, &key_ret, sizeof(unsigned int));       // 拷贝到用户空间
        return sizeof(unsigned int);
    }else   // 缓冲区没数据
    {
        if (filp->f_flags & O_NONBLOCK)                             // 若采用非阻塞方式读取则返回错误
        {
            return -EAGAIN;
        }
        interruptible_sleep_on(&(utukey_dev.wq));                   // 使进程睡眠
        if (signal_pending(current))                               //在这里等中断
        {   // 如果是信号中断
            return -ERESTARTSYS;
        }
        goto retry;
    }
   
    return sizeof(unsigned int);
}

/* ioctl设备控制函数 */
static int utukey_ioctl(struct inode *inodep, struct file *filp, unsigned
  int cmd, unsigned long arg)
{
    unsigned long flags;
   
    switch (cmd)
    {
        case BUF_CLEAR:                             // 清除按键缓冲区
     local_irq_save(flags);     
        utukey_dev.head = utukey_dev.tail = 0;
        local_irq_restore(flags);
        
        printk(KERN_INFO "key buffer is cleared\n");     
        break;

        default:
        return  -EINVAL;
    }
    return 0;
}

/* 定时器中断回调函数 */
static void utukey_timer_callback(unsigned long data)  //定时器时间到了调用  参数代表哪个定时器
{
    int key = data;
    if (ISKEY_DOWN(key))                                    // 按键处于按下状态?
    {
        if (utukey_dev.key_status[key] == KEYSTATUS_DOWNX)  // 已经延时10ms,完成去抖动?
        {
            utukey_dev.key_status[key] = KEYSTATUS_DOWN;   
            key_timer[key].expires = jiffies + KEY_TIME_DELAY; 
            keyEvent(key);                                  // 记录键值,唤醒等待队列
            add_timer(&key_timer[key]);     //抬起按键去抖动延时
        }else
        {   // 已经完成去抖动延时
            key_timer[key].expires = jiffies + KEY_TIME_DELAY;
            add_timer(&key_timer[key]);
        }
    }else
    {   // 按键已经抬起
        utukey_dev.key_status[key] = KEYSTATUS_UP;
        enable_irq(key_info_tab[key].irq_no);
    }
}

static int utukey_open(struct inode *inode, struct file *filp)
{
    printk(KERN_NOTICE "utukey opened\n");
    return 0;
}

static int utukey_release(struct inode *inode, struct file *filp)
{
    printk(KERN_NOTICE "utukey released\n");
    return 0;
}

static const struct file_operations utukey_fops =
{
    .owner = THIS_MODULE,
    .read  = utukey_read,
    .ioctl = utukey_ioctl,
    .open  = utukey_open,
    .release = utukey_release,
};

/* 初始化并注册cdev */
static void utukey_setup_cdev(void)
{
    int err,devno = MKDEV(utukey_major,0);
    cdev_init(&utukey_dev.cdev,&utukey_fops);
    utukey_dev.cdev.owner = THIS_MODULE;
    utukey_dev.cdev.ops = &utukey_fops;
    err = cdev_add(&utukey_dev.cdev, devno, 1);
    if (err)
        printk(KERN_NOTICE "Error %d adding utukey", err);
}

/* 初始化函数 */
static int __init utukey_init(void)
{
    int result, i;
    dev_t devno = MKDEV(utukey_major,0);    // 用主次设备号生成设备号
    /* 申请中断 */
    result = request_irqs();
    if (result) {
        unregister_chrdev_region(devno,1);
        return result;
    }
    /* 申请设备号 */
    if (utukey_major)
        result = register_chrdev_region(devno, 1, DEVICE_NAME);
    else    /* 动态申请设备号 */
    {
        result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
        utukey_major = MAJOR(devno);
        printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, utukey_major);
    }

    if (result < 0)
        return result;

    /* 动态申请设备结构体的内存 */
    utukey_devp = kmalloc(sizeof(struct utukey_dev), GFP_KERNEL);
    if (!utukey_devp)    /* 申请失败 */
    {
        result = -ENOMEM;
        goto fail_malloc;
    }

    memset(utukey_devp, 0, sizeof(struct utukey_dev));      /* 清零分配的设备结构体内存 */
    utukey_setup_cdev();
    init_waitqueue_head(&(utukey_dev.wq));                  /* 初始化等待队列头 */
    utukey_dev.head = utukey_dev.tail = 0;

    /* 初始化按键状态 */   
    for(i = 0; i < KEY_NUM; i++)
    {
        utukey_dev.key_status[i] = KEYSTATUS_UP;
    }

    /* 初始化定时器 */
    for(i = 0; i < KEY_NUM; i++)
    {       
        key_timer[i].function = utukey_timer_callback;
        key_timer[i].data = i;
        init_timer(&key_timer[i]);
    }
    return 0;

    fail_malloc: unregister_chrdev_region(devno, 1);
    return result;
}

/* 退出函数 */
static void __exit utukey_exit(void)
{
    int i;
    cdev_del(&utukey_dev.cdev);                             // 注销cdev
    kfree(utukey_devp);                                     // 释放结构体内存
    unregister_chrdev_region(MKDEV(utukey_major, 0), 1);    // 释放设备号
    free_irqs();                                            // 释放中断
    for(i = 0; i < KEY_NUM; i++)                            // 注销定时器
    {
        del_timer(&key_timer[i]);
    }
}

MODULE_AUTHOR("lxm<lxm650@163.com>");
MODULE_LICENSE("Dual BSD/GPL");

module_init(utukey_init);
module_exit(utukey_exit);

//测试程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

#define BUF_CLEAR _IO(0xFF, 0)

int main(void)
{
    int buttons_fd;
    int key_value;

    buttons_fd = open("/dev/utukey", 0);
    if (buttons_fd < 0) {
        perror("cann't open device /dev/utukey");
        exit(1);
    }
    else
    {
        if (ioctl(buttons_fd, BUF_CLEAR, 0) < 0)
            printf(" ioctl command failed\n");
    }
    
    while(1)

{
        int ret = read(buttons_fd, &key_value, sizeof key_value);
        printf("You pressed buttons %d\n", key_value);
    }

    close(buttons_fd);
    return 0;
}