Input子系统:按键驱动

1. 环境:

1.1 开发板:正点原子 I.MX6U ALPHA V2.2

1.2 开发PC:Ubuntu20.04

1.3 U-boot:uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

1.4 LInux内核:linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

1.5 rootfs:busybox-1.29.0.tar.bz2制作

1.6 交叉编译工具链:gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

 

2. 硬件控制说明

2.1 由GPIO1 PIN18控制,高---松开, 低---按下

 

3. 设备树修改

3.1 在节点iomuxc/imx6ul-evk下新增pinctrl_key节点,如下

1     pinctrl_key: keygrp {
2         fsl,pins = <
3             MX6UL_PAD_UART1_CTS_B__GPIO1_IO18            0xF080
4         >;
5     };

 

 3.2 在根目录"/"中创建"gpiokey"子节点

 1     gpiokey {
 2         compatible = "imx-gpiokeys";    
 3 
 4         #address-cells = <1>;
 5         #size-cells = <1>;
 6         pinctrl-names = "default";
 7         pinctrl-0 = <&pinctrl_key>;
 8         key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
 9         interrupt-parent = <&gpio1>;
10         interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
11         status = "okay";
12     };

 

 3.3 检查设备树,将本GPIO做其他用途的属性注释掉

 

4. 驱动代码

  1 #include <linux/types.h>
  2 #include <linux/kernel.h>
  3 #include <linux/delay.h>
  4 #include <linux/ide.h>
  5 #include <linux/init.h>
  6 #include <linux/module.h>
  7 #include <linux/errno.h>
  8 #include <linux/gpio.h>
  9 #include <linux/cdev.h>
 10 #include <linux/device.h>
 11 #include <linux/of.h>
 12 #include <linux/of_address.h>
 13 #include <linux/of_gpio.h>
 14 #include <linux/input.h>
 15 #include <linux/semaphore.h>
 16 #include <linux/timer.h>
 17 #include <linux/of_irq.h>
 18 #include <linux/irq.h>
 19 #include <asm/mach/map.h>
 20 #include <asm/uaccess.h>
 21 #include <asm/io.h>
 22 
 23 
 24 //自定义一个结构体,将一些需要的变量放在其中,以便管理
 25 struct key_device
 26 {  
 27     struct device_node *node;       //key 设备节点
 28     struct input_dev *key_inputdev; //输入设备结构体
 29     int key_gpio;                   //用于保存GPIO编号
 30     int key_irqnum;                 //用于保存中断号
 31     unsigned char key_value;        //用于包括按键号
 32     irqreturn_t (*handler)(int, void *);      //中断处理函数指针
 33     struct timer_list timer;                  //定时器
 34 };
 35 
 36 struct key_device key_in;
 37 
 38 //中断处理函数
 39 static irqreturn_t key_handler(int irq, void *dev_id)
 40 {
 41     struct key_device *key0 = (struct key_device *)dev_id;
 42     key0->timer.data = (volatile long)dev_id;
 43 
 44     //启动定时器,延时10ms,相当于是按键滤波
 45     mod_timer(&key0->timer, jiffies + msecs_to_jiffies(10));
 46     
 47     return IRQ_RETVAL(IRQ_HANDLED);
 48 }
 49 
 50 //定时器处理函数,当按键滤波完成,并且确定产生按键动作时,此函数用于上报按键值
 51 void timer_function(unsigned long arg)
 52 {
 53     unsigned char value;
 54 
 55     struct key_device *key = (struct key_device *)arg;
 56 
 57     //根据GPIO编号,获取当前读取到的值
 58     value = gpio_get_value(key->key_gpio);
 59 
 60     //上报按键值,1--松开    0--按下
 61     if(0 == value)
 62     {
 63         input_report_key(key->key_inputdev, key->key_value, 1);
 64         input_sync(key->key_inputdev);
 65     }
 66     else
 67     {
 68         input_report_key(key->key_inputdev, key->key_value, 0);
 69         input_sync(key->key_inputdev);
 70     }
 71 
 72 }
 73 
 74 
 75 static int __init gpiokey_init(void)
 76 {
 77     int ret = 0;
 78 
 79     //获取按键节点
 80     key_in.node = of_find_node_by_path("/gpiokey");
 81     if(key_in.node == NULL)
 82     {
 83         printk("key node find fail\n");
 84         return -EINVAL;
 85     }
 86 
 87     //获取GPIO编号
 88     key_in.key_gpio = of_get_named_gpio(key_in.node, "key-gpio", 0);    //key-gpio为dts下gpiokey节点的key-gpio属性
 89     if(key_in.key_gpio < 0)
 90     {
 91         printk("gpio get fail\n");
 92     }
 93 
 94     gpio_request(key_in.key_gpio,"key0");   //申请GPIO
 95     gpio_direction_input(key_in.key_gpio);  //设置GPIO为输入
 96     key_in.key_irqnum = irq_of_parse_and_map(key_in.node, 0);   //获取中断号
 97 
 98     key_in.handler = key_handler;  //中断函数,上半部
 99     key_in.key_value = KEY_0;
100 
101     //注册中断
102     ret = request_irq(key_in.key_irqnum, key_in.handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "KEY0", &key_in);
103     if(ret < 0)
104     {
105          printk("request_irq fail,the irqnum is %d\n",key_in.key_irqnum);
106          return -EFAULT;
107     }
108 
109     init_timer(&key_in.timer);  //初始化定时器,此时时钟不会运行,等待mod_timer函数唤醒
110     key_in.timer.function = timer_function; //定时器处理函数
111 
112     //申请一个输入设备
113     key_in.key_inputdev = input_allocate_device();
114     key_in.key_inputdev->name = "keyinputdev";
115 
116     //设置事件
117     key_in.key_inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
118     input_set_capability(key_in.key_inputdev, EV_KEY, KEY_0);   //KEY_0--事件码,include\uapi\linux\input.h
119 
120     ret = input_register_device(key_in.key_inputdev);
121     if(ret < 0)
122     {
123          printk("input_register_device fail\n");
124          return ret;
125     }
126 
127     return 0;
128 }
129 
130 static void __exit gpiokey_exit(void)
131 {   
132     gpio_free(key_in.key_gpio);
133     del_timer_sync(&key_in.timer);
134     free_irq(key_in.key_irqnum,&key_in);
135     input_unregister_device(key_in.key_inputdev);
136     input_free_device(key_in.key_inputdev);
137    
138 }
139 
140 module_init(gpiokey_init);
141 module_exit(gpiokey_exit);
142 MODULE_LICENSE("Dual BSD/GPL");
View Code

 

5. 测试代码

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <fcntl.h>
 6 #include <linux/input.h>
 7 
 8 int main(int argc, char **argv)
 9 {
10     int fd,err;
11     static struct input_event key_inputevent;
12 
13 
14     fd = open("/dev/input/event1", O_RDWR);
15 
16     printf("fd = %d\n", fd);
17     if(fd < 0)
18     {
19         perror("open fail!\n");
20 
21         exit(1);
22     }
23 
24     while(1)
25     {   
26         err = read(fd, &key_inputevent, sizeof(key_inputevent));
27         if(err < 0)
28         {
29             printf("read fail\n");
30             return -1;
31         }
32         else
33         {
34             switch(key_inputevent.type)
35             {
36                 case EV_KEY:
37                         printf("key input code is %x   && value is %x\n", key_inputevent.code, key_inputevent.value);
38                         break;
39                 default:
40                         break;
41             }
42         }
43 
44     }
45 
46     close(fd);
47     return 0;
48 }
View Code

 

总结:

1. 此input之系统按键驱动,涉及到了中断、定时器

2. 本驱动中断处理放在了上半部,如果是其他比较耗时的中断处理,需要放到中断下半部

3. input子系统产生的文件为/dev/input/eventxx,具体是哪个,可以未加载ko前先查看,然后再加载查看;是否可能指定文件名?不然有多个input ko该如何快速有效区分?

4. 本驱动大概流程为:1.设备树增加pinctrl_、按键节点-->2.获取设备树中的资源-->3.申请、设置GPIO-->4.注册中断-->5.输入设备申请、设置-->6.中断处理函数

 

posted @ 2021-10-31 09:31  秋水寒林  阅读(166)  评论(0编辑  收藏  举报