Input subsystem系统实现分析
Edwin Rong edwinrong@mxic.com.cn
本文将简要介绍Input system系统,通过鼠标驱动实现过程具体介绍Input System系统的实现。
Input subsystem 系统架构
Input subsystem由Drivers、Input Core和Handlers三个组件构成。Drivers负责硬件和Input Core之间的操作,Handlers负责应用程序同Input Core之间的通信。Input Core实现硬件设备的注册以及抽象设备集的注册。
硬件向应用程序反馈信息的路径为: Driversà Input Core à Handlers à Applications
涉及文件
Drivers/input/input.c
Include/linux/input.h
Drivers/input/mousedev.c
Drivers/input/mouse/amimouse.c
主要数据结构
Struct input_dev (Include/linux/input.h)
驱动层具体输入设备的描述结构,在具体硬件设备的驱动模块中定义及初始化。
如:drivers/input/mouse/amimouse.c中
static struct input_dev *amimouse_dev; amimouse_dev 在amimouse_init()中初始化。
Struct input_handler
逻辑设备指的是同一类设备的抽象,如鼠标、操纵杆、键盘等。逻辑设备结构体在drivers/input/mousedev.c中定义及初始化。
在内核中,input_dev表示一个input设备;input_handler来表示input设备的interface。
Struct input_handle
用来创建驱动层Dev和Handler链表的链表项结构。
系统实现
Input Subsystem系统主要由(drivers/input/input.c) input_register_device()和input_register_handler()两个函数实现。Handler组件通过input_register_handler函数将逻辑设备操作结构体注册到input_handler_list链表中;drivers组件通过input_register_device组件将直接对具体硬件的操作结构体注册到input_dev_list链表中。系统可以注册8个逻辑设备结构体和256个具体硬件设备结构体。
下面结合鼠标驱动具体介绍下这两个函数。
Drivers/input/mouse/amimouse.c 中amimouse_init()函数首先初始化amimouse_dev硬件设备结构体,然后调用input_register_device(amimouse_dev) 函数将amimouse硬件设备注册到input_dev_list链表中。
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handle *handle;
struct input_handler *handler;
struct input_device_id *id;
const char *path;
int error;
if (!dev->dynalloc) {
printk(KERN_WARNING "input: device %s is statically allocated, will not register\n"
"Please convert to input_allocate_device() or contact dtor_core@ameritech.net\n",
dev->name ? dev->name : "");
return -EINVAL;
}
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;
}
INIT_LIST_HEAD(&dev->h_list); //初始化操作函数
list_add_tail(&dev->node, &input_dev_list); //将硬件设备结构体添加到input_dev_list链
//表的结尾处。
dev->cdev.class = &input_class;
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
error = class_device_add(&dev->cdev);
if (error)
return error;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group);
if (error)
goto fail1;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
if (error)
goto fail2;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
if (error)
goto fail3;
__module_get(THIS_MODULE);
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
list_for_each_entry(handler, &input_handler_list, node)
//通过dev(这里指的是amimouse_dev)结构体的node成员,匹配input_handler_list中//相应的处理函数。
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev))) //设备匹配,返回id值
if ((handle = handler->connect(handler, dev, id))) {
input_link_handle(handle); //建立设备和处理函数之间的连接
if (handler->start)
handler->start(handle);
}
input_wakeup_procfs_readers();
return 0;
fail3: sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
fail2: sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
fail1: class_device_del(&dev->cdev);
return error;
}
Drivers/input/mousedev.c 中mousedev_init()函数调用
input_register_handler(&mousedev_handler) 函数将mousedev_handler鼠标类设备处理函数结构体注册到input_handler_list链表中。
void input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
struct input_handle *handle;
struct input_device_id *id;
if (!handler)
return;
INIT_LIST_HEAD(&handler->h_list); //初始化处理函数链表
if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list); //将处理函数结构体添加到
//input_handler_list链表中
list_for_each_entry(dev, &input_dev_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id))) {
input_link_handle(handle);
if (handler->start)
handler->start(handle);
}
input_wakeup_procfs_readers();
}
每个input_dev和input_handler是要关联上才能工作的,在注册input_dev或者input_handler的时候,都会遍历上面的列表,找到相匹配的,然后调用 input_handler的connect函数来将它们联系到一起。通常在input_handler的connect函数中,就会创建input_handle,input_handle就是负责将input_dev和input_handler联系在一起的,如图所示:
这里需要额外说明一下的是: input_dev中的h_node是input_handle链表的list节点,也就是说,一个input_dev,可以对应多个input_handle.
当设备产生 input event的时候,例如按下了一个键,驱动就会调用input_handler中的event函数,同时,如果input_dev支持的话,也会调用input_dev的event函数。这样,设备产生的事件就会被驱动记录下来。当用户层的程序需要获知这些事件的时候,会调用input_handler中的struct file_operations *fops中的相应函数,例如read 等等。
设备文件的操作函数
Open()函数
输入设备子系统通过register_chrdev(INPUT_MAJOR, "input", &input_fops)注册到系统中,文件操作结构体input_fops包括了对open()函数的初始化。
static struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
应用程序打开设备文件的时候,首先要执行input_open_file()函数。我们来看一下input_open_file()函数作了些什么。
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5]; //根据次设备号在input_table[]数组中查询相对应的处理函数。Input_table[]在input_register_handler()函数中初始化。
const struct file_operations *old_fops, *new_fops = NULL;
int err;
/* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops))) //获取handler结构体中的文件处理成//员fops。对于鼠标类设备来讲,这里获得的就是drivers/input/mousedev.c文件中//mousedev_handler结构体中的鼠标操作函数结构体。
New_fops=mousedev_fops
通过这个结构体就可以调用鼠标类的其他操作函数(read, write……)。也就是说执行input_open_file()也就相当于执行mousedev_fops.open()函数,也就是执行鼠标类设备文件mousedev_open()函数。
static int mousedev_open(struct inode * inode, struct file * file)
{
struct mousedev_list *list;
struct input_handle *handle;
struct mousedev *mousedev;
int i;
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
if (imajor(inode) == MISC_MAJOR)
i = MOUSEDEV_MIX;
else
#endif
i = iminor(inode) - MOUSEDEV_MINOR_BASE; //获取次设备号
if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
return -ENODEV;
if (!(list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
return -ENOMEM;
spin_lock_init(&list->packet_lock);
list->pos_x = xres / 2;
list->pos_y = yres / 2;
list->mousedev = mousedev_table[i]; //通过次设备号获取具体鼠标设备的信息结构体
list_add_tail(&list->node, &mousedev_table[i]->list);
file->private_data = list;
if (!list->mousedev->open++) {
if (list->mousedev->minor == MOUSEDEV_MIX) {
list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
mousedev = handle->private;
if (!mousedev->open && mousedev->exist)
input_open_device(handle);//打开具体设备
}
} else
if (!mousedev_mix.open && list->mousedev->exist)
input_open_device(&list->mousedev->handle);
}
return 0;
}
return -ENODEV;
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {
fops_put(new_fops);
return -ENODEV;
}
old_fops = file->f_op;
file->f_op = new_fops;
err = new_fops->open(inode, file);//打开设备文件
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}