LINUX下input子系统的经典图如下:
我们习惯于一种思维,便人从用户空间的系统调用到实质的硬件结果.先从宏观框架上自下至上分析这个过程.上述中注意三个椭圆的意义:
Input driver:具体的设备驱动,如s3c2410ts.c;
Event handler:能够处理具体设备的事件驱动,如通用事件驱动evdev.c、触摸屏事件驱动tsdev.c、鼠标事件驱动程序mousedev.c;
Input core:负责关联Input设备驱动及其相应的事件驱动.
也就是说:
我们的输入设备驱动必须关联一个支持的事件驱动程序,由其支持的事件驱动程序面向用户空间的交互(如字符设备).而输入设备的驱动及其相应的事件驱动程序由通过输入子系统核心探测.输入子系统核心的一重要责任就是为我们的输入设备找到其对应的事件驱动程序,从而实现和用户空间的间接交互.
上面这话非常重要,贯穿用户空间到底层硬件的的层层关系.
实例:
下面的实例是从<<精通LINUX设备驱动程序开发>>第七章摘录的虚拟鼠标.
vms.c
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <linux/platform_device.h>
struct input_dev *vms_input_dev;
static struct platform_device *vms_dev;
static ssize_t write_vms(struct device *dev,struct device_attribute *attr,const char *buffer, size_t count)
{
int x,y;
sscanf(buffer, "%d%d", &x, &y);
input_report_rel(vms_input_dev, REL_X, x);
input_report_rel(vms_input_dev, REL_Y, y);
input_sync(vms_input_dev);
return count;
}
DEVICE_ATTR(coordinates, 0644, NULL, write_vms);
static struct attribute *vms_attrs[] = {&dev_attr_coordinates.attr,NULL};
static struct attribute_group vms_attr_group = {.attrs = vms_attrs, };
int __init vms_init(void)
{
vms_dev = platform_device_register_simple("vms", -1, NULL, 0);
if (IS_ERR(vms_dev))
{
PTR_ERR(vms_dev);
printk("vms_init: error\n");
}
sysfs_create_group(&vms_dev->dev.kobj, &vms_attr_group);
vms_input_dev = input_allocate_device();
if (!vms_input_dev)
{
printk("Bad input_alloc_device()\n");
}
set_bit(EV_REL, vms_input_dev->evbit);
set_bit(REL_X, vms_input_dev->relbit);
set_bit(REL_Y, vms_input_dev->relbit);
input_register_device(vms_input_dev);
printk("Virtual Mouse Driver Initialized.\n");
return 0;
}
void vms_cleanup(void)
{
input_unregister_device(vms_input_dev);
sysfs_remove_group(&vms_dev->dev.kobj, &vms_attr_group);
platform_device_unregister(vms_dev);
return;
}
module_init(vms_init);
module_exit(vms_cleanup);
下面以这个简单的例子分析流程:input_device_driver-->input_code-->event_handle-->user_space.
1.首先,必须先有输入设备.下面语句便是为我们分配了一个名叫vms_input_dev的输入设备:
vms_input_dev = input_allocate_device();
2.因为输入设备驱动程序不直接面向用户空间,而是借助input-core找到支持其的事件驱动程序.要想明白其中关系,就必须深入分析核心函数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;
__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;
dev_set_name(&dev->dev, "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;
}
输入设备要找到其对应的事件驱动程序,通过下面语句实现:
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
从上面的框图可以知道,事件驱动程序有好几个,但是哪个事件驱动程序才算是我们这个输入设备想要的呢?或者说,一个输入设备找到其相应的事件驱动程序的依据是什么?由上面的语句可以知道,遍历事件驱动程序链表中每个元素(即每个事件驱动程序)查看哪个事件驱动程序才是我们想要的.因此,接下来要理解两点:一是事件驱动的链表哪来的?二是一个输入设备找到其对应的事件处理程序的依据是什么:
2-1.事件驱动链表input_handler_list是从哪来的?
input_handler_list是drivers/input/input.c一个全局变量.如下:
static LIST_HEAD(input_handler_list);
内核中每一种事件驱动都被注册进内核,如mousedev、evdev、joydev、keyboard等.我们要注册一个输入设备驱动时,是需要明确我们的设备驱动是被哪个事件驱动所支持的(下面的代码分析中可以看到这一点).
input_handler_list是被用来组织事件驱动的.如:
evdev.c:
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,
};
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
joydev.c:
static struct input_handler joydev_handler = {
.event = joydev_event,
.connect = joydev_connect,
.disconnect = joydev_disconnect,
.fops = &joydev_fops,
.minor = JOYDEV_MINOR_BASE,
.name = "joydev",
.id_table = joydev_ids,
.blacklist = joydev_blacklist,
};
static int __init joydev_init(void)
{
return input_register_handler(&joydev_handler);
}
keyboard.c:
static struct input_handler kbd_handler = {
.event = kbd_event,
.connect = kbd_connect,
.disconnect = kbd_disconnect,
.start = kbd_start,
.name = "kbd",
.id_table = kbd_ids,
};
... ...;
int __init kbd_init(void)
{
int i;
int error;
for (i = 0; i < MAX_NR_CONSOLES; i++) {
kbd_table[i].ledflagstate = KBD_DEFLEDS;
kbd_table[i].default_ledflagstate = KBD_DEFLEDS;
kbd_table[i].ledmode = LED_SHOW_FLAGS;
kbd_table[i].lockstate = KBD_DEFLOCK;
kbd_table[i].slockstate = 0;
kbd_table[i].modeflags = KBD_DEFMODE;
kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
}
error = input_register_handler(&kbd_handler);
if (error)
return error;
tasklet_enable(&keyboard_tasklet);
tasklet_schedule(&keyboard_tasklet);
return 0;
}
mousedev.c:
static struct input_handler mousedev_handler = {
.event = mousedev_event,
.connect = mousedev_connect,
.disconnect = mousedev_disconnect,
.fops = &mousedev_fops,
.minor = MOUSEDEV_MINOR_BASE,
.name = "mousedev",
.id_table = mousedev_ids,
};
... ...;
static int __init mousedev_init(void)
{
int error;
mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX);
if (IS_ERR(mousedev_mix))
return PTR_ERR(mousedev_mix);
error = input_register_handler(&mousedev_handler);
if (error) {
mousedev_destroy(mousedev_mix);
return error;
}
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
error = misc_register(&psaux_mouse);
if (error)
printk(KERN_WARNING "mice: could not register psaux device, "
"error: %d\n", error);
else
psaux_registered = 1;
#endif
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
return 0;
}
下面我们来查看一下函数input_register_handler()源码.展开如下:
/**
* input_register_handler - register a new input handler
* @handler: handler to be registered
*
* This function registers a new input handler (interface) for input
* devices in the system and attaches it to all input devices that
* are compatible with the 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;
}
在上述代码中,通过下面语句把自己的事件驱动程序(如evdev)挂进链表input_handler_list:
list_add_tail(&handler->node, &input_handler_list);
2-2.一个输入设备找到其对应的事件驱动层的依据是什么?
上述我们已经知道,内核目前所支持的事件驱动均被组织进链表input_handler_list,有多种事件驱动,而我们的输入设备到底是如何探测到其所对应的事件驱动呢?比如上述我们的vms_input_dev,到底支持这个输入设备驱动的事件驱动是哪个,回到函数input_register_device(),见下面代码:
int input_register_device(struct input_dev *dev)
{
... ...;
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
... ...;
}
输入设备驱动找到其所支持的事件驱动的核心函数理所当然是input_attach_handler(),其源码位于drivers/input/input.c.展开如下:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;
id = input_match_device(handler->id_table, dev);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
可见,一个输入设备找到其相应的事件驱动的核心函数是input_match_device(),参数dev即为我们的vms_input_dev.展开函数input_match_device():
static const struct input_device_id *input_match_device(const struct input_device_id *id,
struct input_dev *dev)
{
int i;
for (; 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);
return id;
}
return NULL;
}
可见,一个输入设备找到其相应的事件驱动程序的依据是这个输入设备的总线类型(dev->id.bustype)、厂家(dev->id.vendor)、产品(dev->id.product)、版本(dev->id.version).回到函数input_attach_handler(),定位到如下语句:
id = input_match_device(handler->id_table, dev);
if (!id)
return -ENODEV;
[注:]语句if (handler->blacklist && input_match_device(handler->blacklist, dev))是joydev类设备专用.不作分析.目前内核支持的事件驱动有:
apmpower_handler、evbug_handler、evdev_handler、rfkill_handler、joydev_handler、kbd_handler、mousedev_handler
evbug_handler作输入子系统调试用,不预理会.剩下的除了evdev_handler之外,都有很丰富的id_table内容.如下:
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,
};
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
而我们的vms_input_dev并没有太多的匹配要求.由vms_input_dev的初始化代码:
46. set_bit(EV_REL, vms_input_dev->evbit);
47. set_bit(REL_X, vms_input_dev->relbit);
48. set_bit(REL_Y, vms_input_dev->relbit);
关于这个vms_input_dev设备的id域并没有指定(如上面指定总线类型、厂家、产品、版本),因此,在函数input_match_device()里面的if条件得不到满足.因此,只能根据下面的条件匹配:
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);
MATCH_BIT()是一个宏,需要在当前展开才可以理解代码.if条件语句里面是一些厂家比较特殊的输入设备的要求,而上述的代码是向内核注册的每一个输入设备都路由的,这相当于所有输入设备的公用代码.用来匹配事件类型.内核支持的事件类型如下:
#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 /*电源管理事件*/
每个内核输入设备均内置一个数组来记录此输入设备支持哪些事件,如下:
struct input_dev
{
... ...;
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)];
... ...;
}
我们的vms_input_dev作为一个输入设备的初始化时,会根据我们的设备特性(如支持按键)会对这些数组进行初始化:
set_bit(EV_REL,vms_input_dev->evbit);
set_bit(REL_X,vms_input_dev->relbit);
set_bit(REL_Y,vms_input_dev->relbit);
上述代码表明,我们这个vms_input_dev的事件类型是EV_REL.同一个事件类型下可以有多种事件编码.比如我们的vm_input_dev是鼠标类,支持X坐标和Y坐标.如果还要支持上按钮单击事件,还需要添加下面两行代码:
set_bit(EV_KEY,vms_input_dev->evbit);
set_bit(BTN_0,vms_input_dev->keybit);
每一个事件编码和具体的事件类型是关联的,比如上面的REL_X、REL_Y和事件EV_REL关联;BTN_0和事件EV_KEY关联.见include/linux/input.h下,按照键盘、相对坐标、绝对坐标等进行了归类.因此,我们的vms_input_dev设备最终会关联到事件驱动mousedev.如下:
static struct input_handler mousedev_handler = {
.event = mousedev_event,
.connect = mousedev_connect,
.disconnect = mousedev_disconnect,
.fops = &mousedev_fops,
.minor = MOUSEDEV_MINOR_BASE,
.name = "mousedev",
.id_table = mousedev_ids,
};
其id_table域如下:
static const struct input_device_id mousedev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
.relbit = { BIT_MASK(REL_X) | BIT_MASK(REL_Y) },
}, /* A mouse like device, at least one button,
two relative axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
.relbit = { BIT_MASK(REL_WHEEL) },
}, /* A separate scrollwheel */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
}, /* A tablet like device, at least touch detection,
two absolute axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
.keybit = { [BIT_WORD(BTN_TOOL_FINGER)] =
BIT_MASK(BTN_TOOL_FINGER) },
.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_PRESSURE) |
BIT_MASK(ABS_TOOL_WIDTH) },
}, /* A touchpad */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
}, /* Mouse-like device with absolute X and Y but ordinary
clicks, like hp ILO2 High Performance mouse */
{ }, /* Terminating entry */
};
接下来我们看mousedev的connect函数.因为匹配完将执行下面语句:
error = handler->connect(handler, dev, id);
这个函数的重要意义是向用户空间暴露了设备节点.下面分析函数mousedev_connect():
static int mousedev_connect(struct input_handler *handler,
struct input_dev *dev,
const struct input_device_id *id)
{
struct mousedev *mousedev;
int minor;
int error;
for (minor = 0; minor < MOUSEDEV_MINORS; minor++)
if (!mousedev_table[minor])
break;
if (minor == MOUSEDEV_MINORS) {
printk(KERN_ERR "mousedev: no more free mousedev devices\n");
return -ENFILE;
}
mousedev = mousedev_create(dev, handler, minor);
if (IS_ERR(mousedev))
return PTR_ERR(mousedev);
error = mixdev_add_device(mousedev);
if (error) {
mousedev_destroy(mousedev);
return error;
}
return 0;
}
其中,函数mousedev_create()为用户空间创建了设备节点,展开如下:
static struct mousedev *mousedev_create(struct input_dev *dev,
struct input_handler *handler,
int minor)
{
... ...;
if (minor == MOUSEDEV_MIX)
dev_set_name(&mousedev->dev, "mice");
else
dev_set_name(&mousedev->dev, "mouse%d", minor);
... ...;
mousedev->dev.class = &input_class;
if (dev)
mousedev->dev.parent = &dev->dev;
mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor);
mousedev->dev.release = mousedev_free;
device_initialize(&mousedev->dev);
... ...;
error = device_add(&mousedev->dev);
... ...;
}
上述的代码帮我们生成了用户空间的设备节点,如/dev/input/mouse0.当然,单纯靠这里的代码是不可能生成/dev/input/xxx设备节点的,在上之前,必须向内核注册输入子系统--这个是整个输入子系统存在的根基.
drivers/input/input.c:
subsys_initcall(input_init);
module_exit(input_exit);
展开函数input_init()函数,可以看到我们/dev/下目录"input"的痕迹:
static int __init input_init(void)
{
int err;
input_init_abs_bypass();
err = class_register(&input_class);
if (err) {
printk(KERN_ERR "input: unable to register input_dev class\n");
return err;
}
err = input_proc_init();
if (err)
goto fail1;
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
if (err) {
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
好了,至此,用户空间的设备节点有了,但是对用户空间来说,最直接的系统调用在哪?由上述函数input_init()可以看到,最直接的系统调用集是input_fops.展开这个全局变量:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
里面只有一个open的系统调用.可见,后续的一系列读写(其实输入设备意思很明白,基本上只有读操作)操作都是在这个open动作找到其相应的事件驱动程序去处理具体的输入设备.展开函数input_open_file():
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
lock_kernel();
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5];
if (!handler || !(new_fops = fops_get(handler->fops))) {
err = -ENODEV;
goto out;
}
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
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);
out:
unlock_kernel();
return err;
}
由于这是输入子系统核心公共代码,每一个/dev/input/xxx设备节点的open动作,均路由此处.在这里找到相应的事件驱动层的fops重载.以后我们要对某具体设备进行系统调用的时候,其实际调用的就是此设备对应的事件驱动层的fops.比如我们的具体设备匹配的事件驱动是mousedev,则mousedev_fops会被重载.如下:
static const struct file_operations mousedev_fops = {
.owner = THIS_MODULE,
.read = mousedev_read,
.write = mousedev_write,
.poll = mousedev_poll,
.open = mousedev_open,
.release = mousedev_release,
.fasync = mousedev_fasync,
};
要找到我们相应的事件驱动程序的探测方法是通过全局数组input_table.见上述代码:
handler = input_table[iminor(inode) >> 5];
if (!handler || !(new_fops = fops_get(handler->fops))) {
err = -ENODEV;
goto out;
}
可见,是通过/dev/input/xxx设备节点的设备号找到其相应的事件处理程序,并获取它的具体操作集来重载input_fops.因此,此处我们需要明白:事件驱动层如何嵌入到input_table这个全局数组.
以我们的vms_input_dev为例,继续往下分析.根据上面的分析,我们知道我们这个vms_input_dev找到的事件驱动程序为mousedev.我们来看一下这个事件驱动层和输入子系统核心交互的部分源码.
drivers/input/mousedev.c
module_init(mousedev_init);
这是入口函数,展开函数mousedev_init,有一个很重要的函数就是input_register_handler().向内核注册这个事件驱动程序:
static int __init mousedev_init(void)
{
int error;
mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX);
if (IS_ERR(mousedev_mix))
return PTR_ERR(mousedev_mix);
error = input_register_handler(&mousedev_handler);
if (error) {
mousedev_destroy(mousedev_mix);
return error;
}
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
error = misc_register(&psaux_mouse);
if (error)
printk(KERN_WARNING "mice: could not register psaux device, "
"error: %d\n", error);
else
psaux_registered = 1;
#endif
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
return 0;
}
把函数input_register_handler()展开,里面有一个很重要的全局数组:input_table,用来记录所有注册进内核的事件驱动程序.如下:
/**
* input_register_handler - register a new input handler
* @handler: handler to be registered
*
* This function registers a new input handler (interface) for input
* devices in the system and attaches it to all input devices that
* are compatible with the 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;
}
在此,我们看到了mousedev这个事件驱动嵌进全局数组input_table的痕迹.像evdev等别的事件驱动程序同样可以看到类似的代码.当用户空间open /dev/input/xxx设备节点时,首先面向的是input_fops下的open,再由input_fops下的open域根据设备节点去重载具体的事件驱动程序的open.对于我们mousedev这个事件驱动程序,它的fops如下(见drivers/input/mousedev.c):
static const struct file_operations mousedev_fops = {
.owner = THIS_MODULE,
.read = mousedev_read,
.write = mousedev_write,
.poll = mousedev_poll,
.open = mousedev_open,
.release = mousedev_release,
.fasync = mousedev_fasync,
};
3. 下面进行小结,最后以usb鼠标这个具体的实例进行数据交互的具体分析:
1).从用户空间看到的只是/dev/input/xxx设备节点,当用户空间去试图操作一个设备时会进行open动作,此时陷入系统调用,被输入子系统核心(input_fops)这个"一级虚操作集"去响应;
2).输入子系统核心相当于一颗树的根,上面的树枝就是挂在这颗树上的事件驱动.这种组织方式是通过全局数组input_table实现.输入子系统核心会根据用户打开的设备节点找到此设备相应的事件驱动程序及绑定的"二级虚操作集",这里的"二级虚操作集"意指如果此事件驱动支持具体设备有封装有自己的操作集,即去重载.
3).事件驱动层负责和具体的输入设备(如鼠标、触摸屏)交互.具体的输入设备通过自身的特性初始化,通过函数input_register_device()找到能处理它的事件驱动;
4).如果某输入设备有自己的操作集,如usbmouse有自己的open函数,将会被重载调用.见下面分析:
用户空间
user space:
open /dev/input/xxx
输入子系统核心
-->
input_core:(drivers/input/input.c)
input_open_file()
事件驱动层
-->
xxx_handler[for example mousedev]:(drivers/input/mousedev.c)
mousedev_open()
->mousedev_open_device()
->input_open_device()
->retval = dev->open(dev)
具体设备层
-->
xxx_input_dev[for example usbmouse]:(drivers/hid/usbhid/usbmouse.c)
input_dev->open = usb_mouse_open;
物理硬件层
-->
usb hid class protocol[usbmouse]
4.usb鼠标实例:
见drivers/hid/usbhid/usbmouse.c
我们主要关注usb鼠标作为input设备部分.见函数usb_mouse_probe:
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct input_dev *input_dev;
input_dev = input_allocate_device();
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
BIT_MASK(BTN_EXTRA);
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
input_set_drvdata(input_dev, mouse);
input_dev->open = usb_mouse_open;
input_dev->close = usb_mouse_close;
error = input_register_device(mouse->dev);
}
可见,根据usbmouse分离出来的input device的初始化,将会匹配到mousedev这一事件驱动(具体匹配过程见上面vms_input_dev分析).当我们点击鼠标左右键或者滑动鼠标时,USB电缆上将产生USB中断,函数usb_mouse_irq()将会被调度.展开函数usb_mouse_irq():
static void usb_mouse_irq(struct urb *urb)
{
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = mouse->dev;
int status;
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
input_report_rel(dev, REL_X, data[1]);
input_report_rel(dev, REL_Y, data[2]);
input_report_rel(dev, REL_WHEEL, data[3]);
input_sync(dev);
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}
上述和输入子系统相关的三个核心函数:input_report_key()、input_report_rel()、input_sync().其中,input_report_key()对应按键类事件;input_report_rel()对应有光标位置生成的事件.对比两者的源码可知,它们均调用函数input_event(),唯一的不同只是事件类型不同:一个是EV_KEY另一个是EV_REL.类似的函数还有input_report_abs()、input_report_switch()等.详见include/linux/input.h.下面以点击鼠标左键为例看消息如何流窜到用户空间.见上述代码中:
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
[注:]data为USB鼠标通过USB电缆传递过来的物理电平(即实现的硬件动作).
4-1.input_report_key()函数:
调用流程如下:
usbmouse:
input_report_key(dev, BTN_LEFT, data[0] & 0x01);//drivers/hid/usbhid/usbmouse.c
-->
input_core:
input_event(dev, EV_KEY, code, !!value);//include/linux/input.h
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
... ...;
input_handle_event(dev, type, code, value);
... ...;
}
->
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
switch (type)
{
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;
}
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
->
static void input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
}
-->mousedev_handler
static void mousedev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
switch (type) {
case EV_KEY:
if (value != 2) {
if (code == BTN_TOUCH &&
test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
mousedev_touchpad_touch(mousedev, value);
else
mousedev_key_event(mousedev, code, value);
}
break;
}
->
static void mousedev_key_event(struct mousedev *mousedev,
unsigned int code, int value)
{
int index;
switch (code) {
case BTN_TOUCH:
case BTN_0:
case BTN_LEFT: index = 0; break;
}
if (value) {
set_bit(index, &mousedev->packet.buttons);
}
}
将USB电缆上的鼠标左键信息保存在变量value,再由此变量路由到mousedev事件驱动程序,片选index保存在mousedev->packet.buttons.
4-2.input_sync()函数:
当我们向内核输入子系统汇报完按键等动作后,都会调用此函数.只有此函数执行完成,用户空间的读动作才会有效.
调用流程:
input_sync(dev);
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
->
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)) {
input_handle_event(dev, type, code, value);
}
}
->
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
switch (type) {
case EV_SYN:
switch (code) {
case SYN_REPORT:
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
}
break;
}
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
->
static void input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
}
->
static void mousedev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
switch (type) {
case EV_SYN:
if (code == SYN_REPORT) {
mousedev_notify_readers(mousedev, &mousedev->packet);
mousedev_notify_readers(mousedev_mix, &mousedev->packet);
mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
mousedev->packet.abs_event = 0;
}
break;
}
重点函数是mousedev_notify_readers().展开如下:
static void mousedev_notify_readers(struct mousedev *mousedev,
struct mousedev_hw_data *packet)
{
struct mousedev_client *client;
struct mousedev_motion *p;
unsigned int new_head;
int wake_readers = 0;
rcu_read_lock();
list_for_each_entry_rcu(client, &mousedev->client_list, node) {
/* Just acquire the lock, interrupts already disabled */
spin_lock(&client->packet_lock);
p = &client->packets[client->head];
if (client->ready && p->buttons != mousedev->packet.buttons) {
new_head = (client->head + 1) % PACKET_QUEUE_LEN;
if (new_head != client->tail) {
p = &client->packets[client->head = new_head];
memset(p, 0, sizeof(struct mousedev_motion));
}
}
if (packet->abs_event) {
p->dx += packet->x - client->pos_x;
p->dy += packet->y - client->pos_y;
client->pos_x = packet->x;
client->pos_y = packet->y;
}
client->pos_x += packet->dx;
client->pos_x = client->pos_x < 0 ?
0 : (client->pos_x >= xres ? xres : client->pos_x);
client->pos_y += packet->dy;
client->pos_y = client->pos_y < 0 ?
0 : (client->pos_y >= yres ? yres : client->pos_y);
p->dx += packet->dx;
p->dy += packet->dy;
p->dz += packet->dz;
p->buttons = mousedev->packet.buttons;
if (p->dx || p->dy || p->dz ||
p->buttons != client->last_buttons)
client->ready = 1;
spin_unlock(&client->packet_lock);
if (client->ready) {
kill_fasync(&client->fasync, SIGIO, POLL_IN);
wake_readers = 1;
}
}
rcu_read_unlock();
if (wake_readers)
wake_up_interruptible(&mousedev->wait);
}
留意到字眼:
client->ready client->buffer mousedev->exist mousedev->wait
及语句:
p->buttons = mousedev->packet.buttons;
wake_up_interruptible(&mousedev->wait);
其中,p->buttons = mousedev->packet.buttons保存的是USB电缆上传过来的USB鼠标信息.现在转存到p->buttons.p为指向client->packets的指针.见上述语句:
p = &client->packets[client->head];
这些动作,将会是用户空间调用read()系统调用抓取数据的"许可证".
4-3.read()的系统调用.
当我们去获取一个输入设备的数据(如鼠标的左键),对应的用户空间的系统调用就是read()函数.而对于mousedev这一事件驱动,对应的read系统调用是:mousedev_read().展开此函数如下:
static ssize_t mousedev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct mousedev_client *client = file->private_data;
struct mousedev *mousedev = client->mousedev;
signed char data[sizeof(client->ps2)];
int retval = 0;
if (!client->ready && !client->buffer && mousedev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(mousedev->wait,
!mousedev->exist || client->ready || client->buffer);
if (retval)
return retval;
if (!mousedev->exist)
return -ENODEV;
spin_lock_irq(&client->packet_lock);
if (!client->buffer && client->ready) {
mousedev_packet(client, client->ps2);
client->buffer = client->bufsiz;
}
if (count > client->buffer)
count = client->buffer;
memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);
client->buffer -= count;
spin_unlock_irq(&client->packet_lock);
if (copy_to_user(buffer, data, count))
return -EFAULT;
return count;
}
语句:
mousedev_packet(client, client->ps2);
此函数主要对参数client->ps2更新到物理电平上的数值.展开mousedev_packet()函数保留相关代码如下:
static void mousedev_packet(struct mousedev_client *client,
signed char *ps2_data)
{
struct mousedev_motion *p = &client->packets[client->tail];
ps2_data[0] = 0x08 |
((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
ps2_data[1] = mousedev_limit_delta(p->dx, 127);
ps2_data[2] = mousedev_limit_delta(p->dy, 127);
p->dx -= ps2_data[1];
p->dy -= ps2_data[2];
switch (client->mode) {
default:
ps2_data[0] |=
((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
p->dz = 0;
client->bufsiz = 3;
break;
}
}
在函数mousedev_notify_readers()分析中,代码如下:
p = &client->packets[client->head];
p->dx += packet->dx;
p->dy += packet->dy;
p->dz += packet->dz;
p->buttons = mousedev->packet.buttons;
即client->packets保存了和USB鼠标信息(如坐标).现在通过client把USB鼠标信息(这里是左键)保存了client->ps2域.
接下来的语句便是把USB鼠标左键信息传递到用户空间:
memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);
client->buffer -= count;
spin_unlock_irq(&client->packet_lock);
if (copy_to_user(buffer, data, count))
return -EFAULT;
5.小结:
5-1.当用户空间open一下输入设备节点时(/dev/input/xxx),直接被input核心层的input_open_file()相应执行;
5-2.input_open_file()函数根据设备节点的设备号探测到支持此输入设备的fops重载;
5-3.如果具体的输入设备有自己的fops,将会重载事件驱动层的fops;
5-4.一个输入设备探测到其相应的事件驱动层的重要是依据事件类型数组(如evbit、relbit等);
5-5.当输入设备有动作要向上层汇报时,汇报完要sync一个动作之后,用户空间调用read才会有实际的动作数据.