Linux input 子系統詳解


1. 模塊概述

1.1.相關資料和代碼研究

drivers/input/
include/uapi/linux/input-event-codes.h

2. 模塊功能

linux核心的輸入框架

3. 模塊學習

3.1.概述

Linux輸入設備種類繁雜,常見的包括觸摸屏、鍵盤、鼠標、搖桿等;這些輸入設備屬於字符設備,而linux將這些設備的共同特性抽象出來,Linux input 子系統就產生了。

3.2.軟件架構

輸入子系統是由設備驅動層(input driver)、輸入核心層(input core)、輸入事件處理層(input event handle)組成,具體架構如圖4.1所示:
圖4.1 Linux input 子系統架構

  • (1)input設備驅動層:負責具體的硬件設備,將底層的硬件輸入轉化為統一的事件形式,向input核心層和匯報;
    *(2)input核心層:連接input設備驅動層與input事件處理層,向下提供驅動層的接口,向上提供事件處理層的接口;
    *(3)input事件處理層:為不同硬件類型提供了用戶訪問以及處理接口,將硬件驅動層傳來的事件報告給用戶程序。

在input子系統中,每個事件的發生都使用事件(type)->子事件(code)->值(value)
所有的輸入設備的主設備號都是13,input-core通過次設備來將輸入設備進行分類,如0-31是游戲桿,32-63是鼠標(對應Mouse Handler)、64-95是事件設備(如觸摸屏,對應Event Handler)。

Linux輸入子系統支持的數據類型

時間類型 編碼 含義
EV_SYN 0x00 同步事件
EV_KEY 0x01 按鍵事件(鼠標,鍵盤等)
EV_REL 0x02 相對坐標(如:鼠標移動,報告相對最后一次位置的偏移)
EV_ABS 0x03 絕對坐標(如:觸摸屏或操作桿,報告絕對的坐標位置)
EV_MSC 0x04 其它
EV_SW 0x05 開關
EV_LED 0x11 按鍵/設備燈
EV_SND 0x12 聲音/警報
EV_REP 0x14 重復
EV_FF 0x15 力反饋
EV_PWR 0x16 電源
EV_FF_STATUS 0x17 力反饋狀態
EV_MAX 0x1f 事件類型最大個數和提供位掩碼支持

定義的按鍵值

#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 KEY_8                   9
#define KEY_9                   10
#define KEY_0                   11
...

3.3.數據結構

三個數據結構input_dev,input_handle,input_handler之間的關系如圖4.2、4.3所示

圖4.2 三個數據結構之間的關系圖4.3 Input_handler_list、Input_dev_list內部結構

input_dev:是硬件驅動層,代表一個input設備。
input_handler:是事件處理層,代表一個事件處理器。
input_handle:屬於核心層,代表一個配對的input設備與input事件處理器。
input_dev 通過全局的input_dev_list鏈接在一起,設備注冊的時候完成這個操作。

input_handler 通過全局的input_handler_list鏈接在一起。事件處理器注冊的時候實現了這個操作(事件處理器一般內核自帶,不需要我們來寫)

input_hande 沒有一個全局的鏈表,它注冊的時候將自己分別掛在了input_dev 和 input_handler 的h_list上了。通過input_dev 和input_handler就可以找到input_handle在設備注冊和事件處理器,注冊的時候都要進行配對工作,配對后就會實現鏈接。通過input_handle也可以找到input_dev和input_handler。

我們可以看到,input_device和input_handler中都有一個h_list,而input_handle擁有指向input_dev和input_handler的指針,也就是說input_handle是用來關聯input_dev和input_handler的。

那么為什么一個input_device和input_handler中擁有的是h_list而不是一個handle呢?
因為一個device可能對應多個handler,而一個handler也不能只處理一個device,比如說一個鼠標,它可以對應even handler,也可以對應mouse handler,因此當其注冊時與系統中的handler進行匹配,就有可能產生兩個實例,一個是evdev,另一個是mousedev,而任何一個實例中都只有一個handle。
至於以何種方式來傳遞事件,就由用戶程序打開哪個實例來決定。后面一個情況很容易理解,一個事件驅動不能只為一個甚至一種設備服務,系統中可能有多種設備都能使用這類handler,比如event handler就可以匹配所有的設備。在input子系統中,有8種事件驅動,每種事件驅動最多可以對應32個設備,因此dev實例總數最多可以達到256個

3.3.1. Input_dev

輸入設備

