input子系统分析

前面对于按键的驱动有了大致的了解,赋予虚拟总线的概念,注册设备、驱动,互相探测,在探测函数中分配设备号、传递硬件设备信息给自定义结构体、初始化fops结构体、注册cdev结构体,这就是一个按键类型的驱动模型:https://blog.csdn.net/qq_40215005/article/details/90384563

但是作为一个操作系统,这样的设备实在是太多了,如果能在虚拟总线的基础上再进行抽象那就好了,所以子系统的概念就这么来了,比如说网络设备,就有网络子系统,输入设备就有输入子系统,显示屏就有framebuffuer子系统。

子系统

一个子系统是一个具有特定功能的模块,那么对于输入子系统来说,所有具有输入功能的设备都应该被集成在子系统中,子系统抽象了三层模型:

  • 设备驱动层:进行底层的设备初始化
  • 核心层:提供上下层的函数接口将一类设备统一管理
  • 事件处理层:进行不同底层事件的处理,提供不同的系统调用给不同的设备节点
    从上到下依次接近用户空间

在这里插入图片描述
在Linux中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。其中设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,而是要关心对各硬件寄存器的操作和提交的输入事件。这也就是为什么在看内核按键驱动源码的时候找不到fops结构体的初始化,这些动作都由于子系统的存在而不复存在了,更加精简的模块做更细致的工作而不用再担心所谓的上层文件操作了。

重要的数据结构

input_dev

struct input_dev {
    const char *name;             //  input设备的名字
    const char *phys;              //  
    const char *uniq;              //
    struct input_id id;             //  

//  这些是用来表示该input设备能够上报的事件类型有哪些   是用位的方式来表示的
    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 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);

    struct ff_device *ff;

    unsigned int repeat_key;
    struct timer_list timer;

    int sync;

    int abs[ABS_CNT];
    int rep[REP_MAX + 1];

    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 absmax[ABS_CNT];
    int absmin[ABS_CNT];
    int absfuzz[ABS_CNT];
    int absflat[ABS_CNT];
    int absres[ABS_CNT];

    int (*open)(struct input_dev *dev);              //    设备的open函数
    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;

    spinlock_t event_lock;
    struct mutex mutex;

    unsigned int users;
    bool going_away;

    struct device dev;                 //  内置的device结构体变量

    struct list_head    h_list;    //  用来挂接input_dev 设备连接的所有handle 的一个链表头
    struct list_head    node;    //  作为链表节点挂接到  input_dev_list 链表上  (input_dev_list链表是input核心层维护的一个用来挂接所有input设备的一个链表头)
};

input_handler

struct input_handler {

    void *private;            //  私有数据

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);   //  handler用于向上层上报输入事件的函数
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);            //   match 函数用来匹配handler 与 input_dev 设备
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);   //  当handler 与 input_dev 匹配成功之后用来连接
    void (*disconnect)(struct input_handle *handle);          //  断开handler 与 input_dev 之间的连接
    void (*start)(struct input_handle *handle);                    

    const struct file_operations *fops;             //  一个file_operations 指针
    int minor;                                      //  该handler 的编号 (在input_table 数组中用来计算数组下标) input_table数组就是input子系统用来管理注册的handler的一个数据结构
    const char *name;                               //  handler的名字

    const struct input_device_id *id_table;      //  指向一个 input_device_id  类型的数组,用来进行与input设备匹配时用到的信息
 
    struct list_head    h_list;       //  用来挂接handler 上连接的所有handle 的一个链表头
    struct list_head    node;        //  作为一个链表节点挂接到 input_handler_list 链表上(input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头)
};

input_handle

struct input_handle {

    void *private;               //   handle  的私有数据

    int open;                     //  这个也是用来做打开计数的
    const char *name;       //   该handle 的名字

    struct input_dev *dev;                //  用来指向该handle 绑定的input_dev 结构体
    struct input_handler *handler;    //  用来指向该handle 绑定的 handler 结构体

    struct list_head    d_node;      //  作为一个链表节点挂接到与他绑定的input_dev ->hlist 链表上
    struct list_head    h_node;      //  作为一个链表节点挂接到与他绑定的handler->hlist 链表上
};

