Linux输入子系统

Linux输入子系统:input.txt

                            Linux Input drivers v1.0

                (c) 1999-2001 Vojtech Pavlik<vojtech@ucw.cz>

                                 Sponsored by SuSE

----------------------------------------------------------------------------

 0.版权说明

~~~~~~~~~~~~~

 This program is free software; you can redistribute it and/or modify it

under the terms of the GNU General PublicLicense as published by the Free

Software Foundation; either version 2 ofthe License, or (at your option)

any later version.

  This program is distributed in the hope that it will be useful, but

WITHOUT ANY WARRANTY; without even theimplied warranty of MERCHANTABILITY

or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for

more details.

   Youshould have received a copy of the GNU General Public License along

with this program; if not, write to theFree Software Foundation, Inc., 59

Temple Place, Suite 330, Boston, MA02111-1307 USA

  Should you need to contact me, the author, you can do so either bye-mail

- mail your message to<vojtech@ucw.cz>, or by paper mail: Vojtech Pavlik,

Simunkova 1594, Prague 8, 182 00 CzechRepublic

   Foryour convenience, the GNU General Public License version 2 is included

in the package: See the file COPYING.

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

本文由DroidPhone 翻译:http://blog.csdn.net/droidphone

Kernel版本:V3.4.10

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. 简介

~~~~~~~~~~~~~~~

这时一组驱动的集合,它们可以用于所有的基于linux的输入设备,虽然目前它只是用于USB输入设备,将来(2.52.6版本)它们将会被扩展并替换掉现存的多数输入系统,这就是为什么它们被放在drivers/input/目录下,而不是drivers/usb/

 输入设备驱动的核心是input模块,它需要在其他输入模块之前被加载--它是输入系统其它两个模块之间通讯的桥梁:

1.1 设备驱动(Device drivers

~~~~~~~~~~~~~~~~~~

  这些模块负责和实际的硬件打交道(例如通过USB),给input模块提供相应的事件(按键,鼠标移动)

1.2 事件处理模块(Event handlers

~~~~~~~~~~~~~~~~~~

这些模块从input模块获得事件信息,并根据需要通过不同的接口传递这些事件--kernel传递按键事件,或者通过模拟的PS/2接口给GPMX传递鼠标移动事件,等等。

 2. 一个简单的例子

~~~~~~~~~~~~~~~

通常,对于大多数配置来说,系统有一个USB鼠标和一个USB键盘,你需要加载以下几个模块(或者把它们编译到内核中):

 

         input

         mousedev

         keybdev

         usbcore

         uhci_hcdor ohci_hcd or ehci_hcd

         usbhid

 

在这之后,USB键盘直接就可以工作了,USB鼠标会作为一个字符设备,主次设备号分别为1363

         crw-r--r--   1 root    root      13,  63 Mar 28 22:45 mice

 你需要主动创建该鼠标设备节点,创建的命令如下:

         cd/dev

         mkdirinput

         mknodinput/mice c 13 63

 之后,你必须告诉GPMXFree你要使用这个鼠标设备 - 对于GPM,应该象以下的指令:

         gpm-t ps2 -m /dev/input/mice

   对于X:

          Section"Pointer"

             Protocol   "ImPS/2"

             Device     "/dev/input/mice"

             ZAxisMapping 4 5

         EndSection

     做完以上步骤以后,你就可以正常地使用USB鼠标和键盘了。

 3. 详细描述

~~~~~~~~~~~~~~~~~~~~~~~

3.1 设备驱动(Device drivers

~~~~~~~~~~~~~~~~~~

设备驱动模块产生输入事件,这些事件在没有经过处理之前是没有什么用处的,所以你需要用到3.2节的某些模块。

 3.1.1 usbhid

~~~~~~~~~~~~

 usbhid可以说是最庞大和复杂的驱动了。它处理所有的HID设备,他之所以这么复杂和庞大,原因是设备类型种类繁多,USB HID的规格也相当不简单。

   目前,它处理USB鼠标,游戏控制杆,方向盘,键盘,轨迹球和数字化仪。

   而且,USB也使用HID来实现监视器控制,扬声器控制,UPSsLCDs等等很多外设。

   监视器和扬声器控制比较容易添加到hid/input接口中,但是UPSsLCDs就不是这么简单,为此,设计了hiddev接口,详细的信息请参考Documentation/hid/hiddev.txt

  usbhid模块非常易于使用,它没有任何参数,自动检测插入的HID设备,是的,它能用合适的方式进行检测。

   不过,因为设备实在是太过于多样性了,你可能遇到某些设备工作的不好。这时你可以在hid-core.c的开始加上#defineDEBUG,把syslog traces发给我。

 3.1.2 usbmouse

~~~~~~~~~~~~~~

  对于嵌入式系统,只为了使用鼠标功能而加入整个庞大的usbhid显然不是一个好的选择,这时可以只使用usbmouse驱动,它只处理usb鼠标。它使用了简易的HIDBP协议。当然这要求你的鼠标必须也要支持这一简易协议。最好,如果你没有很强烈地理由,请使用usbhid

 3.1.3 usbkbd

~~~~~~~~~~~~

  就像usbmouse一样,这个模块用简易的HIDBP协议与键盘通信。它很小,但不支持任何额外的按键。没有特别的原因时,也请使用usbhid

 3.1.4 wacom

~~~~~~~~~~~

  这是Wacom GraphireIntuos tablets的驱动。它不是用于Wacom PenPartner,一个由HID驱动处理的设备。尽管Intuos Graphiretablets声称他们是HID tablets,但实际上不是,所以需要这个特别的驱动。

 3.1.5 iforce

~~~~~~~~~~~~

  用于I-Force游戏杆和滚轮的驱动,通过USBRS232连接。现在它包含了ForceFeedback的支持,尽管Immersion公司认为该协议是商业机密而且没有公开它的任何信息。

 3.2 Event handlers

~~~~~~~~~~~~~~~~~~

  Eventhandlers根据实际需要,把设备的事件分发到用户空间或者内核中。

3.2.1 keybdev

~~~~~~~~~~~~~

  keybdev目前是一个不太好的骇客工具,它把输入事件转换为体系相关的键盘raw模式(x86中的Xlated AT Set2),然后传递给keyboard.c模块中的handle_scancode函数。当体系的keybdev能产生rawmode时,它会工作得很好,其他体系也能添加进来。

   正确地方法是直接把输入事件传递到keyboard.c中,最好的是keyboard.c本身就是一个event handler。这一工作由下面提到的网页提供了一个input patch来完成。

 3.2.2 mousedev

~~~~~~~~~~~~~~

  mousedev也是一个骇客工具,它使得那些需要使用鼠标的程序可以工作。它从鼠标或者数字化仪获取事件,然后生成了一个PS/2类型(例如/dev/psaux)的鼠标设备来让用户空间的程序使用。理想的情况下,程序应该使用一个更加合理的接口,例如evdev

  上面提到的Mousedev设备在/dev/input中的呈现如下:

          crw-r--r--   1 root    root      13,  32 Mar 28 22:45 mouse0

         crw-r--r--   1 root    root      13,  33 Mar 29 00:41 mouse1

         crw-r--r--   1 root    root      13,  34 Mar 29 00:41 mouse2

         crw-r--r--   1 root    root      13,  35 Apr 1 10:50 mouse3

         ...

         ...

         crw-r--r--   1 root    root      13,  62 Apr 1 10:50 mouse30

         crw-r--r--   1 root    root      13,  63 Apr 1 10:50 mice

 除了最后的'mice',每个'mouse'设备被分配给单一的鼠标或者是数字化仪。最后的'mice',这个单一的字符设备由所有的鼠标和数字化仪共享,就算没有任何真正的鼠标连接进来,这个设备也依然存在。这一特性对USB鼠标的热插拔功能很有用。这样尽管没有鼠标连接,程序依然可以打开该设备。

 XFree86中,内核的配置项:CONFIG_INPUT_MOUSEDEV_SCREEN_[XY],指定了屏幕的像素。如果你想在X中使用数字化仪,这点是必要的,因为她的移动会被通过虚拟的PS/2鼠标发送到X中,这时需要计算正确地比例。如果你只是使用鼠标,这个配置值是没有用处的。

  依赖于程序希望读取什么数据,Mousedev会生成PS/2, ImPS/2(Microsoft IntelliMouse) 或者

ExplorerPS/2 (IntelliMouse Explorer)协议格式的数据。你可以把GPMX设置成这里的任一种。如果你想使用USB鼠标上的滚轮,你可以配置为ImPS/2,而当你希望使用额外的按键时,就使用ExplorerPS/2

 3.2.3 joydev

~~~~~~~~~~~~

  Joydev实现了Linux joystick v0.xv1.x版的api,有点类似之前内核使用的驱动:drivers/char/joystick/joystick.c。细节请进一步参考内核文档:joystick-api.txt。一旦有joystick连接到系统中,我们可以通过/dev/input中的以下节点访问它:

          crw-r--r--   1 root    root      13,   0 Apr 1 10:50 js0

         crw-r--r--   1 root    root      13,   1 Apr 1 10:50 js1

         crw-r--r--   1 root    root      13,   2 Apr 1 10:50 js2

         crw-r--r--   1 root    root      13,   3 Apr 1 10:50 js3

         ...

 一直可以到:js31.

 3.2.4 evdev

~~~~~~~~~~~

  evdev是一个通用的输入事件接口,它把内核产生的事件,连同时间戳一起,直接传递到用户空间的应用程序中。该接口的API还在不断完善中,但现在已经可以使用它们。该接口我们会在下面的第5节说明。

   GPMX可以通过该方式来获取键盘和鼠标的事件。无需内核特别支持,它就可以允许X对它进行多线程的访问。事件编码对所有平台都是统一的并且是硬件无关的。

  设备节点位于 /dev/input:

          crw-r--r--   1 root    root      13,  64 Apr 1 10:49 event0

         crw-r--r--   1 root    root      13,  65 Apr 1 10:50 event1

         crw-r--r--   1 root    root      13,  66 Apr 1 10:50 event2

         crw-r--r--   1 root    root      13,  67 Apr 1 10:50 event3

         ...

 一直可以到:event31.

 4. 验证是否可以正常工作

~~~~~~~~~~~~~~~~~~~~~~~~

  在键盘上敲几个键就足以检查USB键盘是否工作正常,也能检查是否内核的驱动是否工作正常。

   敲入命令:"cat /dev/input/mouse0" (c, 13, 32)可以验证鼠标是否被正确地枚举,当你移动鼠标时,屏幕会显示一些字符。

 

  你可以用jstest工具来测试joystick是否正常工作,该工具在joystick包中(参见文档:Documentation/input/joystick.txt

 

  可以使用evtest工具对event设备进行测试,该工具可以从LinuxConsole项目的CVS中获取(见下面的URL

 5. Event interface

~~~~~~~~~~~~~~~~~~

如果你希望在你的任何应用中(X,gpm,svgalib ...)添加event设备的支持,我(vojtech@ucw.cz)非常乐意尽我所能提供帮助。这里我说明一下当前的进展状况,虽然还在不断地扩展中,但是基本的接口是不会改变而导致不兼容的问题:

  你可以在/dev/input/eventX设备上使用阻塞,非阻塞的读操作,也可以用select()操作。你会在一次读取中返回一个完整的输入事件,它的结构如下:

struct input_event {

         structtimeval time;

         unsignedshort type;

         unsignedshort code;

         unsignedint value;

};

   'time'字段是时间戳,它返回时间发生时的时间。关于type字段,EV_REL代表返回的是相对移动值,EV_KEY代表的是按键按下或释放,更多的类型定义可以参见:include/linux/input.h

   'code'字段是事件的编码,例如可以是REL_XKEY_BACKSPACE,你也可以从include/linux/input.h中得到完整的列表。

   'value'字段是该事件携带的参数值。可以是EV_REL事件的相对变化量,EV_ABS事件的一个新的绝对值(joysticks...),对于EV_KEY事件,该值是0代表按键释放,为1代表按键按下,为2代表自动重复。

 

Linux输入子系统:事件的编码 -- event-codes.txt

输入系统协议用类型types和编码codecs来表示输入设备的值并用此来通知用户空间的应用程序。这篇文档对这些类型和编码进行了说明并且指出什么时候和如何使用这些类型和编码。

 一个单一的硬件事件可以产生多个输入事件,每个输入事件包含一个单一数据项的新的数据值。EV_SYN是一个特别的事件类型,它用来把同一时刻产生的多个输入数据分割为多个数据包。在下面的描述中,术语事件(event)是指一个涵盖类型,编码和参数值的单一输入事件。

 input协议是一个基于状态的协议,只有当相应事件编码对应的参数值发生变化时才会发送该事件。不过,状态是由Linux的输入子系统进行维护,驱动程序无需维护输入的状态,就算参数值没有变化时向输入子系统发出事件也不会有问题。用户空间可以用linux/input.h 中定义的EVIOCG*ioctls来获得当前事件编码和参数的状态。设备的所支持的上报事件种类也可以通过sysfsclass/input/event*/device/capabilities/来获取,设备的特性和可以通过class/input/event*/device/properties来获取。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

本文由DroidPhone 翻译:http://blog.csdn.net/droidphone

Kernel版本:V3.4.10

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 Event types:

===========

types对应于一个相同逻辑输入结构的一组Codes。每个type都有一组可用的codes用于产生输入事件。每个type可用的codes的详细信息请参考Codes一节的内容。

* EV_SYN:

  - 用于事件间的分割标志。事件可能按时间或空间进行分割,就像在多点触摸协议中的例子。

 * EV_KEY:

  - 用来描述键盘,按键或者类似键盘设备的状态变化。

 * EV_REL:

  - 用来描述相对坐标轴上数值的变化,例如:鼠标向左方移动了5个单位。

 * EV_ABS:

  -用来描述相对坐标轴上数值的变化,例如:描述触摸屏上坐标的值。

 * EV_MSC:

  - 当不能匹配现有的类型时,使用该类型进行描述。

 * EV_SW:

  - 用来描述具备两种状态的输入开关。

 * EV_LED:

  - 用于控制设备上的LED灯的开和关。

 * EV_SND:

  - 用来给设备输出提示声音。

 * EV_REP:

  -用于可以自动重复的设备(autorepeating)。

 * EV_FF:

  - 用来给输入设备发送强制回馈命令。(震动?)

 * EV_PWR:

  - 特别用于电源开关的输入。.

 * EV_FF_STATUS:

  - 用于接收设备的强制反馈状态。

 Event codes:

===========

Event codes 用于对事件的type进行更精确的定义

 EV_SYN:

----------

EV_SYN 事件没有对values进行具体的定义,它们的使用方式仅在发送evdev的事件串中有定义。

 * SYN_REPORT:

  - 当多个输入数据在同一时间发生变化时,SYN_REPORT用于把这些数据进行打包和包同步。例如,一次鼠标的移动可以上报REL_XREL_Y两个数值,然后发出一个SYN_REPORT。下一次鼠标移动可以再次发出REL_XREL_Y两个数值,然后经跟这另一个SYN_REPORT

 * SYN_CONFIG:

  -TBD

 * SYN_MT_REPORT:

  - 用于同步和分离触摸事件。更多的信息请参考内核文档:multi-touch-protocol.txt

 * SYN_DROPPED:

  - 用来指出evdev客户的事件队列的的缓冲区溢出。客户端顶盖忽略所有的事件,包括下一个SYN_REPORT事件,并且要查询设备来获得它的状态(使用EVIOCG* ioctls)。

 EV_KEY:

----------

EV_KEY事件采取KEY_<name> BTN_<name>的形式,比如,KEY_A代表键盘上的A键,当一个按键被按下时,一个带有按键编码和value1的事件被发出。当一个按键被释放时,一个value0的事件被发出。有些硬件当按键重复时会发出事件,这些事件的value值为2。通常,KEY_<name>用作键盘上的按键,而BTN_<name>则用于开关按钮事件。

 几个 EV_KEY codes具有特别的意义:

 * BTN_TOOL_<name>:

- 这些codes用于配合触控板,平板和触摸屏这些设备的输入,这些设备可以使用手指,笔或者其它工具。当一个事件发生并且检测到某种工具在使用时,相应的BTN_TOOL_<name> code事件应该把value设为1,当该工具不再和输入设备进行交互时,value应该复位为0。所有的触控板,当事件发生时,平板和触摸屏映泰至少使用一种BTN_TOOL_<name> code

 * BTN_TOUCH:

BTN_TOUCH用于触摸接触事件。当一个输入工具被判定为有意义的物理接触时,这一特性的value值应该设为1。所谓有意义的物理接触可以是任何的接触,又或者是满足某种定义条件的接触。例如,触摸板可以当触摸的压力达到某一个值以上时才把value设为1,一个用笔的平板当笔划过但没有接触到平板的表面时,把BTN_TOOL_PENvalue设为1,而把BTN_TOUCHvalue设为0.

 注意:为了配合一些老的传统mousedev模拟驱动程序可以工作,BTN_TOUCH必须作为一个同步帧的第一个evdevcode发出。

 注意:出于历史的原因,用户空间会把带有BTN_TOOL_FINGER BTN_TOUCH的触摸设备解释为触摸板,而类似的不带BTN_TOOL_FINGER的触摸设备则被解释为触摸屏。为了与目前的用户空间应用向后兼容,建议遵循这一区分原则。以后,这一区分方法将会失效,而会使用设备属性ioctl EVIOCGPROP(定义在linux/input.h)来传送设备的类型。

 * BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP,BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP:

- 这些codes表明一个,两个,三个和四个手指参与触摸板和触摸屏的操作。例如,如果用户使用两只手指在触摸板上试图滚动屏幕上的内容,在运动期间,应该发送value1BTN_TOOL_DOUBLETAP。注意的是所有的BTN_TOOL_<name>codes BTN_TOUCH code是用于正交目的的,一个触摸板由手指碰触时,应该在这两组中各生成一个事件code,至少在一个同步帧中带有一个value1BTN_TOOL_<name>code

 注意:出于历史原因,一些驱动会在同一个同步帧内发送多个value1的上报手指数的codes,但是这一方法现在已经过时了(不再使用)。

 注意:在多手指触摸驱动中,应该使用input_mt_report_finger_count()函数来发出以上这些codes,详情请参看内核文档:multi-touch-protocol.txt

 EV_REL:

----------

EV_REL事件描述了某种特性的相对变化量。例如,鼠标向左方移动了几个单位距离,但是他的绝对位置是未知的。如果我们可以知道绝对位置,那我们应该使用EV_ABS而不是EV_REL

 下面这些属于EV_RELcodes有特别的意义:

 * REL_WHEEL, REL_HWHEEL:

  - 这两个codes用于对应的垂直方向和水平方向的滚轮。

 EV_ABS:

----------

EV_ABS事件描述了某一特性的绝对变化值,例如,触摸板会用它发出当前位置的绝对坐标值。

 以下这些属于EV_ABS codes有特殊的意义:

 * ABS_DISTANCE:

- 用来描述触摸工具离触摸表面的距离。这一事件应该只有当触摸工具在表面悬空滑过时发出,也就是说,在靠经触摸表面,但是BTN_TOUCHvalue0的时候。如果输入设备可以工作在3维坐标时,应该考虑使用ABS_Z会更好。

 * ABS_MT_<name>:

- 用于描述多手指触摸输入设备。详情请参考内核文档:multi-touch-protocol.txt

 EV_SW:

----------

EV_SW事件用于描述一个两态开关的状态,比如,SW_LID code用来指出笔记本电脑的屏幕已经合上了。

 当绑定一个设备或者从suspend状态中resuming回来,驱动程序必须上报开关的当前状态。从而保证设备,内核和用户空间的状态保持同步。

 resume时,如果开关的状态和suspend之前相同,input子系统会这一重复的状态上报消息。驱动程序任何时候都无需记住开关的状态。

 EV_MSC:

----------

当事件不能用其它事件类型描述时,使用EV_MSC是将进行输入和输出事件的上报。

 EV_LED:

----------

EV_LED事件用来设置或查询设备上LED的状态。

 EV_REP:

----------

EV_REP 用来指出自动重复事件(autorepeating)。

 EV_SND:

----------

EV_SND用于对那些简单的发声设备发出发音指令。

 EV_FF:

----------

EV_FF事件用来初始化具有强制反馈能力的设备,并可以让这些设备发出反馈动作。

 EV_PWR:

----------

EV_PWR事件是一个特殊的类型,它是电源管理的专用事件,目前没有对它做出良好的定义,将来会完善这一事件。

 设备特性(Device properties:

=================

正常情况下,用户空间基于设备发出的数据(比如事件的types)来建立一个输入设备,当两个设备都发出相同的事件types时,这时设备特性就可以提供额外的识别信息。

 INPUT_PROP_DIRECT + INPUT_PROP_POINTER:

--------------------------------------

INPUT_PROP_DIRECT特性表明设备的坐标直接和屏幕坐标向对应(无需琐碎的转换操作,像缩放,反转,旋转等)。非直接输入设备则需要一些必要的变换,比如触摸板上绝对到相对的变换。典型的直接输入设备有:触摸屏,手写板;非直接输入设备有:触摸板,鼠标。

 INPUT_PROP_POINTER特性表明设备不是利用屏幕来获取输入信息,从而需要一个屏幕上的指针来跟踪用户的移动。典型的指针设备有:触控板,鼠标;非指针设备:触摸屏。

 如果INPUT_PROP_DIRECT或者 INPUT_PROP_POINTER都没有设置,设备将会被认为是未定义,它的类型需要按传统的方式利用事件的types推导出来。

 INPUT_PROP_BUTTONPAD:

--------------------

有些触摸板,它的按键位于触摸板的底部,这样按下触摸板会产生一次按键消息,对于这种设备,应该设置该特性。自2009年以来,配置这种触控板的notebooksmacbooks变多越来越普遍。

 最初,这种按键特性是在bcm5974的驱动中通过名字的版本字段来进行编码,为了向后兼容性,用户空间有必要按这两种方法都进行检查。

 INPUT_PROP_SEMI_MT:

------------------

2008年至2011年之间,很多触摸板只能检测到多个接触点,但是不知道它们的独立位置,只是知道触点的数量和包围它们的一个矩形。对于这样的设备,应该设置这个半多点触摸特性。

 不同的设备,这个矩形可能包围所有的触控点,就像边界框一样,也可能只是包围了一部分触控点,比如最后的两个触控点。这种不确定性限制了这个矩形的用处,只是有些手势识别会对它进行分析。

 如果没有设置INPUT_PROP_SEMI_MT特性,该设备被假设为全多点触控设备。

 使用指引:

==========

以下的使用指引确保了单点触摸和多点触摸可以正常地工作,更详尽的信息请参考文档:multi-touch-protocol.txt

 鼠标:

----------

当鼠标移动时,REL_{X,Y}必须被上报。主键被按下时,BTN_LEFT必须被上报。其它按键按下时,BTN_{MIDDLE,RIGHT,4,5,etc.}应该被上报。鼠标上的滚轮滚动时,REL_WHEEL and REL_HWHEEL事件应该被上报。

 触摸屏:

----------

触摸发生时,必须要用ABS_{X,Y}上报触摸的位置。触摸有效时,BTN_TOUCH必须被上报,而不必用BTN_{MOUSE,LEFT,MIDDLE,RIGHT}来表明一次触摸事件。有可能的话,BTN_TOOL_<name>事件也可以应该上报。

 对于新的硬件,应该设置INPUT_PROP_DIRECT特性。

 触控板:

----------

传统的触控板只是想上面鼠标所说那样上报相对位置信息即可。

 具备绝对位置信息的触控板需要通过ABS_{X,Y}上报触控点的位置信息。同样,触摸有效时应该上报BTN_TOUCH事件。如果支持多点触摸,应该通过BTN_TOOL_<name>上报有效触摸的数量。

 对于新的硬件,应该设置INPUT_PROP_POINTER特性。

 Tablets:

----------

当笔或其它工具被有效检测后,必须上报BTN_TOOL_<name>事件,必须用ABS_{X,Y}上报触控的位置信息,同时应该上报BTN_TOUCH事件。当触控工具上的按钮有效时,应该上报BTN_{STYLUS,STYLUS2}消息。除了BTN_{MOUSE,LEFT}以外,其它任意的按键消息都可以用于上报平板上的按键,对于没有标记的按键,BTN_{0,1,2,etc}是个不错的选择,避免使用有特别意义的按键:像BTN_FORWARD之类,除非设备上特别标明了就是这一按键。

 对于新的硬件,INPUT_PROP_DIRECT INPUT_PROP_POINTER都应该被设置。

 

Linux输入子系统:输入设备编程指南 -- input-programming.txt

输入设备编程指南(Programming input drivers

~~~~~~~~~~~~~~~~~~~~~~~~~

1. 新建一个输入设备驱动程序

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1.0 一个最简单的例子

~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
本文由DroidPhone 翻译:http://blog.csdn.net/droidphone
Kernel
版本:V3.4.10
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

以下是一个非常简单的输入设备驱动程序。该设备只有一个按键,它通过BUTTON_PORT这一i/o端口访问,当按下或释放该按键,会发生BUTTON_IRQ中断,驱动程序看起来就像这样:

[cpp] view plaincopyprint?

1.  #include <linux/input.h>   

2.  #include <linux/module.h>   

3.  #include <linux/init.h>   

4.  #include <asm/irq.h>   

5.  #include <asm/io.h>   

6.    

7.  static struct input_dev *button_dev;  

8.    

9.  static irqreturn_t button_interrupt(int irq, void *dummy)  

10. {  

11.     input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);  

12.     input_sync(button_dev);  

13.     return IRQ_HANDLED;  

14. }  

15.   

16. static int __init button_init(void)  

17. {  

18.     int error;  

19.     if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {  

20.         printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);  

21.         return -EBUSY;  

22.     }  

23.     button_dev = input_allocate_device();  

24.     if (!button_dev) {  

25.         printk(KERN_ERR "button.c: Not enough memory\n");  

26.         error = -ENOMEM;  

27.         goto err_free_irq;  

28.     }  

29.     button_dev->evbit[0] = BIT_MASK(EV_KEY);  

30.     button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);  

31.     error = input_register_device(button_dev);  

32.     if (error) {  

33.         printk(KERN_ERR "button.c: Failed to register device\n");  

34.         goto err_free_dev;  

35.     }  

36.     return 0;  

37. err_free_dev:  

38.     input_free_device(button_dev);  

39. err_free_irq:  

40.     free_irq(BUTTON_IRQ, button_interrupt);  

41.     return error;  

42. }  

43.   

44. static void __exit button_exit(void)  

45. {  

46.     input_unregister_device(button_dev);  

47.     free_irq(BUTTON_IRQ, button_interrupt);  

48. }  

49.   

50. module_init(button_init);  

51. module_exit(button_exit);  

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/io.h>
 
static struct input_dev *button_dev;
 
static irqreturn_t button_interrupt(int irq, void *dummy)
{
 input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
 input_sync(button_dev);
 return IRQ_HANDLED;
}
 
static int __init button_init(void)
{
 int error;
 if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
  printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
  return -EBUSY;
 }
 button_dev = input_allocate_device();
 if (!button_dev) {
  printk(KERN_ERR "button.c: Not enough memory\n");
  error = -ENOMEM;
  goto err_free_irq;
 }
 button_dev->evbit[0] = BIT_MASK(EV_KEY);
 button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
 error = input_register_device(button_dev);
 if (error) {
  printk(KERN_ERR "button.c: Failed to register device\n");
  goto err_free_dev;
 }
 return 0;
err_free_dev:
 input_free_device(button_dev);
err_free_irq:
 free_irq(BUTTON_IRQ, button_interrupt);
 return error;
}
 
static void __exit button_exit(void)
{
 input_unregister_device(button_dev);
 free_irq(BUTTON_IRQ, button_interrupt);
}
 
module_init(button_init);
module_exit(button_exit);
1.1 例子驱动的工作过程

~~~~~~~~~~~~~~~~~~~~~~~~~

首先它必须包含头文件<linux/input.h>,它是input子系统的接口,它提供了所有必要的定义信息。

_init初始化函数,可以通过模块进行加载,也可以编译进内核中,它收集设备需要的资源(也会检查设备是否存在)。

接着,它用input_allocate_device()分配了一个input device结构,设置它的bitfields,设备驱动程序通过这一方式把自身的信息通知input子系统的其他部分:它能产生或者接受什么事件。我们的例子设备只能产生EV_KEY类型的事件,而且只能发出BTN_0事件code。这样我们只需要设置这两个bits,我们也可以用

[cpp] view plaincopyprint?

1.  set_bit(EV_KEY, button_dev.evbit);  

2.  set_bit(BTN_0, button_dev.keybit);  

set_bit(EV_KEY, button_dev.evbit);
set_bit(BTN_0, button_dev.keybit);

进行设置,但是上面例子代码中的方法可以一次设置更多的位。

然后,例子驱动用下面的函数注册input device结构:

[cpp] view plaincopyprint?

1.  input_register_device(&button_dev);  

input_register_device(&button_dev);

这会把button_dev结构添加到input driver的全局链表中,调用device handler模块中的_connect函数来通知他一个新的设备出现了。input_register_device()可能会休眠,所以他不能在中断或者持有一个spinlock的情况下被使用。

功能上,该驱动只使用了函数:

[cpp] view plaincopyprint?

1.  button_interrupt()  

button_interrupt()

在每次中断中通过检查按键的状态,并通过以下函数上报:

[cpp] view plaincopyprint?

1.  input_report_key()  

input_report_key()

该函数会进入input子系统。中断服务程序不必检查它是否会给input子系统报告value重复的事件(例如:按下,按下)。因为input_report_*函数自己会对此进行检查。

然后就是调用:

[cpp] view plaincopyprint?

1.  input_sync()  

input_sync()

该函数告诉事件的接收者,我们已经发送了一次完整的报告信息。这对于我们这个只有一个按键的设备好像不太重要,但有些情况下它是非常重要的,例如当鼠标移动后,你不希望XY值被分开解释,因为那样会被解释为两次移动。

1.2 dev->open() and dev->close()

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

当驱动因为设备没有提供中断能力时,它需要不停地查询设备的状态,但是如果一直进行这个查询显得有点浪费。有时设备需要使用一些有价值的资源(例如中断)。这时,我们可以使用openclose回调函数来实现动态地停止查询和释放中断和决定何时再次恢复查询和获取中断。要实现这一功能,我们的例子驱动需要添加以下代码:

[cpp] view plaincopyprint?

1.  static int button_open(struct input_dev *dev)  

2.  {  

3.      if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {  

4.          printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);  

5.          return -EBUSY;  

6.      }  

7.      return 0;  

8.  }  

9.    

10. static void button_close(struct input_dev *dev)  

11. {  

12.     free_irq(IRQ_AMIGA_VERTB, button_interrupt);  

13. }  

14.   

15. static int __init button_init(void)  

16. {  

17. ...  

18.     button_dev->open = button_open;  

19.     button_dev->close = button_close;  

20.     ...  

21. }  

static int button_open(struct input_dev *dev)
{
  if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
    printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
    return -EBUSY;
  }
  return 0;
}
 
static void button_close(struct input_dev *dev)
{
  free_irq(IRQ_AMIGA_VERTB, button_interrupt);
}
 
static int __init button_init(void)
{
...
  button_dev->open = button_open;
  button_dev->close = button_close;
  ...
}

需要注意的是,input核心会保持设备的使用计数来保证dev->open()只有当第一个用户连接该设备时才被调用,而dev->close()只有当最后一个用户断开和设备的连接时才被调用。对两个回调的调用都是串行化的。

当调用成功时,open()回调返回0,返回非0则表示发生了错误。返回类型是voidclose()回调必须一直成功。

1.3 基本事件类型(types

~~~~~~~~~~~~~~~~~~~~~

最简单的事件类型是EV_KEY,它用于键盘和按钮,它通过以下函数上报给input子系统:

[cpp] view plaincopyprint?

1.  input_report_key(struct input_dev *dev, int code, int value)  

input_report_key(struct input_dev *dev, int code, int value)

linux/input.h定义了该类型可用的valuescode (0 KEY_MAX)Value被解释为真假值,也就是任何非0值意味着键被按下,0则意味着键被松开。input子系统的代码只有当value的值和之前的值不同时才会生成一次事件。

除了EV_KEY外,还有另外两个基本的事件类型:EV_RELEV_ABS 。它们用来提供设备的相对和绝对值。鼠标移动是一种相对值,鼠标报告相对于上一个位置的相对差值,因为它工作在没有任何绝对坐标系系统中。绝对值事件对游戏操纵杆和数字化仪有用,这些设备工作在一个绝对坐标系统中。

设备上报EV_REL就像EV_KEY一样简单,只要设置相应的位然后调用以下函数即可:

[cpp] view plaincopyprint?

1.  input_report_rel(struct input_dev *dev, int code, int value)  

input_report_rel(struct input_dev *dev, int code, int value)


只有当非0value时,事件才会被产生。

不过EV_ABS需要一点点特别的处理。在调用input_register_device之前,你需要在input_dev结构中为你的设备所支持的轴填充的额外字段。假如我们的按钮设备有ABS_X轴:

[cpp] view plaincopyprint?

1.  button_dev.absmin[ABS_X] = 0;  

2.  button_dev.absmax[ABS_X] = 255;  

3.  button_dev.absfuzz[ABS_X] = 4;  

4.  button_dev.absflat[ABS_X] = 8;  

button_dev.absmin[ABS_X] = 0;
button_dev.absmax[ABS_X] = 255;
button_dev.absfuzz[ABS_X] = 4;
button_dev.absflat[ABS_X] = 8;

或者,你只需要这样:

[cpp] view plaincopyprint?

1.  input_set_abs_params(button_dev, ABS_X, 0, 255, 4, 8);  

input_set_abs_params(button_dev, ABS_X, 0, 255, 4, 8);

上述设置适合于一个游系操纵杆设备,它有一个X轴,最小值是0,最大值是255(这表明它必须能提供的范围,偶尔上报超出该范围也不会有问题,但它必须要能达到最大和最小值)

,它的噪声范围是+-4,并且有一个大小是8的中心点。

如果你不需要absfuzzabsflat,你可以把它们设置为0,这意味着它是绝对准确的并总是返回正中心位置(如果他有的话)。

1.4 BITS_TO_LONGS(), BIT_WORD(), BIT_MASK()

~~~~~~~~~~~~~~~~~~~~~~~~~~

3个宏来自bitops.h,有助于进行位域计算:

  • BITS_TO_LONGS(x) - 返回x位的位域数组需要多少个long类型来组成。
  • BIT_WORD(x) - 返回位域数组中第x位所对应的按long为单位的索引。
  • BIT_MASK(x) - 返回位x对应的long型的mask值。
1.5 The id* and name fields

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dev->name字段应该要在驱动程序注册输入设备之前设置好。它是一个像'Generic button device'之类的对用户友好的设备名字符串

id*字段包含了总线的IDPCIUSB...),厂商ID和设备ID。总线IDsinput.h中定义。厂商和设备IDspci_ids.husb_ids.h和类似的头文件中定义。这些字段也应该在注册设备之前被设置好。

idtype字段可以被用作输入设备的专有信息。

这些idname字段可以通过evdev接口传递到用户空间中。

1.6 keycode, keycodemax, keycodesize 字段

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

3个字段用于需要键值映射的输入设备,keycode是一个用于把扫描码转换为输入系统键值的数组,keycodemax是这个数组的大小,keycodesize则是该数组每个元素的大小(以字节为单位)

用户空间可以通过相应的evdev接口,使用EVIOCGKEYCODEEVIOCSKEYCODE ioctls来查询和修改当前的扫描码到键值的映射表。当设备填充了3个上述的字段,驱动程序可以根据kernel的默认实现来设置和查询键值映射表。

1.7 dev->getkeycode() and dev->setkeycode()

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

getkeycode()setkeycode()回调允许驱动程序覆写由input核心代码提供的对keycode/keycodemax/keycodesize的默认映射机制,从而可以实现稀疏的映射方式。

1.8 按键的autorepeat

~~~~~~~~~~~~~~~~~~

... 很简单,它由input.c模块处理。硬件autorepeat没有被使用,因为很多设备不存在该功能,而且就算存在该功能,有时候也不正常(例如,Toshiba笔记本中的键盘)。要使能你的设备的autorepeat功能,只要设置dev->evbit中的EV_REP位即可,其它的事情都有输入子系统处理。

1.9 其它的事件types, 输出事件处理

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

现在为止,其它的事件types有:

  • EV_LED - 用作键盘的LEDs灯。
  • EV_SND - 用于键盘的蜂鸣器。

它们和键盘事件很相似,但是它们按另一个方向走动 - 从系统到输入设备驱动程序。如果你的驱动程序可以处理这些事件,必须设置evbit中相应的位,而且要实现一个回调函数:

[cpp] view plaincopyprint?

1.  button_dev->event = button_event;  

2.    

3.  int button_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);  

4.  {  

5.      if (type == EV_SND && code == SND_BELL) {  

6.          outb(value, BUTTON_BELL);  

7.          return 0;  

8.      }  

9.      return -1;  

10. }  

button_dev->event = button_event;
 
int button_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
{
  if (type == EV_SND && code == SND_BELL) {
    outb(value, BUTTON_BELL);
    return 0;
  }
  return -1;
}

这个回调可以在中断上下文或者BH上下文中被调用,所以它不能睡眠,不要处理太长的时间。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值