/* include/linux/input.h */
struct input_dev {
	    const char *name;  /* 設備名稱 */
	    const char *phys;  /* 設備在系統中的路徑 */
	    const char *uniq;  /* 設備唯一id */
	    struct input_id id;  /* input設備id號 */

	    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

	    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];  /* 設備支持的事件類型,主要有EV_SYNC,EV_KEY,EV_KEY,EV_REL,EV_ABS等*/	
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)];  /* 支持led事件 */
	    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];  /* 支持聲音事件 */
	    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];  /* 支持受力事件 */
	    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];  /* 支持開關事件 */

	    unsigned int hint_events_per_packet;  /*  平均事件數*/

	    unsigned int keycodemax;  /* 支持最大按鍵數 */
	    unsigned int keycodesize;  /* 每個鍵值字節數 */
	    void *keycode;  /* 存儲按鍵值的數組的首地址 */

	    int (*setkeycode)(struct input_dev *dev,
			              const struct input_keymap_entry *ke, unsigned int *old_keycode);
	    int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke);

	    struct ff_device *ff;  /* 設備關聯的反饋結構,如果設備支持 */

	    unsigned int repeat_key;  /* 最近一次按鍵值,用於連擊 */
	    struct timer_list timer;  /* 自動連擊計時器 */

	    int rep[REP_CNT];  /* 自動連擊參數 */

	    struct input_mt *mt;  /* 多點觸控區域 */

	    struct input_absinfo *absinfo;  /* 存放絕對值坐標的相關參數數組 */

	    unsigned long key[BITS_TO_LONGS(KEY_CNT)];  /* 反應設備當前的案件狀態 */
	    unsigned long led[BITS_TO_LONGS(LED_CNT)];  /* 反應設備當前的led狀態 */
	    unsigned long snd[BITS_TO_LONGS(SND_CNT)];  /* 反應設備當前的聲音狀態 */
	    unsigned long sw[BITS_TO_LONGS(SW_CNT)];  /* 反應設備當前的開關狀態 */

	    int (*open)(struct input_dev *dev);  /* 第一次打開設備時調用,初始化設備用 */
	    void (*close)(struct input_dev *dev);  /* 最后一個應用程序釋放設備事件,關閉設備 */
	    int (*flush)(struct input_dev *dev, struct file *file); /* 用於處理傳遞設備的事件 */
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code,          int value);  /* 事件處理函數,主要是接收用戶下發的命令,如點亮led */

	    struct input_handle __rcu *grab;  /* 當前占有設備的input_handle */

	    spinlock_t event_lock;  /* 事件鎖 */
	    struct mutex mutex;  /* 互斥體 */

	    unsigned int users;  /* 打開該設備的用戶數量(input_handle) */
	    bool going_away;  /* 標記正在銷毀的設備 */

	    struct device dev;  /* 一般設備 */

	    struct list_head	h_list;  /* 設備所支持的input handle */
	    struct list_head	node;  /* 用於將此input_dev連接到input_dev_list */

	    unsigned int num_vals;  /* 當前幀中排隊的值數 */
	    unsigned int max_vals;  /*  隊列最大的幀數*/
	    struct input_value *vals;  /*  當前幀中排隊的數組*/

	    bool devres_managed; /* 表示設備被devres 框架管理,不需要明確取消和釋放*/
};

3.3.2. Input_handler

處理具體的輸入事件的具體函數

/* include/linux/input.h */
struct input_handler {

	    void *private;  /* 存放handle數據 */

void (*event)(struct input_handle *handle, unsigned int type, unsigned  int code, int value);
	    void (*events)(struct input_handle *handle,
		           const struct input_value *vals, unsigned int count);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	    bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const  struct input_device_id *id);
	    void (*disconnect)(struct input_handle *handle);
	    void (*start)(struct input_handle *handle);

	    bool legacy_minors;
	    int minor;
	    const char *name;  /* 名字 */

	    const struct input_device_id *id_table;  /* input_dev匹配用的id */

struct list_head	h_list; /* 用於鏈接和handler相關的handle,input_dev與input_handler配對之后就會生成一個input_handle結構 */
	    struct list_head	node;  /* 用於將該handler鏈入input_handler_list,鏈接所有注冊到內核的所有注冊到內核的事件處理器 */
};

3.3.3. Input_handle

連接輸入設備和處理函數

/* include/linux/input.h */
struct input_handle {
	    void *private;  /* 數據指針 */

	    int open;  /* 打開標志,每個input_handle 打開后才能操作 */
	    const char *name;  /* 設備名稱 */

	    struct input_dev *dev;  /* 指向所屬的input_dev */
	    struct input_handler *handler;  /* 指向所屬的input_handler */

	    struct list_head	d_node;  /* 用於鏈入所指向的input_dev的handle鏈表 */
	    struct list_head	h_node;  /* 用於鏈入所指向的input_handler的handle鏈表 */
};

3.3.4. Evdev

字符設備事件

/* drivers/input/evdev.c */
struct evdev {
	    int open;    /* 設備被打開的計數 */
	    struct input_handle handle;  /* 關聯的input_handle */ 
	    wait_queue_head_t wait;  /* 等待隊列,當前進程讀取設備,沒有事件產生時,
進程就會sleep */
	    struct evdev_client __rcu *grab;  /* event響應 */
struct list_head client_list;  /* evdev_client鏈表,說明evdev設備可以處理多個 evdev _client,可以有多個進程訪問evdev設備 */
	    spinlock_t client_lock;
	    struct mutex mutex;
	    struct device dev;
	    struct cdev cdev;
	    bool exist;   /* 設備存在判斷 */
};

3.3.5. evdev_client

字符設備事件響應

/* drivers/input/evdev.c */
struct evdev_client {
	    unsigned int head;  /* 動態索引,每加入一個event到buffer中,head++ */
	    unsigned int tail;  /* 動態索引,每取出一個buffer中到event,tail++ */
	    unsigned int packet_head;  /* 數據包頭部 */
	    spinlock_t buffer_lock;  
	    struct fasync_struct *fasync;  /* 異步通知函數 */
	    struct evdev *evdev;  
	    struct list_head node;  /* evdev_client鏈表項 */
	    int clkid;
	    unsigned int bufsize;
	    struct input_event buffer[];  /* 用來存放input_dev事件緩沖區 */
};

3.3.6. Evdev_handler

evdev_handler事件處理函數

/* drivers/input/input.c */
static struct input_handler evdev_handler = {
	    .event		= evdev_event,   /* 事件處理函數, */  
	    .events	= evdev_events,  /* 事件處理函數, */
	    .connect	= evdev_connect, /* 連接函數,將事件處理和輸入設備聯系起來 */
	    .disconnect	= evdev_disconnect,  /* 斷開該鏈接 */
	    .legacy_minors	= true,
	    .minor		= EVDEV_MINOR_BASE,
	    .name		= "evdev", /* handler名稱 */
	    .id_table	= evdev_ids, /* 斷開該鏈接 */
};

3.3.7. input_event

標准按鍵編碼信息

/* drivers/input/evdev.c */
struct input_event {                                                            
    struct timeval time;   /* 事件發生的時間  */                                
    __u16 type;             /* 事件類型 */                                      
    __u16 code;             /* 事件碼 */                                        
    __s32 value;            /* 事件值 */                                        
};   