input_device_id

struct input_device_id {

    kernel_ulong_t flags;    //  这个flag 表示我们的这个 input_device_id 是用来匹配下面的4个情况的哪一项
                                    //  flag == 1表示匹配总线  2表示匹配供应商   4表示匹配产品  8表示匹配版本
    __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 mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];

    kernel_ulong_t driver_info;
};

核心层分析源码

/driver/input/input.c

输入核心层其实是负责协调上层和下层,使得上层和下层之间能够完成数据传递。当下层发生输入事件的时候,整个系统就被激活了,事件就会通过核心层传递到上层对应的一个或多个handler中,最终会传递到应用空间。

核心层模块注册函数
input_init()

static int __init input_init(void)
{
    int err;

    input_init_abs_bypass();

    err = class_register(&input_class);                //  创建设备类    /sys/class/input
    if (err) {
        printk(KERN_ERR "input: unable to register input_dev class\n");
        return err;
    }

    err = input_proc_init();           //    proc文件系统相关的初始化
    if (err)
        goto fail1;

    err = register_chrdev(INPUT_MAJOR, "input", &input_fops);       //   注册字符设备驱动   主设备号13   input_fops 中只实现了open函数,所以他的原理其实和misc其实是一样的
    if (err) {
        printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
        goto fail2;
    }

    return 0;

 fail2:    input_proc_exit();
 fail1:    class_unregister(&input_class);
    return err;
}
驱动层和核心层接口函数

input设备驱动框架留给设备驱动层的接口函数主要有3个:
input_allocate_device。分配一块input_dev结构体类型大小的内存
input_set_capability。设置输入设备可以上报哪些输入事件
input_register_device。向input核心层注册设备

input_allocate_device

struct input_dev *input_allocate_device(void)
{
    struct input_dev *dev;                 //   定义一个 input_dev  指针

    dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);   //  申请分配内存
    if (dev) {
        dev->dev.type = &input_dev_type;          //  确定input设备的 设备类型     input_dev_type
        dev->dev.class = &input_class;                //  确定input设备所属的设备类   class
        device_initialize(&dev->dev);                   //  input设备的初始化
        mutex_init(&dev->mutex);                        //  互斥锁初始化
        spin_lock_init(&dev->event_lock);            //  自旋锁初始化
        INIT_LIST_HEAD(&dev->h_list);                 //  input_dev -> h_list 链表初始化
        INIT_LIST_HEAD(&dev->node);                 //  input_dev -> node 链表初始化

        __module_get(THIS_MODULE);
    }

    return dev;
}

input_set_capability()

函数原型:input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
dev 输入设备结构体
type 设备可以上报的事件类型
code 这类事件中的那个事件

事件类型

EV_SYN     0x00     同步事件
EV_KEY     0x01     按键事件
EV_REL     0x02     相对坐标(如:鼠标移动,报告的是相对最后一次位置的偏移)
EV_ABS     0x03     绝对坐标(如:触摸屏和操作杆,报告的是绝对的坐标位置)
EV_MSC     0x04     其它
EV_LED     0x11     LED
EV_SND     0x12     声音
EV_REP     0x14     Repeat
EV_FF      0x15     力反馈

input_register_device()

