打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Linux?input子系统?io控制字段

 

http://blog.csdn.net/guoshaobei/archive/2010/08/06/5792635.aspx

include/linux/input.h

 
#define EVIOCGVERSION      _IOR('E', 0x01,int)         
#define EVIOCGID      _IOR('E', 0x02, structinput_id)   
#define EVIOCGREP      _IOR('E', 0x03,int[2])         
#define EVIOCSREP      _IOW('E', 0x03,int[2])         
#define EVIOCGKEYCODE      _IOR('E', 0x04,int[2])         
#define EVIOCSKEYCODE      _IOW('E', 0x04,int[2])         

#define EVIOCGNAME(len)      _IOC(_IOC_READ, 'E', 0x06,len)      
#define EVIOCGPHYS(len)      _IOC(_IOC_READ, 'E', 0x07,len)      
#define EVIOCGUNIQ(len)      _IOC(_IOC_READ, 'E', 0x08,len)      

#define EVIOCGKEY(len)      _IOC(_IOC_READ, 'E', 0x18,len)      
#define EVIOCGLED(len)      _IOC(_IOC_READ, 'E', 0x19,len)      
#define EVIOCGSND(len)      _IOC(_IOC_READ, 'E', 0x1a,len)      
#define EVIOCGSW(len)      _IOC(_IOC_READ, 'E', 0x1b,len)      

#define EVIOCGBIT(ev,len)   _IOC(_IOC_READ, 'E', 0x20 + ev,len)    
#define EVIOCGABS(abs)      _IOR('E', 0x40 + abs, structinput_absinfo)      
#define EVIOCSABS(abs)      _IOW('E', 0xc0 + abs, structinput_absinfo)      

#define EVIOCSFF      _IOC(_IOC_WRITE, 'E', 0x80, sizeof(structff_effect))   
#define EVIOCRMFF      _IOW('E', 0x81,int)         
#define EVIOCGEFFECTS      _IOR('E', 0x84,int)         

#define EVIOCGRAB      _IOW('E', 0x90,int)         

 

原文:http://www.linuxjournal.com/article/6429

尽管原文写于2003,仍有参考价值。

 

<!-- @page { margin: 0.79in } P { margin-bottom:0.08in } H4 { margin-bottom: 0.08in } PRE.cjk { font-family:"DejaVu Sans", monospace } TD P { margin-bottom: 0in }-->

The Linux USB Input Subsystem

Part 1

 

Linux USB输入子系统是一种简单的协调的管理所有输入设备的方式。

本文讨论 4部分内容:输入子系统的描述;内核中输入子系统的实现;输入子系统的用户空间API;在你的程序中如何使用它。

 

什么是输入子系统 ?
输入子系统是Linux内核用于管理各种输入设备(键盘,鼠标,遥控杆,书写板等等)的部分,用户通过输入子系统进行内核,命令行,图形接口之间的交换。输入子系统在内核里实现,因为设备经常要通过特定的硬件接口被访问(例如串口,ps/2 usb等等 ),这些硬件接口由内核保护和管理。内核给用户导出一套固定的硬件无关的input API,供用户空间程序使用。

 

理解内核内部实现

输入子系统分为三块: input core drivers event handlers。他们之间的关系如图 1所示。正常的路径是从底层硬件到驱动,从驱动到 inputcore,从 inputcore eventhandler,从 eventhandler userspace。此外,还存在一个返回路径(return path)。返回路径允许给一个键盘设置 LED,给一个 force feedbackjoystick提供 motioncommands。路径的两个方向(指从内核到用户的方向和从用户到内核的方向)使用相同的event定义和不同的 typeidentifier

 

 

这三个核心模块之间的交互主要通过events数据结构来实现,events的数据结构定义如下:

Listing 1. event-dev-struct.txt

struct input_dev {

void *private;

char *name;
char *phys;
char *uniq;
struct input_id id;

unsigned long evbit[NBITS(EV_MAX)];
unsigned long keybit[NBITS(KEY_MAX)];
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)];
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
int ff_effects_max;

unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;

