1. 事件层:负责与用户程序打交道,将核心层传来的事件报告给用户程序。
2. 核心层:是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
3. 设备驱动层:负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,键盘、鼠标、触摸屏等字符设备驱动功能的实现工作主要在这层。
input_dev结构体用来描述一个input设备,位于设备驱动层,声明一个设备能产生哪类以及该类事件中的哪些事件
struct input_dev {
const char *name; 设备名,将在sys/class/input/XXX/name 中保存
const char *phys; 设备系统层的物理路径, 在sys/class/input/XXX/phys 中保存
const char *uniq;
struct input_id id; 输入设备id 总线类型;厂商编号,产品id,产品版本与input_hander匹配时会用到
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)]; led指示灯标志位
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 hint_events_per_packet;
unsigned int keycodemax; 键盘码表大小
unsigned int keycodesize; 键盘码大小
void *keycode; 键盘码表指针
int (*setkeycode)(struct input_dev *dev,unsigned int scancode, unsigned int keycode); 设置键盘码
int (*getkeycode)(struct input_dev *dev,unsigned int scancode, unsigned int *keycode); 获取键盘码
int (*setkeycode_new)(struct input_dev *dev,const struct input_keymap_entry *ke,unsigned int *old_keycode);
int (*getkeycode_new)(struct input_dev *dev,struct input_keymap_entry *ke);
struct ff_device *ff; 强制反馈设备
unsigned int repeat_key; 重复按键标志位
struct timer_list timer; 定时器
int rep[REP_CNT]; 重复次数
struct input_mt_slot *mt;
int mtsize;
int slot;
struct input_absinfo *absinfo;
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 (*open)(struct input_dev *dev); open方法
void (*close)(struct input_dev *dev); close方法
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 __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
bool sync;
struct device dev; 设备文件
struct list_head h_list; input_handler处理器链表头
struct list_head node; input_device设备链表头
};
input_handler结构体用来描述一个事件处理器,结构体中的input_device_id *id_table成员用来和input_dev设备匹配,看是否支持该输入设备。
struct input_handler {
void *private; 私有数据
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); 事件处理
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); 过滤器
bool (*match)(struct input_handler *handler, struct input_dev *dev); 设备匹配
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; 输入设备 id表
struct list_head h_list; input_handler处理器链表头
struct list_head node; input_device设备链表头
};
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 driver_info;
};
下面我们来跟踪一下内核,看看一个输入设备是如何被注册到系统中工作的。
struct input_dev *input_allocate_device(void) @input.c
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
device_initialize(&dev->dev); 初始化内嵌的device结构体
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
return dev;
}
int input_register_device(struct input_dev *dev) @input.c
{
struct input_handler *handler;
error = device_add(&dev->dev);
list_add_tail(&dev->node, &input_dev_list); 加入input_dev_list链表
对于每一个input_handler,都调用input_attach_handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
}
当向系统注册一个输入设备时,系统会将该设备加入input_dev_list链表中,并从input_handler_list链表中取出每一个handler,调用input_attach_handler看看是否支持该设备,要知道当一个输入设备向系统注册时,可能会有好几个handler同时支持它。假设此时的input_handler是evdev_handler(@evdev.c)。
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) @input.c
{
const struct input_device_id *id;
id = input_match_device(handler->id_table, dev);
error = handler->connect(handler, dev, id);
}
evdev.c中input_device_id的设置:
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
continue;
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
continue;
if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
continue;
if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
continue;
if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
continue;
if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
continue;
if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
continue;
if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
continue;
if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
continue;
if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
continue;
if (!handler->match || handler->match(handler, dev))
return id;
}
return NULL;
}
到此这个handler就可以支持该输入设备,之后将会调用它的connect方法,对于evdev.c来说就是evdev_handler.connect,至于驱动层的事件如何通过核心层和事件层到达用户空间,在后续的evdev.c文件分析的时候会看到。
其实对于编写一个底层的输入设备驱动,并不需要关心这么多,因为Linux系统为我们做了很好的分层,我们只要专注核心层给我们提供的接口,使用这些接口向上注册驱动就可以了,因此编写一个输入设备驱动就变得很简单,下面是基本的框架。
1. 分配input_dev结构体:input_allocate_device()
2. 设置可以支持哪类事件,支持该类事件的那些事件
3. 注册输入设备:input_register_device()
4. 向系统报告事件:input_event()
事件处理层文件主要是用来支持输入设备并与用户空间交互,这部分代码一般不需要我们自己去编写,因为Linux内核已经自带有一些事件处理器,可以支持大部分输入设备,比如evdev.c、mousedev.c、joydev.c等。一个事件处理器用struct input_handler结构体来表示,在evdev.c文件中的定义如下。
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE, //#define EVDEV_MINOR_BASE 64
.name = "evdev",
.id_table = evdev_ids,
};
int input_register_handler(struct input_handler *handler) @input.c
{
struct input_dev *dev;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
input_table[handler->minor >> 5] = handler;
}
list_add_tail(&handler->node, &input_handler_list);
对于每个input_dev,调用input_attach_handler
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
}
当向系统注册一个事件处理器时,系统会将该处理器加入input_handler_list链表中,并从input_dev_list链表中取出每一个dev,调用input_attach_handler看看是否支持该设备。上面蓝色粗体语句表明,当注册一个handler时,它会去设置input_table数组的某一项,input_table的定义在input.c中(static struct input_handler *input_table[8]; ),从这个数组中我们也可以看出该系统最多支持8个事件处理器。
对于输入子系统无论是注册设备input_register_device还是注册事件处理器input_register_handler最终都会调用到input_attach_handler进行匹配,当正确匹配的时候就会调用handler的connect方法,接下来我们就分析evdev.c中evdev_connect到底做了什么。
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) @evdev.c
{
struct evdev *evdev;
int minor;
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
INIT_LIST_HEAD(&evdev->client_list);
dev_set_name(&evdev->dev, "event%d", minor);
evdev->handle.dev = input_get_device(dev);
evdev->handle.handler = handler;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
error = evdev_install_chrdev(evdev);
error = device_add(&evdev->dev);
}
申请一个struct evdev结构体,对于evdev_handler事件处理器,它把所有自己可以支持的输入设备都定义为一个struct evdev设备,并且可以支持最多32该设备。在struct evdev中有一个很重要的成员struct input_handle handle,它是连接dev和handler的桥梁,下面给出它的定义:
struct input_handle{
void *private;
int open;
const char *name;
struct input_dev *dev; 指向input_dev结构体
struct input_handler *handler; 指向input_handler结构体
struct list_head d_node; 通过d_node连接到input_dev上的h_list链表上
struct list_head h_node; 通过h_node连接到input_handler上的h_list链表上
}
在evdev_connect()中,handle的两个成员指针分别指向匹配的dev和handler,最后调用input_register_handle()。
int input_register_handle(struct input_handle *handle) @input.c
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
list_add_tail_rcu(&handle->d_node, &dev->h_list);
list_add_tail(&handle->h_node, &handler->h_list);
}
认真观察input_dev、input_handler和input_handle三个结构体,不难看出,这三个结构体都维护了两个链表。对于input_dev是名为h_list和node的成员,一个系统中可能会有很多输入设备,这些输入设备就是通过node这个成员连成一个双向链表,其中链表头指向input_dev_list。另一个成员h_list维护了一个handle的链表,一个输入设备可能同时会被几个事件处理器支持,而每一次匹配过程都会申请一个input_handle结构体,将输入设备dev和事件处理器handler关联在一起,对于这些handle有一个共同点是它的成员指针handle->dev都指向该输入设备,并且这些input_handle结构体中的成员d_node连接成一个链表并且链表头指向该输入设备的h_list。对于input_handler也一样,有兴趣的可以自己分析,下面给出一个简单的匹配图。
在上面的evdev_connect()函数分析中我们略去了下面几条语句。
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
device_initialize(&evdev->dev);
error = device_add(&evdev->dev);
这些语句是用来创建设备节点,关于Linux下如何自动创建设备节点的详细资料可以参考博文: