linux input subsystem 架构分析 收藏
主要数据结构
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_headh_list;
struct list_headnode;
};
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_headd_node;
struct list_headh_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_headh_list;
struct list_headnode;
};
------------------------------
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_devinput_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_handlerinput_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_devinput_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