unsigned int repeat_key;
struct timer_list timer;

struct pm_dev *pm_dev;
int state;

int sync;

int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];

unsigned long key[NBITS(KEY_MAX)];
unsigned long led[NBITS(LED_MAX)];
unsigned long snd[NBITS(SND_MAX)];

int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];

int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*accept)(struct input_dev *dev,
struct file *file);
int (*flush)(struct input_dev *dev,
struct file *file);
int (*event)(struct input_dev *dev,
unsigned int type,
unsigned int code,
int value);
int (*upload_effect)(struct input_dev *dev,
struct ff_effect *effect);
int (*erase_effect)(struct input_dev *dev,
int effect_id);

struct list_head h_list;
struct list_head node;
};

type域显示了被报告事件的类型,例如,一个 keypress或者 buttonpress relativemotion(比如移动鼠标)或者 absolutemotion(比如移动游戏杆) code域告诉你是哪一个 key或者坐标轴在被操作; value域告诉你现在的状态或者运动情况是什么。

例如,如果 type域是一个 key code域告诉你是哪一个 key value域告诉你该 key是被按下还是抬起。类似的,如果 type域是一个相对坐标轴, code域告诉你是哪一个坐标轴, value域告诉你移动的距离以及相对坐标轴的方向。

如果你以对角线的方向移动鼠标,同时移动滚轮,你将获得三个相对事件:垂直轴上的运动事件(x-axis),水平轴上的运动事件(y-axis),滚轮的运动事件。

 

Event handlers给用户空间提供接口,将标准事件格式转换为特定API所需要的格式。Handlers也通常负责设备节点(/dev entries)。最常见的handler keyboardhandler,它是大多数C程序员熟悉的” standardinput”

驱动通常提供底层硬件的接口,例如 USB PCI memory或者 I/O regions,或者 serial port I/Oregions

在发送给 input core之前,驱动将用户接口的底层硬件形式转换为标准的事件格式。Input core使用标准的内核plugin design:使用input_register_device()来添加设备,使用input_unregister_device()来删除设备。这些调用的参数是 structinput_device, listing-1所描述。尽管这个数据结构看起来很大,但是绝大多数的域被提供,用于保证驱动可以规定一个设备的能力,例如哪种事件类型,设备接受或者发送的codes

 

除了管理驱动和 handlers input core也导出了一些有用的 /proc文件系统接口,用于查看当前活动的设备和事件handlers。下面是查看usb鼠标的例子 (cat/proc/bus/input/devices)

I: Bus=0003 Vendor=046d Product=c002 Version=0120
N: Name="Logitech USB-PS/2 Mouse M-BA47"
P: Phys=usb-00:01.2-2.2/input0
H: Handlers=mouse0 event2
B: EV=7
B: KEY=f0000 0 0 0 0 0 0 0 0
B: REL=103

 

I line:这行包含身份信息,显示了 bustype 3(usb)vendorproductversion等来来自 usbdescriptoer关于鼠标的厂商信息。

N line:这行包含了名字信息。

P line:这行包含了物理设备信息。上述例子包含了 usbcontroller pciaddress, usb tree以及input interface

H line: 这行包含了与设备关联的 handlerdrivers

B line: 这些行包含了显示设备能力的一些位域(bitfield)

 

Listing 2. register.c

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

MODULE_LICENSE("GPL");

struct input_dev ex1_dev;

static int __init ex1_init(void)
{

memset(&ex1_dev, 0, sizeof(struct input_dev));
init_input_dev(&ex1_dev);


ex1_dev.name = "Example 1 device";

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;


set_bit(EV_KEY, ex1_dev.evbit);
set_bit(KEY_B, ex1_dev.keybit);
set_bit(KEY_A, ex1_dev.keybit);


input_register_device(&ex1_dev);

return 0;
}

static void __exit ex1_exit(void)
{
input_unregister_device(&ex1_dev);
}

module_init(ex1_init);
module_exit(ex1_exit);

 

 

/proc接口是一种简单使用的方法来测试一些简单的驱动。考虑如下listing 2的一个驱动例子,在init()里注册,在removal()里注销。该驱动示例使用init_input_dev()来做一些初步的初始化工作:设置名字,物理描述符,身份描述符;然后设置bit arrays来指定设备提供的事件类型是 EV_KEY,两个可能的 codes KEY_A KEY_B。初始化代码然后将设备注册到 inputcore。如果你将这个示例代码modprobe到内核,你会从/proc/bus/input/devices里看到如下信息:

I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="Example 1 device"
P: Phys=A/Fake/Path
H: Handlers=kbd event3
B: EV=3
B: KEY=10000 40000000

 

如果我想从设备驱动发送事件到 inputcore,我们需要调用input_event()或者更便利的封装函数,例如input_report_key()/input_report_abs(),在include/linux/input.h里定义。示例 listing 3使用了这些函数。

这个示例与 listing 2示例基本相同,但是增加了一个 timer,调用 ex2_timeout()。这个新函数发送了 4 KEY_A press 4 KEY_B press。注意这总共会产生 16 press 事件,这是因为每次 event由一次按键或一次释放键产生。这些事件传递给 inputcore,然后传递给 keyboardhandler,产生”aaaabbbb”或者”AAAABBBB”,依赖于shift键是否被选择。timer 4秒后被设置循环运行, 4秒的时间确保你有最够的时间移除模块当你认为打印了足够的测试信息。同样注意需要调用input_sync()函数。该函数用于通知 event handler(这里是 keyboardhandler)设备已经传递完一组完整的数据。在input_sync()函数被调用之前,

handler可能缓存 events

 

Listing 3. aaaabbbb.c

struct input_dev ex2_dev;

void ex2_timeout(unsigned long unused)
{
int x;

for (x=0;x<4;x++) {

input_report_key(&ex2_dev, KEY_A, 1);
input_sync(&ex2_dev);
input_report_key(&ex2_dev, KEY_A, 0);
input_sync(&ex2_dev);
}
for (x=0;x<4;x++) {

input_report_key(&ex2_dev, KEY_B, 1);
input_sync(&ex2_dev);
input_report_key(&ex2_dev, KEY_B, 0);
input_sync(&ex2_dev);
}


mod_timer(&ex2_dev.timer,jiffies+4*HZ );
}

static int __init ex2_init(void)
{

... do initialization ...


init_timer(&ex2_dev.timer);
ex2_dev.timer.function = ex2_timeout;
ex2_dev.timer.expires = jiffies + HZ;
add_timer(&ex2_dev.timer);

return 0;
}

static void __exit ex2_exit(void)
{
del_timer_sync(&ex2_dev.timer);
input_unregister_device(&ex2_dev);
}

 

让我们来看最后一个驱动例子,显示相对信息如何提供,如listing 4所示。这个驱动例子模仿了一个鼠标。初始化代码配置设备有两个坐标轴(REL_XREL_Y)和一个key(BTN_LEFT)。我们使用一个timer来运行ex3_timeout。这个timer调用input_report_rel来提供相对运动 (5个单步 ---相对运动是函数的的 3个参数 ),包含 30步向右, 30 步向下, 30步向左, 30步向上,因此光标构成了一个正方形。为了提供运动动画,

timeout 20毫秒。再次强调的是,input_sync()是保证事件handler处理一个完整的事件数据的,你需要调用input_sync()来确保你的数据已经完整的传递给 inputcore。如果你向对角线移动,你需要这样做:

...
input_report_rel(..., REL_X, ...);
input_report_rel(..., REL_Y, ...);
input_sync(...);
...

 

这样确保了鼠标对角线移动,而不是现横向移动,后竖向移动。

Listing 4. squares.c

void ex3_timeout(unsigned long unused )
{

if (state<30)
input_report_rel(&ex3_dev, REL_X, 5);
else if (state < 60)
input_report_rel(&ex3_dev, REL_Y, 5);
else if (state < 90)
input_report_rel(&ex3_dev, REL_X, -5);
else
input_report_rel(&ex3_dev, REL_Y, -5);

input_sync(&ex3_dev);

if ((state++) >= 120)
state = 0;


mod_timer(&ex3_dev.timer, jiffies+HZ/50);
}

