LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Linux input子系统概述

关键词:input、evdev、mousedev、joydev、rfkill、tslib、evtest等等。

 input子系统处理Linux下输入设备。外设接收到输入事件,发送到input core;input core根据事件类型,将事件交给对应的input handler处理,上报用户空间;用户空间收收到事件后进行对应的处理。

1 input子系统框架

input子系统分为input驱动层、input核心层、input事件处理层,最终给用户空间提供可访问的设备节点。

驱动层:输入设备的驱动程序,负责将底层输入设备的数据转换为Linux输入事件类型,向核心层报告输入内容。

核心层:承上启下的为驱动层提供输入设备的注册和操作接口,通知事件层对输入事件进行处理。提供多种类型handler的注册以及handler和输入设备的关联。

事件层:处理各种不同类型设备,主要和用户空间交互。用户空间打开特定设备,当有数据上报时,它将在触发的输入事件后执行特定的用户定义函数。

2 input子系统配置

如下是对input子系统的配置,input子系统支持多种类型的设备:

Device Drivers
  ->Input device support
    ->Export input device LEDs in sysfs     ->Mouse interface     ->[ ] Provide legacy /dev/psaux device     ->(1024) Horizontal screen resolution     ->(768) Vertical screen resolution     ->Event interface     ->Event debugging     ->Keyboards
      ->GPIO Buttons     ->Mice     ->Touchscreens
      ->Goodix I2C touchscreen
      ->EDT FocalTech FT5x06 I2C Touchscreen support

input子系统主要文件:

drivers/input/
├── evbug.c--注册和实现evbug handler。 ├── evdev.c--注册和实现evdev handler。 ├── ff-core.c--input子系统的压力反馈功能。 ├── input.c--input子系统核心。 ├── input-compat.c ├── input-mt.c--Multitouch相关功能。 ├── input-poller.c--input设备的poll功能支持。 ├── keyboard │   ├── gpio_keys.c--gpio-keys通用驱动。 ├── mousedev.c--注册和实现mousedev handler。 ├── serio │   ├── libps2.c │   ├── serio.c └── touchscreen ├── edt-ft5x06.c ├── goodix.c ├── of_touchscreen.c--Touchscreen的dts解析函数。

input子系统初始化:

input_init
  ->class_register--注册input_class。
  ->input_proc_init--创建/proc/bus/input目录,并创建devices(当前input子系统注册的设备)和handler(当前input子系统注册的handler)。
  ->register_chrdev_region--定义input cdev设备序号范围。

3 input子系统数据结构和API

3.1 数据结构

struct input_dev表示input子系统中一个设备,通过input_register_device()/input_unregister_device()从/到input子系统注册/注销。

struct input_dev {
    const char *name;
    const char *phys;
    const char *uniq;--指定的唯一ID编号。
    struct input_id id;

    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];--记录设备支持的事件类型。参考:input-event-codes.h
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];--记录设备支持的按键类型。参考:input-event-codes.h
    unsigned long relbit[BITS_TO_LONGS(REL_CNT)];--表示能产生哪些相对坐标事件。
    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];--表示能产生哪些绝对坐标事件。
    unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
    unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

    unsigned int hint_events_per_packet;

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

    int (*setkeycode)(struct input_dev *dev,
              const struct input_keymap_entry *ke,
              unsigned int *old_keycode);
    int (*getkeycode)(struct input_dev *dev,
              struct input_keymap_entry *ke);

    struct ff_device *ff;

    struct input_dev_poller *poller;

    unsigned int repeat_key;
    struct timer_list timer;

    int rep[REP_CNT];

    struct input_mt *mt;

    struct input_absinfo *absinfo;

    unsigned long key[BITS_TO_LONGS(KEY_CNT)];
    unsigned long led[BITS_TO_LONGS(LED_CNT)];
    unsigned long snd[BITS_TO_LONGS(SND_CNT)];
    unsigned long sw[BITS_TO_LONGS(SW_CNT)];

    int (*open)(struct input_dev *dev);
    void (*close)(struct input_dev *dev);
    int (*flush)(struct input_dev *dev, struct file *file);
    int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

    struct input_handle __rcu *grab;

    spinlock_t event_lock;
    struct mutex mutex;

    unsigned int users;
    bool going_away;

    struct device dev;

    struct list_head    h_list;--当前设备的input_handle列表。
    struct list_head    node;--放入input_dev_list的列表成员。

    unsigned int num_vals;
    unsigned int max_vals;
    struct input_value *vals;

    bool devres_managed;

    ktime_t timestamp[INPUT_CLK_MAX];
};

