/*
* Event char devices, giving access to raw input device events.
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
#define EVDEV_MIN_BUFFER_SIZE 64U
#define EVDEV_BUF_PACKETS 8
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input/mt.h>
#include <linux/major.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/wakelock.h>
#include "input-compat.h"
struct evdev {
int open; // 打开标志
struct input_handle handle; // 关联的input_handle
wait_queue_head_t wait; // 等待队列 - 当进程读取设备,而没有事件产生的时候,进程就会睡在其上面
struct evdev_client __rcu *grab;
struct list_head client_list; // 一个evdev设备可以处理多个evdev_client,可以有多个进程访问evdev设备
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;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct wake_lock wake_lock;
bool use_wake_lock;
char name[28];
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
int clkid;
bool revoked;
unsigned int bufsize;
struct input_event buffer[];
};
/* flush queued events of type @type, caller must hold client->buffer_lock */
static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
{
unsigned int i, head, num;
unsigned int mask = client->bufsize - 1;
bool is_report;
struct input_event *ev;
BUG_ON(type == EV_SYN);
head = client->tail;
client->packet_head = client->tail;
/* init to 1 so a leading SYN_REPORT will not be dropped */
num = 1;
for (i = client->tail; i != client->head; i = (i + 1) & mask) {
ev = &client->buffer[i];
is_report = ev->type == EV_SYN && ev->code == SYN_REPORT;
if (ev->type == type) {
/* drop matched entry */
continue;
} else if (is_report && !num) {
/* drop empty SYN_REPORT groups */
continue;
} else if (head != i) {
/* move entry to fill the gap */
client->buffer[head].time = ev->time;
client->buffer[head].type = ev->type;
client->buffer[head].code = ev->code;
client->buffer[head].value = ev->value;
}
num++;
head = (head + 1) & mask;
if (is_report) {
num = 0;
client->packet_head = head;
}
}
client->head = head;
}
/* queue SYN_DROPPED event */
static void evdev_queue_syn_dropped(struct evdev_client *client)
{
unsigned long flags;
struct input_event ev;
ktime_t time;
time = (client->clkid == CLOCK_MONOTONIC) ?
ktime_get() : ktime_get_real();
ev.time = ktime_to_timeval(time);
ev.type = EV_SYN;
ev.code = SYN_DROPPED;
ev.value = 0;
spin_lock_irqsave(&client->buffer_lock, flags);
client->buffer[client->head++] = ev;
client->head &= client->bufsize - 1;
if (unlikely(client->head == client->tail)) {
/* drop queue but keep our SYN_DROPPED event */
client->tail = (client->head - 1) & (client->bufsize - 1);
client->packet_head = client->tail;
}
spin_unlock_irqrestore(&client->buffer_lock, flags);
}
static void __pass_event(struct evdev_client *client,
const struct input_event *event)
{
client->buffer[client->head++] = *event; // 将input 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);
}
}
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t mono, ktime_t real)
{
struct evdev *evdev = client->evdev; // 通过client 获取到 evdev
const struct input_value *v;
struct input_event event; // 数据包
bool wakeup = false;
if (client->revoked)
return;
event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? // 填充数据包中的时间戳
mono : real);
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
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;
// 唤醒等待队列 -- 如果调用了input_sync() -- input_event(dev, EV_SYN, SYN_REPORT, 0);
if (v->type == EV_SYN && v->code == SYN_REPORT)
wakeup = true;
}
spin_unlock(&client->buffer_lock);
if (wakeup) // 唤醒等待队列
wake_up_interruptible(&evdev->wait);
}
/*
* Pass incoming events to all connected clients.
*/
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;
ktime_t time_mono, time_real;
time_mono = ktime_get();
time_real = ktime_mono_to_real(time_mono);
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_values(client, vals, count, time_mono, time_real);
// 走这里 -- 数据暂存vals
else
/*
如果多个应用进程打开了同一个input device, 每次open()都会生成一个evdev_client
evdev_client挂载到evdev的client_list链表中
当handler收到一个事件时,会把事件copy到client_list所有的evdev_client的buffer中
这样所有打开同一个设备的进程都会收到这个消息而唤醒
*/
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count,
time_mono, time_real);
rcu_read_unlock
input子系统分析:源码2. kernel-3.18\drivers\input\evdev.c
最新推荐文章于 2023-08-10 17:26:17 发布