static int __init ex3_init(void)
{

memset(&ex3_dev, 0, sizeof(struct input_dev));
init_input_dev(&ex3_dev);


ex3_dev.name = "Example 3 device";

ex3_dev.phys = "A/Fake/Path";
ex3_dev.id.bustype = BUS_HOST;
ex3_dev.id.vendor = 0x0001;
ex3_dev.id.product = 0x0003;
ex3_dev.id.version = 0x0100;


set_bit(EV_REL, ex3_dev.evbit);
set_bit(REL_X, ex3_dev.relbit);
set_bit(REL_Y, ex3_dev.relbit);


set_bit(EV_KEY, ex3_dev.evbit);
set_bit(BTN_LEFT, ex3_dev.keybit);


input_register_device(&ex3_dev);


init_timer(&ex3_dev.timer);
ex3_dev.timer.function = ex3_timeout;
ex3_dev.timer.expires = jiffies + HZ/10;
add_timer(&ex3_dev.timer);

return 0;
}

static void __exit ex3_exit(void)
{
del_timer_sync(&ex3_dev.timer);
input_unregister_device(&ex3_dev);
}

module_init(ex3_init);
module_exit(ex3_exit);

 

Handlers---到达用户空间

我们看到设备驱动位于硬件设备和 inputcore之间,将硬件事件(通常是中断)翻译成 inputevents。为了使用 inputevents,我们使用handlers,它提供了用户空间的接口。

 

input子系统包含了你需要的大多数handlers:一个提供console keyboardhandler;一个供应用程序使用的mouse handler;一个joystick handler以及一个touchscreen handler。同样有一个通用的 eventhandler,向用户空间提供 basicinput events。这意味着你不需要在内核里再写一个handler,因为你可以在用户空间通过访问 eventhandler完成你需要的功能。

 

 

<!-- @page { size: 8.5in 11in; margin: 0.79in } P{ margin-bottom: 0.08in } H4 { margin-bottom: 0.08in } TD P {margin-bottom: 0in } -->

Using the Input Subsystem

part 2

 

Linux input子系统一个很重要的特性是它提供了 eventinterface。它通过字符设备节点对用户空间导出了原生event,允许用户程序操作任何event,不会遗失任何信息。

 

查找 event interface版本

使用 EVIOCGVERSION ioctlfunction。参数是32 int类型,代表 major version (two highbytes) minor version(third byte) patchlevel (low byte)Listing 1显示了使用EVIOCGVERSION的例子:第1个参数是 event devicenode的打开文件描述符。你需要传递一个指向int数据的一个指针作为第3个参数。

Listing 1. Sample EVIOCGVERSION Function


if (ioctl(fd, EVIOCGVERSION, &version)) {
perror("evdev ioctl");
}



printf("evdev driver version is %d.%d.%d\n",
version >> 16, (version >> 8) & 0xff,
version & 0xff);

 

查找设备身份信息

event interface支持获取设备的身份信息,使用 EVIOCGID ioctlfunction。参数是指向input_id数据结构的指针。input_id数据结构定义如listing 2所示。_u16数据类型是16为无符号整型。

Figure 2. input_id Structure Definitions

struct input_id {
__u16 bustype;
__u16 vendor;
__u16 product;
__u16 version;
};

 

bustype 域包含了你需要的准确的数据信息。你应把它当作是一个非透明的枚举类型,需要与通过<linux/input.h>里定义的 BUG_x 类型比较获知。vendor/product/version是与 bus type相关的表示设备身份信息的域。现代设备(如USB PCI)都使用了这些域,但是传统的设备( serial mice,ps/2 keyboard, ISA sound card)没有这些域。 EVIOCGID的使用如 listing 3所示。这个例子调用了 ioctl并打印了结果。

Listing 3. Sample EVIOCGID ioctl


if(ioctl(fd, EVIOCGID, &device_info)) {
perror("evdev ioctl");
}


