NXP i.MX8系列平台开发讲解 - 3.6 Linux 之Input 子系统(二)

 专栏文章目录传送门返回专栏目录


目录

Linux 之Input 子系统(二)

1. Linux 应用层获取Input数据

2. Android 上使用Input相关

2.1 input命令输入

2.2 代码模拟输入

3. 总结


本文实操环境:

开发环境EVK BoardSockernel
Ubuntu 18.04i.MX 8M QUAD EVKi.MX8MQkernel 5.10

从上一个章节已经大概了解Linux Input子系统的概念以及一些实现的原理,本章节主要讲解Linux Input子系统在应用上该如何使用,以及在Android上的一些使用。


1. Linux 应用层获取Input数据

Linux Input子系统已经在内核给用户提供了统一的接口,在用户层我们可以通过总线上的input设备:

  • 命令:cat /proc/bus/input/devices

可以在Linux 系统去查看

图片

在Linux 应用层获取某个input 设备的事件,大概流程是打开/dev/input/eventX设备,再通过read读取,进行解析既可。代码如下:

/*************************************************************************
    > File Name   : input_event_reader.c
    > Author      : Hywel
    > Mail        : huizh2009@126.com
    > Created Time: Wed 14 Jul 2021 05:48:05 PM CST
 ************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <linux/input.h>
​
#define test_bit(bit) (mask[(bit)/8] & (1 << ((bit)%8)))
​
#define DEV_MAXNUM_LIST       32
​
struct input_event event;
​
const char* help_message = "Usage: ./keycode_test <argument>\n"
                           "Arguments:\n"
                           "  <argument> : /dev/input/eventX\n"
                           "  --help     : Display this help message.\n";
​
void list_input_dev(void)
{
    char name[64];           /* RATS: Use ok, but could be better */
    char buf[256] = { 0, };  /* RATS: Use ok */
    unsigned char mask[EV_MAX/8 + 1]; /* RATS: Use ok */
    int version;
    int i, j;
    int fd = 0;
​
    printf("list dev input...\n");
    printf("====================================================\n");
    for (i = 0; i < DEV_MAXNUM_LIST; i++) {
        sprintf(name, "/dev/input/event%d", i);
        if ((fd = open(name, O_RDONLY, 0)) >= 0) {
            ioctl(fd, EVIOCGVERSION, &version);
            ioctl(fd, EVIOCGNAME(sizeof(buf)), buf);
            ioctl(fd, EVIOCGBIT(0, sizeof(mask)), mask);
            printf("%s\n", name);
            printf("    evdev version: %d.%d.%d\n",
                   version >> 16, (version >> 8) & 0xff, version & 0xff);
            printf("    name: %s\n", buf);
            printf("    features:");
            for (j = 0; j < EV_MAX; j++) {
                if (test_bit(j)) {
                    const char *type = "unknown";
                    switch(j) {
                    case EV_KEY:
                        type = "keys/buttons";
                        break;
                    case EV_REL:
                        type = "relative";
                        break;
                    case EV_ABS:
                        type = "absolute";
                        break;
                    case EV_MSC:
                        type = "reserved";
                        break;
                    case EV_LED:
                        type = "leds";
                        break;
                    case EV_SND:
                        type = "sound";
                        break;
                    case EV_REP:
                        type = "repeat";
                        break;
                    case EV_FF:
                        type = "feedback";
                        break;
                    }
                    printf(" %s", type);
                }
            }
            printf("\n");
            close(fd);
        }
    }
    printf("====================================================\n");
​
}
int main(int argc, char **argv)
{
    char name[64];
    int fd = 0, rc = 0, i = 0;
​
    list_input_dev();
​
    if (argc != 2 || strcmp(argv[1], "--help") == 0) {
        printf("Invalid number of arguments.\n");
        printf("%s", help_message);
        return 1;
    }
​
    if (strncmp(argv[1], "/dev/input/event", 16) != 0) {
        printf("Invalid argument format. Please use /dev/input/eventX.\n");
        printf("%s", help_message);
        return 1;
    }
​
    sprintf(name, "%s", argv[1]);
    if ((fd = open(name, O_RDWR, 0)) < 0) {
        perror("Failed to open input device");
        return 1;
    }
    printf("%s: open, fd = %d\n", name, fd);
​
    while ((rc = read(fd, &event, sizeof(event))) > 0) {
        printf("%-24.24s.%06lu type 0x%04x; code 0x%04x;"
               " value 0x%08x; ",
               ctime(&event.time.tv_sec),
               event.time.tv_usec,
               event.type, event.code, event.value);
        printf("\n");
        // add your process code
    }
​
    if (rc < 0) {
        perror("Failed to read input event");
    }
    
    close(fd);
    return 0;
}

