转自:https://segmentfault.com/a/1190000017255939

作为鸡生蛋系列文章,这里主要关注Linux input系统,
主要为触摸事件上报流程.

读该文章最好有对linux驱动的入门知识.
其实当你自己去分析了input系统后,再分析别的就相对很轻松了,
linux里好多套路都差不多的.

本文例子以ft6236.c驱动为例, 当然你也可以用goodix或者别的触摸来分析.
但是分析基于的内核版本用4.19.6(我写这篇文档时最新稳定版)
(https://git.kernel.org/pub/sc...
文档可参看
<<linux-4.19.6>>/Documentation/input/input.rst
<<linux-4.19.6>>/Documentation/input/input-programming.rst

触屏设备驱动

eg:
(https://source.codeaurora.org...

static irqreturn_t ft6236_interrupt(int irq, void *dev_id)
{
......//5. 中断处理中读数据
error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf);
......
for (i = 0; i < touches; i++) {
struct ft6236_touchpoint *point = &buf.points[i];
u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo;
u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo;
......
input_mt_slot(input, id);
input_mt_report_slot_state(input, MT_TOOL_FINGER, act);
......//5. 上报数据, ABS即坐标的绝对值
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
......
input_mt_sync_frame(input);
input_sync(input);
......
} //2. probe函数, 当设备与驱动匹配上时会执行该函数
static int ft6236_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
......// 3. input设备申请
input = devm_input_allocate_device(dev);
......
ft6236->input = input;
input->name = client->name;
input->id.bustype = BUS_I2C;
......// 3. input设备参数/能力申明
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
ft6236->max_x, fuzz_x, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
ft6236->max_y, fuzz_y, 0);
......
error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
...... 5. 中断来时回调到ft6236_interrupt
error = devm_request_threaded_irq(dev, client->irq, NULL,
ft6236_interrupt, IRQF_ONESHOT,
client->name, ft6236);
......4. 注册为Input类设备
error = input_register_device(input);
......
}
//of table和id table在设备和驱动匹配时会用到
#ifdef CONFIG_OF
static const struct of_device_id ft6236_of_match[] = {
.....
MODULE_DEVICE_TABLE(of, ft6236_of_match);
#endif static const struct i2c_device_id ft6236_id[] = {
.....
MODULE_DEVICE_TABLE(i2c, ft6236_id); static struct i2c_driver ft6236_driver = {
.driver = {
.name = "ft6236",
.of_match_table = of_match_ptr(ft6236_of_match),
},
.probe = ft6236_probe,
.id_table = ft6236_id,
};
//1. 模块init, 这是一个宏定义, 里面包含了, module_init, i2c的添加驱动注册,
//module_init可以理解为对该文件的加载顺序,其它的还有core_initcall late_initcall等
module_i2c_driver(ft6236_driver);

简单说明下实现一个触屏驱动包含以下内容

  1. 文件和模块init
  2. 按照linux设备模型填充i2c驱动(设备一般在dts里配置,这里不提)
  3. 设备和驱动匹配上后,执行驱动的probe()函数, probe()里申请input device, 能力填充, 再在里面将设备注册为input类
  4. 当点击屏后,中断来了,回调中断处理函数
  5. 中断处理里, 通过i2c的方法从硬件读取数据,并进行上报.

注意, 触屏上报有个多点触摸协议,可参看文档
<<linux-4.19.6>>/Documentation/input/multi-touch-protocol.rst

上报--input_report_abs()

我们的重点是想知道数据上报流程, 所以自然要分析input_report_abs()

include/linux/input.h
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}

可以看到其为内联函数, 为input_event(,EV_ABS, ...)的二次封装;

input_report_key()       -+                  +- EV_KEY
input_report_rel() -| |- EV_REL
input_report_abs() -| |- EV_ABS
input_report_ff_status() -|--input_event() --|- EV_FF_STATUS
input_report_switch() -| |- EV_SW
input_sync() -| |- EV_SYN, SYN_REPORT
input_mt_sync() -+ +- EV_SYN, SYN_MT_REPORT

对于我们的根据来说,即
input_event(dev, EV_ABS, ABS_MT_POSITION_X, 坐标值)

drivers/input/input.c
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
....//event是否支持, 这个和驱动里probe()时填充能力,设置参数有关,略过
if (is_event_supported(type, dev->evbit, EV_MAX)) {
....
input_handle_event(dev, type, code, value);
...
} static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = input_get_disposition(dev, type, code, &value); //得到disposition
......
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;
} }

