INPUT子系统学习 evdev

  • 学习出发点

本文主要记录Input子系统的实现,当创建此文档时,个人想要理清的问题罗列如下:

1.    open时怎么打开到evdev的,和input核心有什么关联 ---- 目前了解的是open时,经过一些处理后,会走到evdec部分

2.    evdev怎么和input关联起来的

3.    设备驱动注册到Input时需要的信息,及Input接收设备注册后的流程

4.    Input子系统的实现代码

5.    设备上报流程

6.    user空间读取input数据的流程

7.    input子系统中handle  handler的作用,其与evdev input的关联

 

 

INPUT子系统

https://www.cnblogs.com/deng-tao/p/6094049.html

一、什么是input输入子系统?

 

Linux系统支持的输入设备繁多,例如键盘、鼠标、触摸屏、手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型、不同原理、不同的输入信息的输入设备的呢?其实就是通过input输入子系统这套软件体系来完成的。从整体上来说,input输入子系统分为3层:上层(输入事件驱动层)、中层(输入核心层)、下层(输入设备驱动层),如下图所示:

INPUT子系统学习 evdev_第1张图片

联系之前学过的驱动框架做对比,input输入子系统其实就是input输入设备的驱动框架,与之前的学过的驱动框架不同的是,input输入子系统分为3层:上、中、下,所以他的复杂度要高于之前讲的lcd、misc、fb等的驱动框架。

2、图中Drivers对应的就是下层设备驱动层,对应各种各样不同的输入设备,Input Core对应的就是中层核心层,Handlers对应的就是上层输入事件驱动层,最右边的代表的是用户空间。

(1)从图中可以看出,系统中可以注册多个输入设备,每个输入设备的类型可以是不同的,例如一台电脑上可以带有鼠标,键盘....。

(2)上层中的各个handler(Keyboard/Mouse/Joystick/Event)是属于平行关系,他们都是属于上层。不同的handler下对应的输入设备在应用层中的接口命名方式不一样,例如Mouse下的输入设备在应用层的接口是 /dev/input/mousen (n代表0、1、2...),Joystick下的输入设备在应用层的接口是 /dev/input/jsn(n代表0、1、2...),Event下的输入设备在应用层的接口是 /dev/input/eventn(n代表0、1、2...),这个是在input输入子系统中实现的,下面会分析其中的原由。

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

3、输入子系统解决了什么问题?

(1)在GUI界面中,用户的自由度太大了,可以做的事情太多了,可以响应不同的输入类设备,而且还能够对不同的输入类设备的输入做出不同的动作。例如window中的一个软件既可以响应鼠标输入事件,也可以相应键盘输入事件,而且这些事件都是预先不知道的。

(2)input子系统解决了不同的输入类设备的输入事件与应用层之间的数据传输,使得应用层能够获取到各种不同的输入设备的输入事件,input输入子系统能够囊括所有的不同种类的输入设备,在应用层都能够感知到所有发生的输入事件。

4、input输入子系统如何工作?

例如以一次鼠标按下事件为例子来说明我们的input输入子系统的工作过程:当我们按下鼠标左键的时候就会触发中断(中断是早就注册好的),就会去执行中断所绑定的处理函数,在函数中就会去读取硬件寄存器来判断按下的是哪个按键和状态 ---->将按键信息上报给input core层  ---> input core层处理好了之后就会上报给input event层,在这里会将我们的输入事件封装成一个input_event结构体放入一个缓冲区中 --->  应用层read就会将缓冲区中的数据读取出去。

sprd_keypad流程分析

static int sprd_keypad_probe(struct platform_device *pdev)