根据代码中,先list 当前设备存在的input 设备,然后通过传入需要打开的input 设备,当在打开的input输入就可以看到相关打印信息。

gcc与交叉编译:

// 如果在本地运行编译,
gcc -static keycode_test.c -o keycode_test
// i.MX8MQ 下使用,则采用交叉编译器编译
aarch64-linux-gnu-gcc -static keycode_test.c -o keycode_test

放入i.MX8MQ中运行:

图片

这里通过USB接入了一个键盘,可以看到input 设备是

/dev/input/event3

在键盘输入的是时候,可以看到相关的数据,type , code 等等数据。


2. Android 上使用Input相关

2.1 input命令输入

input keyevent <keycode>

这条命令是在android 11 下可以使用的,也是在Linux 上得到支持,对于keycode这里是指在

frameworks/base/core/java/android/view/KeyEvent.java

截取部分代码:

图片

比如

# homeinput keycode 3
# backinput keycode 4
# power input keycode 26
#...

图片

在android 终端可以输入看到效果,其实input 这个命令包含很多输入的类型,具体查看

evk_8mq:/ # input --help
Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]
The sources are:
dpad      keyboard      mouse      touchpad      gamepad      touchnavigation      joystick      touchscreen      stylus      trackball
-d: specify the display ID.      (Default: -1 for key event, 0 for motion event if not specified.)The commands and default sources are:      text <string> (Default: touchscreen)      keyevent [--longpress] <key code number or name> ... (Default: keyboard)      tap <x> <y> (Default: touchscreen)      swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)      draganddrop <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)      press (Default: trackball)      roll <dx> <dy> (Default: trackball)      motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)
Error: Error: Unknown command: --help

支持多种sources,这里再举例常用的几个命令如下:


# 通过坐标模拟点击
evk_8mq:/ # input tap 600 600# 
滑动动作,从(300 400)划动到(600,600)时间100ms
evk_8mq:/ # input swipe 300 400 600 600 100
# 拖动动作 从(300 400)托动到(600,600)时间100ms
evk_8mq:/ # input draganddrop 300 400 600 600 1000
# 文本输入 abc
evk_8mq:/ # input text abc#...

2.2 代码模拟输入

目前在系统中,需要通过代码进行模拟输入一些keyevent, 那么该如何操作,因为在系统调用过程中发现采用input 输入命令的方式,会造成系统卡顿延时的情况,为了解决这个问题采用创建虚拟input的方式解决:

/*************************************************************************
    > File Name   : simulate_input.c
    > Author      : Hywel
    > Mail        : huizh2009@126.com
    > Created Time: Wed 14 Jul 2021 05:48:05 PM CST
 ************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
​
#include <linux/uinput.h>
#include <linux/input.h>
#include "simulate_input.h"
​
#define DEBUG_PRINTF printf
static int uinput_fd;
static struct uinput_user_dev uinput_dev;
​
int creat_user_uinput(void)
{
    int i;
    int ret = 0;
​
    uinput_fd = open("/dev/uinput", O_RDWR | O_NDELAY);
    if(uinput_fd < 0) {
        DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
        return -1;//error process.
    }
​
    //to set uinput dev
    memset(&uinput_dev, 0, sizeof(struct uinput_user_dev));
    //snprintf(uinput_dev.name, UINPUT_MAX_NAME_SIZE, "uinput-custom-dev");
    snprintf(uinput_dev.name, UINPUT_MAX_NAME_SIZE, "virtual-uinput-dev");
    uinput_dev.id.version = 1;
    uinput_dev.id.bustype = BUS_VIRTUAL;
​
    ioctl(uinput_fd, UI_SET_EVBIT, EV_SYN);
    ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY);
    ioctl(uinput_fd, UI_SET_EVBIT, EV_MSC);
​
    for(i = 0; i < 256; i++) {
        ioctl(uinput_fd, UI_SET_KEYBIT, i);
    }
    ioctl(uinput_fd, UI_SET_MSCBIT, KEY_CUSTOM_UP);
    ioctl(uinput_fd, UI_SET_MSCBIT, KEY_CUSTOM_DOWN);
​
    ret = write(uinput_fd, &uinput_dev, sizeof(struct uinput_user_dev));
    if(ret < 0) {
        DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
        return ret;//error process.
    }
​
    ret = ioctl(uinput_fd, UI_DEV_CREATE);
    if(ret < 0) {
        DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
        close(uinput_fd);
        return ret;//error process.
    }
    return ret;
}
​
int report_key(unsigned int type, unsigned int keycode, unsigned int value)
{
    struct input_event key_event;
    int ret;
​
    memset(&key_event, 0, sizeof(struct input_event));
​
    gettimeofday(&key_event.time, NULL);
    key_event.type = type;
    key_event.code = keycode;
    key_event.value = value;
    ret = write(uinput_fd, &key_event, sizeof(struct input_event));
    if(ret < 0) {
        DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
        return ret;//error process.
    }
​
    gettimeofday(&key_event.time, NULL);
    key_event.type = EV_SYN;
    key_event.code = SYN_REPORT;
    key_event.value = 0;//event status sync
    ret = write(uinput_fd, &key_event, sizeof(struct input_event));
    if(ret < 0) {
        DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
        return ret;//error process.
    }
​
    return 0;
}
​
void send_simulate_keycode( int keycode)
{
    report_key(EV_KEY, keycode, 1);
    usleep(100);
    report_key(EV_KEY, keycode, 0);
​
    DEBUG_PRINTF("simulate input keycode %d\n", keycode);
​
}
​
int keycode_event_init(void)
{
    DEBUG_PRINTF("keycode event init...\n");
    int ret = creat_user_uinput();
    if(ret < 0) {
        DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
        return -1;//error process.
    }
​
    return ret;
}
​
int main(int argc, char **argv)
{
    int ret = 0;
    int event_code = 0;
#if 1   //for debug input code
    if (argc != 2) {
        printf("Invalid number of arguments.\n");
        printf("Usage: %s <event_code>\n", argv[0]);
        return 1;
    }
    
    event_code  = atoi(argv[1]);
#endif
​
    ret = keycode_event_init();
​
    if(ret) {
        DEBUG_PRINTF("keycode event failed...\n");
        return ret;
    }
​
    //debug.
    DEBUG_PRINTF("start debug %d...\n", event_code );
    //send_simulate_keycode(KEY_CUSTOM_UP);
    
    send_simulate_keycode(event_code);
​
    //send_simulate_keycode(KEY_CUSTOM_DOWN);
    close(uinput_fd);
    return 0;
}

根据代码编译放入i.MX8MQ中运行。

编译:

aarch64-linux-gnu-gcc -static simulate_input.c -o simulate_input

在开发板运行就可以看到,会有相应的选项选择下一个。


3. 总结

本章节主要讲述关于Input子系统的应用,获取外部输入和模拟一个虚拟的input 设备,这些都使用Linux 系统,Android 系统也适用,Android同样进行交叉静态编译也可以运行。

  • 43
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NXP i.MX8M Mini是NXP公司推出的一款处理器系列。它采用了四核ARM Cortex-A53 + 单核ARM Cortex-M4异构多核处理器设计,具有高性能和低功耗的特点。该处理器广泛应用于工业领域,可以满足复杂应用和工业级温度要求。\[1\]\[3\]针对这款处理器,米尔推出了基于i.MX8M Mini芯片的MYC-C8MMX系列核心板和开发板,提供了丰富的接口和功能,如LVDS显示、MIPI-DSI信号引出、MIPI-CSI摄像头接口、音频输入输出、千兆网、多路USB、多串口、WIFI和蓝牙模块等。此外,该核心板具有高性价比和稳定的供货周期,可满足客户的需求。\[2\] #### 引用[.reference_title] - *1* *2* [基于nxp i.mx8m mini的新一代高性价比核心板之王](https://blog.csdn.net/Jason_zhao_MR/article/details/104608706)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [NXP i.MX 8M Mini 开发板规格参数,四核ARM Cortex-A53 + ARM Cortex-M4](https://blog.csdn.net/Tronlong/article/details/125848422)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值