input子系统即linux提供的输入子系统,给类似于触摸屏这类设备提供交互通信之用。简单来说,对于像触摸屏这类设备想上报事件的话,通过input就可以实现driver到app的通信。同时,它也支持很多种不同的事件。
一般我们在具体的driver中如何使用input?通常都是在probe中用input_allocate_device()函数分配一个struct input_dev *dev类型的设备结构体dev=input_allocate_device(),同时input_allocate_device()完成input_dev的初始化并返回一个指向input_dev类型的指针(input_dev结构代表一个输入设备,其包含了输入设备的一些相关信息,如设备支持的按键类型、设备的名称、设备支持的事件)。再下面就是input的注册了,我们用input_register_device(),此函数就是将我们上面的input_dev注册到input核心中。如果注册失败,用input_free_device()来free。如果注册成功,在卸载的时候用input_unregister_device()。那么input_register_device()究竟实现了什么,看名字我们知道是注册input,那它里面都干了些什么呢?我们来看看:
-
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;
-
-
__set_bit(EV_SYN, dev->evbit);
-
-
/*
-
* 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;
-
-
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
-
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
-
-
error = device_add(&dev->dev);
-
if (error)
-
return error;
-
-
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
-
printk(KERN_INFO "input: %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);
-
-
list_for_each_entry(handler, &input_handler_list, node)
-
input_attach_handler(dev, handler);
-
-
input_wakeup_procfs_readers();
-
-
mutex_unlock(&input_mutex);
-
-
return 0;
-
}
8行:__set_bit(EV_SYN, dev->evbit);设置input_dev支持的事件类型,这里由input_dev的evbit表示,其支持的事件类型如下所示:
-
#define EV_SYN 0x00 /*表示设备支持所有的事件*/
-
#define EV_KEY 0x01 /*键盘或者按键,表示一个键码*/
-
#define EV_REL 0x02 /*鼠标设备,表示一个相对的光标位置结果*/
-
#define EV_ABS 0x03 /*手写板产生的值,其是一个绝对整数值*/
-
#define EV_MSC 0x04 /*其他类型*/
-
#define EV_LED 0x11 /*LED灯设备*/
-
#define EV_SND 0x12 /*蜂鸣器,输入声音*/
-
#define EV_REP 0x14 /*允许重复按键类型*/
-
#define EV_PWR 0x16 /*电源管理事件*/
15-21行:初始化一个timer,用来处理重复延迟的,rep[REP_DELAY]与rep[REP_PERIOD]一般使用默认值,为了自动处理重复按键设置的。
23-27行:判断getkeycode跟setkeycode是否定义,若没有则使用赋默处理函数。input_default_getkeycode是用来获得指定位置的键值,input_default_setkeycode是设置键值。
29-30行:填充input_dev的name,其name以input0-x形式出现在sysfs下面。
32-34行:device_add将input_dev包含的device结构添加到device核心(注册到linux的设备模型中),同时在sysfs文件系统中表现出来。
36-39行:print设备的具体的路径。
41-45、54行:获取互斥题即释放。
47-52行:list_add_tail(&dev->node, &input_dev_list)将input_dev加到input_dev_list链表中,在input_dev_list链表中包含了系统中所有的input_dev设备。list_for_each_entry(handler, &input_handler_list, node)遍历整个input_handler_list链表,找到一个handler的id_table跟input_dev的id一致的handler结构,将此handler跟input_dev结构联系起来。
这里关于input_attach_handler()函数的代码不贴出来了,只简单说说,有兴趣的朋友可以跟踪源码查看,在这个函数里,主要就是handler跟input_dev的匹配。首先判断handler中的blacklist是否赋值,然后判断handler的blacklist跟input_dev的id是否匹配(input_match_device(handler->blacklist, dev)),这里关于blacklist有必要说下,它是一个input_device_id*的类型,它指向input_device_id的一个表,这个表中放的是driver忽略的一些设备。如果没有匹配下面则调用input_match_device(handler->id_table, dev)来匹配handler->id_table跟dev->id的数据,其中handler->id_table也是一个input_device_id类型的指针,保存着driver所支持的设备。最后一旦匹配成功,则调用handler->connect,此涵素将dev跟handler联系起来。
到这里作个小总结,这里注册一个input device,其实就是为此input设备设置一些默认值,同时将这些device挂载到input_dev_list中,然后与挂载在input_handler_list中的handler进行匹配,若匹配成功,则调用handler->connect(),将二者相连。这里同时注意下,handler的connect函数其实调用的是evdev_handler的evdev_connect。evdev_handler跟evdev_connect在evdev.c中有描述。
上面主要谈得是input_dev,其中已经多次出现handler,关于handler的注册是linux自己初始化好的,我们只需要了解它的角色用处。开机时在evdev_init里自动调用input_register_handle,那就看看。一般在子系统初始化后(input.c中加载具体设备驱动之前先input_init),经过注册input类、在proc下建立交互文件、注册cdev之后,会调用open,而这里的oen最后会关联到对应的handler的open,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) {
-
if (input_table[handler->minor >> 5]) {
-
retval = -EBUSY;
-
goto out;
-
}
-
input_table[handler->minor >> 5] = handler;
-
}
-
-
list_add_tail(&handler->node, &input_handler_list);
-
-
list_for_each_entry(dev, &input_dev_list, node)
-
input_attach_handler(dev, handler);
-
-
input_wakeup_procfs_readers();
-
-
out:
-
mutex_unlock(&input_mutex);
-
return retval;
-
}
6-8行:对input_mutex加锁,若失败则返回。
10行:初始化h_list链表,此链表连接与本input_handler相联系的下一个下一个handler。
12-18行:判断handler的fops是否可用,再判断input_table是否可用,最后将handler加到input_table中。
20-24行:将handler加到input_handler_list(包含系统所有的input_handler)中去,再遍历input_dev_list(与上面类似,即先加入到各自的链表中,再与另一条链表的对象匹配),将具体的dev与handler进行匹配,若成功则将二者相连。
25行:相关procfs文件系统内容。
到了这里我们必须面对另外一个结构体了,struct input_handle,前面说那么多,若没有这个桥梁,恐怕都是无用功。input_handle主要用途是用来连接input_dev与input_handler的。我们通过input_register_handle来注册一个新的handle到input子系统中。下面我们看看input_register_handle:
-
int input_register_handle(struct input_handle *handle)
-
{
-
struct input_handler *handler = handle->handler;
-
struct input_dev *dev = handle->dev;
-
int error;
-
-
/*
-
* We take dev->mutex here to prevent race with
-
* input_release_device().
-
*/
-
error = mutex_lock_interruptible(&dev->mutex);
-
if (error)
-
return error;
-
list_add_tail_rcu(&handle->d_node, &dev->h_list);
-
mutex_unlock(&dev->mutex);
-
synchronize_rcu();
-
-
/*
-
* Since we are supposed to be called from ->connect()
-
* which is mutually exclusive with ->disconnect()
-
* we can't be racing with input_unregister_handle()
-
* and so separate lock is not needed here.
-
*/
-
list_add_tail(&handle->h_node, &handler->h_list);
-
-
if (handler->start)
-
handler->start(handle);
-
-
return 0;
-
}
3-4行:定义input_dev与input_handler的结构体,同时指向handle。
14、24行:
list_add_tail_rcu(&handle->d_node, &dev->h_list)将handle加入input_dev的dev->h_list,list_add_tail(&handle->h_node, &handler->h_list)将handle加入到input_handler的handler->h_list.
26-27行:若start存在,则调用之。
可以看出input_register_handle就是将handle挂载到input_dev跟input_handler的h_list链表上,在handler的connect中调用之,下面再说。上面的input_dev、input_handle、input_handler三者之间就这样被绑定联系起来。(可能有点迷糊,仔细推敲上面代码,我们发现input_handle的dev指向input_dev,input_handle的的handler指向input_handler,下面的handle的d_node跟handle的h_node分别指向dev的h_list跟handler的h_list,这样就将三者很好的衔接起来)。
阅读(1038) | 评论(3) | 转发(0) |