int input_register_device(struct input_dev *dev)      //  注册input输入设备
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_handler *handler;                          //  定义一个  input_handler 结构体指针
    const char *path;
    int error;

    /* Every input device generates EV_SYN/SYN_REPORT events. */
    __set_bit(EV_SYN, dev->evbit);                  //   每一个input输入设备都会发生这个事件

    /* KEY_RESERVED is not supposed to be transmitted to userspace. */
    __clear_bit(KEY_RESERVED, dev->keybit);  //  清除KEY_RESERVED 事件对应的bit位,也就是不传输这种类型的事件

    /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
    input_cleanse_bitmasks(dev);           //   确保input_dev中的用来记录事件的变量中没有提到的位掩码是干净的。

    /*
     * If delay and period are pre-set by the driver, then autorepeating
     * is handled by the driver itself and we don't do it in input.c.
     */
    init_timer(&dev->timer);
    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
        dev->timer.data = (long) dev;
        dev->timer.function = input_repeat_key;
        dev->rep[REP_DELAY] = 250;
        dev->rep[REP_PERIOD] = 33;
    }

    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;

    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;

    dev_set_name(&dev->dev, "input%ld",                                  //   设置input设备对象的名字    input+数字
             (unsigned long) atomic_inc_return(&input_no) - 1);

    error = device_add(&dev->dev);         //   添加设备       例如:          /sys/devices/virtual/input/input0     
    if (error)
        return error;

    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);  //  获取input设备对象所在的路径      /sys/devices/virtual/input/input_xxx   
    printk(KERN_INFO "input: %s as %s\n",
        dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
    kfree(path);

    error = mutex_lock_interruptible(&input_mutex);
    if (error) {
        device_del(&dev->dev);
        return error;
    }

    list_add_tail(&dev->node, &input_dev_list);             //   链表挂接:    将 input_dev->node 作为节点挂接到 input_dev_list  链表上

    list_for_each_entry(handler, &input_handler_list, node)  //  遍历input_handler_list 链表上的所有handler
        input_attach_handler(dev, handler);                        //  将handler与input设备进行匹配

    input_wakeup_procfs_readers();                //  更新proc 文件系统

    mutex_unlock(&input_mutex);

    return 0;
}

上面的注册inputdev的接口中还调用了input_attach_handler(dev, handler);

input_attach_handler()
input_attach_handler函数做的事情有两件:调用input_match_device函数进行设备与handler的匹配(通过id_table来匹配)、匹配成功调用handler的连接函数进行连接(至于如何连接将会在后面说到)。

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;                //   定义一个input_device_id 的指针
    int error;

    id = input_match_device(handler, dev);   //  通过这个函数进行handler与input设备的匹配工作
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id);  //  匹配成功则调用 handler 中的 connect 函数进行连接
    if (error && error != -ENODEV)
        printk(KERN_ERR
            "input: failed to attach handler %s to device %s, "
            "error: %d\n",
            handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}

input_match_device():

static const struct input_device_id *input_match_device(struct input_handler *handler,
                            struct input_dev *dev)
{
    const struct input_device_id *id;            //   定义一个 input_device_id  指针
    int i;

    for (id = handler->id_table; id->flags || id->driver_info; id++) {  //  依次遍历handler->id_table 所指向的input_device_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;

    //    下面的这些是匹配我们上传的事件是否属实
        MATCH_BIT(evbit,  EV_MAX);
        MATCH_BIT(keybit, KEY_MAX);
        MATCH_BIT(relbit, REL_MAX);
        MATCH_BIT(absbit, ABS_MAX);
        MATCH_BIT(mscbit, MSC_MAX);
        MATCH_BIT(ledbit, LED_MAX);
        MATCH_BIT(sndbit, SND_MAX);
        MATCH_BIT(ffbit,  FF_MAX);
        MATCH_BIT(swbit,  SW_MAX);

        if (!handler->match || handler->match(handler, dev))
            return id;        //    如果数组中的某个匹配成功了就返回他的地址
    }

    return NULL;
}

设备的驱动注册流程
①在驱动模块加载函数中设置Input设备支持input子系统的哪些事件;
②将Input设备注册到input子系统中;
③在Input设备发生输入操作时(如:键盘被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时等),提交所发生的事件及对应的键值/坐标等状态。

每个事件都有相应的报告函数
在include/linux/input.h中
input_report_rel 鼠标
input_report_abs 触摸
input_report_key 按键
input_sync 同步事件

事件发生后调用报告函数,报告函数再调用input_event()函数告知给核心层
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

所以我们在写驱动的时候只需要申请input设备,进行事件的类型注册和使用核心层的一些接口就行了,这样的子系统结构使得驱动编写简单很多

核心层和事件处理层接口

在input输入核心层向事件层提供的接口主要有两个:
input_register_handler。事件层向核心层注册handler
input_register_handle。事件层向核心层注册handle。 注意上面的是handler,这里是handle,不一样,后面会说到。

input_register_handler()

