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,,其他的类似