按鍵驅動(瀏覽按鍵)

 

//#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<[email protected]>");
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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章