input事件类型如下:

#define EV_SYN            0x00--用来上报事件,当没有输入事件时应用层read阻塞,收到EV_SYN才会唤醒向应用层上报事件。
#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)

struct input_event表示input设备给用户空间上报的事件,应用层的解读根据不同事件类型而不同:

struct input_event {
    __kernel_ulong_t __sec;--上报事件发送时间秒。
    __kernel_ulong_t __usec;--上报事件发送时间微妙。
    __u16 type;--上报事件类型。
    __u16 code;--上报事件编码。
    __s32 value;--上报事件值。
};

 struct input_handler实现对一类input设备的处理:

struct input_handler {

    void *private;

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    void (*events)(struct input_handle *handle,
               const struct input_value *vals, unsigned int count);
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    void (*disconnect)(struct input_handle *handle);
    void (*start)(struct input_handle *handle);

    bool legacy_minors;
    int minor;
    const char *name;

    const struct input_device_id *id_table;

    struct list_head    h_list;--当前input_handler相关联的input_handle列表。
    struct list_head    node;--放入input_handler_list的列表成员。
};

 那么input_dev和input_handler是如何连接起来的呢?struct input_handler则是建立两者连接关系:

struct input_handle {

    void *private;

    int open;
    const char *name;

    struct input_dev *dev;
    struct input_handler *handler;

    struct list_head    d_node;--位于当前input_handle关联input_dev的input_handle列表。
    struct list_head    h_node;--位于当前input_handle关联input_handler的input_handle列表。
};

3.2 API

3.2.1 input_handler注册注销 

对于不同的input事件由不同的input_handler进行处理,这些input_handler通过如下函数注册/注销:

int __must_check input_register_handler(struct input_handler *);
void input_unregister_handler(struct input_handler *);

 input_register_handler()注册input_handler到input子系统:

input_register_handler
  list_add_tail--将当前input_handler加入到input_handler_list。
  list_for_each_entry--遍历input_dev_list进行dev和handler的匹配。
    input_attach_handler

3.2.2 input_dev注册注销

一个完整的input设备周期:

  • 申请:input_allocate_device()/devm_input_allocate_device()。
  • 注册:input_register_device()。
  • 注销:input_unregister_device()。
  • 释放:input_free_device()。
struct input_dev __must_check *input_allocate_device(void);
struct input_dev __must_check *devm_input_allocate_device(struct device *);
void input_free_device(struct input_dev *dev);
int __must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);

void input_reset_device(struct input_dev *);

通过input_register_device()注册设备到input子系统的流程如下:

input_register_device
  ->input_enable_softrepeat--如果dts中打开autorepeat,则此处使能基于timer的重复按键功能。timer函数为input_repeat_key()。REP_DELAY为250ms,REP_PERIOD为33ms。
  ->device_add
  ->list_add_tail--将设备加入到input_dev_list。
  ->list_for_each_entry--遍历input_handler_list列表,然后将新加入的设备和不同的input_handler连接。
    ->input_attach_handler
      ->input_match_device
        ->input_match_device_id
        ->handler->match--调用input_handler的match函数。
      ->handler->connect--调用input_handler的connect函数。
  ->input_wakeup_procfs_readers

3.2.3 input_dev操作

使用过程中通过input_set_capability()设置intput设备的处理能力;通过intput_event()/input_sync()上报事件。

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value);

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_KEY, code, !!value);
}