{

       ……

       //驱动匹配

       ……

       //申请input_dev

       input_dev = devm_input_allocate_device(&pdev->dev);

       ……

       //input_dev保存到私有结构体中

       sprd_kpd->input_dev = input_dev;

       ……

       //keypad配置

       ……

       //中断获取

       sprd_kpd->irq = platform_get_irq(pdev, 0);

       ……

       //中断申请

       error = devm_request_irq(&pdev->dev, sprd_kpd->irq, sprd_keypad_isr,

                     IRQF_NO_SUSPEND, "sprd-keypad", sprd_kpd);

       //input_dev初始化

       input_dev->name = "sprd-keypad";

       input_dev->phys = "sprd-key/input0";

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

       input_set_drvdata(input_dev, sprd_kpd);  àinput_dev->dev->driver_data = sprd_kpd

 

       input_dev->id.bustype = BUS_HOST;

       input_dev->id.vendor = 0x0001;

       input_dev->id.product = 0x0001;

       input_dev->id.version = 0x0100;

       ……

       //配置支持的输入类别 ---- 应该可以这么说吧

       /* there are keys from hw other than keypad controller */

       __set_bit(KEY_POWER, input_dev->keybit);

       __set_bit(EV_KEY, input_dev->evbit);

       if (pdata->repeat)

              __set_bit(EV_REP, input_dev->evbit);

       //注册input_dev

       error = input_register_device(input_dev);

       ……

       platform_set_drvdata(pdev, sprd_kpd);

……

       return 0;

}

按键按下,触发keypad中断:

sprd_keypad_isr:

       input_report_key(sprd_kpd->input_dev, key, 1);

       input_sync(sprd_kpd->input_dev);

devm_input_allocate_device

函数原型:struct input_dev *devm_input_allocate_device(struct device *dev)

暂不清楚此函数的作用,但代码先理一下

此函数的注释如下:

/**

 * devm_input_allocate_device - allocate managed input device

 * @dev: device owning the input device being created

 *

 * Returns prepared struct input_dev or %NULL.

 *

 * Managed input devices do not need to be explicitly unregistered or

 * freed as it will be done automatically when owner device unbinds from

 * its driver (or binding fails). Once managed input device is allocated,

 * it is ready to be set up and registered in the same fashion as regular

 * input device. There are no special devm_input_device_[un]register()

 * variants, regular ones work with both managed and unmanaged devices,

 * should you need them. In most cases however, managed input device need

 * not be explicitly unregistered or freed.

 *

 * NOTE: the owner device is set up as parent of input device and users

 * should not override it.

 */

大致翻译一下:

dev_input_allocate_device: 分配托管的输入设备

dev:拥有正在创建的设备的设备,就是父设备

返回值:准备好的input_dev结构体,失败返回NULL

托管的输入设备不需要显式地卸载或释放,因为当设备所有者从其驱动程序中解绑(或绑定失败)时,将自动执行该操作。一旦托管的输入设备分配成功,它就准备好以与常规输入设备相同的方式建立和注册。没有特殊的注册和卸载函数devm_input_device_[un]register,如果您需要的话,常规的变体可以同时使用托管和非托管设备。然而,在大多数情况下,托管的输入设备不需要显式地未注册或释放。

注意:设备所有者被设置为输入设备的父级,用户不应重写它

此处托管的输入设备有什么用,托管了什么,需要再做分析,暂时先不追究!

input_dev结构中,xxxbit[xxx_cnt]数组,是使用一个bit位代表一个功能或者code。

input_ register_device

函数原型:int input_register_device(struct input_dev *dev)

函数注释:

/**

 * input_register_device - register device with input core

 * @dev: device to be registered

 *

 * This function registers device with input core. The device must be

 * allocated with input_allocate_device() and all it's capabilities

 * set up before registering.

 * If function fails the device must be freed with input_free_device().

 * Once device has been successfully registered it can be unregistered

 * with input_unregister_device(); input_free_device() should not be

 * called in this case.

 *

 * Note that this function is also used to register managed input devices

 * (ones allocated with devm_input_allocate_device()). Such managed input

 * devices need not be explicitly unregistered or freed, their tear down

 * is controlled by the devres infrastructure. It is also worth noting

 * that tear down of managed input devices is internally a 2-step process:

 * registered managed input device is first unregistered, but stays in

 * memory and can still handle input_event() calls (although events will

 * not be delivered anywhere). The freeing of managed input device will

 * happen later, when devres stack is unwound to the point where device

 * allocation was made.

 */

