我们在前面通过pin控制学习了LInux内核的GPIO和Pinctrl子系统。GPIO主要用作简单的引脚控制驱动,今天我们来了解一下一组新的子系统:input子系统。input子系统是Linux内核专门提出来处理按键、鼠标、键盘和触摸屏等输入设备的输入事件的。输入设备从本质上来说还是属于字符设备。input子系统就是将输入事件分隔开,用户只需要负责上报类似键值、坐标等信息,input的核心层负责处理这些事件。

input子系统

input子系统是Linux针对输入设备而创建的框架,借用正点原子教程上的图,input子系统将输入系统分解成下面的结构

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_#define

 

最左边的部分是最底层的具体的输入设备,中间部分属于Linux的内核空间,分为驱动层和核心层。右边的属于用户态。这种结构就符合我们前面一直强调的驱动的分层,即不同的层级干不同的事情:

  • 驱动层负责输入设备的具体驱动,比如按键、鼠标或触摸屏的具体驱动程序,然后向内核报告相关内容
  • 核心层类似一个中间键,起到一个承上启下的作用,一边面向驱动提供接口,另一侧向内核报告输入内容。核心层还有个很重要的功能就是帮我们注册一个字符设备
  • 事件层主要用来处理输入事件,同时也和用户态做数据交互。

我们这一章主要实现input子系统的驱动框架编写。

input驱动编写

input核心层

要写input驱动,我们可以先看一下内核里是怎么使用input核心层的(drivers/input/input.cinput.c)

直接拉倒最底,可以看见类似我们加载模块的一个函数的用法,调用了设备初始化的函数

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_input子系统_02

sysbsys_initcall是一个分支预测用法的宏,大致作用和我们前面的module_init是一样的。看看上面input_init函数

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_input子系统_03

直接截图比较好看,可以看下上面的函数中有个class_register注册了类,还有个register_chrdev_region创建了字符设备,并且创建字符设备的时候还可以看到定义的主设备号(INPUT_MAJOR,宏定义,路径为include/uapi/linux/major.h可以展开看到)。

#define INPUT_MAJOR        13

也就是创建的设备节点名称叫input,主设备号是13。这个过程和我们前面写的字符设备驱动框架内容差不多。创建的类在系统启动后会在sys/class路径下出现

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_#define_04

里面就有一些可以让我们使用的输入设备,并且这个节点在系统启动以后会自动生成这个节点,

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_#define_05

如上图所示,生成了input节点,并且主设备号就是13。这么说来,内核已经帮我们完成了input子系统的大致框架,我们要做的就是根据input子系统的要求来编写实际设备的驱动(input_dev),然后注册到内核里就行了。

input_dev

在使用input子系统的时候我们需要注册一个input设备,内核中使用结构体input_dev来描述input设备(include/linux/input.h)

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_#define_06

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_input子系统_07

1 struct input_dev {
 2     const char *name;
 3     const char *phys;
 4     const char *uniq;
 5     struct input_id id;
 6 
 7     unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
 8 
 9     unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
10     unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
11     unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
12     unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
13     unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
14     unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
15     unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
16     unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
17     unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
18 
19     unsigned int hint_events_per_packet;
20 
21     unsigned int keycodemax;
22     unsigned int keycodesize;
23     void *keycode;
24 
25     int (*setkeycode)(struct input_dev *dev,
26               const struct input_keymap_entry *ke,
27               unsigned int *old_keycode);
28     int (*getkeycode)(struct input_dev *dev,
29               struct input_keymap_entry *ke);
30 
31     struct ff_device *ff;
32 
33     unsigned int repeat_key;
34     struct timer_list timer;
35 
36     int rep[REP_CNT];
37 
38     struct input_mt *mt;
39 
40     struct input_absinfo *absinfo;
41 
42     unsigned long key[BITS_TO_LONGS(KEY_CNT)];
43     unsigned long led[BITS_TO_LONGS(LED_CNT)];
44     unsigned long snd[BITS_TO_LONGS(SND_CNT)];
45     unsigned long sw[BITS_TO_LONGS(SW_CNT)];
46 
47     int (*open)(struct input_dev *dev);
48     void (*close)(struct input_dev *dev);
49     int (*flush)(struct input_dev *dev, struct file *file);
50     int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
51 
52     struct input_handle __rcu *grab;
53 
54     spinlock_t event_lock;
55     struct mutex mutex;
56 
57     unsigned int users;
58     bool going_away;
59 
60     struct device dev;
61 
62     struct list_head    h_list;
63     struct list_head    node;
64 
65     unsigned int num_vals;
66     unsigned int max_vals;
67     struct input_value *vals;
68 
69     bool devres_managed;
70 };

