zhengmeifu的linux家园

按键扫描驱动--linux2.6系统下

以下是我写的驱动代码,贴出来,以备继续改善或参考之用。感兴趣的朋友联系:QQ:26451602

/****************************************************************
    Copyright(c) 2009-2010,zmf    
    模块名称(Filename):        kbd_s3c2410.c
    项目名称(Projectname):        

    版本号(Version):         1.0.0
    创建日期(Date):             2009-8-18       
    作者(Author):                ZMF(Zheng meifu)
    功能描述(Description):     key button driver for linux2.6.14.1
    其他说明(Others):           
    修改记录(History):   
2009-10-26: 依据要求,改为刚按下/按住/释放都要给应用层信号。
   修改标志:ZMF091026_KEYPROCESS
2009-11-2:读键盘,采用非阻塞读时改为返回0,不返回-EAGAIN.
2009-12-4:想增加fasync异步通知信号给应用程序。经调试ok!不用send_sig函数。
   修改标志:ZMF091204_FASYNC
****************************************************************/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/hardware.h>
#include <linux/devfs_fs_kernel.h>
/*
RPM按键共6个。
GPC0  GPC1  GPC2  GPC3  GPC4   GPC5
K1       K2       K3      K4      K5        K6
*/
#define CONFIG_TARGET_RPM 
// 注释掉则开发板按键驱动程序为外部中断方式触发,RPM板则为定时扫描。
// 此参数已在内核配置里定义了,此处为调试目的
#define FOR_YL2410BORD 
// 次参数定义为在开发板调试按键,注释掉为实际RPM目标板
// 实际运行以上两个参数都要注释掉。驱动为定时器扫描键盘。
#define ZMF091026_KEYPROCESS 1
#define ZMF091204_FASYNC 1
#define DEVICE_NAME "keybutton"
#define MAX_KEY_BUF 32 //KEY BUFFER SIZE
#ifdef CONFIG_TARGET_RPM
 #ifdef FOR_YL2410BORD
  #define KEY_NUM  8  //key num
 #else
  #define KEY_NUM  6  //key num
 #endif
#else
#define KEY_NUM  4  //key num
#endif
#define INCBUF(x,mod) ((++(x))&((mod)-1))
#define KEY_DELAYMS(X)  (HZ/(1000/(X)))
#define KEY_S_UP   0x1000
#define KEY_S_HOLD 0x2000
#define KEY_MAJOR 222
static dev_t key_major = KEY_MAJOR;
#define KEY_FIRST_DOWN 0
#define KEY_SECOND_DOWN 1
#define KEY_STATUS_UP 2
typedef unsigned int KEY_RET;
struct key_dev_t{
    struct cdev cdev;
    unsigned int keystatus[KEY_NUM];
    KEY_RET buf[MAX_KEY_BUF];
    unsigned int head,tail;
    wait_queue_head_t wq;
#ifdef CONFIG_TARGET_RPM
    unsigned int key_pressed; // =1 key is pressed =0 release
#endif
 int wait_cond;
#ifdef ZMF091204_FASYNC
 struct fasync_struct * async_queue;
#endif
// struct semaphore sam;
};
struct key_dev_t key_dev;