input_register_device:注册设备到input子系统

dev:被注册的设备

此函数注册设备到input子系统。被注册的设备必须是使用input_allocate_device函数进行进行申请且功能集合在注册前已经被正确设置。如果函数执行失败,设备必须使用input_free_device()函数进行释放。一但设备成功注册,设备可能使用input_unregister_device( )进行卸载而不能在调用input_free_device( )进行释放。

同时此函数也可以用来注册托管的输入设备(设备使用devm_input_allocate_device( )申请)。此类设备不必显示的卸载和释放,他们的释放和卸载由devres结构体控制。另外需要注意的是托管的输入设备的卸载在内部分为两部分:注册的托管输入设备先被卸载,此时依然存在于内存中并且可以继续处理input_event( )调用(尽管事件不会传递给任何函数)。当devres栈释放到分配点时,托管的输入设备将会被释放。

 

int input_register_device(struct input_dev *dev)

{

       ……

       //如果之前使用devm_input_allocate_device申请过,为什么又申请一次

       if (dev->devres_managed) {

              devres = devres_alloc(devm_input_device_unregister,

                                  sizeof(struct input_devres), GFP_KERNEL);

              if (!devres)

                     return -ENOMEM;

              //两个devres->input指针指向同一个input_dev

              devres->input = dev;

       }

 

       /* Every input device generates EV_SYN/SYN_REPORT events. */

       __set_bit(EV_SYN, dev->evbit);

 

       /* KEY_RESERVED is not supposed to be transmitted to userspace. */

       __clear_bit(KEY_RESERVED, dev->keybit);

 

       /* Make sure that bitmasks not mentioned in dev->evbit are clean. */

       //保证未使用的能力集清零

       input_cleanse_bitmasks(dev);

       //根据输入类型计算上报数据需要的存储空间大小,并申请内存空间

       packet_size = input_estimate_events_per_packet(dev);

       if (dev->hint_events_per_packet < packet_size)

              dev->hint_events_per_packet = packet_size;

 

       dev->max_vals = dev-2>hint_events_per_packet + 2;

       dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);

       ……

       //如果驱动自行管理repeat,input子系统核心就不在处理此事

       /*

        * 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.

        */

       if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])

              input_enable_softrepeat(dev, 250, 33);

      

       if (!dev->getkeycode)

              dev->getkeycode = input_default_getkeycode;

 

       if (!dev->setkeycode)

              dev->setkeycode = input_default_setkeycode;

       //添加设备,会进行驱动与设备的匹配

       error = device_add(&dev->dev);

       ……

       //添加设备到input_dev_list管理链表中

       list_add_tail(&dev->node, &input_dev_list);

       //handler attach

       //从input_handler_list链表里取出已有的handler,根据设备的id_table信息,进行匹配

       //如果id_table信息可以匹配上,则调用handler->match(hander,dev)进行匹配,如果match成功,则调用handler->connect(handler,dev,id)进行连接。

       //如果有多个id_table可以匹配,会发生什么情况?会出现此种情形吗?

       list_for_each_entry(handler, &input_handler_list, node)

              input_attach_handler(dev, handler);

 

       input_wakeup_procfs_readers();

 

       mutex_unlock(&input_mutex);

 

       if (dev->devres_managed) {

              dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",

                     __func__, dev_name(&dev->dev));

              devres_add(dev->dev.parent, devres);

       }

       return 0;

 

err_device_del:

       device_del(&dev->dev);

err_free_vals:

       kfree(dev->vals);

       dev->vals = NULL;

err_devres_free:

       devres_free(devres);

       return error;

}

既然有input_register_device,并且在函数中调用了

list_for_each_entry(handler, &input_handler_list, node)

              input_attach_handler(dev, handler);

那就应该对应的input_register_handler的函数。