input_dev定义

这个input_dev的结构体比较大,不过还好的是内核源码给出了大部分成员的作用(就在结构体上面)

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_#define_06

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_input子系统_07

1 /**
 2  * struct input_dev - represents an input device
 3  * @name: name of the device
 4  * @phys: physical path to the device in the system hierarchy
 5  * @uniq: unique identification code for the device (if device has it)
 6  * @id: id of the device (struct input_id)
 7  * @propbit: bitmap of device properties and quirks
 8  * @evbit: bitmap of types of events supported by the device (EV_KEY,
 9  *    EV_REL, etc.)
10  * @keybit: bitmap of keys/buttons this device has
11  * @relbit: bitmap of relative axes for the device
12  * @absbit: bitmap of absolute axes for the device
13  * @mscbit: bitmap of miscellaneous events supported by the device
14  * @ledbit: bitmap of leds present on the device
15  * @sndbit: bitmap of sound effects supported by the device
16  * @ffbit: bitmap of force feedback effects supported by the device
17  * @swbit: bitmap of switches present on the device
18  * @hint_events_per_packet: average number of events generated by the
19  *    device in a packet (between EV_SYN/SYN_REPORT events). Used by
20  *    event handlers to estimate size of the buffer needed to hold
21  *    events.
22  * @keycodemax: size of keycode table
23  * @keycodesize: size of elements in keycode table
24  * @keycode: map of scancodes to keycodes for this device
25  * @getkeycode: optional legacy method to retrieve current keymap.
26  * @setkeycode: optional method to alter current keymap, used to implement
27  *    sparse keymaps. If not supplied default mechanism will be used.
28  *    The method is being called while holding event_lock and thus must
29  *    not sleep
30  * @ff: force feedback structure associated with the device if device
31  *    supports force feedback effects
32  * @repeat_key: stores key code of the last key pressed; used to implement
33  *    software autorepeat
34  * @timer: timer for software autorepeat
35  * @rep: current values for autorepeat parameters (delay, rate)
36  * @mt: pointer to multitouch state
37  * @absinfo: array of &struct input_absinfo elements holding information
38  *    about absolute axes (current value, min, max, flat, fuzz,
39  *    resolution)
40  * @key: reflects current state of device's keys/buttons
41  * @led: reflects current state of device's LEDs
42  * @snd: reflects current state of sound effects
43  * @sw: reflects current state of device's switches
44  * @open: this method is called when the very first user calls
45  *    input_open_device(). The driver must prepare the device
46  *    to start generating events (start polling thread,
47  *    request an IRQ, submit URB, etc.)
48  * @close: this method is called when the very last user calls
49  *    input_close_device().
50  * @flush: purges the device. Most commonly used to get rid of force
51  *    feedback effects loaded into the device when disconnecting
52  *    from it
53  * @event: event handler for events sent _to_ the device, like EV_LED
54  *    or EV_SND. The device is expected to carry out the requested
55  *    action (turn on a LED, play sound, etc.) The call is protected
56  *    by @event_lock and must not sleep
57  * @grab: input handle that currently has the device grabbed (via
58  *    EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
59  *    recipient for all input events coming from the device
60  * @event_lock: this spinlock is is taken when input core receives
61  *    and processes a new event for the device (in input_event()).
62  *    Code that accesses and/or modifies parameters of a device
63  *    (such as keymap or absmin, absmax, absfuzz, etc.) after device
64  *    has been registered with input core must take this lock.
65  * @mutex: serializes calls to open(), close() and flush() methods
66  * @users: stores number of users (input handlers) that opened this
67  *    device. It is used by input_open_device() and input_close_device()
68  *    to make sure that dev->open() is only called when the first
69  *    user opens device and dev->close() is called when the very
70  *    last user closes the device
71  * @going_away: marks devices that are in a middle of unregistering and
72  *    causes input_open_device*() fail with -ENODEV.
73  * @dev: driver model's view of this device
74  * @h_list: list of input handles associated with the device. When
75  *    accessing the list dev->mutex must be held
76  * @node: used to place the device onto input_dev_list
77  * @num_vals: number of values queued in the current frame
78  * @max_vals: maximum number of values queued in a frame
79  * @vals: array of values queued in the current frame
80  * @devres_managed: indicates that devices is managed with devres framework
81  *    and needs not be explicitly unregistered or freed.
82  */

input_dev成员含义

input_dev在使用以前必须先申请!申请过程是下面的函数

1 struct input_dev *input_allocate_device(void)
 2 {
 3     static atomic_t input_no = ATOMIC_INIT(-1);
 4     struct input_dev *dev;
 5 
 6     dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
 7     if (dev) {
 8         dev->dev.type = &input_dev_type;
 9         dev->dev.class = &input_class;
10         device_initialize(&dev->dev);
11         mutex_init(&dev->mutex);
12         spin_lock_init(&dev->event_lock);
13         init_timer(&dev->timer);
14         INIT_LIST_HEAD(&dev->h_list);
15         INIT_LIST_HEAD(&dev->node);
16 
17         dev_set_name(&dev->dev, "input%lu",
18                  (unsigned long)atomic_inc_return(&input_no));
19 
20         __module_get(THIS_MODULE);
21     }
22 
23     return dev;
24 }

申请好的input子设备在卸载时需要释放

void input_free_device(struct input_dev *dev)

有时间可以把这个input.c流程过一下,真的跟我们写的驱动流程差不多!

input_dev结构体

因为input_dev太大了,我们摘几个重要的部分讲一下,首先是下面这一段成员

unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

这几个内容我们要拆开看看

事件类型evbit

事件类型是个长整型,内核给我们指定了宏供我们使用

1 /*
 2  * Event types
 3  */
 4 #define EV_SYN            0x00                //同步
 5 #define EV_KEY            0x01                //按键
 6 #define EV_REL            0x02                //相对坐标,类似鼠标移动坐标
 7 #define EV_ABS            0x03                //绝对坐标,类似触摸屏触摸坐标
 8 #define EV_MSC            0x04                //杂项事
 9 #define EV_SW             0x05                //开关
10 #define EV_LED            0x11                //LED
11 #define EV_SND            0x12                //声音
12 #define EV_REP            0x14                //重复,类似按键按下未释放
13 #define EV_FF             0x15                //压力
14 #define EV_PWR            0x16                //电源
15 #define EV_FF_STATUS      0x17                //压力状态
16 #define EV_MAX            0x1f                //Todo
17 #define EV_CNT            (EV_MAX+1)          //计数器

从宏的定义名称可以看出来,EV就是event事件,后面大多数名称都可以根据实际名称推断出实际作用。我们下面要用按键作为input子系统的底层硬件,所以要将evbit的值设置为EV_KEY。

键值keybit

事件下面的几个成员是根据实际事件的类型使用的,如果我们使用了按键类型以后可以使用key_bit来指定按键对应的键值(include/uapi/linux/input.h)

/*
 * Keys and buttons
 *
 * Most of the keys/buttons are modeled after USB HUT 1.12
 * (see http://www.usb.org/developers/hidpage).
 * Abbreviations in the comments:
 * AC - Application Control
 * AL - Application Launch Button
 * SC - System Control
 */

#define KEY_RESERVED        0
#define KEY_ESC            1
#define KEY_1            2
#define KEY_2            3
#define KEY_3            4
#define KEY_4            5
#define KEY_5            6
#define KEY_6            7
#define KEY_7            8
.........
#define BTN_TRIGGER_HAPPY34        0x2e1
#define BTN_TRIGGER_HAPPY35        0x2e2
#define BTN_TRIGGER_HAPPY36        0x2e3
#define BTN_TRIGGER_HAPPY37        0x2e4
#define BTN_TRIGGER_HAPPY38        0x2e5
#define BTN_TRIGGER_HAPPY39        0x2e6
#define BTN_TRIGGER_HAPPY40        0x2e7

有了上面的宏定义,我们可以把开发板上的按键模拟成键盘上随便一个按键,除了按键,还可以定义成不同的button效果。

