Linux Input Device 介紹: APIs

jollen 發表於 April 8, 2009 12:18 PM

Linux 的 Input Device 是重要的一個 subsystem,在進行實例介紹前,先大略了解一下相關的 API。

Linux Input Device

input.c是Linux的”input”驅動程式,主要支援鍵盤與滑鼠的輸入;input.c介面有趣的地方是採用了事件(event)的方式來處理輸入,以下是input.c介面重要的資料結構與函數:

* struct input_dev
* void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
* void input_register_device(struct input_dev *);
* void input_unregister_device(struct input_dev *);
* void input_register_handler(struct input_handler *);
* void input_unregister_handler(struct input_handler *);

Linux 的input機制可用來實作「虛擬鍵盤」或「虛擬滑鼠」,只要呼叫input_event()將輸入資料發佈給input handler即可。

struct input_dev是用來描述輸入事件的重要資料結構,其原型宣告如下:

struct input_dev {

void *private;

int number;
char *name;
unsigned short idbus;
unsigned short idvendor;
unsigned short idproduct;
unsigned short idversion;

unsigned long evbit[NBITS(EV_MAX)];
unsigned long keybit[NBITS(KEY_MAX)];
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)];
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
int ff_effects_max;

unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;

unsigned int repeat_key;
struct timer_list timer;

int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];

unsigned long key[NBITS(KEY_MAX)];
unsigned long led[NBITS(LED_MAX)];
unsigned long snd[NBITS(SND_MAX)];

int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];

int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
int (*erase_effect)(struct input_dev *dev, int effect_id);

struct input_handle *handle;
struct input_dev *next;
};

定義按鍵

我們可以設定struct input_dev裡的evbit欄位,來定義所要接受的輸入類型,目前共有8種輸入類型如下:

* EV_KEY:Keys and buttons(按鍵與按鈕)。
* EV_REL:Relative axes(相對座標)。
* EV_ABS:Absolute axes(絕對座標)。
* EV_MSC:Misc events(其它事件)。
* EV_LED:LEDs。
* EV_SND:Sounds(聲音輸入)。
* EV_REP:Autorepeat values(自動重覆數值)。
* EV_FF:Force feedback事件。

以下是一個範例,我們指定dev可接受EV_KEY事件:

dev.evbit[0] = BIT(EV_KEY);

evbit是一個陣列,每個元素可以索引一種輸入類型。每種輸入類型均可指定特定的輸入資料,例如:TAB鍵。指定方式是使用set_bit()或BIT巨集來設定每種輸入類型的陣列。以下是各輸入類型的欄位名稱:

* keybit[NBITS(KEY_MAX)]:Keys and buttons(按鍵與按鈕)。
* relbit[NBITS(REL_MAX)]:Relative axes(相對座標)。
* absbit[NBITS(ABS_MAX)]:Absolute axes(絕對座標)。
* mscbit[NBITS(MSC_MAX)]:Misc events(其它事件)。
* ledbit[NBITS(LED_MAX)]:LEDs。
* sndbit[NBITS(SND_MAX)]:Sounds(聲音輸入)。
* ffbit[NBITS(FF_MAX)]:Force feedback事件。

以下是使用set_bit()的範例:

* set_bit(KEY_UP, dev.keybit);
* set_bit(KEY_LEFT, dev.keybit);

或是使用BIT巨集也可以:

* keybit[0] = BIT(KEYUP) | BIT(KEY_LEFT);

input.h裡做位元運算的3個巨集如下:

* NBITS(x):計算要幾個陣列元素,才夠紀錄第x個位元。
* BIT(x):傳回單獨第x個位元為1時所代表的數值,例如:x=0時為0x1,x=1時為0x2,x=2時為0x4。
* LONG(x):第x個位元是屬於第幾個陣列元素(即索引值)。

我們先設定驅動程式能接受EV_KEY事件,然後指定EV_KEY事件的特定輸入值為KEY_UP與KEY_LEFT按鍵。不同事件的輸入資料定義,請參考input.h檔,例如以下是EV_KEY事件的按鍵1~按鍵9定義:

/*
* Keys and buttons
*/

#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

設定好struct input_dev後再呼叫input_register_device()註冊至上層。當”input device”註冊至kernel後,當該input device被開啟時,便呼叫input device的open method;當input device關閉時,便呼叫input device的close method。
實作input device的open method時,若成功應傳回0,失敗的話則傳回任意的非零值;close method則不須傳回值。

Report 按鍵

使用者的按鍵往上層回報所使用的API如下:

* input_report_key(struct input_dev *dev, unsigned int code, int value);
* input_report_rel(struct input_dev *dev, unsigned int code, int value);
* input_report_abs(struct input_dev *dev, unsigned int code, int value);
* input_report_key與input_report_rel其實都是使用input_event()的巨集,input_event()的函數原型與參數說明如下:
* void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);

* @dev:指向input device的指標。
* @type:輸入類型(EV_KEY、EV_ABS等)。
* @code:輸入按鍵(例如EV_KEY的KEY_1)。
* @value:按鍵值。

Input Handler

任何的按鍵輸入,都應呼叫input_event()來將input event送到input.c,再由input.c分派事件(遶送)到每一個”input handler”。有註冊input handler的驅動程式,都能讀取輸入資料。

讀者留言 (1)

  • lanmanck 於 April 2, 2011 23:47:

    不错,学习了!

留言功能維護中。將於近日重新開放。

連絡作者

Jollen Chen,Moko365(仕橙3G教室)講師,熱愛研究 Linux 與 Android 技術。曾為 Motorola、HTC、Foxconn、LG、OPPO、騰迅、廣達電腦、緯創、仁寶等超過 50 家企業講授課程。目前在 MokoVersity 擔任軟體工程師,撰寫 Node.js 程式,也在幾家科技廠兼任 Android Framework 研發顧問。您可透過電子郵件 <jollen (at) jollen (dot) org> 或這裡與我連絡。