我们看一下input_register_handler

input_register_handler

函数原型:int input_register_handler(struct input_handler *handler)

这个函数是由谁调用呢?

函数注释:

/**

 * input_register_handler - register a new input handler

 * @handler: handler to be registered

 *

 * This function registers a new input handler (interface) for input

 * devices in the system and attaches it to all input devices that

 * are compatible with the handler.

 */

input_register_handler:注册一个新的输入handler

handler:被注册的handler

此函数为系统中的输入设备注册新的输入处理程序(接口),将其附加到与处理程序兼容的所有输入设备。

int input_register_handler(struct input_handler *handler)

{

       ……

       //将handler添加到input_handler_list链表中

       list_add_tail(&handler->node, &input_handler_list);

       //对input_dev_list中已经注册的设备进行匹配

       list_for_each_entry(dev, &input_dev_list, node)

              input_attach_handler(dev, handler);

       ……

       return 0;

}

那么谁来调用input_register_handler呢

我们看一下内核提供的evdev.c文件

evdev

https://blog.csdn.net/qq_695538007/article/details/9153135

linux内核中有很多自带的input_handler,其中evdev_handler是最常见的,因为它可以匹配任何的input_dev设备。

evdev的注册过程

static const struct input_device_id evdev_ids[] = {

       { .driver_info = 1 },      /* Matches all devices */

       { },                /* Terminating zero entry */

};

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,

};

 

static int __init evdev_init(void)

{

       return input_register_handler(&evdev_handler);

}

evdev初始化时,调用input_register_handler函数,此时evdev_handler就被添加到input_handler_list链表中,在后续设备添加时,会进行匹配。为什么说它可以匹配任意input device呢,因为在evdev_ids中只定义了成员driver_info = 1。在input_attach_handler函数中调用input_match_device函数,而其中的实现代码如下:

static const struct input_device_id *input_match_device(struct input_handler *handler,

                                                 struct input_dev *dev)

{

       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 (!handler->match || handler->match(handler, dev))

                     return id;

       }

 

       return NULL;

}

for循环中,判定条件为id->flags || id->driver_info,所以对于任意的input device,在扫描到evdev_handler时,都会进入循环体内进行匹配;而id->flasg此时为0,最终走到if(!handler->match || handler->match(handler,dev)判断,在看evdev_handler结构体并没有实现match成员函数,所以返回当前id,之后调用handler->connect函数。

evdev_connect

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

                      const struct input_device_id *id)

{

       //获取新的次设备号

       minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);

       ……

       //申请evdev内存

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

       //client_list是干吗用的??

       INIT_LIST_HEAD(&evdev->client_list);

       spin_lock_init(&evdev->client_lock);

       mutex_init(&evdev->mutex);

       init_waitqueue_head(&evdev->wait);

       evdev->exist = true;

 

       dev_no = minor;

       //这里应该就是在/dev/input/eventX的来源吧

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

 

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

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

       evdev->handle.handler = handler;

       evdev->handle.private = evdev;

       ……

       //注册handle

       error = input_register_handle(&evdev->handle);

       //字符设备初始化,关联evdev_fops

       cdev_init(&evdev->cdev, &evdev_fops);

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

       error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);

       ……

       error = device_add(&evdev->dev);

       ……

       return 0;

 

 err_cleanup_evdev:

       evdev_cleanup(evdev);

 err_unregister_handle:

       input_unregister_handle(&evdev->handle);

 err_free_evdev:

       put_device(&evdev->dev);

 err_free_minor:

       input_free_minor(minor);

       return error;

}

input_handler结构体

