記事ディレクトリ
概要
- 入力サブシステムは、入力を管理するサブシステムです. 他の 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;
};