#define BUF_HEAD (key_dev.buf[key_dev.head])
#define BUF_TAIL (key_dev.buf[key_dev.tail])
#ifdef CONFIG_TARGET_RPM
struct timer_list key_timer;
#else
struct timer_list key_timer[KEY_NUM];
#endif
//static int wait_cond=0;
static struct key_info {
#ifdef CONFIG_TARGET_RPM
    unsigned int pin;//gpio port
    unsigned int pin_setting;
    int key_code;//key value
#else
    int irq;//中断号
    unsigned int pin;//gpio port
    unsigned int pin_setting;
    int key_code;//key value
    char *name;
#endif
}key_info_tab[] = {
#ifdef CONFIG_TARGET_RPM
 #ifdef FOR_YL2410BORD
 { S3C2410_GPF0,S3C2410_GPF0_INP,1  },   
 {   S3C2410_GPB6,S3C2410_GPB6_OUTP,2  },   
 {     S3C2410_GPG5,S3C2410_GPG5_INP,3  },   
 {     S3C2410_GPB7,S3C2410_GPB7_OUTP,4  },   
 {     S3C2410_GPG3,S3C2410_GPG3_INP,5  },   
 {     S3C2410_GPB6,S3C2410_GPB6_OUTP,6  },
 {     S3C2410_GPG11,S3C2410_GPG11_INP,7  },   
 {     S3C2410_GPB7,S3C2410_GPB7_OUTP,8  },
 #else  
 { S3C2410_GPC0,S3C2410_GPC0_INP,1 },   
 {   S3C2410_GPC1,S3C2410_GPC1_INP,2 },   
 {     S3C2410_GPC2,S3C2410_GPC2_INP,3  },   
 {     S3C2410_GPC3,S3C2410_GPC3_INP,4  },   
 {     S3C2410_GPC4,S3C2410_GPC4_INP,5  },   
 {     S3C2410_GPC5,S3C2410_GPC5_INP,6  },
 #endif  
#else
 { IRQ_EINT0,S3C2410_GPF0,S3C2410_GPF0_EINT0,KEY_UP,"Key Up"  },   
/* {   IRQ_EINT1,S3C2410_GPF1,S3C2410_GPF1_EINT1,KEY_DOWN,"Key Down" },   
 {     IRQ_EINT2,S3C2410_GPF2,S3C2410_GPF2_EINT2,KEY_LEFT,"Key Left"    },    */
 {     IRQ_EINT13,S3C2410_GPG5,S3C2410_GPG5_EINT13,KEY_RIGHT,"Key Right"    },   
 {     IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_EINT11,KEY_ENTER,"Key Enter"    },   
 {     IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_EINT19,KEY_EXIT,"Key Exit"    },

#endif
};
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(key_info_tab[key].pin) == 0)
static void key_timer_handler(unsigned long data);
#if  !defined(CONFIG_TARGET_RPM)
static irqreturn_t key_eint_hander(int irq, void *dev_id, struct pt_regs *reg);
static int request_irqs(void);
#endif
static int key_open(struct inode *inode, struct file *filp);
static int key_release(struct inode *inode, struct file *filp);
static ssize_t key_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
static ssize_t key_read(struct file *file,char __user *buffer, size_t count, loff_t *ppos);
#ifdef ZMF091204_FASYNC
static int key_fasync(int fd, struct file *filp, int mode);
#endif
static const struct file_operations key_fops ={
    .owner = THIS_MODULE,
    .read  = key_read,
    .write = key_write,
    .open  = key_open,
    .release = key_release,
#ifdef ZMF091204_FASYNC
    .fasync = key_fasync,
#endif 
};
static int key_open(struct inode *inode, struct file *filp){
 filp->private_data = &key_dev;
 key_dev.head=key_dev.tail=0;
 memset(key_dev.buf, 0, MAX_KEY_BUF);
 key_dev.wait_cond = 1;
 return 0;
}
#ifdef ZMF091204_FASYNC
static int key_fasync(int fd, struct file *filp, int mode){
 struct key_dev_t *dev = (struct key_dev_t *)filp->private_data;
 return fasync_helper(fd,filp, mode, &dev->async_queue);
}
#endif
static int key_release(struct inode *inode, struct file *filp){
#ifdef ZMF091204_FASYNC
 key_fasync(-1,filp,0);
#endif
    return 0;
}
static ssize_t key_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos){
    return 0;
}
static ssize_t key_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos){
    KEY_RET key_ret[MAX_KEY_BUF],xi;
    struct key_dev_t *dev;
    dev = (struct key_dev_t*)filp->private_data;
retry:
    if (key_dev.head != key_dev.tail)   {
  for(xi=0; key_dev.head != key_dev.tail; xi++){
   key_ret[xi] = BUF_TAIL;
   BUF_TAIL=0;
   dev->tail = INCBUF(dev->tail,MAX_KEY_BUF);
  }
  copy_to_user(buffer,(char*)&key_ret,xi*sizeof(KEY_RET));
  return xi;//sizeof(KEY_RET);
    }else {
        if (filp->f_flags & O_NONBLOCK)    {
            return 0;//-EAGAIN;
        }
        /*interruptible_sleep_on(&(dev->wq)); //为安全起见,最好不要调用该睡眠函数*/
        /*用户采用阻塞方式读取,调用该函数使进程睡眠*/
        wait_event_interruptible(dev->wq,key_dev.wait_cond);
  key_dev.wait_cond = 0;
        if (signal_pending(current))  {
            return -ERESTARTSYS;
        }
        goto retry;
    }
    return sizeof(KEY_RET);
}
#if !defined(CONFIG_TARGET_RPM)
static irqreturn_t key_eint_hander(int irq, void *dev_id, struct pt_regs *reg){
    int key =(int)dev_id;
    int i;
    int found = 0;
    for (i=0; i<ARRAY_SIZE(key_info_tab); i++)   {
        if (key_info_tab[i].irq == irq) {
            found = 1;
            break;
        }
    }
    if (!found)  {
        printk(KERN_NOTICE"bad irq %d in button\n", irq);
        return IRQ_NONE;
    }
    //printk(KERN_NOTICE "key_eint_hander:key:%d\n",key);
    disable_irq(key_info_tab[key].irq);
    key_dev.keystatus[key] = KEY_FIRST_DOWN;
    key_timer[key].expires = jiffies + KEY_DELAYMS(10);
    add_timer(&key_timer[key]);   
    return IRQ_HANDLED;
}
#endif