printf("vendor hx product hx version hx",
device_info.vendor, device_info.product,
device_info.version);
switch ( device_info.bustype)
{
case BUS_PCI :
printf(" is on a PCI bus\n");
break;
case BUS_USB :
printf(" is on a Universal Serial Bus\n");
break;
...

 

除了 bustype/vendor/product/version等设备信息,某些设备还会提供一个有意义的名字字符串,使用EVIOCGNAME获取。如果名字串太长,则在返回的参数里被截取。 Listing4显示了它的使用的例子。

Listing 4. Example Truncated String

int fd = -1;
char name[256]= "Unknown";

if ((fd = open(argv[1], O_RDONLY)) < 0) {
perror("evdev open");
exit(1);
}

if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
perror("evdev ioctl");
}

printf("The device on %s says its name is %s\n",
argv[1], name);

close(fd);

 

下面是上述例子运行的结果样例:

The device on /dev/input/event0 says its name is Logitech USB-PS/2 Optical Mouse

 

尽管设备身份信息和名字信息通常很有用,但是它也许并没有提供足够的信息告诉你当前在使用哪个设备。例如,你当前有两个完全相同的遥控杆,你需要确定每个使用哪个端口。这通常属于拓扑信息(topology information),可以使用 EVIOCGPHYSioctl获取。它返回一串字符串(或者一个负值错误码)。 Listing5显示了它的使用的例子。

Listing 5. Using EVIOCGPHYS for Topology Information

if(ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0) {
perror("event ioctl");
}
printf("The device on %s says its path is %s\n",
argv[1], phys);

 

它的运行结果样例如下所示:

The device on /dev/input/event0 says its path is usb-00:01.2-2.1/input0

 

为了了解输出的含义,你需要将其分成几部分。Usb部分意味着这使用usb系统的一个物理拓扑。00:01.2 usb hostcontroller所在的 pci businformation (bus 0, slot 1, function 2) 2.1表示了从 root hub device的路径,这里表示上行 hub接在 root hub的第 2个端口上,设备接在上行 hub的第 1个端口上。 Input0表示这是设备的第 1 event device 节点。大部分设备只有一个 eventdevice节点,但是有些设备例外。比如多媒体键盘,它有一个 eventdevice节点用于 normalkeyboard,另一个 eventdevice节点用于 multimediakeyboard。拓扑示例如下图表示。

如果你在同一根连接线上将一个设备还成另外一个同样的设备,你无法区分设备更换,除非每一个设备有一个唯一号,比如serial number。使用EVIOCGUNIQ可以获取。Listing 6是其示例。绝大多数设备没有这样的唯一号,所以你使用该ioctl将返回一个空字符串。

Listing 6. Finding a Unique Identifier

if(ioctl(fd, EVIOCGUNIQ(sizeof(uniq)), uniq) < 0) {
perror("event ioctl");
}

printf("The device on %s says its identity is %s\n",
argv[1], uniq);

 

确定设备能力和特性

对于一些设备,也许知道设备的身份信息就足够了,因为它允许你根据设备的使用情况处理设备的任何case。但是这总做法的尺度不好。比如,你有一个设备仅有一个滑轮,你想使能处理滑轮的handler,但是你并不想在code里列出每个带有滑轮的鼠标的vendor/product信息。为此,event interface允许你对于某个设备确定有哪些功能和特性。 Eventinterface支持的 featuretypes有:

  • EV_KEY: absolute binary results, such as keys and buttons.

  • EV_REL: relative results, such as the axes on a mouse.

  • EV_ABS: absolute integer results, such as the axes on a joystickor for a tablet.

  • EV_MSC: miscellaneous uses that didn't fit anywhere else.

  • EV_LED: LEDs and similar indications.

  • EV_SND: sound output, such as buzzers.

  • EV_REP: enables autorepeat of keys in the input core.

  • EV_FF: sends force-feedback effects to a device.

  • EV_FF_STATUS: device reporting of force-feedback effects back tothe host.

  • EV_PWR: power management events

这些仅仅是 type features。每个 type feature包含了很大范围的不同的个体 feature。例如, EV_REL type区别了 x轴, y轴, z轴,横轮,竖轮。同样, EV_KEYtype包含了成千上百个keysbuttons