还记得在驱动中断回调函数ft6236_interrupt()里,上报值时,我们调用了这些函数,

            input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
......
input_mt_sync_frame(input);
input_sync(input);

这些值到input_event()对应着

input_report_abs()       -|                  |- EV_ABS
input_sync() -|--input_event() --|- EV_SYN, SYN_REPORT
input_mt_sync() -+ +- EV_SYN, SYN_MT_REPORT

所以我们可以简单看下input_handle_event() --> input_get_disposition()
EV_SYN事件和EV_ABS的返回值

static int input_get_disposition(struct input_dev *dev,
unsigned int type, unsigned int code, int *pval)
{
int disposition = INPUT_IGNORE_EVENT;
......
switch (type) { case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break; case SYN_REPORT:
disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
break;
case SYN_MT_REPORT:
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
......
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, code, &value);//这个可以看看,他会对相同值进行过滤,返回INPUT_IGNORE_EVENT break;
......
return disposition;
}

让我们回到input_handle_event() --> input_pass_values()

static void input_pass_values(struct input_dev *dev,
struct input_value *vals, unsigned int count)
{
......
if (handle) {
count = input_to_handler(handle, vals, count);
} else {
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open) {
count = input_to_handler(handle, vals, count);
if (!count)
break;
}
}
......
}

其重点函数为input_to_handler()

static unsigned int input_to_handler(struct input_handle *handle,
struct input_value *vals, unsigned int count)
{
struct input_handler *handler = handle->handler;
......
if (handler->filter) {
for (v = vals; v != vals + count; v++) {
if (handler->filter(handle, v->type, v->code, v->value))
continue;
.......
}
......
if (handler->events)
handler->events(handle, vals, count); //<--handler的events.
else if (handler->event)
for (v = vals; v != vals + count; v++)
handler->event(handle, v->type, v->code, v->value); return count;
}

分析到这个函数的时候, 似乎有些断了,
我们看到有三个handler->filter(), handler->events(), handler->event()函数调用,
哪这三个函数又调用到哪儿去了呢?这时又该如何继续分析呢?

handler (input_register_device() --> handler)

对此,

  1. 我们可以搜索下哪儿在给这三个函数赋值,但情况不太乐观;
  2. 我们回想下在驱动probe里,我们与input相关的有如下,
static int ft6236_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
......// 3. input设备申请
input = devm_input_allocate_device(dev);
......// 3. input设备参数/能力申明
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
ft6236->max_x, fuzz_x, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
ft6236->max_y, fuzz_y, 0);
......
error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
......4. 注册为Input类设备
error = input_register_device(input);

所以有很大概率是在申请设备, 设备能力, slots设置,注册input类这几个函数里面实现的.

我们这里就直接看答案

int input_register_device(struct input_dev *dev)
{
......//前面有些默认能力参数等的设置,略过
error = device_add(&dev->dev);
......//将设备节点加入到input_dev_list
list_add_tail(&dev->node, &input_dev_list);
//遍历input_handler_list, 然后调用input_attach_handler,看匹配的handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
.......
}

input_dev_list 和 input_handler_list, 是定义的两个list,
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);

我们可以猜测,所有的input dev和handler都会挂在这两个list里,
然后调用上面的input_attach_handler()进行两者的匹配,
对于dev list我们不关注,有兴趣的同学可自己看下,
重点想要知道的是handler相关的,
那我们的问题自然又转为
哪些会挂到input_handler_list上?
搞明白这个问题,然后进一步的分析input_attach_handler()匹配.

通过对drivers/input/input.c搜索, 觉得input_register_handler()这个的可能性最大,
因为list嘛,肯定有对他进行add的地方, 别的地方代码都没有add

int input_register_handler(struct input_handler *handler)
{
......//初始化h_list
INIT_LIST_HEAD(&handler->h_list);
//将node加到list尾部
list_add_tail(&handler->node, &input_handler_list);
//在注册handler的时候也对已有设备调用一次attach()
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
......
}

先看下input_handler定义,里面就有我们想找的event() filter()函数

include/linux/input.h
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);
......
const char *name; const struct input_device_id *id_table; struct list_head h_list;
struct list_head node;
};

然后再进一步,我们就想要知道谁在调用input_register_handler()注册handler了.
通过搜索代码,我这里列举下

File handler名 在哪个函数里注册的
drivers/input/apm-power.c apmpower_handler apmpower_init()
drivers/input/evbug.c evbug_handler evbug_init()
drivers/input/input-leds.c input_leds_handler input_leds_init()
drivers/input/joydev.c joydev_handler joydev_init()
drivers/input/mousedev.c mousedev_handler mousedev_init()
drivers/input/evdev.c evdev_handler evdev_init()
drivers/tty/serial/ kgdboc.c kgdboc_reset_handler kgdboc_restore_input_helper()
drivers/macintosh/mac_hid.c mac_hid_emumouse_handler mac_hid_start_emulation()
net/rfkill/input.c rfkill_handler rfkill_handler_init()
drivers/tty/sysrq.c sysrq_handler sysrq_register_handler()
drivers/tty/vt/keyboard.c kbd_handler kbd_init()

由上我们知道,在各个模块的init里,注册了所支持的handler,
用来处理几类常见的事件,如鼠标、键盘、摇杆等(其中最为基础的是evdev_handler,
它能够接收任意类型的事件,任意id的设备都可以和它匹配连接)
也就是说,最终的handler的调用函数是上面的handler中的一个。

哪我们的触屏究竟用的哪一个handler呢?这就得接下来看attach里的匹配过程了

input_attach_handler() --> input_match_device() --> input_match_device_id()
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error; id = input_match_device(handler, dev); //-->匹配
if (!id)
return -ENODEV; error = handler->connect(handler, dev, id); //-->连接
......
return error;
} bool input_match_device_id(const struct input_dev *dev,
const struct input_device_id *id)
{
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) //Bus总线的匹配
......
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //Vendor匹配
......
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) //Product匹配
......
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
......
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) || //匹配id的evbit和input_dev中evbit的各个位
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
.....) {
return false;
} return true;
} static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id; for (id = handler->id_table; id->flags || id->driver_info; id++) {
//进行id的一个匹配, 如果有match为空或者match成功, 返回id
if (input_match_device_id(dev, id) &&
(!handler->match || handler->match(handler, dev))) {
return id;
}
} return NULL;
}

device id的定义如下,

struct input_device_id {

    kernel_ulong_t flags;

    __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 driver_info;
};

基实整个匹配也就是进行,总线,厂商,能力(evbit, keybit), id_table的匹配,
我们的触屏也是匹配到的evdev_handler,
我们可以再看一下evdev_handler的定义

drivers/input/evdev.c
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
}; MODULE_DEVICE_TABLE(input, evdev_ids); static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
......
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids, //<--id_table
};

回到input_register_handler() --> input_attach_handler() --> handler->connect()
我们以handler drivers/input/evdev.c为例分析
其connect()里做的事情

  • 主要为name,dev,handle,等信息填充,
  • 注册handle, 将device和handler连接起来,
  • 字符设备添加
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
......//从这个定义我们可知,input的从设备号从64开始,可为32个, 所以从设备号为64~95
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
......
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
......
INIT_LIST_HEAD(&evdev->client_list);
......
dev_no = minor;
......
dev_set_name(&evdev->dev, "event%d", dev_no); //<--名字为eventN evdev->handle.dev = input_get_device(dev); //<--handle.dev
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler; //<--注意一个是handle,一个是handler
evdev->handle.private = evdev; evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); <--//主设备号INPUT_MAJOR为13,include/uapi/linux/major.h
evdev->dev.class = &input_class; //<--- 类别为input_class, 即/sys/class/input/
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev); error = input_register_handle(&evdev->handle); //注册handle, 注意我们之前分析的是handler,表示键盘,摇杆等的可处理.
......
cdev_init(&evdev->cdev, &evdev_fops); //<--注意把 file_operations 和cdev->ops关联起来了. error = cdev_device_add(&evdev->cdev, &evdev->dev); //<--cdev添加, 这个时候就可以在/dev/input/看到了
......
}

input_register_handle()所做的就是将handle句柄挂到dev和handler的list里,
当有事件来时就知道咋处理,至此也表示一个handle和dev匹配成功.

/**
.....//可以看看这个注释
* This function puts a new input handle onto device's
* and handler's lists so that events can flow through
* it once it is opened using input_open_device().
......
*/
int input_register_handle(struct input_handle *handle)
{
......
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
......
list_add_tail_rcu(&handle->h_node, &handler->h_list);
......
}

report和handler小结

所以到目前为至,我们知道了

当各个handler init时 --> input_register_handler() --> input_attach_handler() -->   handler->connect()
或者驱动 --> probe() --> input_register_device() --> input_attach_handler --> handler->connect() +--> input_register_handle() dev和handler关联
handler->connect()--> eg:evdev.c events() --+
+-->cdev_device_add() 注册字符设备

对于input_report_abs()上报我这也列举整个流程, 代码不再详细看了

input_report_abs() --> input_event(, EV_ABS, , ) --> input_handle_event() --> input_pass_values() --> input_to_handler() -->
handler->events()/event() --> eg:evdev.c events() --> evdev_pass_values() --> 数据填充 --> __pass_event() --> client->buffer[]
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
......
if (client)
evdev_pass_values(client, vals, count, ev_time);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
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)
{
struct evdev *evdev = client->evdev;
const struct input_value *v;
struct input_event event;
struct timespec64 ts;
......//时间
event.input_event_sec = ts.tv_sec;
event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
......
for (v = vals; v != vals + count; v++) {
......//事件数据填充
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event); //<--放到client->buffer里
}
......
} __pass_event()将event放到client->buffer[]里
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].input_event_sec =
event->input_event_sec;
client->buffer[client->tail].input_event_usec =
event->input_event_usec;
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);
}
}

数据是如何读取的?

我们从上面分析,看到数据已经放到了client->buffer[], 那读取也肯定也是从这里读,
具体分析就不讲了,我这里只列下
还记得evdev_connect()时将file_operations和dev关联起来了

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

evdev的file_operations定义如下:

static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
......
}

(evdev_open分析略过)

所以我们很容易想到读数据其实就是调用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;
struct evdev *evdev = client->evdev;
......
for (;;) {
......//循环读取下一个事件, 并通过input_event_to_user() --> copy_to_user()给用户空间, 这样上面就读到数据了.
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) { if (input_event_to_user(buffer + read, &event))
......
return read;
} 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;
} spin_unlock_irq(&client->buffer_lock); return have_event;
}

read数据小结

read时候 evdev_read--> 从client->buffer[]循环获取事件 evdev_fetch_next_event() --> input_event_to_user() --> copy_to_user()

涉及到的一些数据结构

struct evdev {
int open;
struct input_handle handle; -->
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
struct cdev cdev;
bool exist;
}; struct evdev_client {
unsigned int head;
unsigned int tail;
......
struct evdev *evdev;
struct list_head node;
unsigned int clk_type;
bool revoked;
unsigned long *evmasks[EV_CNT];
unsigned int bufsize;
struct input_event buffer[];
}; struct input_handler { void *private; void (*event)(....);
void (*events)(....);
......
int (*connect)(......);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
......
int minor;
const char *name; const struct input_device_id *id_table; struct list_head h_list;
struct list_head node; --> input_handler_list
}; struct input_handle {
......
int open;
const char *name; struct input_dev *dev;
struct input_handler *handler; struct list_head d_node;
struct list_head h_node;
};

他们可简单用如下图表示, 即有两个列表, input_handler_list和input_dev_list
分别是所有可用的handler和input dev,
他们之间靠input_handle连在一起.

input_handler_list[hander1|hander2|...]           input_dev_list[dev1|dev2|...]
^ ^ ^ ^
| | | |
| | | |
[handle1{handler|dev}]--| ----------------------------------+ |
[handle2{handler|dev}]---------------------------------+
[handle..{handler|dev}]略...

调试相关

对于android可用命令

sendevent/getevent

发送或获取event事件

也可查看一些节点获得信息

/proc/bus/input/
/sys/class/input/
/dev/input/

总结

所以总的来说, 内容有如下

  1. 按照linux设备架构,驱动模型实现driver,
  2. 当各个handler init或者驱动注册input device时,会进行handler的匹配,
    匹配成功后调用handler的connect()通过handle进行device handler的关联,
    并注册字符设备

    当各个handler init时 --> input_register_handler() --> input_attach_handler() -->  handler->connect()
    或者驱动 --> probe() --> input_register_device() --> input_attach_handler --> handler->connect() +--> input_register_handle() dev和handler关联
    handler->connect()--> eg:evdev.c events() --+
    +-->cdev_device_add() 注册字符设备
  3. 当点击触屏后, 进到中断处理,然后读取数据,再report,并存到client的buffer[]里

    input_report_abs() --> input_event(, EV_ABS, , ) --> input_handle_event() --> input_pass_values() --> input_to_handler() -->
    handler->events()/event() --> eg:evdev.c events() --> evdev_pass_values() --> 数据填充 --> __pass_event() --> client->buffer[]
  4. 上层用户空调read时(open我们略过了), 只要有数据,不断从client->buffer[]读取并通过copy_to_user()拷到用户空间, 所以上层就拿到数据了.

    read时候...--> evdev_read--> 从client->buffer[]循环获取事件 evdev_fetch_next_event() -->
    input_event_to_user() --> copy_to_user()
  5. 大体流向

    userpace open()/read() /dev/input/event*
    ----------------------------------------
    kernel

    input handler evdev.c

    input core input.c

    device driver

Linux input系统数据上报流程【转】的更多相关文章

  1. Web系统与自控系统数据通讯架构 之 OPC DA DataChangeEventHandler 非热点数据更新策略 ,

    在使用OPC 采集 工控数据时,在DA模式下.采集数据通常用到 DataChangeEventHandler这个事件.但有时会遇到一些问题,就是当数据不变化时时不会触发 DataChange 这个事件 ...

  2. Linux系统捕获数据包流程

    Linux系统捕获数据包流程 为了提高数据包的捕获效率,瓶颈问题是一个需要非常关注的焦点.减少在捕获数据包过程中的瓶颈,就能够提高数据包捕获的整体性能.下面本文将以Linux操作系统为平台,分析捕获数 ...

  3. linux input输入子系统分析《四》:input子系统整体流程全面分析

    1      input输入子系统整体流程 本节分析input子系统在内核中的实现,包括输入子系统(Input Core),事件处理层(Event Handler)和设备驱动层.由于上节代码讲解了设备 ...

  4. Linux/Android——Input系统之InputMapper 处理 (八)【转】

    本文转载自:http://blog.csdn.net/jscese/article/details/43561773 前文Linux/Android——Input系统之InputReader (七)介 ...

  5. Linux Input子系统浅析(二)-- 模拟tp上报键值【转】

    转自:https://blog.csdn.net/xiaopangzi313/article/details/52383226 版权声明:本文为博主原创文章,未经博主允许不得转载. https://b ...

  6. input上报流程分析【转】

    转自:http://blog.chinaunix.net/uid-28320320-id-3389196.html .参考文章 [Andorid]input系统的事件处理 .源码分析 linux )查 ...

  7. Linux 下Input系统应用编程实战

    作者:杨源鑫(也是我们的校园代理) 经授权转载于公众号嵌入式开发圈,有些许修改. 什么是input子系统?不管是什么操作系统,都有一个程序用于管理各种输入设备,哪些是输入设备?比如,电脑键盘.鼠标,智 ...

  8. Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值

    Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...

  9. Linux内核网络数据包处理流程

    Linux内核网络数据包处理流程 from kernel-4.9: 0. Linux内核网络数据包处理流程 - 网络硬件 网卡工作在物理层和数据链路层,主要由PHY/MAC芯片.Tx/Rx FIFO. ...

随机推荐

  1. 一些矩阵范数的subgradients

    目录 引 正交不变范数 定理1 定理2 例子:谱范数 例子:核范数 算子范数 定理3 定理4 例子 \(\ell_2\) <Subgradients> Subderivate-wiki S ...

  2. scala的多种集合的使用(7)之集Set的操作方法

    1.给集添加元素 1)用+=.++=和add给可变集添加元素. scala> var set = scala.collection.mutable.Set[Int]() set: scala.c ...

  3. Quick Sort(三向切分的快速排序)(Java)

    //三向切分的快速排序 //这种切分方法对于数组中有大量重复元素的情况有比较大的性能提升 public static void main(String[] args) { Scanner input ...

  4. mysql 8.0.X 创建新的数据库、用户并授权

    一.创建数据库 mysql> create database jira; Query OK, 0 rows affected (0.09 sec) 二.创建用户 mysql> create ...

  5. iTextCharp c#

    //引用iTextSharp static void testPdf() { var document = new Document(); var writer = PdfWriter.GetInst ...

  6. 注意:QQ影音视频压缩时长丢失

    客户宣传片发来,高清的,比较大,500多M,需要转成小一点的,放在客户网站上,于是用QQ影音转码压缩下,变成低质量的.如下 一切都很顺利,提示进度100%! 这一切都是电脑自动的,又是提示成功的,千想 ...

  7. pypinyin, jieba分词与Gensim

    一 . pypinyin from pypinyin import lazy_pinyin, TONE, TONE2, TONE3 word = '孙悟空' print(lazy_pinyin(wor ...

  8. codeforces510D

    Fox And Jumping CodeForces - 510D Fox Ciel is playing a game. In this game there is an infinite long ...

  9. Nginx HTTP框架提供的其它变量

    L74

  10. 关于【jq插件开发】

    很详细,原文链接:https://www.cnblogs.com/Wayou/p/jquery_plugin_tutorial.html#commentform和https://www.cnblogs ...