static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_REL, code, value);
}
...
static inline void input_sync(struct input_dev *dev)
{
    input_event(dev, EV_SYN, SYN_REPORT, 0);
}

static inline void input_mt_sync(struct input_dev *dev)
{
    input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);

input_event()将事件发送到input子系统给不同handler进行处理:

input_event
  ->input_handle_event
    ->input_pass_values
      ->input_to_handler
        ->handler->event--调用具体的handler处理。
      ->input_start_autorepeat--以REP_DELAY启动autorepeat timer。
        ->input_repeat_key--以REP_PERIOD间隔启动autorepeat timer。
      ->input_stop_autorepeat--停止autorepeat timer。

当输入的事件具备EV_REP时,timer超时后会重复发送按键键值:

static void input_repeat_key(struct timer_list *t)
{
    struct input_dev *dev = from_timer(dev, t, timer);
    unsigned long flags;

    spin_lock_irqsave(&dev->event_lock, flags);

if (test_bit(dev->repeat_key, dev->key) &&
        is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
        struct input_value vals[] =  {
            { EV_KEY, dev->repeat_key, 2 },--重复发送重复按键键值,并且value为2,是一种特殊值。
            input_value_sync--为此值:{ EV_SYN, SYN_REPORT, 1 }。
        };

        input_set_timestamp(dev, ktime_get());
        input_pass_values(dev, vals, ARRAY_SIZE(vals));--将重复的按键值发送到用户层。

        if (dev->rep[REP_PERIOD])
            mod_timer(&dev->timer, jiffies +
                    msecs_to_jiffies(dev->rep[REP_PERIOD]));
    }

    spin_unlock_irqrestore(&dev->event_lock, flags);
}

3.2.4 input_handle注册/注销

int input_register_handle(struct input_handle *);
void input_unregister_handle(struct input_handle *);

input_register_handle()函数建立了input_dev和input_handle的连接:

int input_register_handle(struct input_handle *handle)
{

    if (handler->filter)
        list_add_rcu(&handle->d_node, &dev->h_list);
    else
        list_add_tail_rcu(&handle->d_node, &dev->h_list);--将handle->d_node加入到dev->h_list中,遍历dev->h_list即可找到每个input_handle,进而找到关联的input_handler。

    list_add_tail_rcu(&handle->h_node, &handler->h_list);--将handle->h_node加入到handler->h_list,遍历handler->h_list即可找到每个input_handle,进而找到关联的input_dev。

    if (handler->start)
        handler->start(handle);--调用handler->start。

    return 0;
}

4 input handler驱动

/proc/bus/input/handlers中列出来系统支持的input handler。

cat /proc/bus/input/handlers
N: Number=0 Name=rfkill
N: Number=1 Name=kbd
N: Number=2 Name=sysrq (filter)
N: Number=3 Name=mousedev Minor=32
N: Number=4 Name=evdev Minor=64

4.1 kbd handler

kbd_init()将kbd_handler注册到input子系统:

chr_dev_init
  tty_init
    vty_init
      kbd_init
        input_register_handler

kbd_handler如下:

static struct input_handler kbd_handler = {
    .event        = kbd_event,
    .match        = kbd_match,--在input_attach_handler()被调用。
    .connect    = kbd_connect,--在input_attach_handler()被调用。
    .disconnect    = kbd_disconnect,
    .start        = kbd_start,--在input_register_handle()被调用。
    .name        = "kbd",
    .id_table    = kbd_ids,
};

kbd_handler成员函数被input子系统调用:

kbd_match
kbd_connect
  ->初始化struct input_handle。
  ->input_register_handle
    ->handler->start--调用handler的start成员函数,即kbd_start()。
  ->input_open_device--打开struct input_dev设备。
kbd_start
  tasklet_enable--使能keyboard_tasklet。
kbd_event
  kbd_keycode
  tasklet_schedule--调度keyboard_tasklet。

4.2 evdev handler

对evdev_handler的注册位于evdev_init()中:

evdev_init
  input_register_handler