3.3.8. input_id

和input輸入設備相關的id信息

/* include/uapi/linux/input.h */
struct input_id {  
    __u16 bustype;  /* 總線類型 */  
    __u16 vendor;  /* 生產廠商 */  
    __u16 product;  /* 產品類型 */ 
    __u16 version;  /* 版本 */
 };

3.3.9. input_device_id

/* include/uapi/linux/input.h */
struct input_device_id {

	    kernel_ulong_t flags;

	    __u16 bustype;  /* 總線類型 */
	    __u16 vendor;  /* 生產廠商 */
	    __u16 product;  /* 產品類型 */
	    __u16 version;  /* 版本 */

	    kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
	    kernel_ulong_t propbit[INPUT_DEVICE_ID_PROP_MAX / BITS_PER_LONG + 1];

	    kernel_ulong_t driver_info;
};

3.3.10. input_even

輸入事件的傳遞已input_event為基本單位

struct input_event {
	struct timeval time; //時間戳
	    __u16 type; //事件總類型
	    __u16 code; //事件子類型
	    __s32 value; //事件值
};

3.4. Linux input 子系統關鍵流程

核心層,執行的時候會注冊設備號,然后在handler層注冊input_handler,也就是evdev_handler會注冊到核心層維護的鏈表中。

然后進行硬件初始化獲取數據,而且需要將設備注冊到鏈表中。注冊進來就就會遍歷input_handler_list鏈表,找到對應的handler,匹配成功后會調用connect方法。connect分配evdev,evdev就記錄了input_handler和input_device之間的關系,同時創建設備節點,還會注冊cdev從而可以讓應用調用。

當應用程序調用open,read等接口的時候就會調用input_handler層實現的xxx_open,那么open就會分配好evdev_client,最終在input_dev層上報數據的時候會自動調用input_handler,input_handler就會調用events填充上報的數據到緩沖區client,此時如果沒有喚醒隊列的話應用read的時候會阻塞,而喚醒隊列后最終使用copy_to_user來給應用數據。
設備驅動程序上報事件的函數有:

input_report_key //上報按鍵事件
input_report_rel //上報相對坐標事件
input_report_abs //上報絕對坐標事件
input_report_ff_status
input_report_switch
input_sync //上報完成后需要調用這些函數來通知系統處理完整事件 
input_mt_sync //上報完成后需要調用這些函數來通知系統處理完整事件

這些函數其實是input_event函數的封裝,調用的都是input_event函數,在輸入設備驅動(input_dev)中,一般通過輪詢或中斷方式獲取輸入事件的原始值(raw value),經過處理后再使用input_event()函數上報;核心層將事件數據(type、code、value)打包、分發至事件處理器;調用關系為:input_event->input_handle_event->input_pass_values,這一函數都在input.c實現。

3.4.1. Input 設備注冊流程

輸入設備注冊過程如圖4.3所示

圖4.3 輸入設備注冊流程圖

3.4.2. 連接設備流程

連接設備流程如圖4.4所示

圖4.4 連接設備流程圖

3.4.3. 事件上報流程

事件上報流程如圖4.5所示
圖4.5 事件上報流程圖

3.4.4. 數據讀取流程

數據讀取流程如圖4.6所示
圖4.6數據讀取流程圖

3.5.關鍵函數解析

3.5.1. input_init

input子系統使用subsys_initcall宏修飾input_init()函數在內核啟動階段被調用。input_init()函數在內核啟動階段被調用。input_init()函數的主要工作是:在sys文件系統下創建一個設備類(/sys/class/input),調用register_chrdev()函數注冊input設備。

/* drivers/input/input.c */
static int __init input_init(void)
{
	    int err;

	    err = class_register(&input_class);  /* 注冊類,放在sys/class下 */  
	    if (err) {
		        pr_err("unable to register input_dev class\n");
		        return err;
	    }

	    err = input_proc_init();  /* 在proc目錄下建立相關目錄 */
	    if (err)
		        goto fail1;

	    err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
				    INPUT_MAX_CHAR_DEVICES, "input");  /* 注冊字符設備編號,INPUT_MAJOR		 永遠是13 */  
	    if (err) {
		        pr_err("unable to register char major %d", INPUT_MAJOR);
		        goto fail2;
	    }

	    return 0;

 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	    return err;
}

3.5.2. Input_register_device

/* drivers/input/input.c */
int input_register_device(struct input_dev *dev)
{
    struct atomic_t  input_no = ATOMIC_INIT(0);
	    struct input_devres *devres = NULL;
	    struct input_handler *handler;
	    unsigned int packet_size;
	    const char *path;
	    int error;

	    if (dev->devres_managed) {
		        devres = devres_alloc(devm_input_device_unregister,
				              sizeof(*devres), GFP_KERNEL);
		    if (!devres)
			        return -ENOMEM;

		    devres->input = dev;
	}

	    /* 每個input_device都會產生EV_SYN/SYN_REPORT時間,所以就放在一起設置 */
	    __set_bit(EV_SYN, dev->evbit);

	    /* KEY_RESERVED is not supposed to be transmitted to userspace. */
	    __clear_bit(KEY_RESERVED, dev->keybit);

	    /* 沒有設置的位,確保被清零 */
	    input_cleanse_bitmasks(dev);
	    /*  */
	    packet_size = input_estimate_events_per_packet(dev);
	    if (dev->hint_events_per_packet < packet_size)
		         dev->hint_events_per_packet = packet_size;

	    dev->max_vals = dev->hint_events_per_packet + 2;
	    dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
	    if (!dev->vals) {
		        error = -ENOMEM;
		        goto err_devres_free;
	    }

    /* 如果延時周期是程序預先設定的,那么是由驅動自動處理,主要是為了處理重復按鍵 */
    	init_timer(&dev->timer);
	    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
		        dev->timer.data = (long) dev;
		        dev->timer.function = input_repeat_key;
		        dev->rep[REP_DELAY] = 250;
		        dev->rep[REP_PERIOD] = 33;
	     }

	    if (!dev->getkeycode)  /* 獲取按鍵值 */
		        dev->getkeycode = input_default_getkeycode;

	    if (!dev->setkeycode)  /* 設置按鍵值 */
		        dev->setkeycode = input_default_setkeycode;

	    error = device_add(&dev->dev);  /* 將dev注冊到sys */
	    if (error)
		        goto err_free_vals;

	    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
	    pr_info("%s as %s\n",
		    dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
	    kfree(path);

	    error = mutex_lock_interruptible(&input_mutex);
	    if (error)
		        goto err_device_del;

	    list_add_tail(&dev->node, &input_dev_list);  /* 將新的dev放入鏈表中 */
/* 遍歷input_handler_list鏈表中的所有input_handler,是否支持這個新input_dev ;
若兩者支持,便進行連接 */
	    list_for_each_entry(handler, &input_handler_list, node)
		    input_attach_handler(dev, handler); 

	    input_wakeup_procfs_readers();

	    mutex_unlock(&input_mutex);

	    if (dev->devres_managed) {
		    dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
			        __func__, dev_name(&dev->dev));
		    devres_add(dev->dev.parent, devres);
	 }
	return 0;

err_device_del:
	    device_del(&dev->dev);
err_free_vals:
	    kfree(dev->vals);
	    dev->vals = NULL;
err_devres_free:
	    devres_free(devres);
	    return error;
}
EXPORT_SYMBOL(input_register_device);

input_dev_list和input_handler_list是全局的一個鏈表

static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);

list_for_each_entry是一個宏,展開如下
獲取input_handler_list的每一項和input_dev匹配,通過for循環遍歷.

for (handler = list_first_entry(input_handler_list, &handler, node);	
     &handler->node != (input_handler_list);					
     &handler = list_next_entry(&handler, node))

     input_attach_handler(dev, handler);

list_first_entry 獲得第一個列表元素
list_next_entry 獲得下一個列表元素

3.5.3. input_register_handle

注冊一個handle,鏈接input_handler和input_dev的h_list

/* drivers/input/input.c */
int input_register_handle(struct input_handle *handle)
{
	    struct input_handler *handler = handle->handler;
	    struct input_dev *dev = handle->dev;
	    int error;

	    error = mutex_lock_interruptible(&dev->mutex);
	    if (error)
		       return error;
    
	    if (handler->filter)
		        list_add_rcu(&handle->d_node, &dev->h_list);
	    else
		       list_add_tail_rcu(&handle->d_node, &dev->h_list);
/* 將handle的d_node,鏈接到其相關的input_dev的h_list鏈表中  */

	    mutex_unlock(&dev->mutex);

	    list_add_tail_rcu(&handle->h_node, &handler->h_list);
/* 將handle的h_node,鏈接到其相關的input_handler的h_list鏈表中 */

	    if (handler->start)
		        handler->start(handle);

	return 0;
}
EXPORT_SYMBOL(input_register_handle);

3.5.4. input_register_handler

注冊一個事件,進行匹配設備和事件的綁定

/* drivers/input/input.c */
int input_register_handler(struct input_handler *handler)
{
	    struct input_dev *dev;
	    int error;

	    error = mutex_lock_interruptible(&input_mutex);
	    if (error)
		        return error;

	   INIT_LIST_HEAD(&handler->h_list);

	    list_add_tail(&handler->node, &input_handler_list);  /* 連接到input_handler_list鏈表中 */
/* 遍歷input_dev_list,配對 input_dev 和 handler */
	    list_for_each_entry(dev, &input_dev_list, node)
		    input_attach_handler(dev, handler);  /* event節點加入列表 */

	    input_wakeup_procfs_readers();

	    mutex_unlock(&input_mutex);
	    return 0;
}
EXPORT_SYMBOL(input_register_handler);

3.5.5. evdev_connect

事件處理器evdev,生成一個新的evdev設備,連接input核心,回調函數。

/* drivers/input/evdev.c */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	    struct evdev *evdev;
	    int minor;
	    int dev_no;
	    int error;
    /* 獲取次設備號,從evdev_table中找到一個未使用的最小的數組項,最大值32 */
	    minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
	    if (minor < 0) {
		        error = minor;
		        pr_err("failed to reserve new minor: %d\n", error);
		        return error;
	    }
    /* 分配空間 */
	    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	    if (!evdev) {
		        error = -ENOMEM;
		        goto err_free_minor;
	    }
/* 初始化client_list鏈表頭,代表多少應用讀寫這個設備 */
	    INIT_LIST_HEAD(&evdev->client_list);    
	    spin_lock_init(&evdev->client_lock);  /* 加鎖 */ 
	    mutex_init(&evdev->mutex);  /*  */
init_waitqueue_head(&evdev->wait);  /* 初始化等待隊列,當evdev沒有數據可讀時,就 在 該隊列上睡眠 */
	    evdev->exist = true;  /* 設備存在 */

	    dev_no = minor;
	
	    if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
		        dev_no -= EVDEV_MINOR_BASE;
	    dev_set_name(&evdev->dev, "event%d", dev_no);  /* 設置設備名為eventX */

	    evdev->handle.dev = input_get_device(dev);  /* 獲取設備 */
	    evdev->handle.name = dev_name(&evdev->dev);  /* 設備名稱 */
	    evdev->handle.handler = handler;  /* handler綁定 */  
	    evdev->handle.private = evdev;  /* evdev數據指向 */

	    evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);  /* sysfs下的設備號 */
	    evdev->dev.class = &input_class;  /* 將input_class作為設備類 */
	    evdev->dev.parent = &dev->dev;  /* input_dev作為evdev的父設備 */
	    evdev->dev.release = evdev_free;  /* 釋放函數 */
	    device_initialize(&evdev->dev);  /* 初始化設備 */
        /* 注冊一個handle處理事件 */
	    error = input_register_handle(&evdev->handle);	if (error)
		        goto err_free_evdev;

	    cdev_init(&evdev->cdev, &evdev_fops);  /* 字符設備初始化 */

	    error = cdev_device_add(&evdev->cdev, &evdev->dev);  /* 添加字符設備 */
	    if (error)
		        goto err_cleanup_evdev;

	    return 0;

