主要数据结构
struct input_dev {
void *private;
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
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)];
unsigned long swbit[NBITS(SW_MAX)];
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 state;
int sync;
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)];
unsigned long sw[NBITS(SW_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 (*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;
struct mutex mutex;
unsigned int users;
struct device dev;
union {
struct device *dev;
} cdev;
struct list_head h_list;
struct list_head node;
};
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
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);
const struct file_operations *fops;
int minor;
const char *name;
const struct input_device_id *id_table;
const struct input_device_id *blacklist;
struct list_head h_list;
struct list_head node;
};
------------------------------
input subsystem 用来统一处理数据输入设备,例如键盘,鼠标,游戏杆,触摸屏等等。
这里引用 Linux Journal 上的一个图来说明input subsystem 的架构:
(http://www.linuxjournal.com/article/6396)
我们可以看到,input core 用来协调硬件的input事件 和 用户层应用之间的通讯。
这里再引用 ELDD 上的一个图片,来详细说明其内部的结构:
在内核中,input_dev 表示一个 input设备;input_handler 来表示input设备的 interface。
所有的input_dev 用双向链表 input_dev_list 连起来,如图所示:
/----------------/
/--------------->| input_dev_list |<------------------/
| /----------------/ |
| |
| |
| |
| |
| input_dev input_dev |
| +------------+ +------------+ |
| | | | | |
| +------------+ +------------+ |
| | | | | |
| +------------+ +------------+ |
| | | | | |
| +------------+ +------------+ |
/--->| node |<---- ...... ----| node |<---/
+------------+ +------------+
| h_list | | h_list |
+------------+ +------------+
在调用 int input_register_device(struct input_dev *dev) 的时候,会将新的 input_dev 加入到这个链表中。
所有的input_handler 用双向链表 input_handler_list 连起来, 如图所示:
/--------------------/
/--------------------->| input_handler_list |<--------------------/
| /--------------------/ |
| |
| |
| |
| |
| input_handler input_handler |
| +------------------+ +------------------+ |
| | private | | private | |
| +------------------+ +------------------+ |
| | (*event)() | | (*event)() | |
| +------------------+ +------------------+ |
| | (*connect)() | | (*connect)() | |
| +------------------+ +------------------+ |
| | (*disconnect)() | | (*disconnect)() | |
| +------------------+ +------------------+ |
| | (*start)() | | (*start)() | |
| +------------------+ +------------------+ |
| | fops | | fops | |
| +------------------+ +------------------+ |
| | minor | | minor | |
| +------------------+ +------------------+ |
| | name | | name | |
| +------------------+ +------------------+ |
| | id_table | | id_table | |
| +------------------+ +------------------+ |
| | blacklist | | blacklist | |
| +------------------+ +------------------+ |
| | hlist | | hlist | |
| +------------------+ +------------------+ |
/--->| node |<--- ...... ---->| node |<---/
+------------------+ +------------------+
在调用 int input_register_handler(struct input_handler *handler) 的时候,会将新的 input_handler 加入到这个链表中。
每个input_dev 和 input_handler 是要关联上才能工作的,在注册 input_dev 或者 input_handler的时候,就遍历上面的列表,找到相匹配的,然后调用 input_handler 的 connect函数来将它们联系到一起。
通常在input_handler 的 connect函数中,就会创建 input_handle, input_handle就是负责将 input_dev 和 input_handler 联系在一起的,如图所示:
/----------------/
/--------------->| input_dev_list |<------------------/
| /----------------/ |
| |
| |
| |
| |
/-----------> input_dev input_dev |
| | +------------+ +------------+ |
| | | | | | |
| | +------------+ +------------+ |
| | | | | | |
| | +------------+ +------------+ |
| | | | | | |
| | +------------+ +------------+ |
| /--->| node |<---- ...... ----| node |<---/
| +------------+ +------------+
| /----->| h_list |<-------/ | h_list |
| | +------------+ | +------------+
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | input_handle |
| | +--------------+ |
| | | private | |
| | +--------------+ |
| | | open | |
| | +--------------+ |
| | | name | |
| | +--------------+ |
| /----->| d_node |<-----/
| +--------------+
/------<-----| *dev |
+--------------+
/-----<--------| *handler |
| +--------------+
| /-----| h_node |<-----/
| | +--------------+ |
| | |
| | |
| | |
| | |
| | |
| | |
/------------> input_handler |
| +------------------+ |
| | private | |
| +------------------+ |
| | (*event)() | |
| +------------------+ |
| | (*connect)() | |
| +------------------+ |
| | (*disconnect)() | |
| +------------------+ |
| | (*start)() | |
| +------------------+ |
| | fops | |
| +------------------+ |
| | minor | |
| +------------------+ |
| | name | |
| +------------------+ |
| | id_table | |
| +------------------+ |
| | blacklist | |
| +------------------+ |
/-->| hlist |<---/
+------------------+
| node |
+------------------+
这里需要额外说明一下的是: input_dev 中的 h_node 是 input_handle 链表的list节点,也就是说,一个input_dev,可以对应多个 input_handle.
当设备产生 input event 的时候,例如按下了一个键,驱动就会调用 input_handler 中的 event 函数,同时,如果input_dev 支持的话,也会调用 input_dev 的 event 函数。
这样,设备产生的事件就会被驱动记录下来。
当用户层的程序需要获知这些事件的时候,会调用 input_handler中的 struct file_operations *fops 中的相应函数,例如 read 等等。
可以看出,整个 input 框架的构思还是比较简洁方便的。
【参考资料】
1. The Linux USB Input Subsystem
Part I : http://www.linuxjournal.com/article/6396
Part II : http://www.linuxjournal.com/article/6429
2. linux kernel 2.6.23.11