int input_register_handler(struct input_handler *handler)    //  向核心层注册handler
{
    struct input_dev *dev;          //  定义一个input_dev 指针
    int retval; 

    retval = mutex_lock_interruptible(&input_mutex);
    if (retval)
        return retval;

    INIT_LIST_HEAD(&handler->h_list);      //  初始化 handler->h_list 链表

    if (handler->fops != NULL) {          //  如果 handler -> fops 存在
        if (input_table[handler->minor >> 5]) {  //  如果input_table 数组中没有该handler  的位置了 则返回
            retval = -EBUSY;
            goto out;
        }
        input_table[handler->minor >> 5] = handler;  //  将 handler 指针存放在input_table 数组中去
    }

    list_add_tail(&handler->node, &input_handler_list);   //  将 handler 通过 handler -> node 节点 挂接到 input_handler_list 链表上

    list_for_each_entry(dev, &input_dev_list, node)     //  遍历 input_dev_list 链表下挂接的所有的 input_dev 设备
        input_attach_handler(dev, handler);          //  然后进行匹配

    input_wakeup_procfs_readers();             //  更新proc 文件系统

 out:
    mutex_unlock(&input_mutex);
    return retval;
}

通过分析了上面的input_register_device和这里的input_register_handler函数可以知道:注册设备的时候,不一定是先注册了handler才能够注册设备。当注册设备时,会先将设备挂接到设备管理链表(input_dev_list)上,然后再去遍历input_handler_list链表匹配hander。同样对于handler注册的时候,也会先将handler挂接到handler管理链表(input_handler_list)上,然后再去遍历input_dev_list链表匹配设备。所以从这里可以看出来,这种机制好像之前说过的platform总线下设备和驱动的匹配过程。而且一个input_dev可以与多个handler匹配成功,从而可以在sysfs中创建多个设备文件,也可以在/dev/目录下创建多个设备节点,并且他们的次设备号是不一样的,这个很好理解。所以就是导致一个设备对应多个次设备号,那这样有没有错呢?当然是没有错的。例如在我们的Ubuntu中,/dev/input/event3 和 /dev/input/mouse1 都是对应鼠标这个设备

input_register_handle()
这个函数的作用就是注册一个handle,handle用来记录一对已经匹配好的dev和handler

总结

核心层(其实就是驱动框架)提供的服务有哪些:

(1)创建设备类、注册字符设备

(2)向设备驱动层提供注册接口

(3)提供上层handler和下层device之间的匹配函数

(4)向上层提供注册handler的接口

但是我们还只看到了不同层的匹配注册,还不知道设备事件发生之后到底是怎么上报的,后面再来解析

事件层源码分析

核心层只提供一套接口在input.c中,但是事件层的有很多不同的连接者,分别代表不同设备的时间,最常用的设备节点/dev/event,按键、触摸屏都是这个类设备节点,对应的事件层的函数就是evdev.c

相关的数据结构

evdev

struct evdev {     
    int exist;
    int open;                                //  这个是用来作为设备被打开的计数
    int minor;                               //   handler 与 input设备匹配成功之后创建的设备对应的device的次设备号相对于基准次设备号的偏移量
    struct input_handle handle;   //   内置的一个  handle ,里面记录了匹配成功的input_dev 和 handler            
    wait_queue_head_t wait;
    struct evdev_client *grab;
    struct list_head client_list;       //   用来挂接与 evdev 匹配成功的evdev_client 的一个链表头 
    spinlock_t client_lock; /* protects client_list */
    struct mutex mutex;             //  互斥锁
    struct device dev;                 //  这个是handler 与 input设备匹配成功之后创建的设备对应的device
};

evdev_client

struct evdev_client {
    struct input_event buffer[EVDEV_BUFFER_SIZE];    //  用来存放input_dev 事件的缓冲区
    int head;
    int tail;
    spinlock_t buffer_lock; /* protects access to buffer, head and tail */
    struct fasync_struct *fasync;
    struct evdev *evdev;              //   evdev 指针
    struct list_head node;            //  作为一个链表节点挂接到相应的 evdev->client_list 链表上
    struct wake_lock wake_lock;
    char name[28];            //  名字
};

input_event

struct input_event {
    struct timeval time;        //  事件发生的时间
    __u16 type;                    //  事件的类型
    __u16 code;                   //   事件的码值
    __s32 value;                   //  事件的状态
};