我们这一章主要是将按键作为input的实际输入设备,所以后面那些type对应的事件就不再展开说了。在初始化input_dev的时候,主要就是要定义好evbit和keybit,如果需要连续按下效果还要在evbit里定义EV_REP。input_dev在初始化完成以后,要使用下面函数的向内核注册(最后要卸妆)

int __must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);

 

input_dev初始化

input_dev是我们申请得到的,在申请以后需要按事件对其进行初始化,初始化有三种方法

1 static int __init xxx_init(void)
 2 {
 3     inputdev = input_allocate_device();     //申请input设备
 4 
 5     inputdev->name = "keytest";             //设置input的name为keytest
 6 
 7     /*初始化方法一*/
 8     __set_bit(EV_KEY,inputdev->evbit);      //设置input事件类型为按键
 9     __set_bit(EV_REP,inputdev->evbit);      //允许input设备重复
10     __set_bit(KEY_0,inputdev->keybit);      //设置按键为KEY_0
11 
12     /*初始化方法二*/
13     inputdev->evbit[0] = BIT_MASK(EV_KEY) |
14                          BIT_MASK(EV_REP);
15     inputdev->keybit[BIT_WORD(KEY_0)] |=
16                       BIT_MASK(KEY_0);
17 
18     /*初始化方法三*/
19     inputdev->evbit[0] = BIT_MASK(EV_KEY) |
20                                     BIT_MASK(EV_REP);
21     input_set_capability(inputdev, EV_KEY, KEY_0);
22 
23     input_register_device(inputdev);        //注册
24 }

上面就是初始化inputdev的三种方法,特别是第三种方法还用到一个新的函数input_set_capability,但是三种方法的效果是一样的。

事件上报

在向Linux内核注册完input_dev以后,我们还需要将输入事件上报给Linux内核,比如我们前面一直使用的按键,在按下按键后触发了中断,然后再中断中调用了定时器用来消抖,定时器溢出时进行按键实际的功能行为,在这里的行为就是向内核上报一个事件,告诉内核按键是否被按下,是哪个按键被按下等等。这个上报事件是input_event函数去处理的。

1 /**
 2  * input_event() - report new input event
 3  * @dev: device that generated the event
 4  * @type: type of the event
 5  * @code: event code
 6  * @value: value of the event
 7  *
 8  * This function should be used by drivers implementing various input
 9  * devices to report input events. See also input_inject_event().
10  *
11  * NOTE: input_event() may be safely used right after input device was
12  * allocated with input_allocate_device(), even before it is registered
13  * with input_register_device(), but the event will not reach any of the
14  * input handlers. Such early invocation of input_event() may be used
15  * to 'seed' initial state of a switch or initial position of absolute
16  * axis, etc.
17  */
18 void input_event(struct input_dev *dev,
19          unsigned int type, unsigned int code, int value)
20 {
21     unsigned long flags;
22 
23     if (is_event_supported(type, dev->evbit, EV_MAX)) {
24 
25         spin_lock_irqsave(&dev->event_lock, flags);
26         input_handle_event(dev, type, code, value);
27         spin_unlock_irqrestore(&dev->event_lock, flags);
28     }
29 }

 函数的参数具体作用我们下面使用的时候会讲到

 针对按键而言,还有一个函数是通过调用input_event来实现按键的事件上报。

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_KEY, code, !!value);
}

重要点:在输入事件上报完成后,一定要用input_sync函数同步!!!

input模板编写

这次这个input驱动框架的编写我们使用前面那个带抖动消除的按键驱动的整体框架来实现。先把整个代码放出来吧

1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/init.h>
  4 #include <linux/fs.h>
  5 #include <linux/uaccess.h>
  6 #include <linux/io.h>
  7 #include <linux/types.h>
  8 #include <linux/cdev.h>
  9 #include <linux/device.h>
 10 #include <linux/of.h>
 11 #include <linux/of_address.h>
 12 #include <linux/of_irq.h>
 13 #include <linux/gpio.h>
 14 #include <linux/of_gpio.h>
 15 #include <linux/irq.h>
 16 #include <linux/interrupt.h>
 17 #include <linux/input.h>
 18 
 19 #define DEVICE_CNT      1
 20 #define DEVICE_NAME    "imx6ukey"
 21 
 22 #define KEY_NUM         1
 23 #define KEY0VALUE       0x01
 24 #define INVALKEYS       0xFF
 25 
 26 /**
 27  * @brief 按键中断结构体 
 28  * 
 29  */
 30 struct irq_keydesc {
 31     int gpio;                           //io编号
 32     int irqnum;                         //中断号
 33     unsigned char value;                //键值
 34     char name[10];                      //按键名字
 35     irqreturn_t (*handler)(int,void*);  //中断处理函数  
 36 };
 37 
 38 /**
 39  * @brief 设备结构体
 40  * 
 41  */
 42 struct new_dev
 43 {
 44 
 45     struct device_node *dev_nd;
 46     int dev_gpio;
 47 
 48     struct irq_keydesc irqkey[KEY_NUM];         //按键描述数组
 49 
 50     struct timer_list timer;    //定时器
 51     int timer_per;
 52 
 53     struct input_dev *inputdev; //输入设备
 54 
 55 };
 56 
 57 struct new_dev new_dev;
 58 
 59 /**
 60  * @brief 中断处理函数
 61  * 
 62  */
 63 static irqreturn_t key0_handle_irq(int irq, void *dev_id)
 64 {   
 65     int value = 0;
 66     struct new_dev *dev = dev_id;
 67     dev->timer.data = dev_id;
 68     mod_timer(&dev->timer,jiffies + msecs_to_jiffies(10));
 69     return IRQ_HANDLED;
 70 }
 71 
 72 /**
 73  * @brief 消抖定时处理函数
 74  * 
 75  * @param arg 
 76  */
 77 timer_func(unsigned long arg){
 78 
 79     int value = 0;
 80     struct new_dev *dev =(struct new_dev*)arg;
 81 
 82     value = gpio_get_value(dev->irqkey[0].gpio);
 83 
 84     /*上报按键值*/
 85     if(value == 0){             //按下  
 86         input_event(dev->inputdev,EV_KEY,KEY_0,1);   
 87         input_sync(dev->inputdev);
 88     }
 89     else if(value == 1){        //释放
 90         input_event(dev->inputdev,EV_KEY,KEY_0,0);   
 91         input_sync(dev->inputdev);
 92     }
 93 }
 94 
 95 static int dev_gpio_init(struct new_dev *dev)
 96 {
 97     int ret = 0;
 98     int i = 0;
 99 
100     //搜索设备树节点
101     dev->dev_nd = of_find_node_by_path("/key");
102     if(dev->dev_nd == NULL){
103         printk("can't find device key\r\n");
104         ret = -EINVAL;
105         goto fail_nd;
106     }
107 
108     for(i=0;i<KEY_NUM;i++)
109     {   
110         dev->irqkey[i].gpio = of_get_named_gpio(dev->dev_nd,"key-gpios",i); //多个按键获取
111         if(dev->irqkey[i].gpio<0){
112             ret = -EINVAL;
113             goto fail_gpio_num;
114         }
115         ret = gpio_request(dev->irqkey[i].gpio,dev->irqkey[i].name);
116         if(ret){
117             ret = -EBUSY;
118             goto fail_gpio_request;
119         }
120         gpio_direction_input(dev->irqkey[i].gpio);
121         dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);           //获取中断号
122     }
123 
124     dev->irqkey[0].handler = key0_handle_irq;
125     dev->irqkey[0].value = KEY0VALUE;
126 
127     for(i=0;i<KEY_NUM;i++){
128         memset(dev->irqkey[i].name,0,sizeof(dev->irqkey[i].name));
129         sprintf(dev->irqkey[i].name,"KEY%d",i);  //将格式化数据写入字符串中
130         ret = request_irq(dev->irqkey[i].irqnum,                            //中断号
131                             key0_handle_irq,                                //中断处理函数
132                             IRQ_TYPE_EDGE_RISING|IRQ_TYPE_EDGE_FALLING,     //中断处理函数
133                             dev->irqkey[i].name,                            //中断名称
134                             dev                                             //设备结构体
135                             );
136         if(ret){
137             printk("irq %d request err\r\n",dev->irqkey[i].irqnum);
138             goto fail_irq;
139         }
140     }
141     //此处不设置定时值,防止定时器add后直接运行
142     init_timer(&dev->timer);
143     dev->timer.function = timer_func;
144 
145     return 0;
146     fail_gpio_request:
147     fail_irq:
148         for(i=0; i<KEY_NUM;i++){
149             gpio_free(dev->irqkey[i].gpio);
150         }
151     fail_gpio_num:
152     fail_nd:
153         return ret; 
154 }
155 
156 static int __init key_init(void)
157 {
158     int ret = 0; 
159 
160     /*GPIO初始化*/
161     ret = dev_gpio_init(&new_dev);
162     if(ret<0){
163         goto fail_gpio_init;
164     }
165 
166     /*input_dev处理*/
167     //申请input_dev
168     new_dev.inputdev = input_allocate_device();
169     if(new_dev.inputdev == NULL){
170         ret = -EINVAL;
171         goto fail_gpio_init;        //申请失败,误操作
172     }
173 
174     //input_dev初始化
175     new_dev.inputdev->name = DEVICE_NAME;
176     __set_bit(EV_KEY,new_dev.inputdev->evbit);         //按键事件
177     __set_bit(EV_REP,new_dev.inputdev->evbit);         //允许重复
178     __set_bit(KEY_0,new_dev.inputdev->keybit);         //设置键值
179 
180     //input_dev注册
181     ret = input_register_device(new_dev.inputdev);
182     if(ret){
183         goto fail_input_register;
184     }
185     return ret;
186 
187 fail_input_register: 
188     input_free_device(new_dev.inputdev);
189 fail_gpio_init:
190 
191     return ret;
192 }
193 
194 static void __exit key_exit(void){
195     int i = 0;
196     //释放中断
197     for(i=0;i<KEY_NUM;i++){
198         free_irq(new_dev.irqkey[i].irqnum,&new_dev);
199     }
200 
201     //释放GPIO
202     for(i=0;i<KEY_NUM;i++){
203         gpio_free(new_dev.irqkey[i].gpio);
204     }
205     //释放定时器
206     del_timer_sync(&new_dev.timer);
207 
208     //注销input_dev
209     input_unregister_device(new_dev.inputdev);
210     input_free_device(new_dev.inputdev);
211 
212 }
213 
214 module_init(key_init);
215 module_exit(key_exit);
216 MODULE_LICENSE("GPL");
217 MODULE_AUTHOR("ZeqiZ");

