Linux Kernel (14) 入力サブシステムの詳細解説 I — サブシステムの紹介と関連構造の分析


概要

  • 入力サブシステムは、入力を管理するサブシステムです. 他の Linux サブシステムと同様に、Linux カーネルによって特定のタイプのデバイス用に作成されたフレームワークです.
  • マウス、キーボード、タッチ スクリーンなどはすべて入力デバイスであり、Linux はこれらのデバイスの共通の特性を抽象化し、入力サブシステムのフレームワークを形成します。
  • Linux カーネルは、入力フレームワークを介してユーザー層に入力イベント (キー値、座標など) を報告するだけでよく、アプリケーション層を気にする必要はありません。
  • 入力デバイスは本質的にキャラクターデバイスであり、入力フレームワークの後、最終的にユーザー空間にアクセス可能なデバイスノードを提供します。

入力サブシステム フレームワーク

ここに画像の説明を挿入
Linux 入力サブシステム フレームワーク

ハードウェア入力デバイス:最も低い特定のデバイス (タッチスクリーン、キーボード、マウスなど)

カーネル空間:

  • ドライバー層:入力デバイスの特定のドライバー。基盤となるハードウェア入力を統一されたイベント形式に変換し、それを入力コア レイヤーに伝達する役割を担います。
  • コア層:ドライバー層とイベント層の間。インターフェースを双方向に提供し、ドライバー層インターフェースを下位に提供し、イベント処理インターフェースを上位に提供します。
  • イベント層:基盤となるデバイスは、対応するインターフェイスを抽象化し、それをアプリケーション層に提供します。基になるデバイスによってトリガーされたイベントは、このインターフェイスを介してアプリケーション層に伝達されます。

入力サブシステムの関連構造の紹介

input_dev 構造体

input_dev 構造体は、入力デバイスを表すハードウェア ドライバー層です。
最下位レベルの特定のデバイスは、input_dev 構造を抽象化します。

// include/linux/input.h 
struct input_dev {
    
    
    const char *name;            /* 设备名称 */
    const char *phys;            /* 设备在系统中的路径 */
    const char *uniq;            /* 设备唯一id */
    struct input_id id;          /* input设备id号 */

    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; 

    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];        /* 设备支持的事件类型,主要有EV_SYNC,EV_KEY,EV_KEY,EV_REL,EV_ABS等*/    
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];        /* 按键所对应的位图 */
    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)];        /* 支持led事件 */
    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;        /* 设备关联的反馈结构,如果设备支持 */

    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)];    /* 反应设备当前的led状态 */
    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);    /* 事件处理函数,主要是接收用户下发的命令,如点亮led */

    struct input_handle __rcu *grab;        /* 当前占有设备的input_handle */

    spinlock_t event_lock;        /* 事件锁 */
    struct mutex mutex;            /* 互斥体 */

    unsigned int users;        /* 打开该设备的用户数量(input_handle) */
    bool going_away;            /* 标记正在销毁的设备 */

    struct device dev;        /* 一般设备 */

    struct list_head    h_list;        /* 设备所支持的input handle */
    struct list_head    node;        /* 用于将此input_dev连接到input_dev_list */

    unsigned int num_vals;        /* 当前帧中排队的值数 */
    unsigned int max_vals;        /*  队列最大的帧数*/
    struct input_value *vals;    /*  当前帧中排队的数组*/

    bool devres_managed;        /* 表示设备被devres 框架管理,不需要明确取消和释放*/
};

Linux のデバイスでサポートされるイベントの種類:

コーディング イベントの説明
EV_SYN 0x00 同期イベント
EV_KEY 0x01 キーイベント (マウス、キーボードなど)
EV_REL 0x02 相対座標 (例: マウスの動き、最後の位置からのオフセットの報告)
EV_ABS 0x03 絶対座標 (例: タッチ スクリーンまたはジョイスティック、レポートの絶対座標位置)
EV_MSC 0x04 他の
EV_SW 0x05 スイッチ
EV_LED 0x11 ボタン/デバイスライト
EV_SND 0x12 音/アラーム
EV_REP 0x14 繰り返す
EV_FF 0x15 フィードバックを強いる
EV_PWR 0x16 電源
EV_FF_STATUS 0x17 強制フィードバック状態
EV_MAX 0x1f イベント タイプとビットマスク サポートの最大数

Linux でキー値を定義します (リスト部分):

#define KEY_RESERVED        0
#define KEY_ESC         1
#define KEY_1           2
#define KEY_2           3
#define KEY_3           4
#define KEY_4           5
#define KEY_5           6
#define KEY_6           7
#define KEY_7           8
#define KEY_8           9
#define KEY_9           10
#define KEY_0           11

input_handler 構造体

input_handler 構造体は、イベント ハンドラーを表すイベント レイヤーです。

// include/linux/input.h 
struct input_handler {
    
    

    void *private;        /* 存放handle数据 */

    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;        /* input_dev匹配用的id */

    struct list_head    h_list;    /* 用于链接和handler相关的handle,input_dev与input_handler配对之后就会生成一个input_handle结构 */
    struct list_head    node;    /* 用于将该handler链入input_handler_list,链接所有注册到内核的所有注册到内核的事件处理器 */
};

input_handle 構造体

input_handle 構造体はコア レイヤーに属し、入力デバイスと入力イベント ハンドラーのペアを表します。

// include/linux/input.h 
struct input_handle {
    
    

    void *private;                /* 数据指针 */

    int open;                    /* 打开标志,每个input_handle 打开后才能操作 */
    const char *name;            /* 设备名称 */

    struct input_dev *dev;            /* 指向所属的input_dev */
    struct input_handler *handler;    /* 指向所属的input_handler */

    struct list_head    d_node;        /* 用于链入所指向的input_dev的handle链表 */
    struct list_head    h_node;        /* 用于链入所指向的input_handler的handle链表 */
};

上記の 3 つの構造体は、入力サブシステムで重要な役割を果たします.構造体のメンバーから、2 つのリンクされたリストがあることがわかります。
input_dev と input_handler リンクされたリスト。3 つのテーブルの接続図は次のとおりです。

ここに画像の説明を挿入
Input-dev、input-handler、input-handle の 3 つの構造関係

Input_dev と input_handler のそれぞれの内部リンク リスト図
ここに画像の説明を挿入

  • input_handlers は、グローバルな input_handler_list を通じて相互にリンクされています。

  • input_hande にはグローバル リンク リストがありません. 登録すると、input_dev と input_handler の h_list にそれぞれハングします。

  • input_dev と input_handler を介して、デバイスの登録とイベント ハンドラーで input_handle を見つけることができます. 登録時にペアリング作業を行う必要があり、ペアリング後にリンクが実現されます.

  • Input_dev と input_handler は、input_handle からも見つけることができます。input_handle は、input_dev と input_handler を関連付けるために使用されます。


EVdev イベント関連構造体

1.Evdev(キャラクターデバイスイベント)の仕組み

/* drivers/input/evdev.c */
struct evdev {
    
    
        int open;    /* 设备被打开的计数 */
        struct input_handle handle;  /* 关联的input_handle */ 
        wait_queue_head_t wait;  /* 等待队列,当前进程读取设备,没有事件产生时,
进程就会sleep */
        struct evdev_client __rcu *grab;  /* event响应 */
struct list_head client_list;  /* evdev_client链表,说明evdev设备可以处理多个 evdev _client,可以有多个进程访问evdev设备 */
        spinlock_t client_lock;
        struct mutex mutex;
        struct device dev;
        struct cdev cdev;
        bool exist;   /* 设备存在判断 */
};

2. evdev_client (キャラクターデバイスイベント応答) 構造体

struct evdev_client {
    
    
    unsigned int head;                 /* 动态索引,每加入一个event到buffer中,head++ */
    unsigned int tail;                /* 动态索引,每取出一个buffer中到event,tail++ */
    unsigned int packet_head;         /* 数据包头部 */
    spinlock_t buffer_lock; 
    struct fasync_struct *fasync;     /* 异步通知函数 */
    struct evdev *evdev;
    struct list_head node;            /* evdev_client链表项 */
    int clkid;
    bool revoked;
    unsigned int bufsize;
    struct input_event buffer[];        /* 用来存放input_dev事件缓冲区 */
};

3. evdev_handler (イベントハンドラ) 構造体

/* drivers/input/input.c */
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", /* handler名称 */
        .id_table    = evdev_ids, /* 断开该链接 */
};

input_event 構造体(標準キーコード情報)

/* drivers/input/evdev.c */
struct input_event {
    
                                                                
    struct timeval time;   /* 事件发生的时间  */                                
    __u16 type;             /* 事件类型 */                                      
    __u16 code;             /* 事件码 */                                        
    __s32 value;            /* 事件值 */                                        
}; 

デバイス関連の情報構造

/* include/uapi/linux/input.h */
struct input_id {
    
      
    __u16 bustype;  /* 总线类型 */  
    __u16 vendor;  /* 生产厂商 */  
    __u16 product;  /* 产品类型 */ 
    __u16 version;  /* 版本 */
 };
/* include/uapi/linux/input.h */
struct input_device_id {
    
    

        kernel_ulong_t flags;

        __u16 bustype;  /* 总线类型 */
        __u16 vendor;  /* 生产厂商 */
        __u16 product;  /* 产品类型 */
        __u16 version;  /* 版本 */

        kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t propbit[INPUT_DEVICE_ID_PROP_MAX / BITS_PER_LONG + 1];

        kernel_ulong_t driver_info;
};

おすすめ

転載: blog.csdn.net/weixin_43564241/article/details/130436591