您的位置:首页 > 运维架构 > Linux

Linux内核输入体系的构架

2013-06-25 18:29 127 查看
1. Linux内核输入体系的构架

      输入设备硬件可能直接连入系统,也可能经由其他总线的接入,因此输入设备驱动可能直接面向硬件,也可能面向下层的总线驱动。输入设备驱动负责管理具体的输入设备硬件,其主要的任务是注册输入设备,然后读取硬件产生的各种信号,并转化为输入事件,向输入系统的核心报告。

     输入子系统由输入子系统核心层、驱动层和事件处理层构成,一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过 Driver ->InputCore -> Eventhandler -> userspace 的顺序到达用户空间传给应用程序。其中Input Core 即 Input Layer 由 driver/input/input.c及相关头文件实现。对下提供了设备驱动的接口,对上提供了Event Handler层的编程接口。

    在系统启动时,输入体系的核心在class子系统内注册主设备号为INPUT_MAJOR、次设备号为0~255的字符设备。

    输入设备驱动主要利用输入体系核心提供的接口向input类别注册输入设备,但是所注册的设备并不对应着任何字符设备,也就是说没有对应的设备文件。因此输入设备驱动报告的事件不能直接到达应用程序,其间还要经过输入事件驱动的过滤和处理。

     输入事件驱动与具体的硬件设备无关,只关心设备所报告的事件。经过处理的事件以设备文件的形式向应用程序传输。特殊的是键盘事件,它可以直接向内核的tty驱动传输,终端程序打开的是tty设备文件而不是输入的设备文件。

     输入设备与事件驱动的关系类似于总线上设备与驱动之间的关系,每当一个新的输入设备注册或一个输入事件驱动注册时,由输入体系的核心代码进行设备和驱动的关联。关联操作氛围两步:首先调用事件驱动的匹配操作,匹配操作仅根据输入设备的基本参数进行简单的判断决定能否关联;匹配成功后调用事件驱动的连接操作,进行事件设备的初始化和注册工作。事件设备也注册在input类别内,并且对应着输入体系核心所注册的字符设备。

     输入设备与事件驱动之间的关系式多对多的关系,同一个设备报告的事件可能被多个事件驱动处理,并以不同的形式通知应用程序。

      Linux2.6.30内核中包含着两个基本的事件驱动:evdev驱动和mousedev驱动。因为内核中有了这些事件驱动,所以实际应用中一般只需要编写输入设备驱动即可。

2. 输入设备编程接口

      头文件linux/input.h

      2.1 输入设备数据结构

      struct input_dev {

 const char *name;//设备名称

 const char *phys;//设备的物理路径

 const char *uniq;//设备的独特标识码

 struct input_id id;//设备标识,包含总线类型、制造商标识、产品标识、版本号

 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 keycodemax;//键盘码表项的个数

 unsigned int keycodesize;//键盘码表项的大小

 void *keycode;//键盘码表的首地址

 int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);

 int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);

 struct ff_device *ff;

 unsigned int repeat_key;

 struct timer_list timer;

 int sync;

 int abs[ABS_MAX + 1];

 int rep[REP_MAX + 1];

 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 absmax[ABS_MAX + 1];

 int absmin[ABS_MAX + 1];

 int absfuzz[ABS_MAX + 1];

 int absflat[ABS_MAX + 1];

 int absres[ABS_MAX + 1];

 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 *grab;

 spinlock_t event_lock;

 struct mutex mutex;

 unsigned int users;

 bool going_away;

 struct device dev;

 struct list_head h_list;

 struct list_head node;

};

