内核的输入子系统是相对分散的,对多种不同类别的输入设备(如键盘,鼠标,跟踪球,操纵杆,触摸屏,加速计和手写板)进行统一抽象处理。输入子系统包括两类驱动程序:事件驱动程序和设备驱动程序。事件驱动程序负责和应用程序的接口,而设备驱动程序负责和底层输入设备的通信。鼠标事件生成文件mousedev属于事件驱动程序,而PS/2鼠标驱动程序是设备驱动程序。事件驱动程序是标准的,对所有的输入类都是可用的,所以要实现的是设备驱动程序而不是事件驱动程序。设备驱动程序可以利用一个已经存在的,合适的事件驱动程序通过输入核心和用户应用程序接口。(事件驱动程序不需要我们去实现)
输入子系统带来了如下好处: 1.统一了物理形态各异的相似的输入设备的处理功能 2.提供了用于分发输入报告给用户应用程序的简单的事件接口 3.抽取出了输入驱动程序的通用部分,简化了驱动,并引入了一致性 如下图,input子系统分三层,最上一层是event handler,中间是intput core,底层是input driver。input driver把event report到input core层。input core对event进行分发,传到event handler,相应的event handler层把event放到event buffer中,等待用户进程来取。
在上一篇中分析的设备驱动程序的probe函数中,先对input_dev结构体进行了属性的配置,然后调用了input_register_device函数向input_core注册这个input_dev设备。下面看看input_register_device这个函数在匹配中做了什么:
首先看input_register_device源码:
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.
*/
//rep主要是处理重复按键,如果没有定义dev->rep[REP_DELAY]和dev->rep[REP_PERIOD],
//则将其赋值为默认值。dev->rep[REP_DELAY]是指第一次按下多久算一次,这里是250ms,
//dev->rep[REP_PERIOD]指如果按键没有被抬起,每33ms算一次。
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);
error = device_add(&dev->dev);//将dev加入到linux的设备模型中
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;
}
//这个函数主要功能时将input_dev与input_handler_list中的handler进行匹配,
//匹配过程看input_attach_handler源码
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
下面 input_attach_handler源码:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
//先执行input_match_device函数对dev和handler进行匹配,
//若匹配成功后,执行handler的connect函数,先看input_match_device函数的匹配
//工程,匹配成功后再看connect函数。
const struct input_device_id *id;
int error;
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
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;
}
input_match_device函数匹配过程:
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;
//这边的input_dev的各项属性,是在设备的驱动程序中注册的,这里的handler的各项属性是在evdev.c(事件驱动程序)
//中赋值的,关于handler的注册将在下面的文章中分析,这里暂时认为这个handler是可以匹配所有的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 (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;
}
匹配成功后,调用handler->connect(handler, dev, id)函数,这个函数的定义也是在evdev.c中进行定义的,先
贴出源码进行分析这个函数:
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;
首先补充几个知识点:(这些知识点也会在下一篇的handler注册中介绍)
// static struct input_handler *input_table[8];
//#define INPUT_DEVICES 256
// 一共有8个input_handler,对应256个设备,所以一个handler对应32个设备。
// 这个问题在我参加的一次linux驱动的面试中被问到,当时真是汗啊!!!
// static struct evdev *evdev_table[EVDEV_MINORS];
// #define EVDEV_MINORS 32
// evdev理论上可对应32个设备,其对应的设备节点一般位于/dev/input/event0~/dev/input/event4
// 下边的for循环,在evdev_table数组中找一个未使用的地方
// evdev_table用来记录此handler下的所有的evdev
for (minor = 0; minor < EVDEV_MINORS; minor++)
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/input下生成eventX节点
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = true;
evdev->minor = minor;
//evdev这个结构体中含有handle这个结构体变量,handle中包含了dev结构体和handler结构体
//dev用来记录input_dev,handler用来记录与dev相匹配的handle,并把evdev作为handle
//的私有变量
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
//设置evdevX这个设备的主设备号和次设备号
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);
// input_register_handle完成的主要功能是:
// list_add_tail_rcu(&handle->d_node, &dev->h_list);将handle->d_node加到dev->h_list中
// list_add_tail(&handle->h_node, &handler->h_list);将handle->h_node加到handler->h_list中
//这样以来无论是先得到dev还是先得到handler都可以通过handle找到与之匹配的handle,handle将dev和handler
//紧密联合起来
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
//evdev_install_chrdev完成的功能是evdev_table[evdev->minor]=evdev;
//将evdev加入到evdev_table中
error = evdev_install_chrdev(evdev);
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;
}
在connect函数中的evdev结构体,其有结构体变量handle,在handle中又有handler和dev这两个结构体
用来描述相匹配的handler和dev。然后向设备模型添加evdev结构体,完成了整个input_dev和handler的匹配过程