内核input子系统

简介
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目录及其源代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值