谭卓 车祸是女明星吗:Linux input子系统分析(网络转载) - 键盘棒棒的日志 - 网易博客

来源:百度文库 编辑:九乡新闻网 时间:2024/04/30 20:44:02
Linux input子系统分析(网络转载)
linux内核编程(2009-05-14 16:24:51阅读562评论0  字号:大中小
因为最近要修改一个键盘的驱动。所以开始看了一下键盘驱动的结构。真是遗憾,我不得不把input子系统的结构给搞清楚,虽然有朋友说键盘驱动不一定要注册到input子系统中。当然,我看input子系统不仅仅是为这个键盘驱动,现在能避开以后也还是会遇到,所以索性就看看吧。在网上找了不少资料,下面就和大家分享一下:
(转载http://blog.csdn.net/colorant/archive/2007/04/12/1561837.aspx)
输入子系统架构Overview
输入子系统(Input Subsystem)的架构如下图所示

输入子系统由 输入子系统核心层( Input Core ),驱动层和事件处理层(EventHandler)三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过 Driver ->InputCore -> Eventhandler -> userspace 的顺序到达用户空间传给应用程序。
其中Input Core 即 Input Layer 由 driver/input/input.c及相关头文件实现。对下提供了设备驱动的接口,对上提供了Event Handler层的编程接口。
1.1 主要数据结构
表 1     Input Subsystem main data structure
数据结构
用途
定义位置
具体数据结构的分配和初始化
Struct input_dev
驱动层物理Input设备的基本数据结构
Input.h
通常在具体的设备驱动中分配和填充具体的设备结构
Struct Evdev
Struct Mousedev
Struct Keybdev…
Event Handler层逻辑Input设备的数据结构
Evdev.c
Mousedev.c
Keybdev.c
Evdev.c/Mouedev.c …中分配
Struct Input_handler
Event Handler的结构
Input.h
Event Handler层,定义一个具体的Event Handler。
Struct Input_handle
用来创建驱动层Dev和Handler链表的链表项结构
Input.h
Event Handler层中分配,包含在Evdev/Mousedev…中。
1.2 输入子系统架构示例图

图2  输入子系统架构示例图
2 输入链路的创建过程
由于input子系统通过分层将一个输入设备的输入过程分隔为独立的两部份:驱动到Input Core,Input Core到Event Handler。所以整个链路的这两部分的接口的创建是独立的。
2.1 硬件设备的注册
驱动层负责和底层的硬件设备打交道,将底层硬件对用户输入的响应转换为标准的输入事件以后再向上发送给Input Core。
驱动层通过调用Input_register_device函数和Input_unregister_device函数来向输入子系统中注册和注销输入设备。
这两个函数调用的参数是一个Input_dev结构,这个结构在driver/input/input.h中定义。驱动层在调用Input_register_device之前需要填充该结构中的部分字段
#include
#include
#include
MODULE_LICENSE("GPL");
struct input_dev ex1_dev;
static int __init ex1_init(void)
{
/* extra safe initialization */
memset(&ex1_dev, 0, sizeof(struct input_dev));
init_input_dev(&ex1_dev);
/* set up descriptive labels */
ex1_dev.name = "Example 1 device";
/* phys is unique on a running system */
ex1_dev.phys = "A/Fake/Path";
ex1_dev.id.bustype = BUS_HOST;
ex1_dev.id.vendor = 0x0001;
ex1_dev.id.product = 0x0001;
ex1_dev.id.version = 0x0100;
/* this device has two keys (A and B) */
set_bit(EV_KEY, ex1_dev.evbit);
set_bit(KEY_B, ex1_dev.keybit);
set_bit(KEY_A, ex1_dev.keybit);
/* and finally register with the input core */
input_register_device(&ex1_dev);
return 0;
}
其中比较重要的是evbit字段用来定义该输入设备可以支持的(产生和响应)的事件的类型。
包括:
EV_RST   0x00  Reset
EV_KEY   0x01  按键
EV_REL   0x02  相对坐标
EV_ABS   0x03  绝对坐标
EV_MSC   0x04  其它
EV_LED   0x11   LED
EV_SND   0x12  声音
EV_REP   0x14  Repeat
EV_FF   0x15  力反馈
一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件,比如EV_KEY事件,支持哪些按键等。
2.2 Event Handler层
2.2.1 注册Input Handler
驱动层只是把输入设备注册到输入子系统中,在驱动层的代码中本身并不创建设备结点。应用程序用来与设备打交道的设备结点的创建由EventHandler层调用Input core中的函数来实现。而在创建具体的设备节点之前,EventHandler层需要先注册一类设备的输入事件处理函数及相关接口
以MouseDev Handler为例:
static struct input_handler mousedev_handler = {
event:  mousedev_event,
connect:  mousedev_connect,
disconnect: mousedev_disconnect,
fops:  &mousedev_fops,
minor:  MOUSEDEV_MINOR_BASE,
};
static int __init mousedev_init(void)
{
input_register_handler(&mousedev_handler);
memset(&mousedev_mix, 0, sizeof(struct mousedev));z
init_waitqueue_head(&mousedev_mix.wait);
mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
mousedev_mix.exist = 1;
mousedev_mix.minor = MOUSEDEV_MIX;
mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE);
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
return 0;
}
在Mousedev_init中调用input.c中定义的input_register_handler来注册一个鼠标类型的Handler. 这里的Handler不是具体的用户可以操作的设备,而是鼠标类设备的统一的处理函数接口。
2.2.2 设备节点的创建
接下来,mousedev_init函数调用input_register_minor注册一个通用mice设备,这才是与用户相关联的具体的设备接口。然而这里在init函数中创建一个通用的Mice设备只是鼠标类EventHandler层的特例。在其它类型的EventHandler层中,并不一定会创建一个通用的设备。
标准的流程见是硬件驱动向Input子系统注册一个硬件设备后,在input_register_device中调用已经注册的所有类型的InputHandler的connect函数,每一个具体的Connect函数会根据注册设备所支持的事件类型判断是否与自己相关,如果相关就调用input_register_minor创建一个具体的设备节点。
void input_register_device(struct input_dev *dev)
{
……
while (handler) {
if ((handle = handler->connect(handler, dev)))
input_link_handle(handle);
handler = handler->next;
}
}
此外如果已经注册了一些硬件设备,此后再注册一类新的Input Handler,则同样会对所有已注册的Device调用新的Input Handler的Connect函数已确定是否需要创建新的设备节点:
void input_register_handler(struct input_handler *handler)
{
……
while (dev) {
if ((handle = handler->connect(handler, dev)))
input_link_handle(handle);
dev = dev->next;
}
}
从上面的分析中可以看到一类Input Handler可以和多个硬件设备相关联,创建多个设备节点。而一个设备也可能与多个Input Handler相关联,创建多个设备节点。
直观起见,物理设备,Input Handler,逻辑设备之间的多对多关系可见下图:

图3  物理设备,Input Handler,逻辑设备关系图
当看了上面还是有点模糊的时候那么就看看下面这段资料吧,然后在结合一起看就很清楚的知道了input子系统 的结构了
转载(http://blog.chinaunix.net/u2/61800/showart_1331639.html)
先介绍几个重要的数据结构:
input_handler:Mousedev.c中注册,是一类设备的驱动器,它可以为若干个input_dev提供驱动。
input_dev:Amimouse.c中注册,是某个具体设备。注册时,会寻找能够处理该设备的input_handler
input_handle:把上面两者关联起来的,也就是指出某个具体input_dev归哪个input_handler管理。
1.初始化
1.1 Mousedev初始化
// Mousedev.c
static int __init mousedev_init(void)
{
input_register_handler(&mousedev_handler);
memset(&mousedev_mix, 0, sizeof(struct mousedev));
INIT_LIST_HEAD(&mousedev_mix.list);
init_waitqueue_head(&mousedev_mix.wait);
mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
mousedev_mix.exist = 1;
mousedev_mix.minor = MOUSEDEV_MIX;
devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX),
S_IFCHR|S_IRUGO|S_IWUSR, "input/mice");
class_device_create(input_class,
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice");
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
if (!(psaux_registered = !misc_register(&psaux_mouse)))
printk(KERN_WARNING "mice: could not misc_register the device\n");
#endif
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
return 0;
}
input_register_handler()主要完成两个任务:
1、把input_handler按次设备号放到input_handler数组input_table里,
2、再用当前所有的输入设备input_dev与input_handler适配。我们先假设注册input_handler时还没有一个input_dev在链表中,即注册时不需要进行驱动器适配。
1.2 Amimouse初始化
//Amimouse.c
static int __init amimouse_init(void)
{
if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE))
return -ENODEV;
amimouse_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
amimouse_dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
amimouse_dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
amimouse_dev.open = amimouse_open;
amimouse_dev.close = amimouse_close;
amimouse_dev.name = amimouse_name;
amimouse_dev.phys = amimouse_phys;
amimouse_dev.id.bustype = BUS_AMIGA;
amimouse_dev.id.vendor = 0x0001;
amimouse_dev.id.product = 0x0002;
amimouse_dev.id.version = 0x0100;
input_register_device(&amimouse_dev);
printk(KERN_INFO "input: %s at joy0dat\n", amimouse_name);
return 0;
}
mousedev_init(Mousedev.c)和amimouse_init(Amimouse.c)的关系:
1、mousedev_init注册input_handler,并初始化input_handler->connect为mousedev_connect。
2、amimouse_init注册input_dev,在调用input_register_device(),该函数内部会通过匹配,找到该input_dev所对应的input_handler(也就是寻找能够处理该设备的handler),然后调用该input_handler的connect函数,也即mousedev_connect()。
3、Mousedev.c是可以理解为通用的鼠标类驱动。
4、Amimouse.c是某个具体的鼠标驱动,它需要mousedev提供的某些函数、数据结构。
注:读input_register_device()的源码时,会发现它初始化了一个timer,这里不用去管他,因为该timer对键盘等设备有用,对鼠标无用。
在mousedev_connect()中,会打开具体的鼠标设备,它通过调用input_open_device()实现:
// input.c
int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int err;
err = down_interruptible(&dev->sem);
if (err)
return err;
handle->open++;
if (!dev->users++ && dev->open)
err = dev->open(dev);
if (err)
handle->open--;
up(&dev->sem);
return err;
}
蓝色部分dev->open(dev),也就是调用input_dev的open函数,即amimouse_open()。
amimouse_open()的功能主要是,从寄存器中获取当前鼠标的X/Y坐标(保存在两个表示X/Y坐标的全局变量中),并注册一个中断amimouse_interrupt。
写到这里,有必要把调用关系拿出来晒一下了:
amimouse_init()
|-- input_register_device()
|   |-- mousedev_connect()
|   |   |-- input_open_device()
|   |   |   |-- amimouse_open()
|   |-- input_link_handle()
input_link_handle(),把input_handle分别与input_handler和input_dev关联起来,这样:
1、input_handler可以知道自己的input_handle,
2、input_dev也可以知道自己的input_handle。
至此,初始化结束。
2.打开鼠标
当系统打开一个设备时如"/dev/input/mouse0",虚拟文件系统会调用input类设备file->f_op的open函数。这里,该open函数是input_open_file()。
input_open_file(),把file->f_op更新为新的file_operations,这里,也就是input_handler的file_operations,即mousedev_fops。
static 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_open_file()更新file->f_op后,再调用open函数,即mousedev_open()。
mousedev_open()主要是创建一个mousedev_list链表(初始化该链表的部分字段),然后调用input_open_device(),做些input_handle->open++以及input_dev->user++等操作。
3.鼠标中断
前面提到的amimouse_open(),会注册一个中断amimouse_interrupt()。
amimouse_interrupt()发生时,会把数据存在一块固定地址(比如全局变量),等待用户读取(也就是后面说到的mousedev_read())。
无敌的调用关系图又要出场了:
amimouse_interrupt()
|-- input_report_rel()、input_report_key()、input_sync()
|   |-- input_event()
|   |   |-- mousedev_event()
|   |   |   |-- mousedev_rel_event()、mousedev_key_event()、mousedev_notify_readers()
4.读取鼠标
读取鼠标位置时,同打开文件一样,系统会调用file的f_op函数集的函数,但此时f_op已经在打开时赋值为Mousedev的操作集指针了,既mousedev_fops,其中的read函数为mousedev_read()。
mousedev_read()把相关的X/Y坐标等数据,返回给用户。
还有一点要注意,mousedev_read()中,需要等待事件list->mousedev->wait,如果不满足,那么阻塞。而在前面提到的鼠标中断函数里,会定期调用input_sync(),也即调用mousedev_notify_readers(),该函数会使能该事件,唤醒mousedev_read()。
Linux input子系统分析(网络转载) - 键盘棒棒的日志 - 网易博客 Linux输入子系统分析 Linux输入子系统分析1 【引用】各省人性格分析大全(转载) - ydq200888的日志 - 网易博客 熟知键盘快捷方式 - 理睬的日志 - 网易博客 【引用】熟知键盘快捷方式 - 鹞子的日志 - 网易博客 熟知键盘快捷方式 - 理睬的日志 - 网易博客 linux+apache+sqlite+php - 唐老鸭的日志 - 网易博客 【荐】键盘上所有快捷键的作用!!! (史上最全的) - 名 仕的日志 - 网易博客 全套瑜伽,有时间就自己练练 (图片) 转载于网络 - 恩荣的日志 - 网易博客 转载:美国国债数据分析 - 风投浪子的日志 - 网易博客 饭局(转载) - 闲散人也的日志 - 网易博客 白想(转载) - 香儿的日志 - 网易博客 初中语文新课程标准(转载) - 宋怀明的日志 - 网易博客 史海钩陈:吴清源升九段(转载) - Accelerate的日志 - 网易博客 新年寄语 (转载) - 1949ncxn的日志 - 网易博客 女人哲学(转载) - wind_dancer7的日志 - 网易博客 仲尼梦奠帖 - 闲散人也的日志 - 网易博客(转载) MP4文件格式解析(转载) - eastnapoleon的日志 - 网易博客 各种秘方!(转载) - 北方客的日志 - 网易博客 低年级家长会发言稿(转载) - 石榴石的日志 - 网易博客 【转载】 裸睡...... - kcskcs的日志 - 网易博客 情●爱○性◎钱○女人● (网络转载) - 善歌的日志 - 网易博客 键盘上的钢琴曲~~~活动你的手指哦 - 闲人SGM的日志 - 网易博客