简介
input子系统的设计是为了支持linux中所有的输入设备。它由三部分组成,分别是设备驱动层,核心层,事件处理层。在使用之前必须要先加载核心层模块(input core),然后在加载相应的设备驱动(device driver)和事件处理层(event handler)。
子系统框架
核心层(core):作为input子系统的核心,它提供了input子系统所需要的大部分功能和接口,向下提供给设备驱动层,向上提供给事件处理层。
设备驱动层:这部分是驱动需要实现的主要部分,每种类型的设备都需要实现相应的驱动层,主要是做一些和硬件相关的操作,并且把设备注册到核心层,由核心层统一管理。
事件处理层:负责将设备产生的event事件,通过设备文件传给应用层,同时也可以将用户传入的事件分发到特定设备。之所以加这一层,也是为了上层应用的统一接口,不管底层的设备是什么类型,面向用户的接口依然可以保持不变。事件处理层一般不需要厂商开发,一些通用的handler实现都可以直接使用。
由上面的介绍可知,一个事件从硬件到用户空间,一共经历如下的过程:
DeviceDriver -> InputCore -> Eventhandler -> userspace
input核心层
input核心层相当于input子系统中的公共库,它提供了input子系统所需要的所有功能函数。
我们看下编译的makefile,目录如下:
/kernel/drivers/input/Makefile
关于核心层的编译规则如下所示:
obj-$(CONFIG_INPUT) += input-core.o
input-core-y := input.o input-compat.o input-mt.o ff-core.o
由此可知,核心层的代码由这几个文件组成:
/kernel/drivers/input/input.c
/kernel/drivers/input/input-compat.c
/kernel/drivers/input/input-mt.c
/kernel/drivers/input/ff-core.c
主要的功能实现在input.c文件中,其余几个文件是一些扩展功能。
在看核心层的代码前,首先需要了解两个结构体,分别是struct input_dev和struct input_handler:
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
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 hint_events_per_packet;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);
int (*getkeycode)(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 *mt;
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);
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 __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list;
struct list_head node;
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
};
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
void (*events)(struct input_handle *handle,
const struct input_value *vals, unsigned int count);
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);
bool legacy_minors;
int minor;
const char *name;
const struct input_device_id *id_table;
struct list_head h_list;
struct list_head node;
};
这两个结构体分别代表着设备层驱动和事件处理层驱动,核心层负责管理input_dev和input_handler。它定义了两个链表来进行管理:
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
当设备层和事件处理层注册以上结构体时,核心层就会把结构体加入到相应的链表中来管理。
既然存在多个设备层和多个事件处理层,那么该如何把设备层的event传递给事件处理层呢?这就需要建立input_dev和input_handler之间的关系。核心层使用了另一个结构体来处理这种联系,那就是struct input_handle:
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;
};
每个input_handle都有一个设备层成员和一个事件处理层成员,这样就把两者关联起来了。
input_handle中保存的是一对一的关系,然而input子系统允许一个设备层(input_dev)对应多个事件处理层(input_handler),或者一个事件处理层(input_handler)对应多个设备层(input_dev)。所以我们还需要把这个结构体加入到两个链表,一个是input_dev.h_list,另一个是input_handler.h_list,由此达到一对多的目的。建立了这些联系以后,核心层就可以进行正确的消息分发和传递了。
核心层提供了input子系统需要的所有功能函数,向下提供给设备层,向上提供给消息处理层,下面将列举出关键的接口。
设备层调用的接口:
struct input_dev __must_check *input_allocate_device(void);
struct input_dev __must_check *devm_input_allocate_device(struct device *);
void input_free_device(struct input_dev *dev);
static inline struct input_dev *input_get_device(struct input_dev *dev)
{
return dev ? to_input_dev(get_device(&dev->dev)) : NULL;
}
static inline void input_put_device(struct input_dev *dev)
{
if (dev)
put_device(&dev->dev);
}
static inline void *input_get_drvdata(struct input_dev *dev)
{
return dev_get_drvdata(&dev->dev);
}
static inline void input_set_drvdata(struct input_dev *dev, void *data)
{
dev_set_drvdata(&dev->dev, data);
}
int __must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);
void input_reset_device(struct input_dev *);
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_REL, code, value);
}
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_FF_STATUS, code, value);
}
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_SW, code, !!value);
}
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
static inline void input_mt_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);
消息处理层调用的接口:
int __must_check input_register_handler(struct input_handler *);
void input_unregister_handler(struct input_handler *);
int __must_check input_get_new_minor(int legacy_base, unsigned int legacy_num,
bool allow_dynamic);
void input_free_minor(unsigned int minor);
int input_handler_for_each_handle(struct input_handler *, void *data,
int (*fn)(struct input_handle *, void *));
int input_register_handle(struct input_handle *);
void input_unregister_handle(struct input_handle *);
int input_grab_device(struct input_handle *);
void input_release_device(struct input_handle *);
int input_open_device(struct input_handle *);
void input_close_device(struct input_handle *);
int input_flush_device(struct input_handle *handle, struct file *file);
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value);
除了提供函数实现外,核心层还实现了一个input_class类,它将在sys文件系统的class下生成input目录。
input设备驱动层
作为一种输入设备,和硬件操作相关的内容,全都在input设备驱动层。这是需要设备厂商自己实现的部分。
不多说了,直接上代码:
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/io.h>
static struct input_dev *button_dev;
static irqreturn_t button_interrupt(int irq, void *dummy)
{
input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
input_sync(button_dev);
return IRQ_HANDLED;
}
static int __init button_init(void)
{
int error;
if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
return -EBUSY;
}
button_dev = input_allocate_device();
if (!button_dev) {
printk(KERN_ERR "button.c: Not enough memory\n");
error = -ENOMEM;
goto err_free_irq;
}
button_dev->evbit[0] = BIT_MASK(EV_KEY);
button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
error = input_register_device(button_dev);
if (error) {
printk(KERN_ERR "button.c: Failed to register device\n");
goto err_free_dev;
}
return 0;
err_free_dev:
input_free_device(button_dev);
err_free_irq:
free_irq(BUTTON_IRQ, button_interrupt);
return error;
}
static void __exit button_exit(void)
{
input_unregister_device(button_dev);
free_irq(BUTTON_IRQ, button_interrupt);
}
module_init(button_init);
module_exit(button_exit);
这个示例驱动中首先需要创建一个struct input_dev结构,需要在该结构中设置设备的能力:
button_dev->evbit[0] = BIT_MASK(EV_KEY);
button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
除了这种方法,也可以使用如下方式达到同样的效果:
set_bit(EV_KEY, button_dev.evbit);
set_bit(BTN_0, button_dev.keybit);
然后调用
input_register_device(button_dev);
注册到核心层中。
设备注册了一个中断服务程序,当按键按下时,触发中断服务程序,调用input核心层代码:
input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
input_sync(button_dev);
来产生一个key event和一个sync event,并传到用户空间。sync event是为了标识一个完整的event事件的,特别是在abs事件和rel事件中比较重要。另外,设备层和事件处理层都有event缓冲区,当上层用户处于读阻塞的时候,如果不发送sync event,将不能及时的唤醒用户并读取事件,事件将会被缓存起来直至缓冲区满才唤醒用户。
input事件处理层
input事件处理层一般作为通用的模块来使用,它统一了不同设备之间的上层用户接口,不需要厂商另行开发。比如常用的/dev/eventX设备文件,本章将主要介绍evdev这个event handler。
代码文件:
/kernel/drivers/input/evdev.c
它实现了一个struct input_handler结构体:
static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
其中比较关键的回调函数是evdev_connect,它负责创建input_handler和input_dev之间的联系。也就是上面所提到的input_handle,并把它注册到核心层。
evdev_events和evdev_event是负责传递消息的回调,它们会把input_dev产生的消息先缓存起来,传递给用户空间。
这个input_handler结构体将被注册到input核心层:
input_register_handler(&evdev_handler);
除了上面的注册input_handler以外,事件处理层代码还需要创建设备文件以及实现file_operations,这样才能被用户空间来操作,这部分就不多做介绍了。
本文的以上内容介绍了input子系统的框架,了解更多信息请查看kernel下的doc目录及其源代码。