linux内核input子系统解析

Android、X windows、qt等众多应用对于linux系统中键盘、鼠标、触摸屏等输入设备的支持越来越倾向于标准的input输入子系统。包括我们要分析的条形码和二维码扫描枪,它们只是模拟了键盘输入,走了Input输入子系统的流程。


一、input输入子系统框架(截图来源于网络)

下图是input输入子系统框架,输入子系统由输入子系统核心层( Input Core ),驱动层和事件处理层(Event Handler)三部份组成。

一个输入事件,如鼠标移动,键盘按键按下,扫描枪录入等等通过 input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。


       

    二、Input driver要点


    1、分配、注册、注销input设备

     struct input_dev *input_allocate_device(void)

        int input_register_device(struct input_dev *dev)
        void input_unregister_device(struct input_dev *dev)

    2、设置input设备支持的事件类型、事件码、事件值的范围、input_id等信息

         参见usb键盘驱动:driver/hid/usbhid/usbkbd.c

     usb_to_input_id(dev, &input_dev->id);//设置bustype、vendo、product等

     input_dev->dev.parent = &iface->dev;

     input_set_drvdata(input_dev, kbd);

     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP);//支持的事件类型

     input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) | BIT_MASK(LED_KANA);// EV_LED事件支持的事件码

     for (i = 0; i < 255; i++)

         set_bit(usb_kbd_keycode[i], input_dev->keybit);//EV_KEY事件支持的事件码


         include/linux/input.h中定义了支持的类型

/*

 * Event types

 */

#define EV_SYN                  0x00

#define EV_KEY                  0x01

#define EV_REL                  0x02

#define EV_ABS                  0x03

#define EV_MSC                  0x04

#define EV_SW                   0x05

#define EV_LED                  0x11

#define EV_SND                  0x12

#define EV_REP                  0x14

#define EV_FF                   0x15

#define EV_PWR                  0x16

#define EV_FF_STATUS            0x17

#define EV_MAX                  0x1f

#define EV_CNT                  (EV_MAX+1)



      一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件码。比如:EV_KEY事件,需要定义其支持哪些按键事件码。


    3、设置input设备的打开、关闭、按键处理时的处理方法。参见usb键盘驱动:usbkbd.c

static int usb_kbd_open(struct input_dev *dev)
static int usb_kid_close(struct input_dev *dev)
static void usb_kbd_irq(struct urb *urb)

    4、在发生输入事件时,向子系统报告事件

    用于报告EV_KEY、EV_REL、EV_ABS等事件的函数有:
    void input_report_key(struct input_dev *dev, unsigned int code, int value) //usb_kbd_irq(...)会调用该函数
    void input_report_rel(struct input_dev *dev, unsigned int code, int value)
    void input_report_abs(struct input_dev *dev, unsigned int code, int value)

    如果你觉得麻烦,你也可以只记住1个函数(因为上述函数都是通过它实现的)

    void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

    三、Event Handler层解析


    1、Input输入子系统数据结构关系图(来源于网络)

        




    2、input_handler结构体


    以evdev.c中的evdev_handler为例:


    static struct input_handler evdev_handler = {
                .event = evdev_event, //向系统报告input事件,系统通过read方法读取
                .connect = evdev_connect, //和input_dev匹配后调用connect构建
                .disconnect = evdev_disconnect,
                .fops = &evdev_fops, //event设备文件的操作方法
                .minor = EVDEV_MINOR_BASE, //次设备号基准值
                .name = "evdev",
                .id_table = evdev_ids, //匹配规则
        };


    3、input字符设备注册过程


    drivers/input/input.c中:
        static int __init input_init(void)
        {
                int err;
                err = class_register(&input_class);
                ……
                err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
                ……
        }


    input_fops定义:


    static const struct file_operations input_fops = {
                .owner = THIS_MODULE,

                .open = input_open_file,

.llseek = noop_llseek,


        };


    Input_dev和input_handler匹配后调用input_handler的connect。以evdev_handler为例:


/*

 * Create new evdev device. Note that input core serializes calls

 * to connect and disconnect so we don't need to lock evdev_table here.

 */

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,

                         const struct input_device_id *id)

{

        struct evdev *evdev;

        int minor;

        int error;


        for (minor = 0; minor < EVDEV_MINORS; minor++)

                if (!evdev_table[minor])

                        break;


        if (minor == EVDEV_MINORS) {

                pr_err("no more free evdev devices\n");

                return -ENFILE;

        }


        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

        if (!evdev)

                return -ENOMEM;


        INIT_LIST_HEAD(&evdev->client_list);

        spin_lock_init(&evdev->client_lock);

        mutex_init(&evdev->mutex);

        init_waitqueue_head(&evdev->wait);


        dev_set_name(&evdev->dev, "event%d", minor);

        evdev->exist = true;

        evdev->minor = minor;


        evdev->handle.dev = input_get_device(dev);

        evdev->handle.name = dev_name(&evdev->dev);

        evdev->handle.handler = handler;

        evdev->handle.private = evdev;

        evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);

        evdev->dev.class = &input_class;

        evdev->dev.parent = &dev->dev;

        evdev->dev.release = evdev_free;

        device_initialize(&evdev->dev);


        error = input_register_handle(&evdev->handle);

        if (error)

                goto err_free_evdev;


        error = evdev_install_chrdev(evdev);

        if (error)

                goto err_unregister_handle;


        error = device_add(&evdev->dev);

        if (error)

                goto err_cleanup_evdev;


        return 0;


 err_cleanup_evdev:

        evdev_cleanup(evdev);

 err_unregister_handle:

        input_unregister_handle(&evdev->handle);

 err_free_evdev:

        put_device(&evdev->dev);

        return error;

}


    4、input字符设备的打开过程

static int input_open_file(struct inode *inode, struct file *file)

{

        struct input_handler *handler;

        const struct file_operations *old_fops, *new_fops = NULL;

        int err;


        err = mutex_lock_interruptible(&input_mutex);

        if (err)

                return err;


        /* No load-on-demand here? */

        handler = input_table[iminor(inode) >> 5];

        if (handler)

                new_fops = fops_get(handler->fops);


        mutex_unlock(&input_mutex);


        /*

         * That's _really_ odd. Usually NULL ->open means "nothing special",

         * not "no device". Oh, well...

         */

        if (!new_fops || !new_fops->open) {

                fops_put(new_fops);

                err = -ENODEV;

                goto out;

        }


        old_fops = file->f_op;

        file->f_op = new_fops;


        err = new_fops->open(inode, file);

        if (err) {

                fops_put(file->f_op);

                file->f_op = fops_get(old_fops);

        }

        fops_put(old_fops);

out:

        return err;

}




    5、input字符设备的其它操作


    由于在open阶段已经把设备文件的操作操作方法重定位了到了具体的input_handler,所以其它接口操作(read、write、ioctl等),由各个input_handler的fops方法决定。如evdev.c中的:evdev_fops。


四、应用层处理

对于上层的处理,可以参考下面的博客

http://blog.csdn.net/tankai19880619/article/details/17019085

你可能感兴趣的:(linux内核input子系统解析)