linux按键驱动编写及测试程序

http://blog.163.com/allegro_tyc/blog/static/337437682011579482347/

 

S3C2440下linux按键驱动编写及测试程序  

2011-06-07 22:19:33|  分类: 默认分类 |  标签: |举报 |字号 订阅

驱动程序tang2440_buttons.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/gpio.h>

#define DEVICE_NAME "buttons"

/*定义中断所用的结构体*/
struct button_irq_desc {
     int irq; //按键对应的中断号
     int pin; //按键所对应的 GPIO 端口
     int pin_setting; //按键对应的引脚描述,实际并未用到,保留
     int number; //定义键值,以传递给应用层/用户态
     char *name; //每个按键的名称
};

/*结构体实体定义*/
static struct button_irq_desc button_irqs [] = {
      {IRQ_EINT8 , S3C2410_GPG(0) , S3C2410_GPG0_EINT8  , 0, "KEY0"},
      {IRQ_EINT11, S3C2410_GPG(3) , S3C2410_GPG3_EINT11 , 1, "KEY1"},
      {IRQ_EINT13, S3C2410_GPG(5) , S3C2410_GPG5_EINT13 , 2, "KEY2"},
      {IRQ_EINT14, S3C2410_GPG(6) , S3C2410_GPG6_EINT14 , 3, "KEY3"},
      {IRQ_EINT15, S3C2410_GPG(7) , S3C2410_GPG7_EINT15 , 4, "KEY4"},
      {IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"},
};

/*开发板上按键的状态变量,注意这里是’0’,对应的 ASCII 码为 30*/
static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};

/*因为本驱动是基于中断方式的,在此创建一个等待队列,以配合中断函数使用;当有按键按下并读取到键
值时,将会唤醒此队列,并设置中断标志,以便能通过 read 函数判断和读取键值传递到用户态;当没有按
键按下时,系统并不会轮询按键状态,以节省时钟资源*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/*中断标识变量,配合上面的队列使用,中断服务程序会把它设置为 1,read 函数会把它清零*/
static volatile int ev_press = 0;

/*本按键驱动的中断服务程序*/
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
      struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
      int down;
      /*获取被按下的按键状态*/
      down = !s3c2410_gpio_getpin(button_irqs->pin);
      /*状态改变,按键被按下,从这句可以看出,当按键没有被按下的时候,寄存器的值为 1(上拉),但按
 键被按下的时候,寄存器对应的值为 0*/
      if (down != (key_values[button_irqs->number] & 1)) { // Changed
      /*如果 key1 被按下,则 key_value[0]就变为’1’,对应的 ASCII 码为 31*/
      key_values[button_irqs->number] = '0' + down;
            ev_press = 1; /*设置中断标志为 1*/
            wake_up_interruptible(&button_waitq); /*唤醒等待队列*/
      }
      return IRQ_RETVAL(IRQ_HANDLED);
}

/*
 *在应用程序执行 open(“/dev/buttons”,...)时会调用到此函数,在这里,它的作用主要是注册 6 个按键的中断。
 *所用的中断类型是 IRQ_TYPE_EDGE_BOTH,也就是双沿触发,在上升沿和下降沿均会产生中断,这样做是为了更加有
 *效地判断按键状态
 */
static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
      int i;
      int err = 0;
      for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
      if (button_irqs[i].irq < 0) {
             continue;
        }
             /*注册中断函数*/
             err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
                                      button_irqs[i].name, (void *)&button_irqs[i]);
             if (err)
                   break;
        }
      if (err) {
             /*如果出错,释放已经注册的中断,并返回*/
             i--;
             for (; i >= 0; i--) {
       if (button_irqs[i].irq < 0) {
         continue;
                  }
             disable_irq(button_irqs[i].irq);
             free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
               }
             return -EBUSY;
        }
        /*注册成功,则中断队列标记为 1,表示可以通过 read 读取*/
      ev_press = 1;
        /*正常返回*/
      return 0;
}

/*
 *此函数对应应用程序的系统调用 close(fd)函数,在此,它的主要作用是当关闭设备时释放 6 个按键的中断处理函数
 */
static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
      int i;
      for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
      if (button_irqs[i].irq < 0) {
             continue;
        }
        /*释放中断号,并注销中断处理函数*/
      free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
        }
      return 0;
}

/*
 *对应应用程序的 read(fd,...)函数,主要用来向用户空间传递键值
 */
static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
      unsigned long err;
      if (!ev_press) {
      if (filp->f_flags & O_NONBLOCK)
                 /*当中断标识为 0 时,并且该设备是以非阻塞方式打开时,返回*/
             return -EAGAIN;
      else
                 /*当中断标识为 0 时,并且该设备是以阻塞方式打开时,进入休眠状态,等待被唤醒*/
             wait_event_interruptible(button_waitq, ev_press);
        }
        /*把中断标识清零*/
      ev_press = 0;
        /*一组键值被传递到用户空间*/
      err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
 return err ? -EFAULT : min(sizeof(key_values), count);
}

static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
      unsigned int mask = 0;
      /*把调用 poll 或者 select 的进程挂入队列,以便被驱动程序唤醒*/
      poll_wait(file, &button_waitq, wait);
      if (ev_press)
            mask |= POLLIN | POLLRDNORM;
      return mask;
}

/*设备操作集*/
static struct file_operations dev_fops = {
      .owner  =    THIS_MODULE,
      .open       =    s3c24xx_buttons_open,
      .release    =    s3c24xx_buttons_close,
      .read       =    s3c24xx_buttons_read,
      .poll       =    s3c24xx_buttons_poll,
};
static struct miscdevice misc = {
      .minor = MISC_DYNAMIC_MINOR,
      .name = DEVICE_NAME,
      .fops = &dev_fops,
};
/*设备初始化,主要是注册设备*/
static int __init dev_init(void)
{
      int ret;
      /*把按键设备注册为 misc 设备,其设备号是自动分配的*/
      ret = misc_register(&misc);
      printk (DEVICE_NAME"\tinitialized\n");
      return ret;
}
/*注销设备*/
static void __exit dev_exit(void)
{
      misc_deregister(&misc);
}

module_init(dev_init);//模块初始化,仅当使用 insmod/podprobe 命令加载时有用,如果设备不是通过模块方式加载,此处将不会被调用
module_exit(dev_exit);//卸载模块,当该设备通过模块方式加载后,可以通过 rmmod 命令卸载,将调用此函数
MODULE_LICENSE("GPL");//版权信息
MODULE_AUTHOR("tang");//作者名字

测试程序btn_test.c

#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;
    char buttons[6] = {'0', '0', '0', '0', '0', '0'}; //定义按键值变量,对于驱动函数中的 key_values 数组
    /*打开按键设备/dev/buttons*/
    buttons_fd = open("/dev/buttons", 0);
    if (buttons_fd < 0) {
          /*打开失败则退出*/
          perror("open device buttons");
          exit(1);
    }
    /*永读按键并打印键值和状态*/
    for (;;) {
          char current_buttons[6];
          int count_of_changed_key;
          int i;
          /*使用 read 函数读取一组按键值(6 个)*/
          if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) {
                 perror("read buttons:");
                 exit(1);
          }
          /*逐个分析读取到的按键值*/
          for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) {
                 if (buttons[i] != current_buttons[i]) {
                       buttons[i] = current_buttons[i];
                       /*打印按键值,并标明按键按下/抬起的状态*/
                       printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" :
"down");
                       count_of_changed_key++;
                 }
          }
          if (count_of_changed_key) {
                 printf("\n");
          }
    }
  /*关闭按键设备文件*/
  close(buttons_fd);
  return 0;
}

 arm-linux-gcc -o buttons_test buttons_test.c

以下是成功加载范例jd2440_buttons.c,位于linux-2.x.xx/drivers/char下

cp -f jd2440_buttons.c /linux-2.x.xx/drivers/char //把驱动源码复制到内核驱动的字符设备下

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <linux/semaphore.h>

#define DEVICE_NAME     "buttons"

struct button_irq_desc {
 int irq;
 int pin;
 int pin_setting;
 int number;
 char *name; 
};

static struct button_irq_desc button_irqs [] = {
 {IRQ_EINT11,S3C2410_GPG3,  S3C2410_GPG3_EINT11, 0, "KEY0"}, //key_right
 {IRQ_EINT8, S3C2410_GPG0,  S3C2410_GPG0_EINT8,  1, "KEY1"}, //key_down
 {IRQ_EINT3, S3C2410_GPF3,  S3C2410_GPF3_EINT3,  2, "KEY2"}, //key_enter
 {IRQ_EINT9, S3C2410_GPG1,  S3C2410_GPG1_EINT9,  3, "KEY3"}, //key_left
 {IRQ_EINT1, S3C2410_GPF1,  S3C2410_GPF1_EINT1,  4, "KEY4"}, //key_up
 {IRQ_EINT5, S3C2410_GPF5,  S3C2410_GPF5_EINT5,  5, "KEY5"}, //usb wakeup
 {       -1,           -1,                  -1,  6, "KEY6"},
};

static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;

static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
     struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
     int down;
     down = !s3c2410_gpio_getpin(button_irqs->pin);
     if (down != (key_values[button_irqs->number] & 1)) {
  key_values[button_irqs->number] = '0' + down;
  ev_press = 1;
  wake_up_interruptible(&button_waitq);
      }
     return IRQ_RETVAL(IRQ_HANDLED);
}


static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
     int i;
     int err = 0;
     for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
  if (button_irqs[i].irq < 0) {
   continue;
  }
       err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
                             button_irqs[i].name, (void *)&button_irqs[i]);
       if (err)
        break;
      }
     if (err) {
  i--;
  for (; i >= 0; i--) {
   if (button_irqs[i].irq < 0) {
    continue;
       }
   disable_irq(button_irqs[i].irq);
      free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
  }
  return -EBUSY;
 }
     ev_press = 1;
     return 0;
}


static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
 int i;
     for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
  if (button_irqs[i].irq < 0) {
       continue;
  }
  free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
     }
     return 0;
}


