frameworks 之getEvent指令
frameworks 之getEvent指令
- 指令解析
- 源码追溯
- 源码解析
- 1.解析参数
- 2.初始化ufds数组
- 3.添加到poll 并做对应处理
通过 getEvent 可以识别按键基本命令和里面的关键信息
涉及到的类如下
- system/core/toolbox/toolbox.c
- system/core/toolbox/tools.h
- system/core/toolbox/getevent.c
指令解析
通过 getEvent -h 可打印出相关的指令帮助,对应的指令注释如下。输出的数字都为16进制。
houji:/ $ getevent -help
Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]-t: show time stamps // 打印时间戳-n: don't print newlines // 不换行-s: print switch states for given bits // 打印指定位的开关状态-S: print all switch states // 打印所有开关状态-v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64) // 根据 mask 值显示相关信息,执行后会一直显示上报数据,详细掩码(错误=1,dev=2,名称=4,信息=8,错误=16,位置事件=32,道具=64)默认显示 dev| name| info| vers = 30-d: show HID descriptor, if available // 如果设备可用,显示设备隐藏的描述信息-p: show possible events (errs, dev, name, pos. events) // 显示设备支持的事件类型和编码方式-i: show all device info and possible events // 显示设备的所有信息和支持的事件,比 -p 显示更多信息-l: label event types and names in plain text // 以文本形式输出事件类型和名称,比 -t 更清楚直观-q: quiet (clear verbosity mask) // 以文本形式输出事件类型和名称,比 -t 更清楚直观-c: print given number of events then exit // 打印固定数量的事件并退出-r: print rate events are received // 显示事件上报速率
其中 -v 打印输入设备的详细信息,默认为 默认显示 dev| name| info| vers = 30。
emulator_x86_64:/ # getevent -v
add device 1: /dev/input/event0bus: 0019vendor 0000product 0001version 0000name: "Power Button"location: "LNXPWRBN/button/input0"id: ""version: 1.0.1
-l 打印的信息 分别对应 type code value。
emulator_x86_64:/ # getevent -l
/dev/input/event3: EV_ABS ABS_MT_TRACKING_ID 00000000
/dev/input/event3: EV_ABS ABS_MT_POSITION_X 000031af
/dev/input/event3: EV_ABS ABS_MT_POSITION_Y 00004447
参数可以组合使用,一次性查看需要的触摸屏信息,常用的命令组合为 getevent -ltr
emulator_x86_64:/ # getevent -ltr
[ 1384.965200] /dev/input/event3: EV_ABS ABS_MT_TRACKING_ID 00000000
源码追溯
可以执行, 则表示 system/bin 有对应的脚本。所以可以通过执行对应的打印 查看文件。通过命令 ls -l | grep event 过滤对应的关键字可以看到 该事件通过 软链接(前面开头为l开头即为软链接)。链接到 toolbox。
emulator_x86_64:/system/bin # ls -l | grep event
lrwxr-xr-x 1 root shell 7 2024-08-17 22:51 getevent -> toolbox
-rwxr-xr-x 1 root shell 3925 2024-03-18 00:26 mm_events
lrwxr-xr-x 1 root shell 6 2024-08-17 22:51 sendevent -> toybox
lrwxr-xr-x 1 root shell 4 2024-08-17 22:51 ueventd -> init
回到源码文件,一般工具类 在 system 目录下,可以先通过该目录查找。执行 find -name toolbox。可以看到在该**./core/toolbox** 文件夹下。
跳转到该文件夹下,查看对应的文件打印,查看对应的Bp文件。包含以下文件,可以看出入口为
toolbox.c 文件夹。
cc_defaults {...srcs: ["toolbox.c","getevent.c","getprop.cpp","modprobe.cpp","setprop.cpp","start.cpp",],...
}
对应的 main 入口如下,该方法通过读取参数,遍历数组 ,然后判断名称和命令是否相等,相等就执行对应的 func 方法。对应 tools[] 数组 在文件前面定义,定义了名词和执行的方法。
会根据 name 拼接对应的执行方法。如 getEvent 拼接完为 getevent_main。相关数组定义在 tools.h 头文件里面。
// system/core/toolbox/tools.c
#define TOOL(name) int name##_main(int, char**); // 拼接对应的执行方法名称
#include "tools.h"
#undef TOOL
// 定义对应的数组,数组内容来自 tools.h定义
static struct {const char* name;int (*func)(int, char**);
} tools[] = {
#define TOOL(name) { #name, name##_main },
#include "tools.h"
#undef TOOL{ 0, 0 },
};int main(int argc, char** argv) {// Let's assume that none of this code handles broken pipes. At least ls,// ps, and top were broken (though I'd previously added this fix locally// to top). We exit rather than use SIG_IGN because tools like top will// just keep on writing to nowhere forever if we don't stop them.signal(SIGPIPE, SIGPIPE_handler);// 获取第一个参数,遍历数组,判断是否和名称相等,相等执行对应的方法。char* cmd = strrchr(argv[0], '/');char* name = cmd ? (cmd + 1) : argv[0];for (size_t i = 0; tools[i].name; i++) {if (!strcmp(tools[i].name, name)) {return tools[i].func(argc, argv);}}printf("%s: no such tool\n", argv[0]);return 127;
}
头文件定义
// system/core/toolbox/tools.h
TOOL(getevent)
TOOL(getprop)
TOOL(modprobe)
TOOL(setprop)
TOOL(start)
TOOL(stop)
TOOL(toolbox)
所以运行脚本 执行的方法 在 getevent.c 下的 getevent_main方法下。
源码解析
1.解析参数
查看对应的方法 main 方法,可以看到 第一步 通过 getopt 解析对应的参数
部分参数通过 或 记录到 print_flags 中。
opterr = 0;do {c = getopt(argc, argv, "tns:Sv::dpilqc:rh"); // 解析对应的参数if (c == EOF)break;switch (c) {case 't':get_time = 1;break;case 'n':newline = "";break;case 's':get_switch = strtoul(optarg, NULL, 0);if(dont_block == -1)dont_block = 1;break;case 'S':get_switch = ~0;if(dont_block == -1)dont_block = 1;break;case 'v':if(optarg)print_flags |= strtoul(optarg, NULL, 0);elseprint_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;print_flags_set = 1;break;case 'd':print_flags |= PRINT_HID_DESCRIPTOR;break;case 'p':print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE| PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS;print_flags_set = 1;if(dont_block == -1)dont_block = 1;break;case 'i':print_flags |= PRINT_ALL_INFO;print_flags_set = 1;if(dont_block == -1)dont_block = 1;break;case 'l':print_flags |= PRINT_LABELS;break;case 'q':print_flags_set = 1;break;case 'c':event_count = atoi(optarg); //解析对应参数值dont_block = 0;break;case 'r':sync_rate = 1;break;case '?':fprintf(stderr, "%s: invalid option -%c\n",argv[0], optopt);case 'h':usage(argv[0]); // 打印帮助exit(1);}} while (1);if(dont_block == -1)dont_block = 0;if (optind + 1 == argc) {device = argv[optind];optind++;}if (optind != argc) {usage(argv[0]);exit(1);}
2.初始化ufds数组
第二步,会通过 device 判断是否 观察单个或者全部 。如如果我们 执行的命令后面加了对应的设备,则只会观察对应的设备 如 getevent -ltr /dev/input/event1。其中 inotify_init 获取了 dev/input 文件夹对应的监听。 然后通过 inotify_add_watch。进行删除和添加的监听。
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;// 通过 open 方法打开对应的驱动,并通过 ioctl 获取对应的信息,并放到对应的 ufds 数组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);if(res < 0) {fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));return 1;}// 遍历该文件夹 dev/input 文件夹下所有的res = scan_dir(device_path, print_flags);if(res < 0) {fprintf(stderr, "scan dir failed for %s\n", device_path);return 1;}}
没指定对应的设备的会 将所有添加到数组中,通过 scan_dir 方法。遍历文件夹下。先通过 opendir,打开文件夹,然后 通过 readdir进行遍历。遍历到符合的 在通过 open_device 将fd 添加到 数组中。
static int scan_dir(const char *dirname, int print_flags)
{char devname[PATH_MAX];char *filename;DIR *dir;struct dirent *de;// // 打开对应的文件夹dir = opendir(dirname);if(dir == NULL)return -1;strcpy(devname, dirname);filename = devname + strlen(devname);*filename++ = '/';while((de = readdir(dir))) {if(de->d_name[0] == '.' &&(de->d_name[1] == '\0' ||(de->d_name[1] == '.' && de->d_name[2] == '\0')))continue;strcpy(filename, de->d_name);// 将fd 添加到 数组open_device(devname, print_flags);}closedir(dir);return 0;
}
最终都是通过 open_device 添加到数组,查看对应的方法,可以看到该方法通过 open 打开对应的节点,在通过 ioctl 获取对应的信息,并根据 设置的 print_flags 按需打印对应信息。最终在存放到 ufds 数组中。
static int open_device(const char *device, int print_flags)
{int version;int fd;int clkid = CLOCK_MONOTONIC;struct pollfd *new_ufds;char **new_device_names;char name[80];char location[80];char idstr[80];struct input_id id;// 打开这个节点fd = open(device, O_RDONLY | O_CLOEXEC);if(fd < 0) {if(print_flags & PRINT_DEVICE_ERRORS)fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));return -1;}// 获取相关的信息if(ioctl(fd, EVIOCGVERSION, &version)) {if(print_flags & PRINT_DEVICE_ERRORS)fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));return -1;}if(ioctl(fd, EVIOCGID, &id)) {if(print_flags & PRINT_DEVICE_ERRORS)fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));return -1;}name[sizeof(name) - 1] = '\0';location[sizeof(location) - 1] = '\0';idstr[sizeof(idstr) - 1] = '\0';if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {//fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));name[0] = '\0';}if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {//fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));location[0] = '\0';}if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {//fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));idstr[0] = '\0';}if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) {fprintf(stderr, "Can't enable monotonic clock reporting: %s\n", strerror(errno));// a non-fatal error}new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));if(new_ufds == NULL) {fprintf(stderr, "out of memory\n");return -1;}ufds = new_ufds;new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));if(new_device_names == NULL) {fprintf(stderr, "out of memory\n");return -1;}device_names = new_device_names;if(print_flags & PRINT_DEVICE)printf("add device %d: %s\n", nfds, device);if(print_flags & PRINT_DEVICE_INFO)printf(" bus: %04x\n"" vendor %04x\n"" product %04x\n"" version %04x\n",id.bustype, id.vendor, id.product, id.version);if(print_flags & PRINT_DEVICE_NAME)printf(" name: \"%s\"\n", name);if(print_flags & PRINT_DEVICE_INFO)printf(" location: \"%s\"\n"" id: \"%s\"\n", location, idstr);if(print_flags & PRINT_VERSION)printf(" version: %d.%d.%d\n",version >> 16, (version >> 8) & 0xff, version & 0xff);if(print_flags & PRINT_POSSIBLE_EVENTS) {print_possible_events(fd, print_flags);}if(print_flags & PRINT_INPUT_PROPS) {print_input_props(fd);}if(print_flags & PRINT_HID_DESCRIPTOR) {print_hid_descriptor(id.bustype, id.vendor, id.product);}// 将 fd 存放到ufds 数组中 ufds[nfds].fd = fd;ufds[nfds].events = POLLIN;// 放到 device_names 数组中device_names[nfds] = strdup(device);nfds++;return 0;
}
3.添加到poll 并做对应处理
添加完数组后,通过 poll 等待消息的监听。poll 和 epoll 是一样的。但是 epoll 更加高效。因为 epoll 是监听到消息 塞到 events 数组并返回 数量。后续遍历变化的即可。但是 poll 是无论如何都要遍历全部数组 找到对应的变化,没 epoll 高效。
当有新消息来后,则做对应的处理。数组第一个为文件夹 ,所以对文件夹做单独的处理。
通过 read_notify 方法。添加对应添加和删除,添加则调用 open_device,删除则调用 close_device 移除对应的监听。
因为第0个已处理,所以下面的遍历从下标1开始。遍历到变化时候, 通过 read 读取对应信息到 变量 event 中(input_event),最后根据设置的 falg 打印相对于的信息格式。
while(1) {//int pollres =// 通过 poll 等待消息的监听poll(ufds, nfds, -1);//printf("poll %d, returned %d\n", nfds, pollres);// 第1个是文件夹的变化,对文件夹内容的监听做单独的处理if(ufds[0].revents & POLLIN) {read_notify(device_path, ufds[0].fd, print_flags);}// 因为第0个已处理,所以下面的遍历从下标1开始for(i = 1; i < nfds; i++) {if(ufds[i].revents) {// 判断到有变化if(ufds[i].revents & POLLIN) {// 读取对应的内容到变量 event中,为 input_event 结构体res = read(ufds[i].fd, &event, sizeof(event));if(res < (int)sizeof(event)) {fprintf(stderr, "could not get event\n");return 1;}if(get_time) {printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);}if(print_device)printf("%s: ", device_names[i]);// 传进去 值,根据 对应的 print_flags 做打印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;}}}}