其中的name、phys等成员与设备的功能无关,但是可以被应用程序通过ioctl系统调用查询,并且也会出现在设备对象的属性上,也就是可以通过sysfs文件系统中的相应目录下的文件查询。

     input_dev结构体中xxxbit的成员,这些成员以标志位的方式表示设备的功能,其中evbit的每个比特位都表示设备对于某种事件类型是否支持,而其他成员的每个比特位都表示对类型事件中具体某个事件是否支持。这些标志位必须设置正确。首先,与事件驱动的匹配主要就是依据这些位标志位,,其次,如果设备报告了他不支持的事件,事件将会被直接被过滤掉。

     成员keycodemax、keycodesize和keycode用于保存键盘码表的信息。这里的码表是一个一般化的数组,它的每个元素的大小是keycodesize,数组中元素的个数是keycodemax,而数组的首地址则保存在keycode里。码表用来进行从扫描码到键盘码的转换。键盘设备驱动从硬件直接读到的编码称为扫描码,以扫描码为下标取码表元素的值,得到的应该是键盘码,键盘码是报告事件时使用的编码。显然,扫描码与键盘码的对应关系完全由设备驱动所决定。实际上,输入体系核心及事件驱动都不会直接访问这些成员,因此它们基本可以作为设备驱动的私有数据使用。

     setkeycode和getkeycode两个操作用于支持应用程序通过ioctl系统调用查询码表和设置码表的功能,可以不实现。

    open和close是设备的打开和关闭操作。当应用程序打开设备文件时,首先进入输入事件驱动的打开操作,在其中一般会回调输入设备的打开操作,关闭造作与此类似。

    2.2 代表输入设备的结构体只能由如下函数进行动态分配

   struct input_dev *input_allocate_device(void)

这个函数的返回值指向分配到的输入设备,如果分配失败,则返回NULL。

释放设备的函数

void input_free_device(struct input_dev *dev)

    其中参数dev指向要释放的输入设备。

    需要注意的是,输入设备如果注册成功,则在注销时会自动释放,因此这个函数只用在注册失败的情况。

    3. 设置输入设备的功能

    由input_allocate_device函数分配得到的输入设备已进行了基本的初始化,但是还有很多数据域需要根据设备的特性来进行具体的设置,其中最重要的步骤就是设置设备的功能。输入体系核心提供了一个设置功能的函数,原型如下:

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)

各个参数的解释如下:

dev: 指向设置的输入设备

type:要设置的事件类型

code:要设置的事件编码

对于支持绝对坐标的设备来说,还有一些属性要设置,可以利用如下函数:

 void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)

参数说明:dev 指向要设置的输入设备

axis 坐标轴的方向,可选值为ABS_X, ABS_Y, ABS_Z等。

min 最小值,

max 最大值,

fuzz 噪音

flat 中央平坦区

 

4. 输入设备的注册与注销

初始化好的输入设备可以向input类别注册,其接口函数原型为:

int input_register_device (struct input_dev *dev)

相反的操作是注销输入设备,其接口函数原型如下:

void input_unregister_device(struct input_dev *dev)

 

5.报告事件

 

     输入设备驱动最重要的工作就是报告各种事件。当驱动检测到硬件上发生信号时,如某个按键被按下或松开,就要报告相应的事件。报告按键事件采用如下函数:

void input_report_key(struct input_device *dev ,unsigned int code, int value)

参数说明:

dev:指向要报告事件的输入设备

code:事件的编码

value:事件的值,1表示键按下,0表示键松开

报告相对坐标事件使用如下函数:

void input_report_rel(struct input_device *dev, unsigned int code, int value)

参数说明:

dev : 指向要报告事件的输入设备

code: 事件的编码

value: 事件的值,即相对坐标的值

报告绝对坐标事件使用以下函数:

void input_report_abs(struct input_dev *dev, unsigned int code, int value)

dev: 指向要报告事件的输入设备

code: 事件的编码

value: 事件的值,即绝对坐标的值

 

注:事件报告以后将被存放在队列里,只有在报告一个同步事件才会得到处理,报告同步事件使用如下函数:

void input_sync(struct input_dev *dev);

其中参数 dev指向要报告事件的输入设备。

6.事件驱动的结构

7. 应用程序的编写
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: