============================================== 1. 应用程序调用open() 以androidM Gsensor为例 hal层中acceleration.cpp中 FindDataFd() fd = open("/sys/class/misc/m_acc_misc/accdevnum", O_RDONLY); len = read(fd, buf, sizeof(buf)-1); buf[len] = '\0'; sscanf(buf, "%d\n", &num); sprintf(buf_s, "/dev/input/event%d", num); fd = open(buf_s, O_RDONLY); readEvents() mInputReader.fill(mdata_fd) mInputReader.readEvent(&event) 即open("/dev/input/event%d", O_RDONLY); ----------------------------------------- vfs sys_open(); // 系统调用 struct file file->f_ops = cdev->ops; file->f_ops->open(); ----------------------------------------- input handler层: evdev.c cdev xxx_ops = { .open = xxx_open, .read = xxx_read, } evdev_connect() cdev_init(&evdev->cdev, &evdev_fops); static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, .write = evdev_write, .poll = evdev_poll, .open = evdev_open, }; 实际上最终调用了evdev_open() evdev_open(struct inode *inode, struct file *file) | struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); // 以小博大,inode->i_cdev就是connect()中住的的cdev unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev); // 通过handle找到 input device,根据input device 获取缓冲区的大小(几个input event),但是我们驱动中未给定缓冲区大小,系统会自动给定一个 unsigned int size = sizeof(struct evdev_client) + // size包含了很多个input event bufsize * sizeof(struct input_event); struct evdev_client *client; client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); // 分配一个client对象,用来描述一个缓冲队列,存放的就是input_event client->bufsize = bufsize; // client中有一个缓冲区 spin_lock_init(&client->buffer_lock); client->evdev = evdev; // evdev_client中记录evdev evdev_attach_client(evdev, client); // 将client 加入到evdev中的一个小链表中 | list_add_tail_rcu(&client->node, &evdev->client_list); file->private_data = client; // evdev_client记录到file中,方便其他接口调用(这里是open(),其他接口还有read()、write()) 总结: 1. 为输入设备分配一个缓冲区evdev_client,用于存放input device层上报的数据 2. evdev_client中记录evdev 3. evdev_client记录到file中,方便其他read() write() 等接口使用
============================================== 2.应用程序调用read() read(fd, &event, sizeof(struct input_event)); ----------------------------------------- vfs sys_read(); // 系统调用 file->f_ops->read(); // fd就是file数组的下表,通过传入的fd找到file,其中的f_ops在open()的时候已经获取并保存 ----------------------------------------- static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) | struct evdev_client *client = file->private_data; // 获取open() 分配的缓冲区 struct evdev *evdev = client->evdev; // 获取到evdev struct input_event event; // 表示一个数据包,要给用户 for (;;) { // 实现非阻塞 -- 队列为空,且为非阻塞模式,直接返回again if (client->packet_head == client->tail && // 队列的头跟尾位置一样 == 队列为空 (file->f_flags & O_NONBLOCK)) // 非阻塞 return -EAGAIN; // while每循环一次取一个input event数据,read加1 while (read + input_event_size() <= count && // 这里判断要取的数据个数是否已取ok,count是要取得数据个数 evdev_fetch_next_event(client, &event)) { // 1. 从client的缓冲区取数据,放到event中 | *event = client->buffer[client->tail++];// 将client->buffer[]队列的尾巴给*event if (input_event_to_user(buffer + read, &event)) // 2. 把数据给用户空间 | copy_from_user(event, buffer, sizeof(struct input_event)) read += input_event_size(); // 3. 统计上报多少数据 } if (!(file->f_flags & O_NONBLOCK)) { // 如果当前不是非阻塞模式,即阻塞模式 error = wait_event_interruptible(evdev->wait, // 休眠 - 条件不满足就睡眠: client->packet_head != client->tail || // 队列头不等于尾 -> 有数据 !evdev->exist || client->revoked); 总结: 1. 如果没数据,就休眠等待 2. 如果有数据,就会从缓冲区client->buffer[client->tail++]拿数据,通过copy_to_user上报给用户 疑问: 1. 数据到底是如何存放在缓冲区的 2. 等待队列是谁唤醒的
============================================== 3. 上报流程: input_report_abs(gt811_dev->input, ABS_MT_POSITION_X, x); input_report_abs(gt811_dev->input, ABS_MT_POSITION_Y, y); input_mt_sync(gt811_dev->input); input_report_abs(struct input_dev *dev, unsigned int code, int value) | input_event(dev, EV_ABS, code, value); | input_handle_event(dev, type, code, value); | if (disposition & INPUT_PASS_TO_HANDLERS) { // input device数据交给input handler处理 struct input_value *v; v = &dev->vals[dev->num_vals++]; // 将input device获取到的数据暂存到dev->vals v->type = type; v->code = code; v->value = value; input_pass_values(dev, dev->vals, dev->num_vals); | list_for_each_entry_rcu(handle, &dev->h_list, d_node) // 通过inpit device中与handle建立连接的 h_list 成员找到 handle if (handle->open) input_to_handler(handle, vals, count); | struct input_handler *handler = handle->handler; // 通过出入的handle找到input handler(这里是evdev) if (handler->events) // 首选events(), 没有才调用event() handler->events(handle, vals, count); // 调用events() else if (handler->event) for (v = vals; v != end; v++) handler->event(handle, v->type, v->code, v->value); 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 void evdev_events(struct input_handle *handle, const struct input_value *vals, unsigned int count) | struct evdev *evdev = handle->private; // 从handle中拿到evdev -- connect()中保存了:evdev->handle.private = evdev; struct evdev_client *client; 如果多个应用进程打开了同一个input device, 每次open()都会生成一个evdev_client evdev_client挂载到evdev的client_list链表中 input_report_abs()时,handler会把数据copy到client_list所有的evdev_client的buffer中 input_mt_sync(),逐一唤醒 list_for_each_entry_rcu(client, &evdev->client_list, node) evdev_pass_values(client, vals, count, time_mono, time_real); | struct evdev *evdev = client->evdev; // 通过client 获取到 evdev const struct input_value *v; struct input_event event; // 数据包 event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? mono : real); // 填充数据包中的时间戳 for (v = vals; v != vals + count; v++) { // 将input device上报的数据封装成 input_event对象 event.type = v->type; event.code = v->code; event.value = v->value; __pass_event(client, &event); // 将input event数据放在缓冲区的头部 -- 读的时候从尾巴开始读 | client->buffer[client->head++] = *event; // 将input event数据放入缓冲区 client->head &= client->bufsize - 1; if (v->type == EV_SYN && v->code == SYN_REPORT) // 唤醒等待队列 -- 如果调用了input_sync() -- input_event(dev, EV_SYN, SYN_REPORT, 0); wakeup = true; } if (wakeup) // 唤醒等待队列 wake_up_interruptible(&evdev->wait); struct input_event { struct timeval time; __u16 type; // 如:EV_ABS __u16 code; // 如:ABS_MT_POSITION_X __s32 value; // 如:x (具体的数值,这里是tp横坐标) }; 总结: 1. 数据到底是如何存放在缓冲区的 input_report_abs()将数据交给handler,调用events(),将数据放入缓冲区client->buffer[client->head++] = *event; 2. 等待队列是谁唤醒的 input_mt_sync() 显式唤醒等待队列 wake_up_interruptible(&evdev->wait); */