使用 EVIOCGBIT ioctl可以获取设备的能力和特性。它告知你设备是否有key或者button

EVIOCGBIT ioctl处理 4个参数 ( ioctl(fd,EVIOCGBIT(ev_type, max_bytes), bitfield)) ev_type是返回的 type feature 0是个特殊 case,表示返回设备支持的所有的 typefeatures)。max_bytes表示返回的最大字节数。bitfield域是指向保存结果的内存指针。 returnvalue表示保存结果的实际字节数,如果调用失败,则返回负值。Listing 7展现了其使用示例,测试了/dev/input/event0设备节点支持哪些 type feature

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <fcntl.h>

#include <string.h>

#include <linux/input.h>

#define BITS_PER_LONG 32

#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)

static int test_bit(int nr, const volatile unsigned long*addr)

{

return 1UL & (addr[BIT_WORD(nr)]>> (nr &(BITS_PER_LONG-1)));

}

int main(int argc, char ** argv)

{

int fd;

unsigned long *evtype_b = malloc(sizeof(int));

int yalv;

if ((fd = open(argv[1], O_RDONLY)) < 0) {

perror("evdev open");

exit(1);

}

memset(evtype_b, 0, sizeof(evtype_b));

if (ioctl(fd, EVIOCGBIT(0, EV_MAX), evtype_b) <0) {

perror("evdev ioctl");

}

printf("Supported event types:\n");

for (yalv = 0; yalv < EV_MAX; yalv++) {

if (test_bit(yalv, evtype_b)) {

printf(" Event type 0xx ", yalv);

switch ( yalv)

{

case EV_SYN :

printf(" (Synch Events)\n");

break;

case EV_KEY :

printf(" (Keys or Buttons)\n");

break;

case EV_REL :

printf(" (Relative Axes)\n");

break;

case EV_ABS :

printf(" (Absolute Axes)\n");

break;

case EV_MSC :

printf(" (Miscellaneous)\n");

break;

case EV_LED :

printf(" (LEDs)\n");

break;

case EV_SND :

printf(" (Sounds)\n");

break;

case EV_REP :

printf(" (Repeat)\n");

break;

case EV_FF :

case EV_FF_STATUS:

printf(" (Force Feedback)\n");

break;

case EV_PWR:

printf(" (Power Management)\n");

break;

default:

printf(" (Unknown: 0xhx)\n",

yalv);

}

}

}

close(fd);

}

 

这个例子使用了 evtype_bit掩码 EV_MAX(在<linux/input.h>里定义),觉得 bit array需要多少内存。当 ioctl提交后,由 event layer填充 bit array。我们测试 bit array里的每一个 bit,来确定设备支持哪些 featuretype。所有的设备都支持EV_SYNC,该bit inputcore来设置。

下面是运行结果的样例:

如果是 keyboard,则

