歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

Linux input子系統分析

Linux的input子系統提供了輸入設備的驅動框架,比如鼠標、鍵盤、觸摸屏等就屬於輸入設備。Linux中關於input子系統的文檔在Documentation/input目錄,input的核心代碼在input.c和input.h中。

本文沒有涉及input的一些細節實現,比如input_dev->grab,以及按鍵的定時事件等。

1. input_handle, input_handler, input_dev

input_handle, input_handler, input_dev是input子系統中最重要的3個數據結構。

l input_handler用於上層應用獲取輸入事件。上層應用打開輸入設備的設備節點,然後對節點進行讀寫操作以獲得鼠標移動信息,或者鍵盤信息等等。這裡對設備節點的文件操作函數就是由input_handler提供。

l input_dev代表的是具體的設備,比如鼠標、鍵盤等等。

l 對於一台Linux電腦,可能會連著多個鼠標、多個鍵盤。每一個鼠標都能控制光標的運動,每一個鍵盤也都能正常使用。這在input子系統中,體現為一個input_handle關聯多個input_dev,能夠同時從多個input_dev獲取輸入消息。與此同時,linux中可能會有多個device節點同時與一個input具體設備關聯,這樣,應用程序通過任何一個設備節點,都可以獲得例如鼠標、鍵盤等具體設備的輸入信息。所以,input_dev和input_handler之間是多對多的關聯關系,而這些關聯就是由input_handle表示。

1.1 handler與dev之間關聯的建立

input_handle中包含一個input_dev的指針,和一個input_handler的指針,所以能建立handler和dev之間的一個一對一的關聯。在input_handler中,有一個鏈表h_list,指向和這個handler關聯的所有input_handle,通過這些handle就可以找到與handler關聯的所有dev。同樣的,在input_dev中,也有一個鏈表h_list,指向與dev關聯的所有input_handle,通過這些handle可以找到與dev相關的所有handler。通過這兩個鏈表和input_handle,input_handler和input_dev之間建立了一個復雜的網狀結構。

那麼,input_handler和input_dev之間建立關聯的規則是什麼?即在什麼情況下需要建立關聯,什麼時候不需要建立關聯?這就需要handler和dev之間有一個匹配機制。

input_handler中有兩個指針,id_table和blacklist,其中blacklist是黑名單,凡是與之匹配的dev都將被強制過濾;而與id_table中任意一項匹配的dev才能與handler建立關聯。

const struct input_device_id *id_table;

const struct input_device_id *blacklist;

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 driver_info;

};

在這個結構體中,flags中包含一些掩碼:

#define INPUT_DEVICE_ID_MATCH_BUS 1

#define INPUT_DEVICE_ID_MATCH_VENDOR 2

#define INPUT_DEVICE_ID_MATCH_PRODUCT 4

#define INPUT_DEVICE_ID_MATCH_VERSION 8

這些掩碼用來匹配bus類型、廠商、產品號、版本號等。如果選擇不匹配這些信息,那麼driver_info需要置位,以跳過flags的匹配過程。

evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1]中包含支持的事件掩碼。input子系統支持以下事件:

#define EV_SYN 0x00 // EV_SYN用於標記一系列輸入的結束

#define EV_KEY 0x01 // 鍵盤輸入

#define EV_REL 0x02 // 鼠標的相對位移

#define EV_ABS 0x03 // 觸摸屏的絕對坐標

#define EV_MSC 0x04

#define EV_SW 0x05

#define EV_LED 0x11 // 比如鍵盤的LED控制

#define EV_SND 0x12

#define EV_REP 0x14

#define EV_FF 0x15

#define EV_PWR 0x16

#define EV_FF_STATUS 0x17

如果handler支持某一種事件,那麼evbit的對應位會置位。如果evbit的某一位置位了,那麼結構體中與具體事件對應的掩碼數組就會有效。例如,EV_KEY置位了,那麼對應的keybit數組就有效,該數組裡面定義了handler支持的具體key的類型。如果handler和dev需要匹配,那麼dev必須能支持所有handler支持的事件;但是,handler卻不一定要處理所有dev能提供的事件。

#define MATCH_BIT(bit, max) \

for (i = 0; i < BITS_TO_LONGS(max); i++) \

if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \

break; \

if (i != BITS_TO_LONGS(max)) \

continue;

關於匹配的具體過程,可以參考函數input_match_device。

2. input_dev

所有的input_dev都是虛擬設備,其class名為”input”,class的登記函數:

class_register(&input_class);

所以,需要先注冊一個物理設備,已此物理設備為父設備注冊input_dev。

2.1 input_dev的注冊

input_dev的注冊由input_register_device函數完成。

int input_register_device(struct input_dev *dev)

__set_bit(EV_SYN, dev->evbit); // 強制設置EV_SYN位

// 初始化定時器,REP_DELAY是第一次延時的超時值,REP_PERIOD是第一次延時 // 之後的超時值

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;

}

// 設置默認的get key和set key函數

if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode;

// 設置設備名稱,然後注冊設備

snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),

"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

device_add(&dev->dev);

list_add_tail(&dev->node, &input_dev_list); // 將設備加入input_dev_list鏈表

// 對每一個已注冊的handler,調用input_attach_handler

list_for_each_entry(handler, &input_handler_list, node)

input_attach_handler(dev, handler); // 見下節

// proc有關的操作

input_wakeup_procfs_readers();

Copyright © Linux教程網 All Rights Reserved