evdev_handler如下:

static struct input_handler evdev_handler = {
    .event        = evdev_event,
    .events        = evdev_events,
    .connect    = evdev_connect,
    .disconnect    = evdev_disconnect,
    .legacy_minors    = true,
    .minor        = EVDEV_MINOR_BASE,
    .name        = "evdev",
    .id_table    = evdev_ids,
};

evdev_connect在input_attach_handler()中被调用,创建了eventX设备并且建立了input_handler和input_dev之间的连接:

evdev_connect
  input_get_new_minor--从EVDEV_MINOR_BASE(64)开始的EVDEV_MINORS(32)个子设备号。
  dev_set_name--设置设备名称为event%d。
  device_initialize--
  input_register_handle
  cdev_init--使用evdev_fops作为设备操作函数集初始化cdev。
  cdev_device_add--创建一个cdev,创建后的设备位于/dev/input/eventX。

evdev_events()通知用户层数据准备好:

evdev_events
  evdev_pass_values
    __pass_event
      kill_fasync--告诉用户层数据准备好。

4.3 mousedev handler

mousedev_init
  mousedev_create
    mousedev_reserve_minor--默认创建一个mixdev。
      input_get_new_minor--子设备号为MOUSEDEV_MIX(63)。
    dev_set_name--设备名为mice。
    device_initialize
    cdev_init--初始化mixdev cdev,操作函数集为mousedev_fops。
    cdev_device_add--添加mixdev的cdev,即/dev/input/mice。
  input_register_handler
  mousedev_psaux_register
mousedev_exit
  mousedev_psaux_unregister
  input_unregister_handler
  mousedev_destroy

mousedev_handler是类鼠标一类设备的处理函数集:

static struct input_handler mousedev_handler = {
    .event        = mousedev_event,
    .connect    = mousedev_connect,
    .disconnect    = mousedev_disconnect,
    .legacy_minors    = true,
    .minor        = MOUSEDEV_MINOR_BASE,
    .name        = "mousedev",
    .id_table    = mousedev_ids,
};

当注册input_handle时,mousedev_connect()被调用; mousedev_event()在上报数据时被调用。

mousedev_connect
  mousedev_create--非mixdev设备。
    mousedev_reserve_minor
      input_get_new_minor--分配从MOUSEDEV_MINOR_BASE(32)开始的MOUSEDEV_MINORS(31)个子设备号,即32~62。
    dev_set_name--设备名为mouseX。
    device_initialize
    input_register_handle
    cdev_init
    cdev_device_add--创建设备/dev/input/mouseX。
  mixdev_add_device
mousedev_event
  EV_ABS
    mousedev_abs_event
  EV_REL
    mousedev_rel_event
  EV_KEY
    mousedev_key_event
  EV_SYN
    mousedev_notify_readers
      kill_fasync--通知用户空间数据准备好。

5 input设备驱动

5.1 gpio key驱动

 gpio-keys表示基于GPIO一类的按键设备,每个按键配置如下:

/ {
    gpio-keys {
            compatible = "gpio-keys";
            autorepeat;--使能软件autorepeat功能。在input->evbit使能EV_REP。
            key0 {
                label = "USER-KEY0";--按键名称。
                linux,code = <114>;--按键键值。
                gpios = <&gpiog 3 GPIO_ACTIVE_LOW>;--GPIO及其有效状态,即按下的状态。
                gpio-key,wakeup;--表示按键可以唤醒系统。
            };

            key1 {
                label = "USER-KEY1";
                linux,code = <115>;
                gpios = <&gpioh 7 GPIO_ACTIVE_LOW>;
                gpio-key,wakeup;
            };
    };
};

 更多参考:《gpio-keys.txt - Documentation/devicetree/bindings/input/gpio-keys.txt - Linux source code (v5.4.34) - Bootlin (free-electrons.com)》。

 gpio-keys的注册包括:

  • 获取DTS中gpio-key描述。
  • 创建input_dev并初始化。
  • 为每个按键创建基于中断驱动的按键上报处理程序。