/**

 * struct input_handler - implements one of interfaces for input devices

 * @private: driver-specific data

 * @event: event handler. This method is being called by input core with

 *    interrupts disabled and dev->event_lock spinlock held and so

 *    it may not sleep

 * @events: event sequence handler. This method is being called by

 *    input core with interrupts disabled and dev->event_lock

 *    spinlock held and so it may not sleep

 * @filter: similar to @event; separates normal event handlers from

 *    "filters".

 * @match: called after comparing device's id with handler's id_table

 *    to perform fine-grained matching between device and handler

 * @connect: called when attaching a handler to an input device

 * @disconnect: disconnects a handler from input device

 * @start: starts handler for given handle. This function is called by

 *    input core right after connect() method and also when a process

 *    that "grabbed" a device releases it

 * @legacy_minors: set to %true by drivers using legacy minor ranges

 * @minor: beginning of range of 32 legacy minors for devices this driver

 *    can provide

 * @name: name of the handler, to be shown in /proc/bus/input/handlers

 * @id_table: pointer to a table of input_device_ids this driver can

 *    handle

 * @h_list: list of input handles associated with the handler

 * @node: for placing the driver onto input_handler_list

 *

 * Input handlers attach to input devices and create input handles. There

 * are likely several handlers attached to any given input device at the

 * same time. All of them will get their copy of input event generated by

 * the device.

 *

 * The very same structure is used to implement input filters. Input core

 * allows filters to run first and will not pass event to regular handlers

 * if any of the filters indicate that the event should be filtered (by

 * returning %true from their filter() method).

 *

 * Note that input core serializes calls to connect() and disconnect()

 * methods.

 */

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;

};

注释:无法在第一时间明白的就不再罗列

struct input_handler:实现输入设备的接口之一

private:驱动私有数据

event:事件处理函数。此方法在中断关闭并且得到dev->event_lock 自旋锁时由input子系统核心层调用,所以此实现方法可能无法进入睡眠。在evdev中,event调用events函数。

events:事件序列处理程序。此方法在中断关闭并且得到dev->event_lock 自旋锁时由input子系统核心层调用,所以此实现方法中不应该进入睡眠。

match:在比较设备id和处理程序id_table之后,调用设备和处理程序之间的细粒度匹配。

connect:在将处理程序附加到输入设备时调用。

start:为给定的handle启动handler。这冰的“城市功能核心右后输入connect()方法和工艺,也当“grabbed稿”,一个设备的信息。在connect( )调用完成后立即由input子系统核心调用,同时当一个进程“获取到一个设备释放时调用。--此处不理解另外一种调用情况。在evdev中没有实现start方法。

input dev open流程

static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)

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

       error = input_register_handle(&evdev->handle);

 

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

       struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);

       client->evdev = evdev;

       error = evdev_open_device(evdev);

       file->private_data = client;

 

static int evdev_open_device(struct evdev *evdev)

       retval = input_open_device(&evdev->handle);

 

int input_open_device(struct input_handle *handle)

       struct input_dev *dev = handle->dev;

       retval = dev->open(dev);

在gpio_keys.c中:

static int gpio_keys_probe(struct platform_device *pdev)

       input = devm_input_allocate_device(dev);

       input->open = gpio_keys_open;

       input->close = gpio_keys_close;

 

static int gpio_keys_open(struct input_dev *input)

 

数据关系

现在来理下struct input_dev, struct input_handler, struct input_handle, struct evdev, struct evdev_client的关系吧

struct input_dev:和一个硬件相关联,主要描述了该硬件支持什么事件,上报事件;

struct input_handler:主要就提供了设备的操作函数,比如读写等

struct input_handle:当input_dev和struct input_handler相匹配的时候就新建一个struct input_handle作为它们的中间人

struct evdev:当某个struct input_dev和evdev_handler匹配成功后就新建一个struct evdev,看到这里是不是有点和struct input_handle产生的时间一致呀,起始该结构体内部就包含一个struct input_handle;不过结合了一些其他信息,可以把它看做是struct input_handle的升级版。

struct evdev_client 主要跟应用程序有关.每打开一次设备就新建一个struct evdev_client,里面保存了应用程序要读取的数据以及数据的位置。

INPUT子系统学习 evdev_第2张图片

事件上报input_event

在sprd_keypad.c中

static irqreturn_t sprd_keypad_isr(int irq, void *dev_id)

input_report_key(sprd_kpd->input_dev, key, 1);

       input_sync(sprd_kpd->input_dev);

 

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)

input_event(dev, EV_KEY, code, !!value);

 

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

input_handle_event(dev, type, code, value);

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

       input_pass_values(dev, dev->vals, dev->num_vals);

 

static void input_pass_values(struct input_dev *dev, struct input_value *vals, unsigned int count)

       handle = rcu_dereference(dev->grab);

evdev read 流程

当应用程序调用read函数后将导致evdev_read函数被调用,数据读取流程如下:

 

static ssize_t evdev_read(struct file *file, char __user *buffer,  size_t count, loff_t *ppos)

        evdev_fetch_next_event(client, &event)

 

static int evdev_fetch_next_event(struct evdev_client *client, struct input_event *event)

         *event = client->buffer[client->tail++];

evdev_read

static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)

{

         struct evdev_client *client = file->private_data;   //在evdev_open末尾被赋值

         struct evdev *evdev = client->evdev;

         struct input_event event;

         size_t read = 0;

         int error;

 

         if (count != 0 && count < input_event_size())

                   return -EINVAL;

         for (;;) {

                   if (!evdev->exist || client->revoked)

                            return -ENODEV;

                   if (client->packet_head == client->tail &&

                       (file->f_flags & O_NONBLOCK))

                            return -EAGAIN;

                   /*

                    * count == 0 is special - no IO is done but we check

                    * for error conditions (see above).

                    */

                   if (count == 0)

                            break;

                   while (read + input_event_size() <= count &&

                           //取出缓冲区中的数据

                          evdev_fetch_next_event(client, &event)) {

                           //转换数据并传递到用户空间

                            if (input_event_to_user(buffer + read, &event))

                                     return -EFAULT;

                            read += input_event_size();

                   }

                   if (read)

                            break;

                  //如果是阻塞方式读取,并且目前缓冲区中无数据,等待缓冲区中有新的数据写入

                   if (!(file->f_flags & O_NONBLOCK)) {

                            error = wait_event_interruptible(evdev->wait,

                                               client->packet_head != client->tail ||

                                               !evdev->exist || client->revoked);

                            if (error)

                                     return error;

                   }

         }

         return read;

}

 

evdev_fetch_next_event

该函数主要是判断缓冲区中是否有数据,如果有,取出数据并维护首尾指针

static int evdev_fetch_next_event(struct evdev_client *client, struct input_event *event)

{

         int have_event;

         spin_lock_irq(&client->buffer_lock);

         have_event = client->packet_head != client->tail;

         if (have_event) {

                   *event = client->buffer[client->tail++];

                   client->tail &= client->bufsize - 1;

                   if (client->use_wake_lock &&

                       client->packet_head == client->tail)

                            wake_unlock(&client->wake_lock);

         }

         spin_unlock_irq(&client->buffer_lock);

        return have_event;

}

 

input_event_to_user

函数在input_compat.c文件中实现,主要是转换数据到用户空间。

驱动数据到event缓冲区的流程

那么,数据是如何写入到client->buffer中的呢?

在代码,我们上报event数据时,会使用input_report_XXX/input_synt两个接口函数。看一下它们的实现方式。

input_report_XXX

input_report_XXX接口函数最终会调用input_handle_event接口。以input_report_key为例:


static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)

         input_event(dev, EV_KEY, code, !!value);


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

         input_handle_event(dev, type, code, value);


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

         if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)

                   dev->event(dev, type, code, value);

         input_pass_values(dev, dev->vals, dev->num_vals);


static void input_pass_values(struct input_dev *dev, struct input_value *vals, unsigned int count)

         handle = rcu_dereference(dev->grab); //grabinput_handle的类型,在哪里赋值的呢?

         count = input_to_handler(handle, vals, count);


static unsigned int input_to_handler(struct input_handle *handle, struct input_value *vals, unsigned int count)

         if (handler->filter(handle, v->type, v->code, v->value))

         if (handler->events)

                   handler->events(handle, vals, count);  //最终调用evdev中的evdev_events函数

         else if (handler->event)

                   for (v = vals; v != vals + count; v++)

                            handler->event(handle, v->type, v->code, v->value);


static void evdev_events(struct input_handle *handle, const struct input_value *vals, unsigned int count)

         client = rcu_dereference(evdev->grab);

         evdev_pass_values(client, vals, count, ev_time);


static void evdev_pass_values(struct evdev_client *client,const struct input_value *vals, unsigned int count,ktime_t *ev_time)

         __pass_event(client, &event);

       if (wakeup)

                  wake_up_interruptible(&evdev->wait);


static void __pass_event(struct evdev_client *client, const struct input_event *event)

{

         client->buffer[client->head++] = *event;

         client->head &= client->bufsize - 1;

 

         if (unlikely(client->head == client->tail)) {

                   /*

                    * 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 (client->use_wake_lock)

                            wake_unlock(&client->wake_lock);

         }

 

         if (event->type == EV_SYN && event->code == SYN_REPORT) {

                   client->packet_head = client->head;

                   if (client->use_wake_lock)

                            wake_lock(&client->wake_lock);

                   kill_fasync(&client->fasync, SIGIO, POLL_IN);

         }

}

 

 

input_event

/**

 * input_event() - report new input event

 * @dev: device that generated the event

 * @type: type of the event

 * @code: event code

 * @value: value of the event

 *

 * This function should be used by drivers implementing various input

 * devices to report input events. See also input_inject_event().

 *

 * NOTE: input_event() may be safely used right after input device was

 * allocated with input_allocate_device(), even before it is registered

 * with input_register_device(), but the event will not reach any of the

 * input handlers. Such early invocation of input_event() may be used

 * to 'seed' initial state of a switch or initial position of absolute

 * axis, etc.

 */

注释:

input_event( ):上报新的输入事件

dev:产生事件的设备(在驱动的probe函数中,会申请一个input_dev,此dev既为当时申请的结构体指针)

type:事件类型

code:事件代码

value:事件值

此功能应该由实现各种输入设备的驱动程序使用来报告输入事件,input_inject_event( )也是同样的要求。

注意:input_event( )可以在input device使用input_allocate_device申请之后立即安全的使用,甚至在他使用input_register_device( )注册之前,但是事件将不会到达任何input handlers。这种早期的调用input_event( )可以用做“种子”的初始状态或者绝对坐标的初始位置。

 

input_handle_event

此函数会真正的将数据放入client->buffer中。

static void input_handle_event(struct input_dev *dev,

                            unsigned int type, unsigned int code, int value)

{

       int disposition;

 

       disposition = input_get_disposition(dev, type, code, &value);

       //如果需要传递给设备并且设备已经实现了dev->event方法,则调用dev->event

       if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)

              dev->event(dev, type, code, value);

 

       if (!dev->vals)

              return;

       //需要传递到handlers

       if (disposition & INPUT_PASS_TO_HANDLERS) {

              struct input_value *v;

 

              if (disposition & INPUT_SLOT) {

                     v = &dev->vals[dev->num_vals++];

                     v->type = EV_ABS;

                     v->code = ABS_MT_SLOT;

                     v->value = dev->mt->slot;

              }

 

              v = &dev->vals[dev->num_vals++];

              v->type = type;

              v->code = code;

              v->value = value;

       }

 

       if (disposition & INPUT_FLUSH) {

              if (dev->num_vals >= 2)

                     input_pass_values(dev, dev->vals, dev->num_vals);

              dev->num_vals = 0;

       } else if (dev->num_vals >= dev->max_vals - 2) {

              dev->vals[dev->num_vals++] = input_value_sync;

              input_pass_values(dev, dev->vals, dev->num_vals);

              dev->num_vals = 0;

       }

 

}

 

 

其他的记录

内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。

你可能感兴趣的:(linux)