第1章 Linux 輸入子系統裝置驅動
Linux 輸入子系統将輸入驅動抽象為三層:事件處理層、核心層、裝置驅動層。應用程式隻需要跟事件處理層打交道,不需要察覺裝置的變化。核心層是負責管理輸入裝置,并将消息在事件處理層和裝置驅動層之間傳遞。
由于事件處理和裝置驅動的分離,使得應用程式讀取輸入資訊的接口固定不變就可以适應新的同類輸入裝置。
表示事件處理層的資料結構是 struct input_handler ,每個 handler 代表一種處理事件的方式,允許多個 handler 共存。代表裝置驅動層的資料結構是 struct input_dev 。 input_dev 和 handler 可以建立連接配接,連接配接它們的就是 struct input_handle 。核心層一般被稱為 input core 。
1.1 重要的資料結構
在輸入子系統的裝置驅動中,最重要的資料結構是 struct input_dev ,如 程式清單 1 .1 所示。需要完成的大部分工作都是圍繞着它來的,它是驅動的主體。每個 struct input_dev 代表一個輸入裝置。
程式清單 1 . 1 struct input_dev 成員介紹
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
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)];
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
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 (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
int going_away;
struct device dev;
struct list_head h_list;
struct list_head node;
};
struct input_event 是事件傳送的載體,輸入子系統的事件都是包裝成 struct input_event 傳給使用者空間。各個成員如 程式清單 1 .2 所示。
程式清單 1 . 2 struct input_event 成員介紹
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
struct input_dev 注冊的時候需要跟比對的 hanlder 建立連接配接,比對的依據就是 struct input_dev 所包含的 struct input_id 。 struct input_id 的各個成員如 程式清單 1 .3 所示。
程式清單 1 . 3 struct input_id 成員描述
struct input_id {
__u16 bustype;
__u16 vendor;
__u16 product;
__u16 version;
};
1.2 基于輸入子系統的鍵盤驅動例程
下面提供一個基于輸入子系統的鍵盤驅動例程,這裡隻是介紹編寫一個鍵盤驅動需要哪些步驟,并不解釋本程式的全部細節。如 程式清單 1 .4 所示。
程式清單 1 . 4 輸入子系統裝置驅動例程 - 鍵盤驅動
#define NR_SCANCODES (4)
typedef unsigned char KEYCODE_T;
static KEYCODE_T keypad_keycode[NR_SCANCODES] =
{
KEY_BACKLIGHT, KEY_X, KEY_Y, KEY_Z
};
typedef struct {
struct input_dev *input;
int irq;
KEYCODE_T keycode[ARRAY_SIZE(keypad_keycode)];
KEYCODE_T lastkey;
char phys[32];
}KEYPAD_T;
static int __init keypad_init(void)
{
int err;
int i;
zlgkpd = kzalloc(sizeof(KEYPAD_T), GFP_KERNEL); ①
if (unlikely(!zlgkpd))
{
return -ENOMEM;
}
strcpy(zlgkpd->phys, "keypad/input0");
zlgkpd->input = input_allocate_device(); ②
if (!zlgkpd->input)
{
kfree(zlgkpd);
return -ENOMEM;
}
zlgkpd->input->name = CHIP_NAME; ③
zlgkpd->input->phys = zlgkpd->phys;
zlgkpd->input->id.bustype = BUS_HOST; ④
zlgkpd->input->id.vendor = 0x0001;
zlgkpd->input->id.product = 0x0001;
zlgkpd->input->id.version = 0x0100;
zlgkpd->input->keycode = zlgkpd->keycode; ⑤
zlgkpd->input->keycodesize = sizeof(KEYCODE_T);
zlgkpd->input->keycodemax = ARRAY_SIZE(keypad_keycode);
zlgkpd->input->open = keypad_open; ⑥
zlgkpd->input->close = keypad_close;
zlgkpd->irq = KEYPAD_IRQ;
set_bit(EV_KEY, zlgkpd->input->evbit); ⑦
for (i = 0; i < ARRAY_SIZE(keypad_keycode); i++) { ⑧
set_bit(keypad_keycode[i], zlgkpd->input->keybit);
}
clear_bit(KEY_RESERVED, zlgkpd->input->keybit);
err = input_register_device(zlgkpd->input); ⑨
if (err)
goto err;
DPRINTK("init OK!/n");
return 0;
err:
DPRINTK("init error/n");
input_free_device(zlgkpd->input);
kfree(zlgkpd);
return err;
}
static void __exit keypad_exit(void)
{
input_unregister_device(zlgkpd->input); ⑩
input_free_device(zlgkpd->input);
kfree(zlgkpd);
}
module_init(keypad_init);
module_exit(keypad_exit);
下面來一步步解釋:
① 為自己定義的類型申請空間。這一步不是典型輸入子系統驅動的必須步驟。
② 申請 struct input_dev 并做初始化。
③ 初始化裝置的名字。
④ 初始化裝置 ID 。在注冊裝置之前,名字和 ID 是必須初始化的。
⑤ 初始化裝置鍵值數組首位址、鍵值位元組大小和按鍵的個數。
⑥ 初始化裝置的打開和關閉函數指針。
⑦ 使裝置支援按鍵事件。隻有注冊過的事件類型才能響應。
⑧ 注冊支援的按鍵鍵值。隻有注冊過的鍵值才能響應。
⑨ 注冊裝置結構體。
⑩ 釋放占用的資源。
發送事件的代碼如下
程式清單 1 . 5 發送按鍵事件
static void keypad_do_workqueue(struct work_struct *data)
{
if (pressed) {
············
input_report_key(zlgkpd->input, code, KEY_PRESSED); ①
input_sync(zlgkpd->input);
zlgkpd->lastkey = code;
DPRINTK("KEY is %d, pressed: %d, index:%d/n", code, pressed, index);
input_report_key(zlgkpd->input, code, KEY_RELEASED); ②
input_sync(zlgkpd->input);
}
enable_irq(KEYPAD_IRQ);
}
static irqreturn_t keypad_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
disable_irq(KEYPAD_IRQ);
schedule_work(&key_wq);
return IRQ_HANDLED;
}
在中斷底半部中,讀取硬體決定發送的按鍵值,首先發送按鍵按下的資訊,然後發送按鍵擡起的資訊。每次發送事件之後都同步一次。