天天看點

Linux驅動開發十六.input系統——1.input_dev

我們在前面通過pin控制學習了LInux核心的GPIO和Pinctrl子系統。GPIO主要用作簡單的引腳控制驅動,今天我們來了解一下一組新的子系統:input子系統。input子系統是Linux核心專門提出來處理按鍵、滑鼠、鍵盤和觸摸屏等輸入裝置的輸入事件的。輸入裝置從本質上來說還是屬于字元裝置。input子系統就是将輸入事件分隔開,使用者隻需要負責上報類似鍵值、坐标等資訊,input的核心層負責處理這些事件。

input子系統

input子系統是Linux針對輸入裝置而建立的架構,借用正點原子教程上的圖,input子系統将輸入系統分解成下面的結構

Linux驅動開發十六.input系統——1.input_dev

最左邊的部分是最底層的具體的輸入裝置,中間部分屬于Linux的核心空間,分為驅動層和核心層。右邊的屬于使用者态。這種結構就符合我們前面一直強調的驅動的分層,即不同的層級幹不同的事情:

  • 驅動層負責輸入裝置的具體驅動,比如按鍵、滑鼠或觸摸屏的具體驅動程式,然後向核心報告相關内容
  • 核心層類似一個中間鍵,起到一個承上啟下的作用,一邊面向驅動提供接口,另一側向核心報告輸入内容。核心層還有個很重要的功能就是幫我們注冊一個字元裝置
  • 事件層主要用來處理輸入事件,同時也和使用者态做資料互動。

我們這一章主要實作input子系統的驅動架構編寫。

input驅動編寫

input核心層

要寫input驅動,我們可以先看一下核心裡是怎麼使用input核心層的(drivers/input/input.cinput.c)

直接拉倒最底,可以看見類似我們加載子產品的一個函數的用法,調用了裝置初始化的函數

Linux驅動開發十六.input系統——1.input_dev

sysbsys_initcall是一個分支預測用法的宏,大緻作用和我們前面的module_init是一樣的。看看上面input_init函數

Linux驅動開發十六.input系統——1.input_dev

直接截圖比較好看,可以看下上面的函數中有個class_register注冊了類,還有個register_chrdev_region建立了字元裝置,并且建立字元裝置的時候還可以看到定義的主裝置号(INPUT_MAJOR,宏定義,路徑為include/uapi/linux/major.h可以展開看到)。

#define INPUT_MAJOR        13      

也就是建立的裝置節點名稱叫input,主裝置号是13。這個過程和我們前面寫的字元裝置驅動架構内容差不多。建立的類在系統啟動後會在sys/class路徑下出現

Linux驅動開發十六.input系統——1.input_dev

裡面就有一些可以讓我們使用的輸入裝置,并且這個節點在系統啟動以後會自動生成這個節點,

Linux驅動開發十六.input系統——1.input_dev

如上圖所示,生成了input節點,并且主裝置号就是13。這麼說來,核心已經幫我們完成了input子系統的大緻架構,我們要做的就是根據input子系統的要求來編寫實際裝置的驅動(input_dev),然後注冊到核心裡就行了。

input_dev

在使用input子系統的時候我們需要注冊一個input裝置,核心中使用結構體input_dev來描述input裝置(include/linux/input.h)

Linux驅動開發十六.input系統——1.input_dev
Linux驅動開發十六.input系統——1.input_dev
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的結構體比較大,不過還好的是核心源碼給出了大部分成員的作用(就在結構體上面)

Linux驅動開發十六.input系統——1.input_dev
Linux驅動開發十六.input系統——1.input_dev
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檔案,加載子產品以後可以測試一下

Linux驅動開發十六.input系統——1.input_dev

可以看到,在沒有加載子產品前,/dev/input路徑下隻有2個檔案,在加載完子產品以後會有個新的檔案event1(加載後生成的input2檔案在解除安裝後重新加載會變成input3,不知道是不是哪個資源沒釋放),這個event1就是對應我們的按鍵。那麼怎麼測試這個按鍵呢?要用到一個新的指令:hexdump

Linux驅動開發十六.input系統——1.input_dev
Linux驅動開發十六.input系統——1.input_dev

 看看,列印出來了一堆資訊,這個資訊就是對應了input_event結構體。這個結構體在我們後面的章節會再分析。這個event1檔案就是我們使用者态程式要擷取按鍵資訊時所操作的對象。