最基本的结构体evdev_handler

static struct input_handler evdev_handler = {
    .event      = evdev_event,
    .connect    = evdev_connect,
    .disconnect = evdev_disconnect,
    .fops       = &evdev_fops,			//文件操作结构体
    .minor      = EVDEV_MINOR_BASE,		//次设备起始编号
    .name       = "evdev",				//设备name域
    .id_table   = evdev_ids,
};

模块初始化函数


static int __init evdev_init(void)
{
    return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
    input_unregister_handler(&evdev_handler);
}

再来说说
evdev_handler中的三个函数
evdev_event函数:
evdev_connect函数:
evdev_fops变量:

evdev_connect()

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
    struct evdev *evdev;                 //  定义一个 evdev 指针
    int minor;
    int error;

    for (minor = 0; minor < EVDEV_MINORS; minor++)  //  从evdev_table 数组中找到一个没有被使用的最小的数组项  最大值32
        if (!evdev_table[minor])
            break;

    if (minor == EVDEV_MINORS) {
        printk(KERN_ERR "evdev: no more free evdev devices\n");
        return -ENFILE;
    }

    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //  给evdev 申请分配内存
    if (!evdev)
        return -ENOMEM;

    INIT_LIST_HEAD(&evdev->client_list);            //  初始化 evdev->client_list 链表
    spin_lock_init(&evdev->client_lock);              //  初始化自旋锁 evdev->client_lock
    mutex_init(&evdev->mutex);                          //  初始化互斥锁 evdev->mutex
    init_waitqueue_head(&evdev->wait);

    dev_set_name(&evdev->dev, "event%d", minor);  // 设置input设备的名字
    evdev->exist = 1;
    evdev->minor = minor;                                     //  input设备的次设备号的偏移量 

    evdev->handle.dev = input_get_device(dev);              //  将我们传进来的 input_dev 指针存放在 evdev->handle.dev 中
    evdev->handle.name = dev_name(&evdev->dev);     //  设置 evdev -> dev 对象的名字,并且把名字赋值给 evdev->handle.name
    evdev->handle.handler = handler;          //  将我们传进来的 handler 指针存放在 handle.handler 中
    evdev->handle.private = evdev;             //  把evdev 作为handle 的私有数据

    evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);        //  设置 evdev->device 设备的设备号
    evdev->dev.class = &input_class;                                                  //  将 input_class 作为 evdev->device 的设备类
    evdev->dev.parent = &dev->dev;                                                // 将input_dev  -> device 作为evdev->device 的父设备
    evdev->dev.release = evdev_free;                   //  evdev -> device 设备的卸载函数
    device_initialize(&evdev->dev);                      //  设备初始化

    error = input_register_handle(&evdev->handle);       //  注册handle 
    if (error)
        goto err_free_evdev;

    error = evdev_install_chrdev(evdev);       // 安装evdev   其实就是将evdev 结构体指针存放在evdev_table数组当中  下标就是evdev->minor
    if (error)
        goto err_unregister_handle;

    error = device_add(&evdev->dev);     //  添加设备到系统          /sys/devices/virtual/input/input0/event0        event0就是表示建立的设备文件
    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;
}

该函数就是在目录树下创建一些相关的设备文件

evdev_event()

static void evdev_event(struct input_handle *handle,
            unsigned int type, unsigned int code, int value)
{
    struct evdev *evdev = handle->private;
    struct evdev_client *client;
    struct input_event event;

    do_gettimeofday(&event.time);
    event.type = type;
    event.code = code;
    event.value = value;

    rcu_read_lock();

    client = rcu_dereference(evdev->grab);
    if (client)
        evdev_pass_event(client, &event);
    else 
        list_for_each_entry_rcu(client, &evdev->client_list, node)//查找client_list链表中的client
        	evdev_pass_event(client, &event);//查找到了就执行

    rcu_read_unlock();

    if (type == EV_SYN && code == SYN_REPORT)
        wake_up_interruptible(&evdev->wait);
}

大致意思就是在事件到来之后,查找evdev_client,并执行evdev_pass_event函数

ev_pass_event()