首先我们可以看到,这个new_dev结构体和原先的比起来简化了好多,起码主从设备号、类、设备、cdev、按键状态的原子变量等成员都被删掉了,因为input子系统会帮我们自动创建设备节点。

在设备初始化函数key_init函数中,我们先对GPIO进行初始化,这个过程前面用过了很多次了,就不再说了。

174~178行是我们这一章节所讲的input_dev申请以及初始化

181~184行是将初始化的input_dev注册给内核。

整个驱动把文件操作集合也都删除了,因为这个input子系统是通过事件上报来处理具体信息的,这里在定时中断函数里调用了上报事件的函数,函数一共4个参数

input_event(dev->inputdev,EV_KEY,KEY_0,1);     //按键按下
input_event(dev->inputdev,EV_KEY,KEY_0,0);     //按键释放

可以看出来按键在按下和释放时调用函数所传递的参数只有最后一个不同,所以input_event函数的参数第一个就是input_dev对象,第二个就是input事件,在这里就是在初始化input_dev时候定义的EV_KEY,第三个参数是根据事件类型来定的,如果是按键事件就是键值,最后一个value就是当前按键是否被按下。

驱动测试

整个模板完成以后,就可以make生成ko文件,加载模块以后可以测试一下

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_input子系统_10

 

可以看到,在没有加载模块前,/dev/input路径下只有2个文件,在加载完模块以后会有个新的文件event1(加载后生成的input2文件在卸载后重新加载会变成input3,不知道是不是哪个资源没释放),这个event1就是对应我们的按键。那么怎么测试这个按键呢?要用到一个新的命令:hexdump

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_#define_11

 

 

我们在之前分析过input子系统和tty设备驱动架构 input 子系统_#define_12

 看看,打印出来了一堆信息,这个信息就是对应了input_event结构体。这个结构体在我们后面的章节会再分析。这个event1文件就是我们用户态程序要获取按键信息时所操作的对象。