static void keyevent_intimerhandler(KEY_RET key){
#ifdef CONFIG_TARGET_RPM
#ifdef ZMF091026_KEYPROCESS
 if (key_dev.keystatus[key] == KEY_FIRST_DOWN) {/*刚按下*/
  BUF_HEAD = key_info_tab[key].key_code;
  key_dev.key_pressed=1;
  key_dev.keystatus[key] = KEY_SECOND_DOWN;
  key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);
 }else if (key_dev.keystatus[key] == KEY_SECOND_DOWN){//一直按下
  if(BUF_HEAD == (key_info_tab[key].key_code | KEY_S_HOLD)){
   (key_dev.buf[((key_dev.head+1)&(MAX_KEY_BUF-1))])++;
  }else{
   BUF_HEAD = key_info_tab[key].key_code | KEY_S_HOLD;
//   key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);
  }
 }else if (key_dev.keystatus[key] == KEY_STATUS_UP){
  if(key_dev.key_pressed == 1) return; // second  press other key,not do anything
  key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);
  key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);
  BUF_HEAD = key_info_tab[key].key_code | KEY_S_UP;
  key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);
#ifdef ZMF091204_FASYNC  
  kill_fasync(&key_dev.async_queue, SIGIO, POLL_IN);
#else
  send_sig(SIGUSR2, current, 1);
#endif
 }
 wake_up_interruptible(&(key_dev.wq));
 key_dev.wait_cond = 1;  
#else
 int i;
 if (key_dev.key_pressed==0){  // 若之前未按下则为有效键
  if (key_dev.keystatus[key] == KEY_FIRST_DOWN) {/*刚按下*/
   BUF_HEAD = key_info_tab[key].key_code;
   key_dev.key_pressed=1;
   key_timer.data=0;
   key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);
   wake_up_interruptible(&(key_dev.wq));
   key_dev.wait_cond = 1;  
  }else if (key_dev.keystatus[key] == KEY_STATUS_UP){ /*抬起*/
   key_dev.keystatus[key] = KEY_FIRST_DOWN;
   for (i=0; i<KEY_NUM; i++) // 为防止第二次扫到其他键
   if (i != key) key_dev.keystatus[i] = KEY_STATUS_UP;
  }
 }
 // 若一直按着则不处理
#endif
#else
 if (key_dev.keystatus[key] == KEY_FIRST_DOWN)//刚按下
  BUF_HEAD = key_info_tab[key].key_code;
 else if (key_dev.keystatus[key] == KEY_SECOND_DOWN)//一直按下
  BUF_HEAD = key_info_tab[key].key_code | KEY_S_HOLD;
 else if (key_dev.keystatus[key] == KEY_STATUS_UP)//抬起
  BUF_HEAD = key_info_tab[key].key_code | KEY_S_UP;
 key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);
 wake_up_interruptible(&(key_dev.wq));
 // 以上程序刚按键和按键保持及释放都会产生键值
 key_dev.wait_cond = 1;  
 // 若key_read()里使用了wait_event_interruptible()函数,则此条件要打开
#endif
}

static void key_timer_handler(unsigned long data){
#ifdef CONFIG_TARGET_RPM
 #ifdef FOR_YL2410BORD
 #ifdef ZMF091026_KEYPROCESS
 u32 temp_read;
 mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); 
 s3c2410_gpio_setpin(S3C2410_GPB6, 0);
 s3c2410_gpio_setpin(S3C2410_GPB7, 0);
 if ((ISKEY_DOWN(1-1)||ISKEY_DOWN(3-1)||ISKEY_DOWN(5-1)||ISKEY_DOWN(7-1))
  &&(key_dev.key_pressed==1) )
  goto scan_line;
 if (key_dev.key_pressed==1) {
  key_dev.key_pressed=0;
  for (temp_read=0; temp_read<KEY_NUM; temp_read++){
   if (key_dev.keystatus[temp_read] == KEY_SECOND_DOWN){
    key_dev.keystatus[temp_read] = KEY_STATUS_UP;
    keyevent_intimerhandler(temp_read); 
   }
   key_dev.keystatus[temp_read] = KEY_STATUS_UP;  // release to KEY_STATUS_UP
  }
  goto scan_comp;
 }
scan_line:
 s3c2410_gpio_setpin(S3C2410_GPB6, 0);
 s3c2410_gpio_setpin(S3C2410_GPB7, 1);
 if (ISKEY_DOWN(1-1)){
  if(key_dev.key_pressed==0) key_dev.keystatus[1-1] = KEY_FIRST_DOWN;
  keyevent_intimerhandler(1-1);goto scan_comp;}
 else if (ISKEY_DOWN(3-1)){
  if(key_dev.key_pressed==0) key_dev.keystatus[3-1] = KEY_FIRST_DOWN;
  keyevent_intimerhandler(3-1); goto scan_comp;}
 else if (ISKEY_DOWN(5-1)){
  if(key_dev.key_pressed==0) key_dev.keystatus[5-1] = KEY_FIRST_DOWN;
  keyevent_intimerhandler(5-1); goto scan_comp;}
 else if (ISKEY_DOWN(7-1)){
  if(key_dev.key_pressed==0) key_dev.keystatus[7-1] = KEY_FIRST_DOWN;
  keyevent_intimerhandler(7-1); goto scan_comp;}
 s3c2410_gpio_setpin(S3C2410_GPB6, 1);
 s3c2410_gpio_setpin(S3C2410_GPB7, 0);
 if (ISKEY_DOWN(1-1)){
  if(key_dev.key_pressed==0) key_dev.keystatus[2-1] = KEY_FIRST_DOWN;
  keyevent_intimerhandler(2-1); goto scan_comp;}
 else if (ISKEY_DOWN(3-1)){
  if(key_dev.key_pressed==0) key_dev.keystatus[4-1] = KEY_FIRST_DOWN;
  keyevent_intimerhandler(4-1); goto scan_comp;}
 else if (ISKEY_DOWN(5-1)){
  if(key_dev.key_pressed==0) key_dev.keystatus[6-1] = KEY_FIRST_DOWN;
  keyevent_intimerhandler(6-1); goto scan_comp;}
 else if (ISKEY_DOWN(7-1)){
  if(key_dev.key_pressed==0) key_dev.keystatus[8-1] = KEY_FIRST_DOWN;
  keyevent_intimerhandler(8-1); goto scan_comp;}
scan_comp:
 s3c2410_gpio_setpin(S3C2410_GPB6, 1);
 s3c2410_gpio_setpin(S3C2410_GPB7, 1);
 #else   // is not ZMF091026_KEYPROCESS  & yes for FOR_YL2410BORD
 u32 temp_read;
/* if(down_trylock(&key_dev.sam)){
  mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); 
  return ;
 } */
 mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); 
 s3c2410_gpio_setpin(S3C2410_GPB6, 0);
 s3c2410_gpio_setpin(S3C2410_GPB7, 0);
 if ((ISKEY_DOWN(1-1)||ISKEY_DOWN(3-1)||ISKEY_DOWN(5-1)||ISKEY_DOWN(7-1))
  &&(key_dev.key_pressed==1) )
  return;
 if (key_dev.key_pressed==1) {
  for (temp_read=0; temp_read<KEY_NUM; temp_read++)
   if (key_dev.keystatus[temp_read] != KEY_STATUS_UP)
    key_dev.keystatus[temp_read] = KEY_STATUS_UP;
  key_dev.key_pressed=0;
 }
 s3c2410_gpio_setpin(S3C2410_GPB6, 0);
 s3c2410_gpio_setpin(S3C2410_GPB7, 1);
 if (ISKEY_DOWN(1-1)){keyevent_intimerhandler(1-1);goto scan_comp;}
 else if (ISKEY_DOWN(3-1)){keyevent_intimerhandler(3-1); goto scan_comp;}
 else if (ISKEY_DOWN(5-1)){keyevent_intimerhandler(5-1); goto scan_comp;}
 else if (ISKEY_DOWN(7-1)){keyevent_intimerhandler(7-1); goto scan_comp;}
 s3c2410_gpio_setpin(S3C2410_GPB6, 1);
 s3c2410_gpio_setpin(S3C2410_GPB7, 0);
 if (ISKEY_DOWN(1-1)){keyevent_intimerhandler(2-1); goto scan_comp;}
 else if (ISKEY_DOWN(3-1)){keyevent_intimerhandler(4-1); goto scan_comp;}
 else if (ISKEY_DOWN(5-1)){keyevent_intimerhandler(6-1); goto scan_comp;}
 else if (ISKEY_DOWN(7-1)){keyevent_intimerhandler(8-1); goto scan_comp;}
scan_comp:
 s3c2410_gpio_setpin(S3C2410_GPB6, 1);
 s3c2410_gpio_setpin(S3C2410_GPB7, 1);
// up(&key_dev.sam);
 #endif  // end ZMF091026_KEYPROCESS
 #else  // not for yl-2410 devp board
 #ifdef ZMF091026_KEYPROCESS
 int keyscan_hist = data;
 int keyscan_read_io;
 u32 temp_read;
 temp_read=__raw_readl(S3C2410_GPCDAT);
 keyscan_read_io = temp_read & 0x3f;
 if ( keyscan_read_io != 0x3f){
  switch(keyscan_read_io){
   case 0x3e: temp_read=0; break;
   case 0x3d: temp_read=1; break;
   case 0x3b: temp_read=2; break;
   case 0x37: temp_read=3; break;
   case 0x2f: temp_read=4; break;
   case 0x1f: temp_read=5; break;
   default:  temp_read=0xff; break;
  }
  if(temp_read != 0xff){
   if ( key_dev.key_pressed==0)
    key_dev.keystatus[temp_read] = KEY_FIRST_DOWN;
   keyevent_intimerhandler(temp_read); 
  } /*else{
   for (temp_read=0; temp_read<KEY_NUM; temp_read++)
    key_dev.keystatus[temp_read] = KEY_STATUS_UP;
   key_dev.key_pressed=0;
  }*/  //  press double key error!! Not do anything
  key_timer.data=0;
 }else if ( key_dev.key_pressed==1) {
  if (++keyscan_hist >=2) {
   key_dev.key_pressed=0;
   for (temp_read=0; temp_read<KEY_NUM; temp_read++){
    if(key_dev.keystatus[temp_read] == KEY_SECOND_DOWN){
     key_dev.keystatus[temp_read] = KEY_STATUS_UP;
     keyevent_intimerhandler(temp_read); 
    }
    key_dev.keystatus[temp_read] = KEY_STATUS_UP; // release to KEY_STATUS_UP
   }
   key_timer.data=0;
  }else key_timer.data=keyscan_hist;
 }
 mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); 
 #else  // not for yl2410, not modify on ZMF091026_KEYPROCESS
 int keyscan_hist = data;
 int keyscan_read_io;
 u32 temp_read;
 temp_read=__raw_readl(S3C2410_GPCDAT);
 keyscan_read_io = temp_read & 0x3f;
 if ( keyscan_read_io != 0x3f) {
  switch(keyscan_read_io) {
  case 0x3e: keyevent_intimerhandler(0); break;
  case 0x3d: keyevent_intimerhandler(1); break;
  case 0x3b: keyevent_intimerhandler(2); break;
  case 0x37: keyevent_intimerhandler(3); break;
  case 0x2f: keyevent_intimerhandler(4); break;
  case 0x1f: keyevent_intimerhandler(5); break;
  default: break;
  }
 }else if ( key_dev.key_pressed==1) {
  for (temp_read=0; temp_read<KEY_NUM; temp_read++)
   if (key_dev.keystatus[temp_read] != KEY_STATUS_UP)
    key_dev.keystatus[temp_read] = KEY_STATUS_UP;
  if (++keyscan_hist >=2) {
   key_dev.key_pressed=0;
   key_timer.data=0;
  }else key_timer.data=keyscan_hist;
 }
 mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); 
 #endif  //  end ZMF091026_KEYPROCESS
 #endif // end  FOR_YL2410BORD
#else  // is not CONFIG_TARGET_RPM
    int key = data;
    if (ISKEY_DOWN(key))   {
        //printk(KERN_NOTICE"key_dev.keystatus[key]:%d\n",key);
        if (key_dev.keystatus[key] == KEY_FIRST_DOWN)//从中断进入
        {
            keyevent_intimerhandler(key);
            key_dev.keystatus[key] = KEY_SECOND_DOWN;
            key_timer[key].expires = jiffies + KEY_DELAYMS(200);           
            add_timer(&key_timer[key]);
        }else {
            keyevent_intimerhandler(key);
            key_timer[key].expires = jiffies + KEY_DELAYMS(200);//HOLD key,每隔200MS发送一次           
            add_timer(&key_timer[key]);
        }
    }else {
        key_dev.keystatus[key] = KEY_STATUS_UP;
        keyevent_intimerhandler(key);
        enable_irq(key_info_tab[key].irq);
    }