err_cleanup_evdev:
	    evdev_cleanup(evdev);
err_unregister_handle:
	    input_unregister_handle(&evdev->handle);
err_free_evdev:
	    put_device(&evdev->dev);
err_free_minor:
	    input_free_minor(minor);
	    return error;
}
  • (1)是在保存區驅動設備名字,比如下圖(鍵盤驅動)event1:因為沒有設置設備號,默認從小到大排序,其中event0是表示input子系統,所以鍵盤驅動名字就是event1。
  • (2)是保存驅動設備的主次設備號,其中主設備號INPUT_MAJOR=13,次設備號=EVSEV_MINOR_BASE+驅動程序本身設備號。
  • (3)會在/sys/class/input類下創建驅動設備event%d,比如鍵盤驅動event1
  • (4)最終進入input_register_handler()函數來注冊handle。

3.5.6. input_attach_handler

設備匹配具體實現

/* drivers/input/input.c */
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)  
{  
    const struct input_device_id *id;  
    int error;  

/* blacklist是handler該忽略input設備類型 */    
    if (handler->blacklist && input_match_device(handler->blacklist, dev))  
        return -ENODEV;  
        id = input_match_device(handler->id_table, dev);  
    /* 這個是主要的配對函數,匹配handler和device的ID */
    if (!id)  
        return -ENODEV;  
  
    error = handler->connect(handler, dev, id);  
    /* 配對成功調用handler的connect函數,這個函數在事件處理器中定義,主要生成一個input_handle結構,並初始化,還生成一個事件處理器相關的設備結構 */
    if (error && error != -ENODEV)  
        printk(KERN_ERR  
            "input: failed to attach handler %s to device %s, "  
            "error: %d\n",  
            handler->name, kobject_name(&dev->dev.kobj), error);  
        /* 出錯處理 */  
    return error;  
 }

3.5.7. input_match_device

比較input_dev中的id和handler支持的id,存放在handler中的id_table。

/* drivers/input/input.c */
static const struct input_device_id *input_match_device(struct input_handler *handler, struct input_dev *dev)
{
	    const struct input_device_id *id;
    /* 遍歷id_table的id匹配 */
	    for (id = handler->id_table; id->flags || id->driver_info; id++) {
		        if (input_match_device_id(dev, id) &&
		            (!handler->match || handler->match(handler, dev))) {
			            return id;
		        }
	     }

	    return NULL;
}

3.5.8. input_allocate_device

初始化input_dev設備

/* drivers/input/evdev.c */
struct input_dev *input_allocate_device(void)
{
	    static atomic_t input_no = ATOMIC_INIT(-1);
	    struct input_dev *dev;
/* 遍歷id_table的id匹配 */
	    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	    if (dev) {
		        dev->dev.type = &input_dev_type;
		        dev->dev.class = &input_class;
		        device_initialize(&dev->dev);
		        mutex_init(&dev->mutex);
		        spin_lock_init(&dev->event_lock);
		        timer_setup(&dev->timer, NULL, 0);
		        INIT_LIST_HEAD(&dev->h_list);
		        INIT_LIST_HEAD(&dev->node);

		         dev_set_name(&dev->dev, "input%lu",
			     (unsigned long)atomic_inc_return(&input_no));

		__module_get(THIS_MODULE);
	}

	return dev;
}
EXPORT_SYMBOL(input_allocate_device);

3.5.9. input_event

調用input_handle_event進行事件處理
dev是上報事件的設備,type是事件總類型,code是事件子類型,value是事件值。

/* drivers/input/input.c */
void (struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
	    unsigned long flags;
    /* 判斷輸入事件是否支持該設備 */
	    if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);  /* 事件加鎖 */
		        input_handle_event(dev, type, code, value); /* 發送事件調用input_pass_values */
		        spin_unlock_irqrestore(&dev->event_lock, flags);
	    }
}EXPORT_SYMBOL(input_event);

3.5.10. input_handle_event

按鍵上報的處理函數

/* drivers/input/input.c */
static void input_handle_event(struct input_dev *dev,
			       unsigned int type, unsigned int code, int value)
{   /* 處理事件 */
	    int disposition = input_get_disposition(dev, type, code, &value);
/* 處理EV_SYN事件 */
	    if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
		        add_input_randomness(type, code, value);
/* 一些特殊事件需要對dev也上報,比如led */
	    if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
		        dev->event(dev, type, code, value);

	    if (!dev->vals)
		        return;
    /* 向上層handler匯報事件 */
	    if (disposition & INPUT_PASS_TO_HANDLERS) {
		        struct input_value *v;

		        if (disposition & INPUT_SLOT) {
			            v = &dev->vals[dev->num_vals++];
			            v->type = EV_ABS;
			            v->code = ABS_MT_SLOT;
			            v->value = dev->mt->slot;
		        }
/* 緩存event事件 */
		        v = &dev->vals[dev->num_vals++];
		        v->type = type;
		        v->code = code;
		        v->value = value;
	    }
    /* 向上層handler匯報事件,刷新緩沖區,上報event事件 */
	    if (disposition & INPUT_FLUSH) {
		        if (dev->num_vals >= 2)
			            input_pass_values(dev, dev->vals, dev->num_vals);
		        dev->num_vals = 0;
       /* 緩沖的時間超過上限,也進行上報處理 */
	    } else if (dev->num_vals >= dev->max_vals - 2) {
		        dev->vals[dev->num_vals++] = input_value_sync;
		        input_pass_values(dev, dev->vals, dev->num_vals);/* 上報event事件 */
		        dev->num_vals = 0;
	    }
}
描述符宏定義 功能
INPUT_INGORE_EVENT 0 忽略該事件
INPUT_PASS_TO_HANDLERS 1 事件由handler處理
INPUT_PASS_TO_DEVICE 2 事件有設備處理
INPUT_SLOT 4 多點觸摸事件
INPUT_FLUSH 8 刷新設備事件的緩沖區
INPUT_PASS_TO_ALL 3 事件有handler與設備同時處理

3.5.11. input_get_disposition

獲取input事件類型

/* drivers/input/input.c */
static int input_get_disposition(struct input_dev *dev,
			  unsigned int type, unsigned int code, int *pval)
{
	    int disposition = INPUT_IGNORE_EVENT;  /* 定義初始變量,如果沒有更新,最后忽略 */
	    int value = *pval;
     /* 處理各類事件 */
	    switch (type) {
/* 同步事件 */
	        case EV_SYN:
		           switch (code) {
		           case SYN_CONFIG:
			               disposition = INPUT_PASS_TO_ALL;
			               break;

		            case SYN_REPORT:
			                 disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
			                 break;
		            case SYN_MT_REPORT:
			                 disposition = INPUT_PASS_TO_HANDLERS;
			                 break;
		    }
		    break;

	    case EV_KEY:
        /* 判斷是否支持該鍵,同時判斷按鍵狀態是否改變 */
		       if (is_event_supported(code, dev->keybit, KEY_MAX)) {
if (value == 2) {
				                disposition = INPUT_PASS_TO_HANDLERS;
				                break;
			            }
/* 判斷value是否改變 */
			       if (!!test_bit(code, dev->key) != !!value) {
				            __change_bit(code, dev->key);  /* 按位取反 */
				            disposition = INPUT_PASS_TO_HANDLERS;
			       }
		    }
		    break;

	    case EV_SW:
		    if (is_event_supported(code, dev->swbit, SW_MAX) &&
		        !!test_bit(code, dev->sw) != !!value) {

			        __change_bit(code, dev->sw);
			          disposition = INPUT_PASS_TO_HANDLERS;
		    }
		    break;

	    case EV_ABS:
		        if (is_event_supported(code, dev->absbit, ABS_MAX))
			            disposition = input_handle_abs_event(dev, code, &value);

		         break;

	    case EV_REL:
		        if (is_event_supported(code, dev->relbit, REL_MAX) && value)
			              disposition = INPUT_PASS_TO_HANDLERS;

		         break;

	    case EV_MSC:
		        if (is_event_supported(code, dev->mscbit, MSC_MAX))
			            disposition = INPUT_PASS_TO_ALL;

		        break;

	    case EV_LED:
		        if (is_event_supported(code, dev->ledbit, LED_MAX) &&
		            !!test_bit(code, dev->led) != !!value) {

			           __change_bit(code, dev->led);
			          disposition = INPUT_PASS_TO_ALL;
		       }
		       break;

	     case EV_SND:
		         if (is_event_supported(code, dev->sndbit, SND_MAX)) {
			              if (!!test_bit(code, dev->snd) != !!value)
				                  __change_bit(code, dev->snd);
			              disposition = INPUT_PASS_TO_ALL;
		        }
		        break;

	     case EV_REP:
		         if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
			             dev->rep[code] = value;
			             disposition = INPUT_PASS_TO_ALL;
		         }
		       break;

	    case EV_FF:
		    if (value >= 0)
			       disposition = INPUT_PASS_TO_ALL;
		       break;

	    case EV_PWR:
		        disposition = INPUT_PASS_TO_ALL;
		        break;
	    }

	    *pval = value;
	    return disposition;
}

3.5.12. input_pass_event

調用input_pass_values

/* drivers/input/input.c */
static void input_pass_event(struct input_dev *dev,
			     unsigned int type, unsigned int code, int value)
{
	    struct input_value vals[] = { { type, code, value } };

	    input_pass_values(dev, vals, ARRAY_SIZE(vals));  /* 調用input_pass_values */
}

3.5.13. input_pass_values

上報事件的處理

/* drivers/input/input.c */
static void input_pass_values(struct input_dev *dev,
			      struct input_value *vals, unsigned int count)
{
	    struct input_handle *handle;
	    struct input_value *v;

	    if (!count)
		        return;

	    rcu_read_lock();
    /* grab是強制為input device綁定的handler ,如果存在就直接調用 */
	    handle = rcu_dereference(dev->grab);
	    if (handle) {
		        count = input_to_handler(handle, vals, count);
	    } else {
    /* 如果device綁定具體的handle,則遍歷這個dev上的所有handle,向應用層open過的發送信息 */  
		        list_for_each_entry_rcu(handle, &dev->h_list, d_node)
			        if (handle->open) {
				            count = input_to_handler(handle, vals, count);
				            if (!count)
					                break;
			       }
	    }

	    rcu_read_unlock();

	    /* 按鍵事件自動回復 */
	    if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
		        for (v = vals; v != vals + count; v++) {
			            if (v->type == EV_KEY && v->value != 2) {
				                if (v->value)
					                    input_start_autorepeat(dev, v->code);
				                else
					                    input_stop_autorepeat(dev);
			            }
		        }
	    }
}

函數最終就會調用到handler->event,對事件進行處理。

3.5.14. input_to_handler

/* drivers/input/input.c */
static unsigned int input_to_handler(struct input_handle *handle,
			struct input_value *vals, unsigned int count)
{
	    struct input_handler *handler = handle->handler;
	    struct input_value *end = vals;
	    struct input_value *v;

	    if (handler->filter) {
		        for (v = vals; v != vals + count; v++) {
			            if (handler->filter(handle, v->type, v->code, v->value))
				                continue;
			            if (end != v)
				                *end = *v;
			            end++;
	        	}
		        count = end - vals;
	    }

