说明:

  平台设备:正点原子IMX6ULL

第一部分:

  修改设备树,在跟节点下添加key设备: 

key {
  #address-cells = <1>;
  #size-cells = <1>;
  compatible = "atkalpha-key";
  pinctrl-names = "default";
  pinctrl-0 = <&pinctrl_key>;
  key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
  interrupt-parent = <&gpio1>;
  interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
  status = "okay";
};

第二部分:

  irqkey.c 驱动文件

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define KEY_CNT 1
#define KEY_NAME "irqkey"

atomic_t key_status = ATOMIC_INIT(0); //定义整型原子变量, 保存按键状态, 设置初始值为0

struct key_dev{
  dev_t devid;                       //设备号
  struct cdev cdev;                  //字符设备
  struct class *class;               //
  struct device *device;             //设备
  int major;                         //主设备号
  int minor;                         //次设备号
  struct device_node *nd;            //设备节点

  int gpio_number;
  int irq_number;
};

struct key_dev irqkey;


static irqreturn_t irqkey_hander(int irq, void *dev_id)
{
  atomic_inc(&key_status);          //按键状态加1
  return IRQ_HANDLED;
}


static int irqkey_open(struct inode *inode, struct file *filp)
{
  int error = -1;

  irqkey.nd = of_find_node_by_path("/key");                                                          //获取按键设备树节点
  if(NULL == irqkey.nd)
  {
    printk("of_find_node_by_path error!");
    return -1;
  }

  irqkey.gpio_number = of_get_named_gpio(irqkey.nd, "key-gpio", 0);                                  //获取按键所使用的GPIO
  if(0 == irqkey.nd)
  {
    printk("of_get_named_gpio error!");
    return -1;
  }


  error = gpio_request(irqkey.gpio_number, "irqkey_gpio");                                           //申请GPIO 
  if(error != 0)
  {
    printk("gpio_request error");
    gpio_free(irqkey.gpio_number);
    return -1;
  }

  gpio_direction_input(irqkey.gpio_number);                                                          //引脚设置, 将引脚设置为输入

  irqkey.irq_number = irq_of_parse_and_map(irqkey.nd, 0);                                            //获取引脚中断号

  error = request_irq(irqkey.irq_number, irqkey_hander, IRQF_TRIGGER_RISING, "irq_key", &irqkey);    //申请中断
  if(error != 0)
  {
    printk("request_irq error");
    free_irq(irqkey.irq_number, &irqkey);
    return -1;
  }
  return 0;
}

static ssize_t irqkey_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
  int error = -1;
  int button_countervc = 0;

  button_countervc = atomic_read(&key_status);                                                       //读取按键状态值
  error = copy_to_user(buf, &button_countervc, sizeof(button_countervc));                            //拷贝到用户空间
  if(error < 0)
  {
    printk("copy_to_user error");
    return -1;
  }
  atomic_set(&key_status, 0);                                                                        //清零按键状态值
  return 0;
}

static ssize_t irqkey_release(struct inode *inode, struct file *filp)
{
  gpio_free(irqkey.gpio_number);                                                                     //释放引脚
  free_irq(irqkey.irq_number, &irqkey);                                                              //释放中断
  return 0;
}

static struct file_operations irqkey_fops = {
  .owner = THIS_MODULE,
  .open = irqkey_open,
  .read = irqkey_read,
  .release = irqkey_release,
};

static int __init irqkey_init(void)
{

  if(irqkey.major)
  {
    irqkey.devid = MKDEV(irqkey.major, 0);
    register_chrdev_region(irqkey.devid, KEY_CNT, KEY_NAME);
  }
  else
  {
    alloc_chrdev_region(&irqkey.devid, 0, KEY_CNT, KEY_NAME);
    irqkey.major = MAJOR(irqkey.devid);
    irqkey.minor = MINOR(irqkey.devid);
  }
  printk("irqkey major = %d, minor = %d \r\n", irqkey.major, irqkey.minor);


  irqkey.cdev.owner = THIS_MODULE;                                                                   //初始化字符设备并向Linux内核添加
  cdev_init(&irqkey.cdev, &irqkey_fops);
  cdev_add(&irqkey.cdev, irqkey.devid, KEY_CNT);

  irqkey.class = class_create(THIS_MODULE, KEY_NAME);                                                //创建设备类
  if(IS_ERR(irqkey.class))
  {
    return PTR_ERR(irqkey.class);
  }

  irqkey.device = device_create(irqkey.class, NULL, irqkey.devid, NULL, KEY_NAME);                   //创建设备
  if(IS_ERR(irqkey.device))
  {
    return PTR_ERR(irqkey.device);
  }

  return 0;
}


static void __exit irqkey_exit(void)
{
  cdev_del(&irqkey.cdev);                   //注销字符设备
  unregister_chrdev_region(irqkey.devid, KEY_CNT);      //注销设备号
  device_destroy(irqkey.class, irqkey.devid);           //注销设备节点
  class_destroy(irqkey.class);                          //注销设备类
}

module_init(irqkey_init);
module_exit(irqkey_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LMENG");

 

第三部分:

 

  Makefile 文件:

 

KERNELDIR := /home/lmeng/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

 

CURRENT_PATH := $(shell pwd)

 

obj-m := irqkey.o

 

build : kernel_modules

 

kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第四部分:

  irqkeyApp.c 文件:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  int error = -20;
  int button_status = 0;

  int fd;
  char *filename;

  filename = argv[1];
  fd = open(filename, O_RDWR);

  if(fd < 0)
  {
    printf("can't open file %s\r\n", filename);
    return -1;
  }

  printf("wait button down... \n");
  printf("wait button down... \n");

  do
  {
    error = read(fd, &button_status, sizeof(button_status)); //读取按键状态

    if (error < 0)
    {
      printf("read file error! \n");
    }
    usleep(1000 * 100); //延时100毫秒

  } while (0 == button_status);

  printf("button Down !\n");

  error = close(fd);

  if (error < 0)
  { 
    printf("close file error! \n");
  }
  return 0;
}