Supported event types:
Event type 0x00 (Synchronization Events)
Event type 0x01 (Keys or Buttons)
Event type 0x11 (LEDs)
Event type 0x14 (Repeat

 

如果是 mouse,则

Supported event types:
Event type 0x00 (Synchronization Events)
Event type 0x01 (Keys or Buttons)
Event type 0x02 (Relative Axes)

 

获取设备 (from or to) input

 

当确定了设备的能力和特性后,你就知道了设备的事件类型。

获取设备的 event通过 char设备的 read function。当你从 event device (例如 /dev/input/event0)里读取 event时,你将获得一系列 events,每个 event input_event结构表示。

Listing 8示例展示了读取一个打开文件描述符 fd的事件。它过滤了不属于 key的事件,并打印 input_event结构里的每个域。

Listing 8. Checking for Busy Spots


size_t rb;

struct input_event ev[64];

rb=read(fd,ev,sizeof(struct input_event)*64);

if (rb < (int) sizeof(struct input_event)) {
perror("evtest: short read");
exit (1);
}

for (yalv = 0;
yalv < (int) (rb / sizeof(struct input_event));
yalv++)
{
if (EV_KEY == ev[yalv].type)
printf("%ld.ld ",
ev[yalv].time.tv_sec,
ev[yalv].time.tv_usec,
printf("type %d code %d value %d\n",
ev[yalv].type,
ev[yalv].code, ev[yalv].value);
}

 

下面是运行的结果样例:

Event: time 1033621164.003838, type 1, code 37, value 1
Event: time 1033621164.027829, type 1, code 38, value 0
Event: time 1033621164.139813, type 1, code 38, value 1
Event: time 1033621164.147807, type 1, code 37, value 0
Event: time 1033621164.259790, type 1, code 38, value 0
Event: time 1033621164.283772, type 1, code 36, value 1
Event: time 1033621164.419761, type 1, code 36, value 0
Event: time 1033621164.691710, type 1, code 14, value 1
Event: time 1033621164.795691, type 1, code 14, value 0

 

对于每个 key,你会获得一个 key press和一个 key depress event

这是 char设备的标准 read接口,所以你不需要在程序里 busyloop。此外,如果你想同时获得许多设备的input事件时,使用poll/select函数。

给设备发送信息,使用 char设备的标准 write函数,发送的数据必须是input_event数据结构。

Listing 9展示了简单的给设备写数据的示例。这个例子先让 Caps LockLED打开,等200毫秒,然后让 Caps LockLED关闭;然后让 Num LockLED打开,等200毫秒,然后让 Num LockLED关闭。不断循环这个过程,你会看到键盘上的指示灯交替闪烁。

Listing 9. Sample Data Write Function

struct input_event ev; 


ev.type = EV_LED;
ev.code = LED_CAPSL;
ev.value = 0;
retval = write(fd, &ev, sizeof(struct input_event));
ev.code = LED_NUML;
retval = write(fd, &ev, sizeof(struct input_event));
ev.code = LED_SCROLLL;
retval = write(fd, &ev, sizeof(struct input_event));

while (1)
{
ev.code = LED_CAPSL;
ev.value = 1;
write(fd, &ev, sizeof(struct input_event));
usleep(200000);
ev.value = 0;
write(fd, &ev, sizeof(struct input_event));

ev.code = LED_NUML;
ev.value = 1;
write(fd, &ev, sizeof(struct input_event));
usleep(200000);
ev.value = 0;
write(fd, &ev, sizeof(struct input_event));
}

 

现在我们清楚的知道如何接收或者发送一个事件---key按下/抬起,鼠标移动等等。对于一些程序,可能还需要知道设备的一些全局状态。比如,一个管理keyboard的程序需要知道当前的哪些指示灯在亮,哪些键被释放。

EVIOCGKEY ioctl用于确定一个设备的全局key/button状态,它在 bitarray里设置了每个key/button是否被释放。Listing 10展示了该示例。

Listing 10. Determining a Device's Global Key and Button

State

uint8_t key_b[KEY_MAX/8 + 1];

memset(key_b, 0, sizeof(key_b));
ioctl(fd, EVIOCGKEY(sizeof(key_b)), key_b);

for (yalv = 0; yalv < KEY_MAX; yalv++) {
if (test_bit(yalv, key_b)) {

printf(" Key 0xx ", yalv);
switch ( yalv)
{
case KEY_RESERVED :
printf(" (Reserved)\n");
break;
case KEY_ESC :
printf(" (Escape)\n");
break;

case BTN_STYLUS2 :
printf(" (2nd Stylus Button )\n");
break;
default:
printf(" (Unknown key)\n");
}
}
}

 

EVIOCGLED EVIOCGSND ioctl EVIOCGKEY类似,分别表示当前 LED亮灯和声音通道打开。 Listing 11展示了 EVIOCGLED的使用。

Listing 11. Using EVIOCGLED

memset(led_b, 0, sizeof(led_b));
ioctl(fd, EVIOCGLED(sizeof(led_b)), led_b);

for (yalv = 0; yalv < LED_MAX; yalv++) {
if (test_bit(yalv, led_b)) {

printf(" LED 0xx ", yalv);
switch ( yalv)
{
case LED_NUML :
printf(" (Num Lock)\n");
break;
case LED_CAPSL :
printf(" (Caps Lock)\n");
break;

default:
printf(" (Unknown LED: 0xhx)\n",
yalv);
}
}
}

 

使用 EVIOCGREP ioctl来获取键盘的重复速率。 Listing12展示了该示例。(重复速率指你按下键后,输出的事件的次数。例如,按下1键且不释放,console里会输出多个1的速率)

Listing 12. Checking the Repeat Rate Settings

int rep[2];

if(ioctl(fd, EVIOCGREP, rep)) {
perror("evdev ioctl");
}

printf("[0]= %d, [1] = %d\n", rep[0], rep[1]);

其中, rep[0]表示在按键重复出现之前 delay的时间; rep[1]表示按键重复出现的时间间隔。

 

使用 EVIOCSREP ioctl来设置键盘的重复速率。 Listing13展示了该示例。

Listing 13. Setting the Repeat Rates

int rep[2];

rep[0] = 2500;
rep[1] = 1000;

if(ioctl(fd, EVIOCSREP, rep)) {
perror("evdev ioctl");
}

rep[0]/rep[1]的含义同 Listing 12

 

有些 input driver支持 key mapping。使用 EVIOCGKEYCODEioctl获取一个key对应的scancode Listing14/Listing 15展示了 keymapping的示例。注意有些键盘驱动并不支持这个特性(比如USB键盘)。如果你想修改 keymapping,使用EVIOCSKEYCODE ioctl即可。

Listing 14. Looping over Scancodes

int codes[2];

for (i=0; i<130; i++) {
codes[0] = i;
if(ioctl(fd, EVIOCGKEYCODE, codes)) {
perror("evdev ioctl");
}
printf("[0]= %d, [1] = %d\n",
codes[0], codes[1]);
}

 

Listing 15. Mapping Keys

int codes[2];

codes[0] = 58;
codes[1] = 49;

if(ioctl(fd, EVIOCSKEYCODE, codes)) {
perror("evdev ioctl");
}

 

使用 EVIOCGABS ioctl提供绝对坐标轴设备的状态信息。它为每一个绝对坐标轴提供了一个input_absinfo数据结构(参考Listing 16)。如果你想查看设备的全局状态,对每一个坐标轴调用EVIOCGABS ioctl函数。Listing 17展示了该示例。

Listing 16. input_absinfo for an Absolute Axis

struct input_absinfo {
__s32 value; // current value of the axis
__s32 minimum; // current limits of the axis
__s32 maximum; // current limits of the axis
__s32 fuzz;
__s32 flat;
};

 

Listing 17. Checking Global State by Axis

uint8_t abs_b[ABS_MAX/8 + 1];
struct input_absinfo abs_feat;

ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_b)), abs_b);

printf("Supported Absolute axes:\n");

for (yalv = 0; yalv < ABS_MAX; yalv++) {
if (test_bit(yalv, abs_b)) {
printf(" Absolute axis 0xx ", yalv);
switch ( yalv)
{
case ABS_X :
printf("(X Axis) ");
break;
case ABS_Y :
printf("(Y Axis) ");
break;
default:
printf("(Unknown abs feature)");
}
if(ioctl(fd, EVIOCGABS(yalv), &abs_feat)) {
perror("evdev EVIOCGABS ioctl");
}
printf("%d (min:%d max:%d flat:%d fuzz:%d)",
abs_feat.value,
abs_feat.minimum,
abs_feat.maximum,
abs_feat.flat,
abs_feat.fuzz);
printf("\n");
}
}

 

Force Feedback (力回馈)

有三个 ioctl用于控制 force-feedback设备: EVIOCSFF EVIOCRMFF EVIOCGEFFECT。这三个 ioctl分别表示发送一个 force-feedbackeffect,删除一个force-feedback effect,获取当前多少个 effect在同时使用。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
Linux input子系统 (2)
使用UInput模拟系统键盘鼠标动作
linux应用编程
input subsystem
Linux输入子系统分析
英飞凌OKT507如何捕获按键事件
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服