static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
     unsigned long err;
     if (!ev_press) {
  if (filp->f_flags & O_NONBLOCK)
       return -EAGAIN;
  else
       wait_event_interruptible(button_waitq, ev_press);
     }
     ev_press = 0;
     err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
     return err ? -EFAULT : min(sizeof(key_values), count);
}

static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
     unsigned int mask = 0;
     poll_wait(file, &button_waitq, wait);
     if (ev_press)
         mask |= POLLIN | POLLRDNORM;
     return mask;
}


static struct file_operations dev_fops = {
     .owner   =   THIS_MODULE,
     .open    =   s3c24xx_buttons_open,
     .release =   s3c24xx_buttons_close,
     .read    =   s3c24xx_buttons_read,
     .poll    =   s3c24xx_buttons_poll,
};

static struct miscdevice misc = {
 .minor = MISC_DYNAMIC_MINOR,
 .name = DEVICE_NAME,
 .fops = &dev_fops,
};

static int __init dev_init(void)
{
 int ret;
 ret = misc_register(&misc);
 printk (DEVICE_NAME"\tinitialized\n");
 return ret;
}

static void __exit dev_exit(void)
{
 misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("modelsim");


#gedit /linux-2.x.xx/drivers/char/Kconfig //添加按键设备配置
config JD2440_BUTTONS
      tristate "buttons driver for jd2440"
      depends on MACH_JD_ARM_JD2440
      default y if MACH_JD_ARM_JD2440
      help
            this is buttons driver for jd2440


#gedit /linux-2.x.xx/drivers/char/Makefile //添加按键设备配置
obj-$(CONFIG_JD2440_BUTTONS) += jd2440_buttons.o

 

参考链接http://blogold.chinaunix.net/u3/101649/showart_2097608.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以给您提供一些参考资料,以帮助您编写 Linux 按键驱动程序:Linux 内核文档,Linux 驱动开发完整指南,Linux 设备驱动程序开发,Linux 中断管理和应用,以及 Linux 设备树编程教程。 ### 回答2: 编写一个Linux按键驱动程序需要遵循以下步骤: 1. 首先,需要了解目标按键的硬件接口和读取机制。查阅相关文档或资料,了解按键的连接方式、传输协议和读取方式等。 2. 创建一个新的Linux驱动程序源代码文件,并命名为键盘驱动程序。例如,可以使用C语言编写,文件扩展名为.c。 3. 在驱动程序源代码中,包括头文件和相关库函数的声明。 4. 在驱动程序初始化函数中,初始化按键设备并分配相关资源。这包括设置设备的名称、类型和设备号等。 5. 按键设备初始化后,需要为其注册一个字符设备。使用`register_chrdev()`函数可以实现字符设备的注册。 6. 编码驱动程序的读取函数,该函数会在有按键按下时触发。通过该函数可以读取按键的状态,并作出相应的响应。 7. 在驱动程序中,实现对中断的处理函数。这个函数将在按键按下时被调用,以便在硬件级别完成按键的读取。 8. 编译驱动程序源代码,生成内核模块文件。 9. 将内核模块文件加载到Linux内核中,可以使用insmod命令加载驱动程序。 10. 测试驱动程序的功能和稳定性。通过按下按键触发中断,并观察对应的驱动程序是否成功读取到按键状态。 11. 如果需要,可以加入其他功能,如设置按键的触发条件、处理多个按键的同时按下等。 注意:以上步骤是一个简单的指导,实际编写一个Linux按键驱动程序可能涉及更多复杂的细节和特定的硬件要求。在编写过程中,可以参考相关的文档、教程和示例代码,并在遇到问题时查阅相关资料或寻求专业帮助。 ### 回答3: 编写Linux键盘驱动程序通常涉及以下几个步骤: 1. 头文件与宏定义:包含必要的头文件,如"linux/input.h"和"linux/module.h"。同时,在代码中定义所需的宏,如MODULE_LICENSE和MODULE_AUTHOR等。 2. 输入设备的注册和注销:使用input_register_device()函数将键盘输入设备注册到Linux系统中,并使用input_unregister_device()函数在驱动程序退出时注销该设备。 3. 初始化和释放资源:在驱动程序加载时,需要进行初始化操作,如分配内存,注册中断处理函数等。在驱动程序卸载时,需要进行资源的释放操作。 4. 中断处理函数:编写中断处理函数来处理接收到的键盘输入。这个函数需要根据传入的参数判断按下还是释放,并根据键码值进行相应的处理。 5. 键盘输入事件的生成:使用input_report_key()函数向系统报告键盘事件,并使用input_sync()函数将键盘事件传递给输入子系统。 6. Makefile文件:编写Makefile文件用来编译驱动程序,并生成对应的ko文件。 编写一个完整的Linux键盘驱动程序需要对硬件,中断处理和输入子系统有一定的了解。同时,还需要关注Linux内核版本和所使用的硬件平台之间的差异,以确保驱动程序的正确性和兼容性。以上只是一个简要的概述,实际编写驱动程序需要更多的细节和代码实现才能完成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值