static void evdev_pass_event(struct evdev_client *client,
						struct input_event *event)
{
    /* Interrupts are disabled, just acquire the lock. */
    spin_lock(&client->buffer_lock);

    client->buffer[client->head++] = *event;//将event放进client中的buffer中,buttfer在open函数中分配内存
    client->head &= client->bufsize - 1;

    if (unlikely(client->head == client->tail)) {//环形buffer是否已满
        /*
         * This effectively "drops" all unconsumed events, leaving
         * EV_SYN/SYN_DROPPED plus the newest event in the queue.
         */
        client->tail = (client->head - 2) & (client->bufsize - 1);

        client->buffer[client->tail].time = event->time;
        client->buffer[client->tail].type = EV_SYN;
        client->buffer[client->tail].code = SYN_DROPPED;
        client->buffer[client->tail].value = 0;

        client->packet_head = client->tail;
    }

    if (event->type == EV_SYN && event->code == SYN_REPORT) {
            client->packet_head = client->head;
        kill_fasync(&client->fasync, SIGIO, POLL_IN);
    }

    spin_unlock(&client->buffer_lock);
}

该函数就是将事件加入到client的buffer中并判断buffer是否已满。

再来看看提供给应用层的操作
evdev_open()

static int evdev_open(struct inode *inode, struct file *file)
{
    struct evdev *evdev;                       //  定义一个 evdev 结构体指针
    struct evdev_client *client;             //   定义一个evdev_client 指针
    int i = iminor(inode) - EVDEV_MINOR_BASE;   //  通过inode 获取 需要打开的设备对应的evdev_table 数组中的下标变量
    int error;

    if (i >= EVDEV_MINORS)
        return -ENODEV;

    error = mutex_lock_interruptible(&evdev_table_mutex);
    if (error)
        return error;
    evdev = evdev_table[i];             //  从evdev_table  数组中找到evdev 
    if (evdev)
        get_device(&evdev->dev);
    mutex_unlock(&evdev_table_mutex);

    if (!evdev)
        return -ENODEV;

    client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);      //  给 client 申请分配内存
    if (!client) {
        error = -ENOMEM;
        goto err_put_evdev;
    }

    spin_lock_init(&client->buffer_lock);
    snprintf(client->name, sizeof(client->name), "%s-%d",
            dev_name(&evdev->dev), task_tgid_vnr(current));
    wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
    client->evdev = evdev;                          //  通过client->evdev 指针指向 evdev
    evdev_attach_client(evdev, client);   //  其实这个函数就是做了一个链表挂接:  client->node  挂接到 evdev->client_list

    error = evdev_open_device(evdev); //  打开 evdev 设备   最终就会打开 input_dev -> open 函数
    if (error)
        goto err_free_client;

    file->private_data = client;              //   将evdev_client 作为file 的私有数据存在
    nonseekable_open(inode, file);

    return 0;

 err_free_client:
    evdev_detach_client(evdev, client);
    kfree(client);
 err_put_evdev:
    put_device(&evdev->dev);
    return error;
}

该函数会调用evdev_open_device,然后会调用到核心层的input_open_device
evdev_open------->evdev_open_device------->input_open_device-------->open.

)input设备注册的流程:

下层通过调用核心层的函数来向子系统注册input输入设备

input_register_device

    device_add:  /sys/devices/virtual/input/input0

    链表挂接: input_dev->node    ------->  input_dev_list

    input_attach_handler                          //  进行input_dev和handler之间的匹配

        调用handler->connect进行连接

            构建evdev结构体,加入evdev_table数组

            input_register_handle

            device_add:  /sys/devices/virtual/input/input0/event0

handler注册流程

input_register_handler

    input_table[handler->minor >> 5] = handler

    链表挂接:  handler->node  ----->   input_handler_list

    input_attach_handler

        handler->connect                  // 调用handler的connect函数进行连接
事件如何传递到应用层

input子系统下层通过调用input_event函数项核心层上报数据


input_event

    input_handle_event

        input_pass_event

            handler->event()            //  最终会调用到handler 中的event函数

                evdev_pass_event

                    client->buffer[client->head++] = *event;     //  会将input输入事件数据存放在evdev_client结构体中的缓冲去中

input子系统就大致就是这样的模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值