该系列文章总纲链接:专题分纲目录 Android Framework 输入子系统
本章关键点总结 & 说明:
以上是迭代导图,主要关注➕ 基础部分->调试工具部分即可,同时上图是总图,局部显示的有点小,局部截图,如下所示:
本章节的思维导图放大后如上所示,这里主要研究getevent 和sendevent两个工具命令以及实现原理。
1 getevent
1.1 工具使用:
@1 getevent直接使用,显示当前有那些输入设备,数量与 /dev/input 目录下相同,如下:
$getevent
add device 1: /dev/input/event3
name: "ILITEK Multi-Touch-V3020"
add device 2: /dev/input/event2
name: "PC Camera"
add device 3: /dev/input/event1
name: "gsensor"
add device 4: /dev/input/event0
name: "rk29-keypad"
@2 getevent查看默认上报数据,如下:
$getevent /dev/input/event3
0003 0039 00000004
0003 0035 00002324
0003 0036 00001a9f
0001 014a 00000001
0003 0000 00002324
0003 0001 00001a9f
0000 0000 00000000
0003 0039 ffffffff
0001 014a 00000000
0000 0000 00000000
@3 getevent显示时间戳,如下:
$getevent -t /dev/input/event3
[ 1141.248434] 0003 0039 0000000e
[ 1141.248434] 0003 0035 00002cd4
[ 1141.248434] 0003 0036 00001a09
[ 1141.248434] 0001 014a 00000001
[ 1141.248434] 0003 0000 00002cd4
[ 1141.248434] 0003 0001 00001a09
[ 1141.248434] 0000 0000 00000000
[ 1141.322181] 0003 0039 ffffffff
[ 1141.322181] 0001 014a 00000000
[ 1141.322181] 0000 0000 00000000
@4 getevent 根据 mask 值显示相关信息,执行后会一直显示上报数据,如下:
【errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64,说明:这里默认显示 dev| name| info| vers = 30;】
$getevent -v /dev/input/event3
add device 1: /dev/input/event3
bus: 0003
vendor 222a
product 004d
version 0110
name: "ILITEK Multi-Touch-V3020"
location: "usb-ff540000.usb-1.3/input0"
id: ""
version: 1.0.1
$getevent -v2 /dev/input/event3
add device 1: /dev/input/event3
$getevent -v8 /dev/input/event3
bus: 0003
vendor 222a
product 004d
version 0110
location: "usb-ff540000.usb-1.3/input0"
id: ""
$getevent -v16 /dev/input/event3
version: 1.0.1
$getevent -v30 /dev/input/event3
add device 1: /dev/input/event3
bus: 0003
vendor 222a
product 004d
version 0110
name: "ILITEK Multi-Touch-V3020"
location: "usb-ff540000.usb-1.3/input0"
id: ""
version: 1.0.1
$getevent -v32 /dev/input/event3
events:
KEY (0001): 014a
ABS (0003): 0000 : value 12008, min 0, max 16384, fuzz 0, flat 0, resolution 31
0001 : value 5790, min 0, max 9600, fuzz 0, flat 0, resolution 32
002f : value 0, min 0, max 9, fuzz 0, flat 0, resolution 0
0035 : value 0, min 0, max 16384, fuzz 0, flat 0, resolution 31
0036 : value 0, min 0, max 9600, fuzz 0, flat 0, resolution 32
0039 : value 0, min 0, max 65535, fuzz 0, flat 0, resolution 0
@5 getevent 显示设备支持的事件类型和编码方式,如下:
$getevent -p /dev/input/event3
add device 1: /dev/input/event3
name: "ILITEK Multi-Touch-V3020"
events:
KEY (0001): 014a
ABS (0003): 0000 : value 13084, min 0, max 16384, fuzz 0, flat 0, resolution 31
0001 : value 5284, min 0, max 9600, fuzz 0, flat 0, resolution 32
002f : value 0, min 0, max 9, fuzz 0, flat 0, resolution 0
0035 : value 0, min 0, max 16384, fuzz 0, flat 0, resolution 31
0036 : value 0, min 0, max 9600, fuzz 0, flat 0, resolution 32
0039 : value 0, min 0, max 65535, fuzz 0, flat 0, resolution 0
input props:
INPUT_PROP_DIRECT
@6 getevent 以文本形式输出事件类型和名称,比 -t 更清楚直观,如下:
$getevent -l /dev/input/event3
// 事件类型 事件码 事件值
EV_ABS ABS_MT_TRACKING_ID 0000000f
EV_ABS ABS_MT_POSITION_X 00002bbc
EV_ABS ABS_MT_POSITION_Y 00001b6d
EV_KEY BTN_TOUCH DOWN
EV_ABS ABS_X 00002bbc
EV_ABS ABS_Y 00001b6d
EV_SYN SYN_REPORT 00000000
EV_ABS ABS_MT_TRACKING_ID ffffffff
EV_KEY BTN_TOUCH UP
EV_SYN SYN_REPORT 00000000
@7 getevent 显示事件上报速率,如下:
$getevent -r /dev/input/event3
0003 0039 00000015
0003 0035 000030d8
0003 0036 00001954
0001 014a 00000001
0003 0000 000030d8
0003 0001 00001954
0000 0000 00000000 rate 0
0003 0039 ffffffff
0001 014a 00000000
0000 0000 00000000 rate 9
@8 getevent 一次性查看需要的触摸屏信息,如下:
$getevent -tlr /dev/input/event3
[ 2514.550104] EV_ABS ABS_MT_TRACKING_ID 0000001c
[ 2514.550104] EV_ABS ABS_MT_POSITION_X 00002dac
[ 2514.550104] EV_ABS ABS_MT_POSITION_Y 000018ca
[ 2514.550104] EV_KEY BTN_TOUCH DOWN
[ 2514.550104] EV_ABS ABS_X 00002dac
[ 2514.550104] EV_ABS ABS_Y 000018ca
[ 2514.550104] EV_SYN SYN_REPORT 00000000 rate 0
[ 2514.638845] EV_ABS ABS_MT_TRACKING_ID ffffffff
[ 2514.638845] EV_KEY BTN_TOUCH UP
[ 2514.638845] EV_SYN SYN_REPORT 00000000 rate 11
1.2 源码该要解读:
int getevent_main(int argc, char *argv[])
{
//...
const char *device_path = "/dev/input";
opterr = 0;
do {
c = getopt(argc, argv, "tns:Sv::dpilqc:rh");
if (c == EOF)
break;
switch (c) {
case 't':
get_time = 1;
break;
//...
case 'h':
usage(argv[0]);
exit(1);
}
} while (1);
//...
nfds = 1;
ufds = calloc(1, sizeof(ufds[0]));
ufds[0].fd = inotify_init();
ufds[0].events = POLLIN;
if(device) {
if(!print_flags_set)
print_flags |= PRINT_DEVICE_ERRORS;
res = open_device(device, print_flags);
if(res < 0) {
return 1;
}
} else {
if(!print_flags_set)
print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
print_device = 1;
res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
//...
res = scan_dir(device_path, print_flags);
//...
}
//...
while(1) {
//int pollres =
poll(ufds, nfds, -1);
//printf("poll %d, returned %d\n", nfds, pollres);
if(ufds[0].revents & POLLIN) {
read_notify(device_path, ufds[0].fd, print_flags);
}
for(i = 1; i < nfds; i++) {
if(ufds[i].revents) {
if(ufds[i].revents & POLLIN) {
res = read(ufds[i].fd, &event, sizeof(event));
//...
if(get_time) {
printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);
}
if(print_device)
printf("%s: ", device_names[i]);
print_event(event.type, event.code, event.value, print_flags);
if(sync_rate && event.type == 0 && event.code == 0) {
int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
if(last_sync_time)
printf(" rate %lld", 1000000LL / (now - last_sync_time));
last_sync_time = now;
}
printf("%s", newline);
if(event_count && --event_count == 0)
return 0;
}
}
}
}
return 0;
}
这里getevent看上去较为繁琐,但实际上无非就是几个机制的结合,inofity机制(/dev/input目录变化),poll机制(监听具体某个event文件变化),其他的无非就是根据具体情况做一些处理,输出不同的信息罢了。
2 sendevent
2.1 工具使用:
sendevent命令的格式是:
sendevent /dev/input/eventX type code value
其中 /dev/input/eventX 对应一个event设备,可以通过getevent获得可用的event设备,而type, code, value的定义可参看kernel/include/linux/input.h,其中type如下定义:
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
常用的是EV_KEY, EV_REL, EV_ABS, EV_SYN,分别对应keyboard, 相对坐标, 绝对坐标, 同步事件;EV_SYN则表示一组完整事件已经完成,需要处理,EV_SYN的code定义事件分发的类型,接下来对三种事件的code值分别进行说明。
EV_SYN对应的code如下:
#define SYN_REPORT 0
#define SYN_CONFIG 1
#define SYN_MT_REPORT 2
EV_REL对应的code如下:
#define REL_X 0x00
#define REL_Y 0x01
#define REL_Z 0x02
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)
EV_ABS对应的code如下:
#define ABS_X 0x00
#define ABS_Y 0x01
#define ABS_Z 0x02
#define ABS_RX 0x03
#define ABS_RY 0x04
#define ABS_RZ 0x05
#define ABS_THROTTLE 0x06
#define ABS_RUDDER 0x07
#define ABS_WHEEL 0x08
#define ABS_GAS 0x09
#define ABS_BRAKE 0x0a
#define ABS_HAT0X 0x10
#define ABS_HAT0Y 0x11
#define ABS_HAT1X 0x12
#define ABS_HAT1Y 0x13
#define ABS_HAT2X 0x14
#define ABS_HAT2Y 0x15
#define ABS_HAT3X 0x16
#define ABS_HAT3Y 0x17
#define ABS_PRESSURE 0x18
#define ABS_DISTANCE 0x19
#define ABS_TILT_X 0x1a
#define ABS_TILT_Y 0x1b
#define ABS_TOOL_WIDTH 0x1c
#define ABS_VOLUME 0x20
#define ABS_MISC 0x28
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */
#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
#define ABS_MAX 0x3f
#define ABS_CNT (ABS_MAX+1)
sendevent命令的使用方法如下:
按键类事件使用案例:
sendevent /dev/input/event1: 0001 014a 00000001 // BTN touch事件 值为1
sendevent /dev/input/event1: 0001 014a 00000000 // BTN touch事件 值为0
sendevent /dev/input/event1: 0000 0000 00000000 // sync事件
触摸屏事件使用案例:
sendevent /dev/input/event1 0003 0000 0000015e // ABS x 坐标
sendevent /dev/input/event1: 0003 0001 000000df // ABS y 坐标
sendevent /dev/input/event1: 0003 0018 00000000 // ABS pressure事件
sendevent /dev/input/event1: 0000 0000 00000000 // sync事件
2.2 源码该要解读:
因为sendevent的代码并不多,因此这里直接全部贴出来,如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
//#include <linux/input.h> // this does not compile
#include <errno.h>
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */
#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */
#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */
#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */
#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */
#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */
#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */
#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */
#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */
#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */
#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */
#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */
#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */
#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
int sendevent_main(int argc, char *argv[])
{
int fd;
ssize_t ret;
int version;
struct input_event event;
if(argc != 5) {
fprintf(stderr, "use: %s device type code value\n", argv[0]);
return 1;
}
fd = open(argv[1], O_RDWR);
if(fd < 0) {
fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
return 1;
}
if (ioctl(fd, EVIOCGVERSION, &version)) {
fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno));
return 1;
}
memset(&event, 0, sizeof(event));
event.type = atoi(argv[2]);
event.code = atoi(argv[3]);
event.value = atoi(argv[4]);
ret = write(fd, &event, sizeof(event));
if(ret < (ssize_t) sizeof(event)) {
fprintf(stderr, "write event failed, %s\n", strerror(errno));
return -1;
}
return 0;
}
这里实现上比较简单,就是打开设备节点argv[1], 之后对event事件 结构题首先初始化,type、code、value赋值后将event事件直接写入到节点argv[1]中。一般的设备节点都是“dev/input/eventX”这种。