gpio_keys_probe
  ->gpio_keys_get_devtree_pdata--遍历每一个子节点,读取gpio key的属性存在struct gpio_keys_button。
  ->devm_input_allocate_device--分配一个带资源管理的struct input_dev。
  ->初始化struct input_dev结构体。
  ->gpio_keys_setup_key
    devm_fwnode_get_gpiod_from_child--获取当前按键对应的GPIO结构体struct gpio_desc。
    ->根据GPIO DTS进行配置:debounce、irq、wakeup_trigger_type等。
      ->gpiod_set_debounce
      ->gpiod_to_irq--根据GPIO分配中断号,需要硬件支持。
    ->input_set_capability--设置当前struct input_dev具备何种事件处理能力。
    ->devm_add_action
    ->devm_request_any_context_irq--注册一个资源管理的中断处理函数。
      ->request_any_context_irq--注册一个中断处理函数。
        ->gpio_keys_gpio_isr--当中断产生时,调用GPIO中断处理函数。
          ->gpio_keys_gpio_work_func--中断中触发对work的调度。
            ->gpio_keys_gpio_report_event--读取GPIO的值,并转换成按键值上报到用户空间。
              ->gpiod_get_value_cansleep--读取GPIO的值。
              ->input_event--将事件上传到input子系统缓冲。
              ->input_sync--通知input子系统将当前设备换冲区内容上报用户空间。
  ->input_register_device--将分配并初始化好后的struct input_dev注册到input子系统。
  ->device_init_wakeup--只要有一个设备具备wakeup功能,则设置gpio-keys具备wakeup功能。
gpio_keys_shutdown
  ->gpio_keys_suspend

5.2 touchscreen驱动

5.2.1 Goodix Touchscreen驱动

 以Goodix Touchscreen驱动为例,dts如下:

&i2c2 {

    ft5x06: ft5x06@38 {
        compatible = "edt,edt-ft5206";
        reg = <0x38>;
        irq-gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
        reset-gpios = <&gpioh 15 GPIO_ACTIVE_LOW>;
        status = "okay";
    };

    gt9147: gt9147@14 {
        compatible = "goodix,gt9147","goodix,gt1151";
        reg = <0x14>;
        interrupt-parent = <&gpioi>;
        interrupts = <1 IRQ_TYPE_EDGE_RISING>;--配置中断。
        irq-gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
        reset-gpios = <&gpioh 15 GPIO_ACTIVE_HIGH>;--配置复位引脚。
        status = "okay";
    };
};

 Goodix Touchscreen驱动基于I2C通信,读取Firmware进行配置。由中断驱动,读取Touchscreen坐标值并上报到用户层:

goodix_ts_driver
  goodix_ts_probe
    goodix_get_gpio_config--从dts获取avdd28/vddio/gpiod_int/gpiod_rest属性。
    regulator_enable
    goodix_read_version--读取GOODIX_REG_ID获取芯片ID和版本号。
    goodix_get_chip_data--根据ID选择特有的私有数据。
    request_firmware_nowait--如果存在gpiod_int/gpiod_rst,则加载固件重新初始化Touchscreen IC。
      goodix_config_cb
        goodix_send_cfg--将固件内容通过I2C写入IC。
        goodix_configure_dev
          devm_input_allocate_device
          input_set_capability--设置input_dev的能力为EV_KEY/EV_ABS。
          input_set_abs_params--设置绝对坐标范围。
          goodix_read_config
          touchscreen_parse_properties
          input_mt_init_slots
          input_register_device--注册Touchscreen到input子系统。
          goodix_request_irq--注册Touchscreen的中断处理函数。
            devm_request_threaded_irq
              goodix_ts_irq_handler--TP中断处理函数,上报触摸点坐标。
                goodix_process_events
                  goodix_ts_read_input_report--通过I2C触摸点坐标和触摸点数。
                  input_report_key--将每个触摸点上传到input子系统缓存中。
                  input_mt_sync_frame
                  input_sync--通知input子系统将当前设备的缓存同步到用户空间。
                goodix_i2c_write_u8
        release_firmware--释放Firmware资源。
        complete_all--通知所有ts->firmware_loading_complete结束等待。
  goodix_ts_remove

5.2.2 tslib测试

tslib提供一个用户校准和使用触摸屏的libts以及一些工具。

主要工具有:

  • ts_calibrate:校准触摸屏。
  • ts_conf
  • ts_finddev
  • ts_harvest
  • ts_print
  • ts_print_mt
  • ts_print_raw
  • ts_test
  • ts_test_mt
  • ts_uinput
  • ts_verify

更多参考:《tslib/README.md at master · libts/tslib · GitHub》。 

6 input子系统sysfs

 input类设备位于:

/sys/class/input/
|-- event0 -> ../../devices/platform/soc/40013000.i2c/i2c-1/1-0014/input/input0/event0
|-- event1 -> ../../devices/platform/gpio-keys/input/input1/event1
|-- input0 -> ../../devices/platform/soc/40013000.i2c/i2c-1/1-0014/input/input0
|-- input1 -> ../../devices/platform/gpio-keys/input/input1
|-- mice -> ../../devices/virtual/input/mice
`-- mouse0 -> ../../devices/platform/soc/40013000.i2c/i2c-1/1-0014/input/input0/mouse0

input设备位于:

/dev/input/
|-- event0
|-- event1
|-- mice
`-- mouse0

当前系统所支持的input设备列表:

cat /proc/bus/input/devices
I: Bus=0018 Vendor=0416 Product=0486 Version=0100
N: Name="Goodix Capacitive TouchScreen"
P: Phys=input/ts
S: Sysfs=/devices/platform/soc/40013000.i2c/i2c-1/1-0014/input/input0
U: Uniq=
H: Handlers=kbd mouse0 event0
B: PROP=2
B: EV=b
B: KEY=400 0 0 0 0 0 0 20000000 0 0 0
B: ABS=2608000 3

I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="gpio-keys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/platform/gpio-keys/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=100003
B: KEY=c0000 0 0 0

当前系统所支持的input_handler列表:

 cat /proc/bus/input/handlers
N: Number=0 Name=rfkill
N: Number=1 Name=kbd
N: Number=2 Name=sysrq (filter)
N: Number=3 Name=mousedev Minor=32
N: Number=4 Name=evdev Minor=64

7 input设备测试

7.1 evtest

 evetst用于调试内核evdev内核事件的工具。

对gpio-keys的evdev时间读取如下:

evtest /dev/input/event1
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100
Input device name: "gpio-keys"
Supported events:--支持的事件类型。
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)--支持的按键键值。
    Event code 114 (KEY_VOLUMEDOWN)
    Event code 115 (KEY_VOLUMEUP)
Key repeat handling:
  Repeat type 20 (EV_REP)--支持按键重复上报功能,第一次250ms延时,后续间隔33ms上报一次。
    Repeat code 0 (REP_DELAY)
      Value    250
    Repeat code 1 (REP_PERIOD)
      Value     33
Properties:
Testing ... (interrupt to exit)
Event: time 1499.651198, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 1--按键按下。
Event: time 1499.651198, -------------- SYN_REPORT ------------
Event: time 1499.827658, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 0--按键抬起。
Event: time 1499.827658, -------------- SYN_REPORT ------------
Event: time 1502.163360, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 1--按键按下。
Event: time 1502.163360, -------------- SYN_REPORT ------------
Event: time 1502.420039, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 2--持续250ms后,上报持续按下状态。
Event: time 1502.420039, -------------- SYN_REPORT ------------
Event: time 1502.470048, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 2--持续33ms后,上报持续按下状态。
Event: time 1502.470048, -------------- SYN_REPORT ------------
Event: time 1502.520035, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 2
Event: time 1502.520035, -------------- SYN_REPORT ------------
Event: time 1502.570034, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 2
Event: time 1502.570034, -------------- SYN_REPORT ------------
Event: time 1502.620034, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 2
Event: time 1502.620034, -------------- SYN_REPORT ------------
Event: time 1502.670033, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 2
Event: time 1502.670033, -------------- SYN_REPORT ------------
Event: time 1502.720030, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 2
Event: time 1502.720030, -------------- SYN_REPORT ------------
Event: time 1502.720030, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 0--按键抬起。
Event: time 1502.720030, -------------- SYN_REPORT ------------

对Touchscreen的evdev事件读取如下:

evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x18 vendor 0x416 product 0x486 version 0x100
Input device name: "Goodix Capacitive TouchScreen"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 125 (KEY_LEFTMETA)
    Event code 330 (BTN_TOUCH)
  Event type 3 (EV_ABS)--支持绝对坐标类型事件。
    Event code 0 (ABS_X)
      Value    472
      Min        0
      Max      800
    Event code 1 (ABS_Y)
      Value     94
      Min        0
      Max      480
    Event code 47 (ABS_MT_SLOT)
      Value      3
      Min        0
      Max        4
    Event code 53 (ABS_MT_POSITION_X)
      Value      0
      Min        0
      Max      800
    Event code 54 (ABS_MT_POSITION_Y)
      Value      0
      Min        0
      Max      480
    Event code 57 (ABS_MT_TRACKING_ID)
      Value      0
      Min        0
      Max    65535
Properties:
  Property type 1 (INPUT_PROP_DIRECT)
Testing ... (interrupt to exit)
Event: time 1562.156705, type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 0
Event: time 1562.156705, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 120--触摸点击以及坐标。
Event: time 1562.156705, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 370
Event: time 1562.156705, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 319
Event: time 1562.156705, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1
Event: time 1562.156705, type 3 (EV_ABS), code 0 (ABS_X), value 370
Event: time 1562.156705, type 3 (EV_ABS), code 1 (ABS_Y), value 319
Event: time 1562.156705, -------------- SYN_REPORT ------------
Event: time 1562.194219, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value -1--触摸抬起。
Event: time 1562.194219, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 0
Event: time 1562.194219, -------------- SYN_REPORT ------------

7.2 input_event裸数据

通过hexdump /dev/intput/event1得到如下数据,每一行数据表示一个struct input_event

hexdump /dev/input/event1
0000000 37d2 0000 5b39 000d 0001 0072 0001 0000--表示EV_KEY,Code为KEY_VOLUMEDOWN,Value为1。
0000010 37d2 0000 5b39 000d 0000 0000 0000 0000--表示EV_SYN。0000020 37d3 0000 a87e 0000 0001 0072 0000 0000--表示EV_KEY,Code为KEY_VOLUMEDOWN,Value为0。
0000030 37d3 0000 a87e 0000 0000 0000 0000 0000--表示EV_SYN。4行数据表示了一个按键的按下和抬起的动作。
0000040 37d4 0000 fc8a 0007 0001 0072 0001 0000--表示EV_KEY,Code为KEY_VOLUMEDOWN,Value为1表示按键按下。
0000050 37d4 0000 fc8a 0007 0000 0000 0000 0000--表示EV_SYN。
0000060 37d4 0000 e703 000b 0001 0072 0002 0000--当具备autorepeat属性时,按键按下不松开,启动timer周期性超时函数input_repeat_key()中发送此键值且value为2的input_value。
0000070 37d4 0000 e703 000b 0000 0000 0001 0000
...
0000120 37d5 0000 389f 0001 0001 0072 0002 0000
0000130 37d5 0000 389f 0001 0000 0000 0001 0000
0000140 37d5 0000 389f 0001 0001 0072 0000 0000--表示EV_KEY,Code为KEY_VOLUMEDOWN,Value为0表示按键抬起。 0000150 37d5 0000 389f 0001 0000 0000 0000 0000--表示EV_SYN。

 

posted on 2024-01-19 23:59  ArnoldLu  阅读(472)  评论(0编辑  收藏  举报

导航