https://www.cnblogs.com/zhaobinyouth/p/6257871.html;
https://blog.csdn.net/qq_16777851/article/details/81212534;
input 子系统详解;
一Linux输入子系统的框架如下:
1./dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。
2.输入事件驱动成都为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到输入事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为输入事件处理层的Mouse Handler已经有了对应事件处理的方法。
3.输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。
联系之前学过的驱动框架做对比,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中,最终会传递到应用空间。
(4)input子系统解决了不同的输入类设备的输入事件与应用层之间的数据传输,使得应用层能够获取到各种不同的输入设备的输入事件,input输入子系统能够囊括所有的不同种类的输入设备,在应用层都能够感知到所有发生的输入事件
4、input输入子系统如何工作?
例如以一次鼠标按下事件为例子来说明我们的input输入子系统的工作过程:
当我们按下鼠标左键的时候就会触发中断(中断是早就注册好的),就会去执行中断所绑定的处理函数,在函数中就会去读取硬件寄存器来判断按下的是哪个按键和状态 ---->
将按键信息上报给input core层 —> input core层处理好了之后就会上报给input event层,在这里会将我们的输入事件封装成一个input_event结构体放入一个缓冲区中 —>
应用层read就会将缓冲区中的数据读取出去。
二 input core
static int __init input_init(void)
{
int err;
err = class_register(&input_class);// 创建设备类 /sys/class/input
if (err) {
pr_err(“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) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
err = mutex_lock_interruptible(&input_mutex);
if (err)
return err;
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5];// 通过次设备号在 input_table 数组中找到对应的 handler
if (handler)
new_fops = fops_get(handler->fops);// 将handler 中的fops 指针赋值给 new_fops
mutex_unlock(&input_mutex);
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops || !new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op;// 将 file->fops 先保存到 old_fops 中,以便出错时能够恢复
file->f_op = new_fops;// 用new_fops 替换 file 中 fops
err = new_fops->open(inode, file);// 执行 file->open 函数
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
return err;
}
三、主要input通用数据结构
- 输入设备 input_dev,这是input设备基本的设备结构,每个input驱动程序中都必须分配初始化这样一个结构,成员比较多。
-
- input_handler 这是事件处理器的数据结构,代表一个事件处理器。
- 3.input_handle 结构体代表一个成功配对的input_dev和input_handler。
- 4.三个数据结构之间的关系
input_dev: 是硬件驱动层,代表一个input设备。
input_handler: 是事件处理层,代表一个事件处理器。
input_handle: 属于核心层,代表一个配对的input设备与input事件处理器。
input_dev 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作。
input_handler 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作(事件处理器一般内核自带,一般不需要我们来写)
input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。通过input_dev 和input_handler就可以找到input_handle在设备注册和事件处理器,注册的时候都要进行配对工作,配对后就会实现链接。通过input_handle也可以找到input_dev和input_handler。
我们可以看到,input_device和input_handler中都有一个h_list,而input_handle拥有指向input_dev和input_handler的指针,也就是说input_handle是用来关联input_dev和input_handler的。
那么为什么一个input_device和input_handler中拥有的是h_list而不是一个handle呢?因为一个device可能对应多个handler,而一个handler也不能只处理一个device,比如说一个鼠标,它可以对应even handler,也可以对应mouse handler,因此当其注册时与系统中的handler进行匹配,就有可能产生两个实例,一个是evdev,另一个是mousedev,而任何一个实例中都只有一个handle。至于以何种方式来传递事件,就由用户程序打开哪个实例来决定。后面一个情况很容易理解,一个事件驱动不能只为一个甚至一种设备服务,系统中可能有多种设备都能使用这类handler,比如event handler就可以匹配所有的设备。在input子系统中,有8种事件驱动,每种事件驱动最多可以对应32个设备,因此dev实例总数最多可以达到256个。
四、输入子系统驱动层分析(以tps6507x为例)
1.platform device的注册
2.platform driver注册
static struct platform_driver tps6507x_ts_driver = {
.driver = {
.name = “tps6507x-ts”,
.owner = THIS_MODULE,
},
.probe = tps6507x_ts_probe,
.remove = __devexit_p(tps6507x_ts_remove),
};
static int __init tps6507x_ts_init(void)
{
return platform_driver_register(&tps6507x_ts_driver);
}
module_init(tps6507x_ts_init);
3.platform driver与platform device匹配成功后会调用tps6507x_ts_probe
static int tps6507x_ts_probe(struct platform_device *pdev)
{
int error;
struct tps6507x_ts *tsc;
struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
struct touchscreen_init_data *init_data;
struct input_dev *input_dev;
struct tps6507x_board *tps_board;
int schd;
/**
* tps_board points to pmic related constants
* coming from the board-evm file.
*/
tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data;
if (!tps_board) {
dev_err(tps6507x_dev->dev,
"Could not find tps6507x platform data\n");
return -EIO;
}
/**
* init_data points to array of regulator_init structures
* coming from the board-evm file.
*/
init_data = tps_board->tps6507x_ts_init_data;
tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
if (!tsc) {
dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
error = -ENOMEM;
goto err0;
}
tps6507x_dev->ts = tsc;
tsc->mfd = tps6507x_dev;
tsc->dev = tps6507x_dev->dev;
input_dev = input_allocate_device();//分配一个input_dev接口,并初始化一些基本的成员
if (!input_dev) {
dev_err(tsc->dev, "Failed to allocate input device.\n");
error = -ENOMEM;
goto err1;
}
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);//设备支持的事件类型为按键事件和绝对坐标事件
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);/按键事件支持的子事件类型
input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);//这个是设置ad转换的x坐标
input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);//这个是设置ad转换的y坐标
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);//这个是设置触摸屏是否按下的标志
input_dev->name = "TPS6507x Touchscreen";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = tsc->dev;
snprintf(tsc->phys, sizeof(tsc->phys),
"%s/input0", dev_name(tsc->dev));
input_dev->phys = tsc->phys;
dev_dbg(tsc->dev, "device: %s\n", input_dev->phys);
input_set_drvdata(input_dev, tsc);
tsc->input_dev = input_dev;
INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler);
if (init_data) {
tsc->poll_period = init_data->poll_period;
tsc->vref = init_data->vref;
tsc->min_pressure = init_data->min_pressure;
input_dev->id.vendor = init_data->vendor;
input_dev->id.product = init_data->product;
input_dev->id.version = init_data->version;
} else {
tsc->poll_period = TSC_DEFAULT_POLL_PERIOD;
tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE;
}
error = tps6507x_adc_standby(tsc);
if (error)
goto err2;
error = input_register_device(input_dev);//注册一个input设备
if (error)
goto err2;
schd = schedule_delayed_work(&tsc->work,
msecs_to_jiffies(tsc->poll_period));
if (schd)
tsc->polling = 1;
else {
tsc->polling = 0;
dev_err(tsc->dev, "schedule failed");
goto err2;
}
platform_set_drvdata(pdev, tps6507x_dev);
return 0;
err2:
cancel_delayed_work_sync(&tsc->work);
input_free_device(input_dev);
err1:
kfree(tsc);
tps6507x_dev->ts = NULL;
err0:
return error;
}
四 input_dev注册过程
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
/* 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);
if (!dev->hint_events_per_packet)
dev->hint_events_per_packet =
input_estimate_events_per_packet(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",
(unsigned long) atomic_inc_return(&input_no) - 1);//设置input_dev中device的名字,这个名字会在/class/input中出现
error = device_add(&dev->dev);//添加input设备,注册到linux设备模型中,生成一系列的sys相关文件,udev会根据dev文件生成设备节点
if (error)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%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设备连接到input_dev_list链表上
list_for_each_entry(handler, &input_handler_list, node)//遍历input_handler_list链表,配对input_dev和input_handler
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
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)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *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;
}
五. 事件处理层分析(以evdev事件处理器为例)
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 fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
unsigned int bufsize;
struct input_event buffer[];//这个是一个input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type),编码code),值(value);
};
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,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush,
.llseek = no_llseek,
};
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = “evdev”,
.id_table = evdev_ids,
};
static int __init evdev_init(void)
{ //模块初始化函数就调用一个注册handler函数,将evdev_handler注册到系统中。
return input_register_handler(&evdev_handler);
}
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
//input_table,每个注册的handler都会将自己保存到这里,索引值为handler->minor右移5为,也就是除以32
//为什么会这样呢,因为每个handler都会处理最大32个input_dev,所以要以minor的32为倍数对齐,这个minor是传进来的handler的MINOR_BASE
//每一个handler都有一个这一个MINOR_BASE,以evdev为例,EVDEV_MINOR_BASE = 64,可以看出系统总共可以注册8个handler
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
//连接到input_handler_list链表中
list_add_tail(&handler->node, &input_handler_list);
//配对,遍历input_dev_list链表中的input_dev设备,与对应的input_handler结构配对,和注册input_dev过程一样的
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();//与proc文件系统有关
out:
mutex_unlock(&input_mutex);
return retval;
}
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);//这个是主要的配对函数,主要比较id中的各项
if (!id)
return -ENODEV;
//调用evdev_connect
error = handler->connect(handler, dev, id);//配对成功调用handler的connect函数,这个函数在事件处理器中定义,主要生成一个input_handle结构,并初始化,还生成一个事件处理器相关的设备结构,
if (error && error != -ENODEV)
pr_err(“failed to attach handler %s to device %s, error: %d\n”,
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
int i;
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 (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);
//没有match函数
if (!handler->match || handler->match(handler, dev))
return id;
}
return NULL;
}
//配对成功 调用evdev 的connect函数;
如果匹配上了就会创建一个evdev,它里边封装了一个handle,会把input_dev和input_handler关联到一起。关系如下
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct 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) {
pr_err("no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
dev_set_name(&evdev->dev, "event%d", minor);// 设置input设备的名字
evdev->exist = true;
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->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
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);
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;
}
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 数组中的下标变量
unsigned int bufsize;
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;
bufsize = evdev_compute_buffer_size(evdev->handle.dev);
client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event),
GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
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;
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;
}
4.用户进程读取event的底层实现
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函数中保存的evdev_client
struct evdev *evdev = client->evdev;
struct input_event event;
int retval;
if (count < input_event_size())
return -EINVAL;
//如果client的环形缓冲区中没有数据并且是非阻塞的,那么返回-EAGAIN,也就是try again
if (client->packet_head == client->tail && evdev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
//如果没有数据,并且是阻塞的,则在等待队列上等待吧
retval = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail || !evdev->exist);
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
//如果获得了数据则取出来,调用evdev_fetch_next_event
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
/input_event_to_user调用copy_to_user传入用户程序中,这样读取完成
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += input_event_size();
}
return retval;
}
七. 事件传递过程
-
事件产生
当按下触摸屏时,进入触摸屏按下中断,开始ad转换,ad转换完成进入ad完成中断,在这个终端中将事件发送出去,调用
input_report_abs(dev, ABS_X, xp);
input_report_abs(dev, ABS_Y, yp);
这两个函数调用了 input_event(dev, EV_ABS, code, value) 所有的事件报告函数都调用这个函数。 -
事件报告
1)input_event 函数分析,这个函数定义在input.c中
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
unsigned long flags;
//判断是否支持此种事件类型和事件类型中的编码类型
if (is_event_supported(type, dev->evbit, EV_MAX)) {
//对系统随机熵池有贡献,因为这个也是一个随机过程
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);//这个函数是事件处理的关键函数
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
检查按键是否为驱动所支持,只有之前注册过的按键才会继续传递;检查报告的按键状态是否和上次相同,如果连续多次报告按键按下,则只处理第一次
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
dev->sync = true;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = false;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, code, &value);
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
这个函数主要是根据事件类型的不同,做相应的处理。这里之关心EV_KEY类型,其他函数和事件传递关系不大,只要关心disposition这个是事件处理的方式,默认的是INPUT_IGNORE_EVENT,忽略这个事件,如果是INPUT_PASS_TO_HANDLERS则是传递给事件处理器,如果是INPUT_PASS_TO_DEVICE,则是传递给设备处理,触摸屏驱动没有定义这个。下面分析input_pass_event函数。
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handler *handler;
struct input_handle *handle;
rcu_read_lock();
//获取独占设备的handle的指针。如果有独占设备的handle,则仅仅将事件传给独占的handle对应的handler
handle = rcu_dereference(dev->grab);
if (handle)
//这里直接调用了handler事件驱动对应的XX_event函数,这个XX_event函数把事件数据包传递给了handler,当应用程序使用XX_read时就可以读取到这些数据包
handle->handler->event(handle, type, code, value);
else {
bool filtered = false;
//如果没有绑定,则遍历dev的h_list链表,寻找handle,如果handle已经打开,说明有进程读取设备关联的evdev。
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;
handler = handle->handler;
if (!handler->filter) {
if (filtered)
break;
handler->event(handle, type, code, value);//调用相关的事件处理器的event函数,进行事件的处理,即evdev_event
} else if (handler->filter(handle, type, code, value))
filtered = true;
}
}
rcu_read_unlock();
}
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;
//将传过来的事件,赋值给input_event结构
do_gettimeofday(&event.time);
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab);//如果evdev绑定了client那么,处理这个客户端,触摸屏驱动没有绑定
if (client)
evdev_pass_event(client, &event);
else
//遍历client链表,调用evdev_pass_event函数
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();
if (type == EV_SYN && code == SYN_REPORT)
wake_up_interruptible(&evdev->wait);
}
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;//将事件赋值给客户端的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 (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);
}
总结一下事件的传递过程:首先在驱动层中,调用inport_report_abs,然后他调用了input core层的input_event,input_event调用了input_handle_event对事件进行分派,调用input_pass_event,在这里他会把事件传递给具体的handler层,然后在相应handler的event处理函数中,封装一个event,然后把它投入evdev的那个client_list上的client的事件buffer中,等待用户空间来读取。
当我们的应用层通过open打开event0这个设备节点时最终会调用到input_init函数中注册的字符设备input时注册的file_operations->open() 函数
input_open_file
handler = input_table[iminor(inode) >> 5]
handler->fops->open()
evdev = evdev_table[i];
evdev_open_device
input_open_device
input_dev->open() // 最终就是执行input设备中的open函数
file->private_data = evdev_client;