Input子系统内核
重要结构体解析:
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned int hint_events_per_packet;
//这个函数接口是用于计算每个数据包的大小。好准备好大小合适的数据包buffer.
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int rep[REP_CNT];
struct input_mt_slot *mt;
int mtsize;
int slot;
int trkid;
struct input_absinfo *absinfo;
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
bool sync;
struct device dev;
struct list_head h_list;
struct list_head node;
};
struct input_handler {
//存放的是此handler的私有变量
void *private;
//进行时间传递的函数接口,此时中断disable,获取dev->event_lock spinlock锁,因
此此时不进入睡眠
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
//对某些时间进行特殊处理
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
//device和handler的匹配函数
bool (*match)(struct input_handler *handler, struct input_dev *dev);
//device和handler匹配成功后的connect函数,在此产生对应的次设备号和注册handle
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
//取消device和handler的链接
void (*disconnect)(struct input_handle *handle);
//通过给定的handle来启用对用的handler
void (*start)(struct input_handle *handle);
const struct file_operations *fops;//此handler的文件操作接口
int minor;//handler的次设备号基地址
//handler的名字,通常显示在/proc/bus/input/handlers路径下
const char *name;
//指向此handler能够处理的属性
/*通常属性包含以下几种:
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 mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
kernel_ulong_t driver_info;
};
*/
const struct input_device_id *id_table;
//这是handler自己的handle链表管理
struct list_head h_list;
//代表此handler挂在在input_handler_list链表上面
struct list_head node;
};
struct input_handle {
//handle的私有数据
void *private;
//标识此handle是否被打开
int open;
//handle的名字
const char *name;
//与handle相关连的device
struct input_dev *dev;
//与handle相关连的handler
struct input_handler *handler;
//将此handle挂载到device对应的管理handle的链表中
struct list_head d_node;
//将此handle挂载到handler对应的管理handle的链表中
struct list_head h_node;
};
Input子系统解析
属性文件和设备文件的创建。
相关的属性文件
首先类文件夹创建:
路劲:/sys/class/input/里面有所有的设备文件和所有的handle文件。
设备文件:就是调用input_register_device()函数注册的设备
这样的文件一般命名方式为:"input%ld"
最终调用device_add函数注册,并生成一些相关的属性文件
Handle文件:就是device与相对应的handler相匹配后,调用handler的connect函数,创建的文件
这样的文件一般命名方式为:以eventhandler为例:"event%d"对应的handler名称加ID
最终调用device_add函数注册,并生成一些相关的属性文件
NOTE:在创建一些对应的属性文件的同时,也会创建一系列的软link和硬link
调用input_proc_init()函数在proc文件系统中生成一些必备的文件
路劲:/proc/bus/input/
下面有devices和handlers两个文件
Devices文件记录所有的input设备
Handlers记录input中所有的handler
Input子系统与hander:
设备文件:分主设备号和此设备号:
路劲:
/sys/dev/char 主设备号为13
次设备号对应各个handerl的设备
一共有256个次设备号,设计上每个handler占用32个次设备号。故最多有8个handler,
1.由EventHub.h中的
// Epoll FD list size hint.
static const int EPOLL_SIZE_HINT = 8;
也可以推得此信息。
2.由input_register_handler()函数中input_table[handler->minor >> 5] = handler;
也可以推得此信息
3.由static struct input_handler *input_table[8];数组也可以推得此信息
常见的几种handler:
Joydev
#define JOYDEV_MINOR_BASE 0
#define JOYDEV_MINORS 16
Evdev
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
Mousedev
#define MOUSEDEV_MINOR_BASE 32
#define MOUSEDEV_MINORS 32
一般内核中形形色色的handler有很多,但是上层看到到一般最多只有八种,那么那些特征的handler上层可见呢?
Input子系统管理上层可见的handler是通过一个8个 handler容量的数组
static struct input_handler *input_table[8];此数组是全局变量
此时我们看看hander的注册函数,什么条件的handler才会存进此数组中。
int input_register_handler(struct input_handler *handler)
{
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
}
由此可知:只有满足带有文件操作接口的hander才会存进此全局数组中。
其他情况的handler只在内核里面进行链表似的管理。
Hander与devices:
在注册设备的时候会从链表中与handler进行匹配,匹配成功后会调用对应handler的connect函数。在此函数中产生对应段的次设备号。和注册一个关联devices和handler的handle.
通常情况下:每一个handler也有一个全局的数组用来存放此handler关联成功的devices
通常都是调用xxxdev_install_chrdev()函数
Evdev:static struct evdev *evdev_table[EVDEV_MINORS];
Mousedev:static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
Joydev:static struct joydev *joydev_table[JOYDEV_MINORS];
Handler特性:
每一个handler都对应着多个client。也就是说上层可以有多个实例同时关注此handler,此handler对应的设备有时间产生的时候,会通报相关的所有client.
Client通常都是通过链表来管理的,机制是RCU
Client都是在对应handler的open函数中调用对应的XXXdev_attach_client函数进行注册。
Handler结构体通常需要填充的属性:
以eventhandler为例:
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,
};
几种术语:
Device:input设备
Handler:处理设备时间的handler
Handle:关联device和handler
下面从几个方面来了解input子系统。
注册流程
注销流程
消息上报流程
Handler和device匹配流程
生成handle流程
Device
device注册流程
/*
**1.device必须是函数input_allocate_device()申请的结构体
**2.device的capabilities必须在注册之前就设置好
**3.如果注册device失败必须调用函数input_free_device()释放之前申请的device
** 的空间
**4.如果注册成功,需要注销的时候必须调用函数input_unregister_device()
**5.
**
**
*/
int input_register_device(struct input_dev *dev)
{
//首先是填充input_dev结构体的相关属性
//所有的device都得支持EV_SYN事件
__set_bit(EV_SYN, dev->evbit);
//所有的device都不支持KEY_RESERVED事件
__clear_bit(KEY_RESERVED, dev->keybit);
//将不支持的事件占有位都清零
input_cleanse_bitmasks(dev);
//计算每个数据包的事件数目
if (!dev->hint_events_per_packet)
dev->hint_events_per_packet =
input_estimate_events_per_packet(dev);
//此处是处理重复按键的
//如果在驱动中设置了延迟事件和周期,在inputcore中就不进行处理了,由驱动//自己控制
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;
}
//填充设置和获取keycode的函数
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
//该函数对原子类型的变量V原子的增加1并且返回指向V的指针
//给device以input+number的方式进行命名
dev_set_name(&dev->dev, "input%ld",
(unsigned long)atomic_inc_return(&input_no) - 1);
//将此device添加进经典模型中,在sys文件系统下面生成对应的文件
error = device_add(&dev->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;
}
//将此device添加进input-core全局的dev链表中
list_add_tail(&dev->node, &input_dev_list);
//遍历handler链表,调用input_attach_handler函数与handler进行匹配
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
//
//这个函数就是唤醒poll的等待队列,poll函数会返回return POLLIN | //POLLRDNORM;
//标识此设备有消息可读,这样就可以对此device进行读操作获取相关的信息了。
//此处就是一个poll模型的应用不多说,需要详细了解的关注下此模型
input_wakeup_procfs_readers();
//去锁互斥变量
mutex_unlock(&input_mutex);
Device注销流程
/*
**注销掉先前注册的设备
**
**
**
**
*/
void input_unregister_device(struct input_dev *dev)
{
//注销前的准备工作,详细信息见下面函数单独分析
input_disconnect_device(dev);
//互斥变量上锁
mutex_lock(&input_mutex);
//在注册的时候,会和handler匹配,如果匹配成功就会调用handler的connect
//函数,在这里与前面相对应的是,注销的时候就会调用相应的disconnect函//数
list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
handle->handler->disconnect(handle);
WARN_ON(!list_empty(&dev->h_list));
//将设备的timer删除
del_timer_sync(&dev->timer);
//将设备从全局设备链表中删除
list_del_init(&dev->node);
//此函数的功能已经在注册函数中描述清楚,这里就不再重复了
input_wakeup_procfs_readers();
//互斥变量解锁
mutex_unlock(&input_mutex);
//之前的注册的时候有调用device_add()函数,这里就调用对应的device注销//函数,具体细节不描述,如果有兴趣,可详细研究设备驱动模型
device_unregister(&dev->dev);
}
static void input_disconnect_device(struct input_dev *dev)
{
//添加此标识,标识设备已经离开
mutex_lock(&dev->mutex);
dev->going_away = true;
mutex_unlock(&dev->mutex);
//加锁
spin_lock_irq(&dev->event_lock);
//在将设备移除之前模拟一个keyup消息,防止出现某些那件一直处于按下状//态,
input_dev_release_keys(dev);
//遍历与device匹配成功的handler链表,将生成的handle,open属性置为0,
//标识此handle没有被引用,引用计数为0,device产生的事件不再传送到//handler中了,进行处理之后或许设备还有有事件产生,但是此时的事件已经//不传递到//handler中去了
list_for_each_entry(handle, &dev->h_list, d_node)
handle->open = 0;
//解锁
spin_unlock_irq(&dev->event_lock);
}
二.Handler
1.handler注册流程
/*
**1.注册一个新的handler,且与所有的devices进行适配
*/
int input_register_handler(struct input_handler *handler)
{
//加锁互斥变量,但是可被中断打断(这是我个人的了解,不一定正确)
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
//初始化handle链表,与此handler相关联的handle,都挂载在此链表上面
INIT_LIST_HEAD(&handler->h_list);
//前面已经提到过,handler分为带文件操作接口和不带文件操作接口,而且带文
//件操作接口的handler,最多只能有8个。
//input_table数组是全局的,大小为8,存放贷文件接口的handler.索引为次设备号//偏移5位,这里与每个带文件操作接口handler,最多关联32个设备相对应,对应//着32个次设备号
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
//将此handler挂载进全局的handler管理链表中去
list_add_tail(&handler->node, &input_handler_list);
//遍历device全局链表,调用input_attach_handler进行匹配
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
//此函数在之前已经详细描述过,这里面不重复
input_wakeup_procfs_readers();
}
2.handler注销流程
/*
**1.将之前注册的handler进行撤销
*/
void input_unregister_handler(struct input_handler *handler)
{
//互斥变量加锁
mutex_lock(&input_mutex);
//在注册的时候会和所有的device进行适配,如果适配成功就会调用对应//handler的connect函数,这里面要调用对应的disconnect函数
list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
handler->disconnect(handle);
WARN_ON(!list_empty(&handler->h_list));
//将此handler从handler的全局链表中删除
list_del_init(&handler->node);
//前面注册的时候会填充input_table对应索引,此处应该将对应索引值赋为空
if (handler->fops != NULL)
input_table[handler->minor >> 5] = NULL;
//此函数不多解释,前面已经进行过详细的描述
input_wakeup_procfs_readers();
//互斥变量解锁
mutex_unlock(&input_mutex);
}
三.Handle
综述:handle的注册于注销都是在对应handler的connect和disconnect函数中进行
的
1.handle注册流程
/*
**注册一个handle进入对应handler和device链表中,用于关联二者
**所以只要handle的open属性不为0,对应设备产生的事件就会通过它被传送到**上层去,此函数被handler的connect函数调用
*/
int input_register_handle(struct input_handle *handle)
{
//首先提取出与此handle相关联的handler和device
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
//给互斥变量上锁(可被中断打断),主要为了防止input_release_device().在此期间//被调用
error = mutex_lock_interruptible(&dev->mutex);
//如果此handler带有filter功能则将handle加到设备链表头,否则加到设备链表//尾
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
//互斥变量解锁
mutex_unlock(&dev->mutex);
//由于此函数是被connect函数调用,与disconnect函数式互斥的,不可能同时调//用input_unregister_handle()函数,所以不需要独立的互斥变量
//将handle添加到handler链表的尾
list_add_tail_rcu(&handle->h_node, &handler->h_list);
//通过给定的handle,开始handler,(此处目前我也不怎么理解)
//此函数通常被调用刚好在connect函数之后,也可以在一个进程grabbed一个设备然后释放他的时候调用例如函数:__input_release_device
if (handler->start)
handler->start(handle);
}
2.handle注销流程
/*
**注销函数主要就是将handle的节点从handler和device的链表中删除
*/
void input_unregister_handle(struct input_handle *handle)
{
//将handle从handler的链表中删除
list_del_rcu(&handle->h_node);
/*
* Take dev->mutex to prevent race with input_release_device().
*/
mutex_lock(&dev->mutex);
//将handle从device的链表中删除
list_del_rcu(&handle->d_node);
mutex_unlock(&dev->mutex);
//同步RCU,RCU小解:
//RCU机制是Linux2.6之后提供的一种数据一致性访问的机制,从RCU//(read-copy-update)的名称上看,我们就能对他的实现机制有一个大//概的了解,在修改数据的时候,首先需要读取数据,然后生成一个副本,//对副本进行修改,修改完成之后再将老数据update成新的数据,此所
//谓RCU
synchronize_rcu();
}
Handler的connect和disconnect函数
这里面evdev这个handler进行举例分析
五:消息上报流程
首先从驱动中的上报消息函数开始开流程
input_report_key()àinput_event()àinput_handle_event()àdev->event()
【设备要监听消息】
input_pass_event()
【handler监听消息】
下面主要看handler的消息流程:
input_pass_event()àhandler->event()会调用相应handler的event函数
下面来显示看看这些函数流程
/*
**根据支持的事件传递事件类型,调用input-core的事件传递函数
*/
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
//事件类型是EV_KEY,,其他的类似
input_event(dev, EV_KEY, code, !!value);
}
/*
**首先判断此device是否支持此事件,然后调用对应的handle的event传递函数
*/
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事件的传递
input_handle_event(dev, type, code, value);
//解锁事件锁
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
Note:
//每一个数据包的结束都会调用此函数进行同步标识
input_mt_sync()
//多点事件的结束时调用此函数进行标识同步(一整个事件中会有多个数据包)
input_sync()
/*
**1.根据事件类型进行判断事件disposition类型
** a. #define INPUT_IGNORE_EVENT 0
** b.#define INPUT_PASS_TO_HANDLERS 1
** c.#define INPUT_PASS_TO_DEVICE 2
**d.#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
**2.根据事件处置的三种类型分别:
** a.忽略事件
** b.将事件传递到device的event的处置函数中
** c.将事件传递到handler的event的处置函数中
*/
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:
//标识此种事件处置等级是TOALL
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
//此处将设备的事件同步信号设置成true,完整事件的同步
dev->sync = true;
//标识此事件的处置等级是只传递给handlers
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
//此处将设备的事件同步信号设置成true,数据包的同步
dev->sync = false;
//标识此事件的处置等级是只传递给handlers
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;
}
//如果此事件处理类型为忽略,且不是EV_SYN同步事件,将同步信号设置成false
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;
//如果此device支持事件处理,而且事件处理方式为指向设备的
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
//这是主流流程,事件处理方式是传向handler,则进入handler的事件流处理
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
下面以EVDEVhandler为例子来讲解handler的消息传递流程
在注册此handler的时候,已经初始化了event事件处理函数
函数为evdev_event
/*
**
*/
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;
struct timespec ts;
//首先获取事件的时间戳
ktime_get_ts(&ts);
//填充事件的属性参数
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;//事件类型
event.code = code;//事件码
event.value = value;//事件码
rcu_read_lock();//获取读RCU锁
//首先判断此handler是否被某个event_client给grab住
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);//如果有,则将此事件只传给此client
else
//否则将此事件传递给所有相关联的client
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();//释放读RCU锁
//如果是同步事件,则唤醒等待队列
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. */
//首先获取共享资源buffer的操作锁
spin_lock(&client->buffer_lock);
//将此事件,添加进buffer头
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].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);
}
spin_unlock(&client->buffer_lock);
}
//inputmanager系统
Inputmanager系统
综述:此模型大致关系
InputmanageràInputReaderInterface负责读取input子系统上报的事件
àInputDispatcherInterface负责将读取到的事件上报给上层
首先来看InputManager.cpp/.h
InputManager类有四个对外的公有成员函数
virtual status_t start();
virtual status_t stop();
virtual sp<InputReaderInterface> getReader();
virtual sp<InputDispatcherInterface> getDispatcher();
下面对这几个函数进行简单的解析
/*
**1.开启发送消息的线程
**2.开启获取消息的线程
*/
status_t InputManager::start() {
status_t result=mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
return OK;
}
/*
**1.停止获取消息的线程
**2.停止发送消息的线程
*/
status_t InputManager::stop() {
status_t result = mReaderThread->requestExitAndWait();
result = mDispatcherThread->requestExitAndWait();
return OK;
}
/*
**获取读消息的类实例
*/
sp<InputReaderInterface> InputManager::getReader() {
return mReader;
}
/*
**获取发送消息的类实例
*/
sp<InputDispatcherInterface> InputManager::getDispatcher() {
return mDispatcher;
}
下面进入类
InputReader
InputReaderThread
InputReaderThread类主要就是继承了系统thread的属性
/*
**类的构造函数,实例化继承的类thread和mread类
*/
InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
Thread(/*canCallJava*/ true), mReader(reader) {
}
/*
**类的析构函数
*/
InputReaderThread::~InputReaderThread() {
}
/**
**线程循环的回调函数,mread类对事件的获取函数式looponce,是事件获取的主脉络
*/
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
下面进入到InputReader类中
主要来看对外的函数public函数:
/*
**构造函数
*/
InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener);
/*
**析构函数
*/
virtual ~InputReader();
/*
**打印dumplog
*/
virtual void dump(String8& dump);
/*
**监测函数,防止死锁
*/
virtual void monitor();
/*
**线程回调函数
*/
virtual void loopOnce();
/*
**获得输入设备的配置信息
*/
virtual void getInputConfiguration(InputConfiguration* outConfiguration);
/*
**获取输入设备的信息
*/
virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
/*
**获取输入设备的ID
*/
virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
/*
**获取扫描码
*/
virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
int32_t scanCode);
/*
**获取key码
*/
virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
int32_t keyCode);
/*
**获取switch状态
*/
virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
int32_t sw);
/*
**判断是否有KEY
*/
virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
/*
**获取更新的配置
*/
virtual void requestRefreshConfiguration(uint32_t changes);
下面主要来看看线程的回调函数looponce
void InputReader::loopOnce() {
int32_t timeoutMillis;
{ // acquire lock
//加锁
AutoMutex _l(mLock);
//判断配置信息是否需要更新
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
refreshConfigurationLocked(changes);
}
timeoutMillis = -1;
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
} // release lock
//调用eventhub的getevent函数来获取事件
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
if (count) {
processEventsLocked(mEventBuffer, count);
}
if (!count || timeoutMillis == 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
#if DEBUG_RAW_EVENTS
LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
} // release lock
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener->flush();
}
下面进入到eventhub类来进一步分析事件的获取流程
Eventhub.cpp
共管理了那些设备
管理了设备的那些状态
怎么管理的?怎么从底层获取数据?怎么上报给上层的!
所有的设备都是用一个devices结构体进行管理。
而这些所有的设备通过一个KeyedVector<int32_t, Device*> mDevices;模版类进行管理。其实就是一个适量的数据结构。
Device对应的数据结构为:
struct Device {
Device* next;//维护一个device的链表
int fd;//设备对应的文件句柄
const int32_t id;//设备对应的ID
const String8 path;//设备文件对应的路劲
const InputDeviceIdentifier identifier;//设备标识
uint32_t classes;//设备属于的类
//设备通常支持的几种事件
uint8_t keyBitmask[(KEY_MAX + 1) / 8];//key事件
uint8_t absBitmask[(ABS_MAX + 1) / 8];//绝对坐标事件
uint8_t relBitmask[(REL_MAX + 1) / 8];//相对坐标事件
uint8_t swBitmask[(SW_MAX + 1) / 8];//switch事件
uint8_t ledBitmask[(LED_MAX + 1) / 8];//led相关的事件
uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];//设备的属性相关的事件
String8 configurationFile; //目前的理解是对应的配置文件
PropertyMap* configuration;
VirtualKeyMap* virtualKeyMap;//虚拟按键映射
KeyMap keyMap;//实体按键映射
Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);//设备的构造函数接口
~Device();//设备的析构函数的接口
void close();//设备的关闭函数接口
};
这个函数主要是通过ioctl函数接口与kernel的设备文件进行交互。
主要就是以上支持的几种事件,的一些属性状态的获取
主要的函数接口是getevent和openDeviceLocked两个函数,其他的函数主要就是对属性相关的进行设备和获取。
Open所有的设备。
Close所有的设备
GetEvent获取设备的事件
首先看openDeviceLocked函数,根据传进来的文件名称,通过ioct函数接口,获取次文件设备的一系列属性值。其实就是对device的结构体进行初始化。
在opendevice的时候通过epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)机制来监听对应设备文件的事件变化。
在closedevice的时候通过epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL))机制来删除之前建立的监听机制。
GetEvent函数中主要是通过epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);函数进行事件的监听。
InputReader.cpp解析
获取数据的方式:
通过一个线程InputReaderThread进行轮询,去调用EVENThub的getevent接口来获取事件。
Inputmanager.cpp
他是事件处理的核心,处于管理地位。
主要干两件事情:
开启mDispatcherThread线程进行数据发送。在队列上面等待事件的输入,并异步的将事件发送到对应的应用程序。
开启mReaderThread进行数据获取,负责数据原始数据进行处理最终发送到mDispatcherThread线程的队列上面去。
Note:两者间的通信时单方向的。不共享内部的状态
mReaderThreadàmDispatcherThread:通过InputDispatcherPolicyInterface类进行交互。
Note:inputmanager类从来都不和JAVA直接交互,与上层进行交互的是InputDispatcherPolicyInterface类,并且启用Dalvik虚拟机(DVM)。
首先看看eventHub对外的接口函数
class EventHubInterface : public virtual RefBase {
protected:
//构造函数
EventHubInterface() { }
//析构函数
virtual ~EventHubInterface() { }
public:
// Synthetic raw event type codes produced when devices are added or removed.
enum {
// Sent when a device is added.
DEVICE_ADDED = 0x10000000,
// Sent when a device is removed.
DEVICE_REMOVED = 0x20000000,
// Sent when all added/removed devices from the most recent scan have been reported.
// This event is always sent at least once.
FINISHED_DEVICE_SCAN = 0x30000000,
FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
};
/*
**获取设备的类
*/
virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
/*
**获取设备的名字
*/
virtual String8 getDeviceName(int32_t deviceId) const = 0;
/*
**获取设备的配置
*/
virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
/*
**获取设备绝对坐标信息
*/
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const = 0;
/*
**获取设备的相对坐标信息
*/
virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0;
/*
**判断是否具有输入性能
*/
virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
/*
**键值映射
*/
virtual status_t mapKey(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const = 0;
/*
**坐标轴映射
*/
virtual status_t mapAxis(int32_t deviceId, int scancode,
AxisInfo* outAxisInfo) const = 0;
// Sets devices that are excluded from opening.
// This can be used to ignore input devices for sensors.
virtual void setExcludedDevices(const Vector<String8>& devices) = 0;
/*
* Wait for events to become available and returns them.
* After returning, the EventHub holds onto a wake lock until the next call to getEvent.
* This ensures that the device will not go to sleep while the event is being processed.
* If the device needs to remain awake longer than that, then the caller is responsible
* for taking care of it (say, by poking the power manager user activity timer).
*
* The timeout is advisory only. If the device is asleep, it will not wake just to
* service the timeout.
*
* Returns the number of events obtained, or 0 if the timeout expired.
*/
virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
/*
* Query current input state.
*/
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
int32_t* outValue) const = 0;
/*
* Examine key input devices for specific framework keycode support
*/
virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) const = 0;
virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0;
virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
virtual void getVirtualKeyDefinitions(int32_t deviceId,
Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
virtual String8 getKeyCharacterMapFile(int32_t deviceId) const = 0;
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
virtual void requestReopenDevices() = 0;
/* Wakes up getEvents() if it is blocked on a read. */
virtual void wake() = 0;
/* Dump EventHub state to a string. */
virtual void dump(String8& dump) = 0;
/* Called by the heatbeat to ensures that the reader has not deadlocked. */
virtual void monitor() = 0;
};
整体来看这个类,核心的接口就是:
Opendevices()
Getevent()
其他的接口都是一些辅助性质的接口
首先看看此类是怎么管理设备的。
//存储所有设备的适量数据结构
KeyedVector<int32_t, Device*> mDevices;
//记录所有打开的设备
Device *mOpeningDevices;
//记录所有关闭的设备
Device *mClosingDevices;
下面首先来看
/*
**1.扫描文件夹,打开对应文件夹下面所有的设备文件
*/
status_t EventHub::scanDirLocked(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
//打开对应的文件夹,获得文件夹的句柄
dir = opendir(dirname);
if(dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
//遍历文件夹下面的设备文件,将合适的设备文件打开
while((de = readdir(dir))) {
if(de->d_name[0] == '.' &&
(de->d_name[1] == '\0' ||
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
openDeviceLocked(devname);
}
closedir(dir);
return 0;
}
/*
**主要是填充device的结构体
**1.填充InputDeviceIdentifier结构体
** String8 name;//设备名
** String8 location;
** String8 uniqueId;
** uint16_t bus;
** uint16_t vendor;
** uint16_t product;
** uint16_t version;
**2. 检查此设备是否在排除的列表中
**3.获取device driver的version
**4.获取设备标示符
__u16 bustype;
__u16 vendor;
__u16 product;
__u16 version;
**5. 设备此设备文件以非阻塞的方式进行轮询
**6.
**7.
**8.
**9.
**10.
**11.
**12.
*/
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
const char *temp1 = "Button Jack";
//打开设备文件,获取文件句柄
int fd = open(devicePath, O_RDWR);
InputDeviceIdentifier identifier;
// Get device name.
//获取设备的名字
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
//检查此设备是否在排除的列表中
// Check to see if the device is on our excluded list
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const String8& item = mExcludedDevices.itemAt(i);
if (identifier.name == item) {
LOGI("ignoring event id %s driver %s\n", devicePath, item.string());
close(fd);
return -1;
}
}
// Get device driver version.
//设备设备驱动的版本号
int driverVersion;
if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
// Get device identifier.
//获取设备标示符
struct input_id inputId;
if(ioctl(fd, EVIOCGID, &inputId)) {
identifier.bus = inputId.bustype;
identifier.product = inputId.product;
identifier.vendor = inputId.vendor;
identifier.version = inputId.version;
// Get device physical location.
//获取设备的物理位置
if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1)
// Get device unique id.
//获取设备独一无二的id
if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1)
//设备此设备文件以非阻塞的方式进行轮询
// Make file descriptor non-blocking for use with poll().
if (fcntl(fd, F_SETFL, O_NONBLOCK))
//将获取的设备信息进行整合new一个新的device结构体此时此设备获取FD的所有权
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
// Load the configuration file for the device.
//下载设备的配置信息
loadConfigurationLocked(device);
// Figure out the kinds of events the device reports.
//算出此设备支持的事件类型
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
// See if this is a keyboard. Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
//判断此设备社否为keyboard,忽略button范围的所有信息,除了joystick(操纵杆)和
bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
sizeof_bit_array(BTN_9))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
sizeof_bit_array(KEY_MAX + 1));
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
sizeof_bit_array(BTN_MOUSE))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
sizeof_bit_array(BTN_DIGI));
if (haveKeyboardKeys || haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
// See if this is a cursor device such as a trackball or mouse.
if (test_bit(BTN_MOUSE, device->keyBitmask)
&& test_bit(REL_X, device->relBitmask)
&& test_bit(REL_Y, device->relBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
}
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
&& test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
// Some joysticks such as the PS3 controller report axes that conflict
// with the ABS_MT range. Try to confirm that the device really is
// a touch screen.
if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
}
// Is this an old style single-touch driver?
} else if (test_bit(BTN_TOUCH, device->keyBitmask)
&& test_bit(ABS_X, device->absBitmask)
&& test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
}
// See if this device is a joystick.
// Assumes that joysticks always have gamepad buttons in order to distinguish them
// from other devices such as accelerometers that also have absolute axes.
if (haveGamepadButtons) {
uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
for (int i = 0; i <= ABS_MAX; i++) {
if (test_bit(i, device->absBitmask)
&& (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
device->classes = assumedClasses;
break;
}
}
}
// Check whether this device has switches.
for (int i = 0; i <= SW_MAX; i++) {
if (test_bit(i, device->swBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_SWITCH;
break;
}
}
// Configure virtual keys.
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
// Load the virtual keys for the touch screen, if any.
// We do this now so that we can make sure to load the keymap if necessary.
status_t status = loadVirtualKeyMapLocked(device);
if (!status) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
}
// Load the key map.
// We need to do this for joysticks too because the key layout may specify axes.
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
// Load the keymap for the device.
keyMapStatus = loadKeyMapLocked(device);
}
// Configure the keyboard, gamepad or virtual keyboard.
if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
// Register the keyboard as a built-in keyboard if it is eligible.
if (!keyMapStatus
&& mBuiltInKeyboardId == -1
&& isEligibleBuiltInKeyboard(device->identifier,
device->configuration, &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
if (hasKeycodeLocked(device, AKEYCODE_Q)) {
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
// See if this device has a DPAD.
if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
// See if this device has a gamepad.
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
break;
}
}
}
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == 0) {
LOGV("Dropping device: id=%d, path='%s', name='%s'",
deviceId, devicePath, device->identifier.name.string());
delete device;
return -1;
}
// Determine whether the device is external or internal.
if (isExternalDeviceLocked(device)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
}
// Register with epoll.
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = deviceId;
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
LOGE("Could not add device fd to epoll instance. errno=%d", errno);
delete device;
return -1;
}
LOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s",
deviceId, fd, devicePath, device->identifier.name.string(),
device->classes,
device->configurationFile.string(),
device->keyMap.keyLayoutFile.string(),
device->keyMap.keyCharacterMapFile.string(),
toString(mBuiltInKeyboardId == deviceId));
mDevices.add(deviceId, device);
device->next = mOpeningDevices;
mOpeningDevices = device;
return 0;
}