	    if (!count)
		        return 0;

	    if (handler->events)
		        handler->events(handle, vals, count);
	    else if (handler->event)
		        for (v = vals; v != vals + count; v++)
			            handler->event(handle, v->type, v->code, v->value);

	    return count;
}

3.5.15. evdev_events

事件處理函數

/* drivers/input/evdev.c */
static void evdev_events(struct input_handle *handle,
			     const struct input_value *vals, unsigned int count)
{
	    struct evdev *evdev = handle->private;
	    struct evdev_client *client;
	    ktime_t time_mono, time_real;
  	    /* 獲取時間信息 */
	    time_meno = ktime_get();
	    time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());

	    rcu_read_lock();

	    client = rcu_dereference(evdev->grab);
	/* 如果該evdev有個專用的client,那么就將事件發給它,如果發送給它,如果該evdev不存在專用的 cliect,那就把該事件發送給evdev上client_list鏈表上所有的client */
	    if (client)
		        evdev_pass_values(client, vals, count, ev_time);  	/* 打包數據 */
	    else
		        list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_values(client, vals, count, ev_time);

	rcu_read_unlock();
}

3.5.16. evdev_pass_values

填充event數據。

/* drivers/input/evdev.c */
static void evdev_pass_values(struct evdev_client *client,
			      const struct input_value *vals, unsigned int count, ktime_t *ev_time)
{
	    struct evdev *evdev = client->evdev; 
	    const struct input_value *v;
	    struct input_event event;
	    struct timespec64 ts;
	    bool wakeup = false;

	    if (client->revoked)
		        return;

	    ts = ktime_to_timespec64(ev_time[client->clk_type]); /* 獲取時間戳 */
	    event.input_event_sec = ts.tv_sec;
	    event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;

	    /* 中斷禁止, 獲取鎖 */
	   spin_lock(&client->buffer_lock);
    /* 多個數據 */
	    for (v = vals; v != vals + count; v++) {
		        if (__evdev_is_filtered(client, v->type, v->code))
			             continue;

		        if (v->type == EV_SYN && v->code == SYN_REPORT) {
			            /* drop empty SYN_REPORT */
			            if (client->packet_head == client->head)
				                  continue;
			            wakeup = true;
		        }
        /* 數據重新封裝為event對象 */
		        event.type = v->type;
		        event.code = v->code;
		        event.value = v->value;
		        __pass_event(client, &event);// 在里面做消息傳遞
	    }

	    spin_unlock(&client->buffer_lock);
      /* 喚醒隊列 */
	    if (wakeup)
		        wake_up_interruptible(&evdev->wait);
}

3.5.17. __pass_event

設備驅動上報事件並不是直接傳遞給用戶空間的,在通用事件處理器(evdev)中,事件被緩沖存在緩沖區中。__pass_event函數里會將input_event放到client結構結構體的環形緩沖區里,即evdev_client結構體的buffer,用戶程序通過read()函數從環形緩沖區中獲取input_event事件。

/* drivers/input/evdev.c */
static void __pass_event(struct evdev_client *client,
			 const struct input_event *event)
{
	    client->buffer[client->head++] = *event; /*將事件賦值給客戶端的input_event緩沖區*/
	    client->head &= client->bufsize - 1; /*對頭head自增指向下一個元素空間 */

    /*當隊頭head與隊尾tail相等時,說明緩沖區已滿 */
	    if (unlikely(client->head == client->tail)) {
		        /*
		         * This effectively "drops" all unconsumed events, leaving
		         * EV_SYN/SYN_DROPPED plus the newest event in the queue.
		        */
		        client->tail = (client->head - 2) & (client->bufsize - 1);
client->buffer[client->tail].input_event_sec = event->input_event_sec;
		        client->buffer[client->tail].input_event_usec = event->input_event_usec;
		        client->buffer[client->tail].type = EV_SYN;
		        client->buffer[client->tail].code = SYN_DROPPED;
		        client->buffer[client->tail].value = 0;
client->packet_head = client->tail;
	    }
      /* 當遇到EV_SYN/ SYN_REPORT同步事件時,packet_head移動到對頭head位置*/
	    if (event->type == EV_SYN && event->code == SYN_REPORT) {
		        client->packet_head = client->head;
		        kill_fasync(&client->fasync, SIGIO, POLL_IN);
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	    struct evdev *evdev;
	    int minor;
	    int dev_no;
	    int error;

	    minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
	    if (minor < 0) {
		        error = minor;
		        pr_err("failed to reserve new minor: %d\n", error);
		        return error;
	    }

	    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	    if (!evdev) {
		        error = -ENOMEM;
		        goto err_free_minor;
	    }

	    INIT_LIST_HEAD(&evdev->client_list);
	    spin_lock_init(&evdev->client_lock);
	    mutex_init(&evdev->mutex);
	    init_waitqueue_head(&evdev->wait);
	    evdev->exist = true;

	    dev_no = minor;
	    /* Normalize device number if it falls into legacy range */
	    if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
		        dev_no -= EVDEV_MINOR_BASE;

/*這里我們能看到最終生成的設備文件,例如event0、event1等待
	    dev_set_name(&evdev->dev, "event%d", dev_no); 

	    evdev->handle.dev = input_get_device(dev);
	    evdev->handle.name = dev_name(&evdev->dev);
	    evdev->handle.handler = handler;
	    evdev->handle.private = evdev;

    /*在設備驅動視圖/sys/class/input/和/sys/devices/目錄下產生eventx設備,最終依event機制和mdev在/dev目錄生成對應的設備文件*/
	    evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
	    evdev->dev.class = &input_class;
	    evdev->dev.parent = &dev->dev;
	    evdev->dev.release = evdev_free;
	    device_initialize(&evdev->dev);

	    error = input_register_handle(&evdev->handle);
	    if (error)
		        goto err_free_evdev;

	    cdev_init(&evdev->cdev, &evdev_fops);
	    evdev->cdev.kobj.parent = &evdev->dev.kobj;
	    error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
	    if (error)
		        goto err_unregister_handle;

	    error = device_add(&evdev->dev);
	    if (error)
		        goto err_cleanup_evdev;

	    return 0;

err_cleanup_evdev:
	    evdev_cleanup(evdev);
err_unregister_handle:
	    input_unregister_handle(&evdev->handle);
err_free_evdev:
	    put_device(&evdev->dev);
err_free_minor:
	    input_free_minor(minor);

	    return error;
}
	    }
}

3.5.18. evdev_connect

創建一個新的evdev 設備

3.5.19. input_proc_init

創建對應的目錄結構

/* drivers/input/input.c */
static int __init input_proc_init(void)
{
	    struct proc_dir_entry *entry;
/* 在/proc/bus目錄下創建input */
	    proc_bus_input_dir = proc_mkdir("bus/input", NULL);
	    if (!proc_bus_input_dir)
		        return -ENOMEM;
/* 在/proc/bus/input目錄下創建devices文件 */
	    entry = proc_create("devices", 0, proc_bus_input_dir, &input_devices_fileops);
	    if (!entry)
		        goto fail1;
/* 在/proc/bus/input目錄下創建handlers文件 */
	    entry = proc_create("handlers", 0, proc_bus_input_dir, &input_handlers_fileops);
	    if (!entry)
		        goto fail2;

	    return 0;
fail2:	
remove_proc_entry("devices", proc_bus_input_dir);
fail1: 
remove_proc_entry("bus/input", NULL);
	    return -ENOMEM;
}

3.5.20. input_set_capability

設置輸入設備可以上報哪些事件,需要注意一次只能設置一個事件,如果設備上報多個事件,需要重復調用

/* drivers/input/input.c */
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
	    switch (type) {
	        case EV_KEY:
		        __set_bit(code, dev->keybit);
		        break;

	        case EV_REL:
		        __set_bit(code, dev->relbit);
		        break;

	        case EV_ABS:
		        input_alloc_absinfo(dev);
		        if (!dev->absinfo)
			            return;

		        __set_bit(code, dev->absbit);
		        break;

	        case EV_MSC:
		        __set_bit(code, dev->mscbit);
		        break;

	       case EV_SW:
		       __set_bit(code, dev->swbit);
		       break;

	        case EV_LED:
		        __set_bit(code, dev->ledbit);
		        break;

	        case EV_SND:
		        __set_bit(code, dev->sndbit);
		        break;

	       case EV_FF:
		       __set_bit(code, dev->ffbit);
		       break;

	      case EV_PWR:
		      /* do nothing */
		      break;

	    default:
		    pr_err("%s: unknown type %u (code %u)\n", __func__, type, code);
		    dump_stack();
		    return;
	}

	    __set_bit(type, dev->evbit);
}
EXPORT_SYMBOL(input_set_capability);

3.5.21. evdev_read

讀取event數據

static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
	    struct evdev_client *client = file->private_data;
	    struct evdev *evdev = client->evdev;
	    struct input_event event;
	    size_t read = 0;
	    int error;

	    if (count != 0 && count < input_event_size())
		        return -EINVAL;

	    for (;;) {
		        if (!evdev->exist || client->revoked)
			             return -ENODEV;
/* client的環形緩沖區中沒有數據並且是非阻塞的,那么返回-EAGAIN,也就是try again */
		        if (client->packet_head == client->tail &&(file->f_flags & O_NONBLOCK))
			             return -EAGAIN;

		       /*
		        * count == 0 is special - no IO is done but we check
		        * for error conditions (see above).
		        */
		        if (count == 0)
			            break;
/* 如果獲得了數據,就取出來 */
		        while (read + input_event_size() <= count &&
		            evdev_fetch_next_event(client, &event)) {
/* 傳給用戶 */
			       if (input_event_to_user(buffer + read, &event))
				           return -EFAULT;

			         read += input_event_size();
    }

		    if (read)
			        break;
/* 如果沒有數據,並且是阻塞的,則在等待隊列上等待 */
		    if (!(file->f_flags & O_NONBLOCK)) {
			        error = wait_event_interruptible(evdev->wait,
					        client->packet_head != client->tail || !evdev->exist || client->revoked);
			    if (error)
				        return error;
		    }
	}

	return read;
}

如果read進行進入休眠狀態,則會被evdev_event函數喚醒

4. 模塊測試

4.1.測試概述

應用有兩條路徑,如下所示
/sys/class/input

console:/sys/class/input # ls
event0 event1 event2 input0 input1 input2 mice

/dev/input

console:/dev/input # ls
event0 event1 event2 mice

查看設備信息

$ cat /proc/bus/input/devices
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="lombo-ir"
P: Phys=lombo-ir/input0
S: Sysfs=/devices/platform/4014800.ir/rc/rc0/input0
U: Uniq=
H: Handlers=event0
B: PROP=0
B: EV=100013
B: KEY=1 0 0 0 0 0 0 0 1680 0 0 ffc
B: MSC=10

event節點里面存放的數據都是沒有經過處理的原始數據流。cat 對應的eventX節點就可以查看輸入的數據。

$ cat event0

4.2.應用測試

#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <linux/input.h>


int main(void)
{
	   int ret;
	   int fd;

	   struct input-event value;

	   fd = open("/dev/input/event1", O_RDWR);
	   if (fd < 0) {
		       printf("open event1 failed %d\n", fd);
		       return 0;
	   }

	   while(1) {
		        ret = read(fd, &value, sizeof(value));
		        if (ret < 0)
			            printf("read failed\n");
		
		        printf("input type:%d code:%d value:%d\n", value.type,
			            value.code, value.value);
	    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM