記事のディレクトリ
1入力サブシステムの概要
入力サブシステムは、入力を管理するサブシステムです。pinctrlおよびgpioサブシステムと同様に、特定のタイプのデバイス用にLinuxカーネルによって作成されたフレームワークです。入力サブシステムは入力トランザクションを処理します。任意の入力デバイスドライバーは、入力入力サブシステムによって提供されるインターフェイスを介してカーネルに登録し、サブシステムによって提供される機能を使用してユーザースペースと対話できます。入力デバイスには通常、カーネルで入力デバイスとして表示されるキーボード、マウス、タッチスクリーンなどが含まれます。
入力サブシステムは階層構造であり、ハードウェアドライバー層、サブシステムコア層、イベント処理層の合計3つの層に分かれています。
(1)ハードウェアドライバー層は、特定のハードウェアデバイスの操作を担当します。この層のコードは特定のドライバー用であり、ドライバーの作成者が作成する必要があります。
(2)サブシステムのコア層は、他の2つの層の間のリンクとブリッジであり、ドライバー層のインターフェイスを下向きに、イベント処理層のインターフェイスを上向きに提供します。
(3)イベント処理層は、ユーザープログラムを処理し、ハードウェアドライバー層からユーザープログラムにイベントを報告する責任があります。
さまざまなレイヤー間の通信の基本単位はイベントです。キーボードを押す、タッチスクリーンを押す、マウスを動かすなど、入力デバイスのアクションをイベントに抽象化できます。イベントには、タイプ、コード、値の3つの属性があります。入力サブシステムでサポートされるすべてのイベントは、サポートされるすべてのタイプとそのタイプでサポートされるコードを含め、input.hで定義されます。イベント送信の方向は、ハードウェアドライバーレイヤー->サブシステムコア->イベント処理レイヤー->ユーザースペースです。
2入力ドライバーの書き込みプロセス
まず、入力コアレイヤーに実装されている関数を見てみましょう。入力コアレイヤーファイルはinput.c、パスはdrivers / input / input.cで、内容の一部は次のとおりです。
1767 struct class input_class = {
1768 .name = "input",
1769 .devnode = input_devnode,
1770 };
......
2414 static int __init input_init(void)
2415 {
2416 int err;
2417
2418 err = class_register(&input_class);
2419 if (err) {
2420 pr_err("unable to register input_dev class\n");
2421 return err;
2422 }
2423
2424 err = input_proc_init();
2425 if (err)
2426 goto fail1;
2427
2428 err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
2429 INPUT_MAX_CHAR_DEVICES, "input");
2430 if (err) {
2431 pr_err("unable to register char major %d", INPUT_MAJOR);
2432 goto fail2;
2433 }
2434
2435 return 0;
2436
2437 fail2: input_proc_exit();
2438 fail1: class_unregister(&input_class);
2439 return err;
2440 }
2418行目で、入力クラスが登録されています。システムの起動後、図2.1に示すように、入力クラスのサブディレクトリが/ sys / classディレクトリに生成されます。
2428行目と2489行目では、文字デバイスが登録されているため、入力サブシステムは基本的に文字デバイスドライバーです。メジャーデバイス番号はINPUT_MAJORであり、INPUT_MAJORはinclude / uapi / linux /major.hファイルで定義されています。次のように
#define INPUT_MAJOR 13
入力サブように、システム内のすべてのデバイスのメジャーデバイス番号は13で、プロセスの入力デバイスへの入力サブシステムを使用している場合、文字デバイスを登録する必要はありません我々は唯一のシステムでinput_deviceを登録する必要があります。
1. Input_dev構造体
input_dev構造体は、入力デバイスの基本的なデバイス構造体です。各入力ドライバーは、そのような構造体を割り当てて初期化する必要があります。構造体は、include / linux / input.hファイルで定義され、次のように定義されます。
121 struct input_dev {
122 const char *name;
123 const char *phys;
124 const char *uniq;
125 struct input_id id;
126
127 unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
128
129 unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件类型的位图 */
130 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按键值的位图 */
131 unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相对坐标的位图 */
132 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 绝对坐标的位图 */
133 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 杂项事件的位图 */
134 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /*LED 相关的位图 */
135 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/* sound 有关的位图 */
136 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 压力反馈的位图 */
137 unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /*开关状态的位图 */
......
189 bool devres_managed;
190 };
129行目で、evbitは入力イベントタイプを表します。オプションのイベントタイプは、include / uapi / linux /input.hファイルで定義されています。イベントタイプは次のとおりです。
#define EV_SYN 0x00 /* 同步事件 */
#define EV_KEY 0x01 /* 按键事件 */
#define EV_REL 0x02 /* 相对坐标事件 */
#define EV_ABS 0x03 /* 绝对坐标事件 */
#define EV_MSC 0x04 /* 杂项(其他)事件 */
#define EV_SW 0x05 /* 开关事件 */
#define EV_LED 0x11 /* LED */
#define EV_SND 0x12 /* sound(声音) */
#define EV_REP 0x14 /* 重复事件 */
#define EV_FF 0x15 /* 压力事件 */
#define EV_PWR 0x16 /* 电源事件 */
#define EV_FF_STATUS 0x17 /* 压力状态事件 */
使用するデバイスごとに異なるイベントタイプを選択します。この章の実験では、主要なデバイスを使用するため、EV_KEYイベントタイプを選択する必要があります。
input_dev構造体の129行目から137行目にあるevbit、keybit、およびその他のメンバー変数を確認してください。これらは、対応するさまざまなイベントタイプの値です。たとえば、キーイベントに対応するキービットメンバー。キービットは、キーイベントで使用されるビットマップです。Linuxカーネルは多くのキー値を定義します。これらのキー値は、include / uapi / linux /input.hファイルで定義されます。 。キー値は次のとおりです:
215 #define KEY_RESERVED 0
216 #define KEY_ESC 1
217 #define KEY_1 2
218 #define KEY_2 3
219 #define KEY_3 4
220 #define KEY_4 5
221 #define KEY_5 6
222 #define KEY_6 7
223 #define KEY_7 8
224 #define KEY_8 9
225 #define KEY_9 10
226 #define KEY_0 11
......
794 #define BTN_TRIGGER_HAPPY39 0x2e6
795 #define BTN_TRIGGER_HAPPY40 0x2e7
入力デバイスドライバーを作成するときは、最初にinput_dev構造変数を作成する必要がありますが、手動で作成する必要はありません。入力サブシステムには、input_dev構造変数を作成および登録解除するための次の2つの関数があります。
struct input_dev *input_allocate_device(void) //申请input_dev结构体
void input_free_device(struct input_dev *dev) //注销input_dev结构体
input_allocate_device函数不需要参数,直接返回申请到的input_dev结构体。
input_free_device函数用来释放掉前面申请到的input_dev结构体。
input_dev構造体を適用した後、自分のデバイスに応じてイベントタイプとイベント値を初期化して指定する必要があります。たとえば、キーデバイスのイベントタイプはevbitで、イベント値はkeybitです。
input_dev構造体が初期化されたら、input_register_device関数を使用して、input_devデバイスをLinuxカーネルに登録します。関数プロトタイプは次のとおり
int input_register_device(struct input_dev *dev)
です。dev:登録するinput_dev。
戻り値:0、input_dev登録は成功しました。負の値、input_dev登録は失敗しました。
同様に、入力ドライバの登録を解除するときは、予め登録input_devを登録解除するinput_unregister_device関数を使用する必要がinput_unregister_device関数プロトタイプとして以下れる:
void input_unregister_device(struct input_dev *dev)
上記の内容を要約すると、input_devを登録プロセスは、以下のステップに分けられる:
①最初の使用をinput_devに適用するinput_allocate_device関数。
②input_devのイベントタイプとイベント値を初期化します。
③input_register_device()関数は、入力コアが提供する関数です。この関数は、input_dev構造体を入力サブシステムコア
に登録します。④input_register_device()関数の登録に失敗した場合は、input_free_device()関数を呼び出して、割り当てられたスペースを解放する必要があります。関数が正常に登録されたら、input_unregister_device()関数をアンインストール関数で呼び出して、入力デバイス構造の登録を解除する必要があります。
input_dev登録プロセスのサンプルコードは次のとおりです。
1 struct input_dev *inputdev; /* input 结构体变量 */
2
3 /* 驱动入口函数 */
4 static int __init xxx_init(void)
5 {
6 ......
7 inputdev = input_allocate_device(); /* 申请 input_dev */
8 inputdev->name = "test_inputdev"; /* 设置 input_dev 名字 */
9
10 /*********第一种设置事件和事件值的方法***********/
11 __set_bit(EV_KEY, inputdev->evbit); /* 设置产生按键事件 */
12 __set_bit(EV_REP, inputdev->repbit); /* 重复事件 */
13 __set_bit(KEY_0, inputdev->keybit); /*设置产生哪些按键值 */
14 /************************************************/
15
16 /*********第二种设置事件和事件值的方法***********/
17 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
18 keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
19 /************************************************/
20
21 /*********第三种设置事件和事件值的方法***********/
22 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
23 input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
24 /************************************************/
25
26 /* 注册 input_dev */
27 input_register_device(inputdev);
28 ......
29 return 0;
30 }
31
32 /* 驱动出口函数 */
33 static void __exit xxx_exit(void)
34 {
35 input_unregister_device(inputdev); /* 注销 input_dev */
36 input_free_device(inputdev); /* 删除 input_dev */
37 }
10〜23行目はすべて入力デバイスのイベントとキー値を初期化しています。イベントとキー値の設定には3つの方法が使用されます。
2.入力イベントが報告され
ます。input_dev構造が適用され、入力デバイスドライバーに登録された後、入力デバイスが何らかの情報を入力するため、入力サブシステムは正常に使用できませんが、Linuxカーネルは入力情報の意味を認識していません。そしてそれが何を意味するのか関数なので、特定の入力値または入力イベントを取得するためにドライブしてから、入力イベントをLinuxカーネルに報告する必要があります。たとえば、ボタンデバイスの場合、ボタンが生成された後、Linuxカーネルにボタン値を報告する必要があります。Linuxカーネルは、特定のボタン値を取得した後、対応する関数を実行します。さまざまなイベントがさまざまな関数を報告します。一般的に使用されるAPI関数を見てみましょう。
input_event関数:指定されたイベントと対応する値を報告するために使用されます。関数プロトタイプは次のとおりです。
void input_event(struct input_dev *dev,
unsigned int type,
unsigned int code,
int value)
関数パラメーターと戻り値の意味は次のとおりです:
dev:報告されるinput_dev。
type:EV_KEYなど、報告されたイベントのタイプ。
code:KEY_0、KEY_1など、登録したキー値であるイベントコード。
value:イベント値。たとえば、1はボタンが押されたことを意味し、0はボタンが離されたことを意味します。
戻り値:なし。
input_event関数を使用して、すべてのイベントタイプとイベント値をレポートできます。
input_report_key関数:主要なイベントを報告します。具体的な機能内容は以下のとおりです。
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
input_report_key関数の本質はinput_event関数であることがわかります。もちろん、どの関数を使用しても問題はありません。さまざまなデバイスに対応する関数を使用する方が適切です。
他のイベントに対応するいくつかのレポート機能もあります。
void input_report_rel(struct input_dev *dev, unsigned int code, int value)
void input_report_abs(struct input_dev *dev, unsigned int code, int value)
void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
void input_report_switch(struct input_dev *dev, unsigned int code, int value)
void input_mt_sync(struct input_dev *dev)
input_sync関数:Linuxカーネル入力サブシステムに終了を報告するように指示するために使用されます。input_sync関数は、基本的に同期イベントを報告します。関数プロトタイプは次のとおりです。
void input_sync(struct input_dev *dev)
いくつかの関数がリストされています。例としてキーデバイスを取り上げ、その使用方法を見てみましょう。
1 /* 用于按键消抖的定时器服务函数 */
2 void timer_function(unsigned long arg)
3 {
4 unsigned char value;
5
6 value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */
7 if(value == 0){
/* 按下按键 */
8 /* 上报按键值 */
9 input_report_key(inputdev, KEY_0, 1); /* 最后一个参数 1,按下 */
10 input_sync(inputdev); /* 同步事件 */
11 } else {
/* 按键松开 */
12 input_report_key(inputdev, KEY_0, 0); /* 最后一个参数 0,松开 */
13 input_sync(inputdev); /* 同步事件 */
14 }
15 }
キーの値を取得し、キーが押されているかどうかを判断し、input_report_key関数を使用してキーの値を報告すると、input_sync関数はレポートの終了を示します。
3input_event構造
Linuxカーネルは、input_event構造体を使用して、すべての入力イベントを表します。input_envent構造体は、include / uapi / linux /input.hファイルで定義されています。構造体の内容は次のとおりです。
24 struct input_event {
25 struct timeval time;
26 __u16 type;
27 __u16 code;
28 __s32 value;
29 };
input_event構造体の各メンバー変数を順番に見てみましょう。time:
このイベントが発生する時刻であるtimeは、timeval構造体タイプであり、timeval構造体は次のように定義されています。
1 typedef long __kernel_long_t;
2 typedef __kernel_long_t __kernel_time_t;
3 typedef __kernel_long_t __kernel_suseconds_t;
4
5 struct timeval {
6 __kernel_time_t tv_sec; /* 秒 */
7 __kernel_suseconds_t tv_usec; /* 微秒 */
8 };
2つのメンバー変数tv_secとtv_usecは、32ビットのlong型です。これは覚えておく必要があり、後でイベントレポートデータを分析するときに使用します。
type:EV_KEYなどのイベントタイプ。このイベントがキーイベントであり、このメンバー変数が16ビットであることを示します。
code:イベントコード。たとえば、EV_KEYイベントでは、codeはKEY_0、KEY_1などの特定のキーコードを表します。このメンバー変数は16ビットです。
value:value。たとえば、EV_KEYイベントでは、valueはキー値であり、キーが押されたかどうかを示します。1の場合はキーが押されたことを意味し、0の場合はキーが押されていないことを意味します。またはキーが離されます。
input_envent構造は非常に重要です。これは、すべての入力デバイスがinput_event構造に従って最終的にユーザーに提示され、ユーザーアプリケーションがinput_eventを介して特定の入力イベントまたはキー値などの関連値を取得できるためです。