#endif  // end CONFIG_TARGET_RPM
}
#if  !defined(CONFIG_TARGET_RPM)
//申请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].pin, key_info_tab[i].pin_setting);
        set_irq_type(key_info_tab[i].irq, IRQT_FALLING);//下降沿触发
        if (request_irq(key_info_tab[i].irq, key_eint_hander, SA_INTERRUPT, DEVICE_NAME, (void *)i))
        {
            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, (void *)i);
    }
}
#endif
static void key_init_cdev(void){
    int i;
    int err,devno = MKDEV(key_major,0);
    cdev_init((struct cdev *)&(key_dev.cdev),(struct file_operations *)&key_fops);
    key_dev.cdev.owner = THIS_MODULE;
    key_dev.cdev.ops = (struct file_operations *)&key_fops;
    key_dev.wait_cond = 0;
    err = cdev_add(&key_dev.cdev, devno, 1);
    if (err) {
        printk(KERN_NOTICE "Error %d adding key",err);       
    }
    key_dev.head = key_dev.tail = 0;
    for(i=0; i<KEY_NUM; i++)   {
        key_dev.keystatus[i] = KEY_STATUS_UP;
    }
    init_waitqueue_head(&(key_dev.wq));
 #ifdef CONFIG_TARGET_RPM
 key_dev.key_pressed=0;
 init_timer(&key_timer);
 key_timer.expires = jiffies + KEY_DELAYMS(50);
 key_timer.function = key_timer_handler;
        key_timer.data = 0x00;
 add_timer(&key_timer);
 #else
 request_irqs();
 for(i=0; i<KEY_NUM; i++)    {       
     key_timer[i].function = key_timer_handler;
     key_timer[i].data = i;
     init_timer(&key_timer[i]);
 }
 #endif
}
static int __init key_init(void){
    int result;
    dev_t devno = MKDEV(key_major,0);
    if (key_major)    {
        result = register_chrdev_region(devno, 1, DEVICE_NAME);
    }else    {
        result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
        key_major = MAJOR(devno);
    }
    if (result < 0)    {
        return result;
    }
// init_MUTEX(&key_dev.sam);
 #ifdef CONFIG_TARGET_RPM
  for (result=0; result<KEY_NUM; result++)
  s3c2410_gpio_cfgpin(key_info_tab[result].pin, key_info_tab[result].pin_setting);
 #else
 s3c2410_gpio_cfgpin(S3C2410_GPB6, S3C2410_GPB6_OUTP);
 s3c2410_gpio_cfgpin(S3C2410_GPB7, S3C2410_GPB7_OUTP);
 s3c2410_gpio_setpin(S3C2410_GPB6, 0);
 s3c2410_gpio_setpin(S3C2410_GPB7, 1);
 #endif
       key_init_cdev();
 #ifdef CONFIG_DEVFS_FS
 devfs_mk_cdev(MKDEV(key_major, 0), S_IFCHR|S_IRUSR|S_IWUSR, DEVICE_NAME);
 #endif
    printk(KERN_NOTICE "/dev/keybutton inited: zhengmeifu@sina.com\n");
    return 0;
}
static void __exit key_exit(void){
#if !defined(CONFIG_TARGET_RPM)
    int i;
#endif
    cdev_del(&key_dev.cdev);
    unregister_chrdev_region(MKDEV(key_major,0),1);
 #ifdef CONFIG_DEVFS_FS
 devfs_remove(DEVICE_NAME);
 #endif
#ifdef CONFIG_TARGET_RPM
 del_timer(&key_timer);
#else
    for(i=0; i<KEY_NUM; i++)    {
        del_timer(&key_timer[i]);
    }
    free_irqs();
#endif
}
module_init(key_init);
module_exit(key_exit);

MODULE_AUTHOR("zhengmeifu@sina.com");
MODULE_DESCRIPTION("RPM or (yls3c2410 devlp board) keyboard driver");
MODULE_LICENSE("Dual BSD/GPL");

/*linux驱动开发之key
今天做了key的驱动,程序测试ok。
key的驱动牵涉的内核知识很多,有中断,内核定时器,阻塞。
后续有时间我会再写一个详细的分析。

测试如下:
先通过cat /proc/devices 插看keybutton的主号为222.
mknod /dev/keybutton c 222 0

测试程序如下:
*/
/*
 *      Buttons Example for linux s3c2410 rpm
 */
 /*
#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>

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

    buttons_fd = open("/dev/keybutton", 0);
    if (buttons_fd < 0) {
        perror("cann't open device /dev/keybutton");
        exit(1);
    }

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

    close(buttons_fd);
    return 0;
}

驱动程序如上:
*/

 

posted on 2010-03-11 10:27  zhengmeifu  阅读(2105)  评论(0编辑  收藏  举报