Linux入力サブシステムフレームワーク原理の分析

Linux入力サブシステムフレームワーク原理の分析

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

Linux 入力サブシステムは、上から下に向かって、入力サブシステム イベント処理層 (EventHandler)、入力サブシステム コア層 (InputCore)、および入力サブシステム デバイス ドライバー層の 3 つの層で実装されます。

マウスの動き、キーボードボタンの押下、ジョイスティックの動きなどの入力イベントは、入力ドライバー -> 入力コア -> イベント ハンドラー -> ユーザー空間を経由してユーザー空間に到達し、アプリケーションに渡されます。

【注意】 keyboard.c は /dev/input 以下にノードを生成しませんが、ttyn 端末(シリアルポート端末を除く)の入力として機能します。

ドライバー層

入力サブシステムのデバイス ドライバー層では、主にハードウェア デバイスへの読み取りおよび書き込みアクセス、割り込み設定を実装し、ハードウェアによって生成されたイベントをコア層で定義された仕様に変換してイベント処理層に送信します。低レベルのハードウェア入力を統一されたイベント形式に変換し、入力コアに報告します。

入力サブシステムコア層

コア層では、デバイス ドライバー層の仕様とインターフェイスを提供します。デバイス ドライバー レイヤーでは、ハードウェアを駆動してハードウェア データ (押されたボタン データなど) を取得し、コア レイヤーが提供するインターフェイスを呼び出す方法のみを考慮する必要があります。コア レイヤーは、イベント処理レイヤーにデータを自動的に送信します。ドライバー層に input_register_device などの入力デバイス登録および操作インターフェイスを提供し、イベント処理層にイベントを処理するように通知し、/Proc の下に対応するデバイス情報を生成します。

イベント処理層

イベント処理層の場合、これはユーザー プログラミング インターフェイス (デバイス ノード) であり、ドライバー層によって送信されたデータを処理します。主にユーザー空間と対話します (Linux では、すべてのデバイスはユーザー空間のファイルとして扱われます。一般的なドライバーでは fops インターフェイスが提供され、対応するデバイス ファイルは /dev の下に生成されるため、これらの操作は入力サブシステムのイベント処理層によって完了します)。

/dev/input ディレクトリには、カーネルに登録されているデバイス プログラミング インターフェイスが表示されます。ユーザーはこれらのデバイス ファイルを開いて、ハードウェア操作用のさまざまな入力デバイスを開きます。

イベント処理層は、さまざまなハードウェア タイプに対するユーザー アクセスおよび処理インターフェイスを提供します。たとえば、デバイス /dev/input/mice を開くと、イベント処理層のマウス ハンドラーが呼び出され、入力イベントが処理されます。これは、マウス ハンドラーにはすでに対応するイベント処理メソッドがあるため、デバイス ドライバー層がデバイス ファイルの操作を気にする必要がないことも意味します。

入力サブシステムは、カーネル コード drivers/input/input.c で構成されています。これにより、ユーザーとデバイス ドライバー間のやり取りの詳細が隠蔽され、デバイス ドライバー層とイベント処理層が相互に通信するための統一されたインターフェイスが提供されます。

上の図は、入力サブシステムのコア層によって提供されるサポートと、入力イベント ドライバーにイベントを報告する方法を示しています。

入力デバイスのドライバー開発者は、次の手順を実行する必要があります。

  • ドライバー読み込みモジュールで、入力デバイスでサポートされているイベントタイプを設定します。
  • 割り込み処理関数を登録します。たとえば、キーボード デバイスではキーの上げ下げをプログラムする必要があり、タッチ スクリーン デバイスでは押す、上げる、絶対移動をプログラムする必要があり、マウス デバイスではシングル クリック、上げる、相対移動をプログラムする必要があり、必要に応じてハードウェア データ (キー値/座標/ステータスなど) を送信する必要があります。
  • 入力デバイスを入力サブシステムに登録する

/// ...

入力コアは、入力デバイスの割り当て/解放など、基盤となる入力デバイス ドライバーに必要な API を提供します。

構造体 input_dev *input_allocate_device(void);
void input_free_device(構造体 input_dev *dev);

/**
 * input_allocate_device - 新しい入力デバイスにメモリを割り当てる
 *
 * 準備された構造体 input_dev または NULL を返します。
 *
 * 注意: 解放されていないデバイスを解放するにはinput_free_device()を使用してください。
 * 登録済み; input_unregister_device() は既に登録済みのデバイスに使用してください
 * 登録されたデバイス。
 */
構造体 input_dev *input_allocate_device(void)
{
  構造体 input_dev *dev;
     /*input_dev構造体を割り当て、0に初期化する*/ 
  dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
  もし(dev){
    dev->dev.type = &input_dev_type;/*デバイスタイプを初期化します*/ 
    dev->dev.class = &input_class; /*入力デバイスクラスに設定*/ 
    device_initialize(&dev->dev);/*デバイス構造体を初期化する*/ 
    mutex_init(&dev->mutex); /*ミューテックスを初期化する*/ 
    spin_lock_init(&dev->event_lock); /*イベントスピンロックを初期化する*/ 
    INIT_LIST_HEAD(&dev->h_list);/*リンクリストを初期化する*/ 
    INIT_LIST_HEAD(&dev->node); /*リンクリストを初期化する*/ 

    __module_get(THIS_MODULE);/*モジュール参照テクノロジープラス1*/ 
  }

  dev を返します。
}

入力デバイスの登録/登録解除のインターフェースは次のとおりです。

int __must_check input_register_device(struct input_dev *);
void input_unregister_device(構造体 input_dev *);

/**
 * input_register_device - 入力コアにデバイスを登録する
 * @dev: 登録するデバイス
 *
 * この関数は入力コアにデバイスを登録します。デバイスは
 * input_allocate_device() とそのすべての機能を使用して割り当てられます
 * 登録する前に設定してください。
 * 関数が失敗した場合、デバイスはinput_free_device()で解放する必要があります。
 * デバイスの登録が完了すると、登録を解除できます。
 * input_unregister_device() を使用する場合、input_free_device() は使用しないでください。
 * この場合は呼び出されます。
 */
int input_register_device(構造体 input_dev *dev)
{
    //いくつかの関数で使用されるローカル変数を定義します。static atomic_t input_no = ATOMIC_INIT(0);
  構造体 input_handler *ハンドラ;
  const char *パス;
  整数エラー;
  //evbit メンバーで表される、input_dev でサポートされるイベント タイプを設定します。具体的な種類については後ほどまとめます。
  /* すべての入力デバイスは EV_SYN/SYN_REPORT イベントを生成します。 */
  __set_bit(EV_SYN、dev->evbit);

  /* KEY_RESERVED はユーザー空間に送信されないはずです。 */
  __clear_bit(KEY_RESERVED、dev->keybit);

  /* dev->evbit に記載されていないビットマスクがクリーンであることを確認します。 */
  input_cleanse_bitmasks(dev);

   // ボタンの繰り返しクリックを処理するためにタイマーを初期化します。 (デバウンス)
  /*
   * 遅延と期間がドライバーによって事前に設定されている場合、自動繰り返し
   * はドライバー自体によって処理され、input.c では実行されません。
   */
  init_timer(&dev->timer);
    //rep[REP_DELAY]と[REP_PERIOD]が設定されていない場合はデフォルト値を割り当てます。デバウンスする。
  !dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]の場合{
    dev->timer.data = (long) dev;
    dev->timer.function = input_repeat_key;
    dev->rep[REP_DELAY] = 250;
    dev->rep[REP_PERIOD] = 33;
  }
   // 次の 2 つの関数が定義されているかどうかを確認します。定義されていない場合は、デフォルト値を割り当てます。
  if (!dev->getkeycode)
    dev->getkeycode = input_default_getkeycode; //指定された位置のキー値を取得します if (!dev->setkeycode)
    dev->setkeycode = input_default_setkeycode; //指定された位置のキー値を設定します //input_devのデバイス名をinputNに設定します
    // input0 input1 input2 は sysfs ファイルシステム dev_set_name(&dev->dev, "input%ld", に表示されます。
       (符号なしロング) atomic_inc_return(&input_no) - 1);
    //input->dev に含まれるデバイス構造を Linux デバイス モデルに登録します。
  エラー = device_add(&dev->dev);
  (エラー)の場​​合
    エラーを返します。
  //デバイス パスを印刷し、デバッグ情報を出力します。path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
  printk(KERN_INFO "入力: %s を %s として\n",
    dev->name ? dev->name : "未指定のデバイス", path ? path : "N/A");
  kfree(パス);

  エラー = mutex_lock_interruptible(&input_mutex);
  if (エラー) {
    device_del(&dev->dev);
    エラーを返します。
  }
    // input_dev を input_dev_list リンク リストに追加します (このリンク リストにはすべての入力デバイスが含まれます)
  list_add_tail(&dev->node, &input_dev_list);

  list_for_each_entry(ハンドラ、&input_handler_list、ノード)
   //ハンドラーと input_dev を一致させるために input_attatch_handler() 関数を呼び出します。
    // この関数は非常に重要なので、後で個別に分析します。
    input_attach_handler(デバイス、ハンドラー);

  入力ウェイクアップ_procfs_readers();

  mutex_unlock(&input_mutex);

  0を返します。
}

カーネルは、すべての入力イベントを記述するために統一されたデータ構造を使用します。このデータ構造はinput_eventです。

/*
 * イベント構造自体
 */

構造体 input_event {
  struct timeval time; //<入力イベントが発生した時刻__u16 type; //<入力イベントのタイプ__u16 code; //<入力イベントタイプのコード__s32 value; //<コードの値};

入力イベントタイプ --input_event.type

/*
 * イベントの種類
 */

#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 //< ボタン/デバイス ライト#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)

Linux入力サブシステムは、デバイスドライバ層が入力イベントを報告する機能を提供する。

入力イベントを報告するためのインターフェースは次のとおりです。

/* 指定されたタイプとコードの入力イベントを報告します*/
void input_event(struct input_dev *dev、unsigned int 型、unsigned int コード、int 値);
/* キー値を報告 */
静的インライン void input_report_key(struct input_dev *dev、unsigned int コード、int 値)
{
  input_event(dev、EV_KEY、コード、!!値);
}
/* 相対座標を報告する */
静的インライン void input_report_rel(struct input_dev *dev、unsigned int コード、int 値)
{
  input_event(dev、EV_REL、コード、値);
}
/* 絶対座標を報告する */
静的インライン void input_report_abs(struct input_dev *dev、unsigned int コード、int 値)
{
  input_event(dev、EV_ABS、コード、値);
}
...

入力デバイスによって生成された入力イベントを送信した後、次の関数を呼び出して、デバイスによって生成された完全なイベントを処理するように入力サブシステムに通知する必要があります。

void input_sync(構造体 input_dev *dev);

[例] ドライバーの実装 - 報告終了 input_sync() 同期は、入力コア サブシステムに報告が終了したことを通知するために使用されます。タッチ スクリーン デバイス ドライバーでは、クリックの報告プロセス全体は次のようになります。

input_reprot_abs(input_dev,ABS_X,x); //x座標input_reprot_abs(input_dev,ABS_Y,y); //y座標input_reprot_abs(input_dev,ABS_PRESSURE,1);
input_sync(input_dev); //同期終了

[例] キー割り込みプログラム

//ボタンの初期化 static int __init button_init(void)
// 割り込み要求 if(request_irq(BUTTON_IRQ,button_interrupt,0,"button",NUll))
    –EBUSY を返します。
  set_bit(EV_KEY,button_dev.evbit); //EV_KEYイベントをサポート set_bit(BTN_0,button_dev.keybit); //デバイスの2つのキーをサポート set_bit(BTN_1,button_dev.keybit); //
  input_register_device(&button_dev); //入力デバイスを登録する}
/*キー割り込みのイベントを報告*/
静的 void button_interrupt(int irq,void *dummy,struct pt_regs *fp)
{
  input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));//レジスタBUTTON_PORT0の値を読み取ります input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1));
  input_sync(&button_dev);
}

[概要] 入力サブシステムは依然として文字デバイス ドライバーですが、コードの量が大幅に削減されています。入力サブシステムは、初期化とイベント レポートの 2 つのタスクのみを完了する必要があります (Linux では、これは割り込みによって実現されます)。

イベントハンドラ層分析

入力サブシステムのデータ構造図

Input_handler 構造体

構造体 input_handle;

/**
 * struct input_handler - 入力デバイスのインターフェースの1つを実装します
 * @private: ドライバー固有のデータ
 * @event: イベントハンドラ。このメソッドは入力コアによって呼び出されます。
 * 割り込みが無効になり、dev->event_lockスピンロックが保持されるなど
 * 眠らないかもしれない
 * @filter: @eventに似ています。通常のイベントハンドラを
 * 「フィルター」。
 * @match: デバイスのIDとハンドラーのid_tableを比較した後に呼び出されます
 * デバイスとハンドラー間のきめ細かいマッチングを実行する
 * @connect: 入力デバイスにハンドラをアタッチするときに呼び出されます
 * @disconnect: ハンドラを入力デバイスから切断します
 * @start: 指定されたハンドルのハンドラを開始します。この関数は、
 * connect()メソッドの直後およびプロセスの実行時に入力コア
 * デバイスを「つかんだ」人がそれを解放する
 * @fops: このドライバが実装するファイル操作
 * @minor: このドライバのデバイスの32個のマイナー範囲の始まり
 * 提供できる
 * @name: ハンドラの名前。/proc/bus/input/handlers に表示されます。
 * @id_table: このドライバが使用できるinput_device_idのテーブルへのポインタ
 * ハンドル
 * @h_list: ハンドラに関連付けられた入力ハンドルのリスト
 * @node: ドライバーをinput_handler_listに配置する
 *
 * 入力ハンドラは入力デバイスに接続し、入力ハンドルを作成します。
 * 特定の入力デバイスには複数のハンドラが接続されている可能性があります。
 * 同時に、全員が入力イベントのコピーを受け取ります。
 * デバイス。
 *
 * 入力フィルタの実装にも同じ構造が使われています。入力コア
 * フィルターを最初に実行し、イベントを通常のハンドラーに渡さないようにします
 * いずれかのフィルターがイベントをフィルタリングする必要があることを示している場合(
 * filter() メソッドから %true を返します。
 *
 * 入力コアはconnect()とdisconnect()の呼び出しをシリアル化することに注意してください。
 * メソッド。
 */
構造体 input_handler {

  void *プライベート;

  void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
  bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
  bool (*match)(構造体 input_handler *handler、構造体 input_dev *dev);
  int (*connect)(構造体 input_handler *handler、構造体 input_dev *dev、const 構造体 input_device_id *id);
  void (*切断)(構造体input_handle *handle);
  void (*start)(構造体 input_handle *handle);

  定数構造体 file_operations *fops;
  int マイナー;
  定数char *名前;

  定数構造体 input_device_id *id_table;

  構造体 list_head h_list;
  構造体 list_head ノード;
};

[例] evdev.c の evdev_handler を例に挙げます。

静的構造体 input_handler evdev_handler = {
        .event = evdev_event, //<read メソッドで読み取られた入力イベントをシステムに報告します。connect = evdev_connect, //<input_dev と一致した後、connect を呼び出してビルドします。disconnect = evdev_disconnect,
        .fops = &evdev_fops, //<イベントデバイスファイルの操作方法。minor = EVDEV_MINOR_BASE, //<マイナーデバイス番号のベース値。name = "evdev",
        .id_table = evdev_ids, //<一致ルール};

入力デバイスドライバの簡単な例

document/input/input-programming.txt ファイルでは、入力デバイス ドライバーを作成するための基本的な手順について説明しています。

入力ドライバのプログラミング
~~~~~~~~~~~~~~~~~~~~~~~~~

1. 入力デバイスドライバの作成
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1.0 最も単純な例
~~~~~~~~~~~~~~~~~~~~~~~~

入力デバイスドライバの非常に簡単な例を示します。デバイスには
ボタンは1つだけで、そのボタンはI/OポートBUTTON_PORTからアクセスできます。
押されたり放されたりすると、BUTTON_IRQ が発生します。ドライバーは次のようになります。

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/io.h>

静的構造体 input_dev *button_dev;

静的 irqreturn_t button_interrupt(int irq, void *dummy)
{
  input_report_key(button_dev、BTN_0、inb(BUTTON_PORT) & 1);
  input_sync(ボタンデバイス);
  IRQ_HANDLED を返します。
}

静的 int __init button_init(void)
{
  整数エラー;

  if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
        printk(KERN_ERR "button.c: irq %d を割り当てることができません\n", button_irq);
        -EBUSY を返します。
    }

  ボタンデバイス = input_allocate_device();
  if (!button_dev) {
    printk(KERN_ERR "button.c: メモリが足りません\n");
    エラー = -ENOMEM;
    err_free_irq に移動します。
  }

  button_dev->evbit[0] = BIT_MASK(EV_KEY);
  button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);

  エラー = input_register_device(button_dev);
  if (エラー) {
    printk(KERN_ERR "button.c: デバイスの登録に失敗しました\n");
    err_free_dev に移動します。
  }

  0を返します。

 エラー_free_dev:
  ボタンデバイスを入力
 err_free_irq:
  free_irq(BUTTON_IRQ、button_interrupt);
  エラーを返します。
}

静的void __exit button_exit(void)
{
    input_unregister_device(button_dev);
  free_irq(BUTTON_IRQ、button_interrupt);
}

module_init(ボタン_init);
module_exit(ボタン終了);

1.1 例で行っていること
~~~~~~~~~~~~~~~~~~~~~~~~~

まず、<linux/input.h>ファイルをインクルードする必要があります。これは、
入力サブシステム。これにより、必要なすべての定義が提供されます。

_init関数はモジュールのロード時または
カーネルを起動すると、必要なリソースを取得します(また、
デバイスの存在のため)。

次に、input_allocate_device()で新しい入力デバイス構造体を割り当てます。
入力ビットフィールドを設定します。このようにしてデバイスドライバは他のデバイスに
入力システムの部分とは何か - どのようなイベントが生成されるか、
この入力デバイスで受け入れられます。この例のデバイスはEV_KEYのみを生成できます。
型イベントとBTN_0イベントコードのみを設定します。したがって、これらのイベントのみを設定します。
2ビット。

  set_bit(EV_KEY、button_dev.evbit);
  set_bit(BTN_0、button_dev.keybit);

同様に、1ビット以上の場合は、最初のアプローチは次のようになります。
短くなります。

次に、サンプルドライバは入力デバイス構造体を登録するために、

  input_register_device(&button_dev);

これにより、入力ドライバのリンクリストにbutton_dev構造が追加され、
デバイスハンドラモジュールの_connect関数を呼び出して新しい入力を通知する
デバイスが出現しました。input_register_device()はスリープする可能性があるため、
割り込みから、またはスピンロックが保持された状態で呼び出されることはありません。

使用中、ドライバーの唯一の使用機能は

  ボタン割り込み()

ボタンからの割り込みごとに状態をチェックし、報告する
経由で

  入力レポートキー()

入力システムへの呼び出し。割り込みが
ルーチンは2つの同じ値のイベント(例えば、押す、押す)を報告しません
入力システム、input_report_*関数は以下をチェックする。
彼ら自身。

そして、

  入力同期()

イベントを受け取った人に電話して、完全なレポートを送ったことを伝えます。
これは1つのボタンの場合重要ではないようですが、非常に重要です
例えばマウスの動きでは、XとYの値は不要です
別々に解釈すると、異なる動きが生じるため、別々に解釈する必要があります。

1.2 dev->open() と dev->close()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ドライバーがデバイスを繰り返しポーリングする必要がある場合、
そこから割り込みが発生し、ポーリングを実行するにはコストがかかりすぎる
常に、またはデバイスが貴重なリソース(割り込みなど)を使用する場合、
オープンコールバックとクローズコールバックを使用して、ポーリングを停止できるタイミングを知ることができます。
割り込みを解放し、ポーリングを再開するか割り込みを取得する必要があるとき
もう一度。これを行うには、サンプル ドライバーに以下を追加します。

静的 int button_open(構造体 input_dev *dev)
{
  if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
        printk(KERN_ERR "button.c: irq %d を割り当てることができません\n", button_irq);
        -EBUSY を返します。
    }

    0を返します。
}

静的void button_close(構造体input_dev *dev)
{
    free_irq(IRQ_AMIGA_VERTB、ボタン割り込み);
}

静的 int __init button_init(void)
{
  ...
  button_dev->open = button_open;
  button_dev->close = button_close;
  ...
}

入力コアはデバイスのユーザー数を追跡し、
最初のユーザーが接続したときにのみdev->open()が呼び出されるようにする
デバイスに接続し、最後のユーザーがデバイスを終了したときにdev->close()が呼び出されます。
切断されます。両方のコールバックの呼び出しはシリアル化されます。

open()コールバックは成功した場合は0を返し、失敗した場合は0以外の値を返す。
失敗した場合。close() コールバック (void) は常に成功する必要があります。

1.3 基本的なイベントの種類
~~~~~~~~~~~~~~~~~~~~~

最も単純なイベント タイプは EV_KEY で、キーとボタンに使用されます。
入力システムに報告されるのは次の経由です:

  input_report_key(構造体 input_dev *dev、int コード、int 値)

コードの許容値(0からKEY_MAXまで)についてはlinux/input.hを参照してください。
値は真理値として解釈されます。つまり、ゼロ以外の値はキーを意味します。
押された場合、ゼロ値はキーが離されたことを意味します。入力コードはイベントのみを生成します
値が以前と異なる場合。

EV_KEYに加えて、EV_RELと
EV_ABS。これらは、
デバイス。相対値とは、たとえば X 軸のマウスの動きのことです。
マウスはそれを最後の位置からの相対的な差として報告します。
絶対座標系が存在しないからです。絶対座標系は
イベントは、ジョイスティックとデジタイザーのためのものです。
絶対座標系。

デバイスにEV_RELボタンを報告させるのはEV_KEYの場合と同じくらい簡単です。
対応するビットを設定し、

  input_report_rel(構造体input_dev *dev、intコード、int値)

関数。イベントはゼロ以外の値に対してのみ生成されます。

しかし、EV_ABSには少し特別な注意が必要です。
input_register_deviceでは、input_devに追加のフィールドを入力する必要があります。
デバイスが持つ各絶対軸の構造体。ボタンデバイスにも
ABS_X軸:

  button_dev.absmin[ABS_X] = 0;
  button_dev.absmax[ABS_X] = 255;
  button_dev.absfuzz[ABS_X] = 4;
  button_dev.absflat[ABS_X] = 8;

または、次のように言うこともできます。

  input_set_abs_params(button_dev、ABS_X、0、255、4、8);

この設定はジョイスティックのX軸に適しており、最小値は
0、最大255(ジョイスティックが到達できる必要がありますが、
時にはそれ以上の報告をすることもあるが、常に最小値に到達できなければならない。
最大値)、データ内のノイズは最大+- 4、中央はフラット
サイズ8の位置。

absfuzzとabsflatが必要ない場合は、ゼロに設定することができます。つまり、
物体は正確であり、常に正確に中心位置に戻る
(もしあれば)。

1.4 BITS_TO_LONGS()、BIT_WORD()、BIT_MASK()
~~~~~~~~~~~~~~~~~~~~~~~~~~

bitops.h の次の 3 つのマクロは、ビットフィールドの計算に役立ちます。

  BITS_TO_LONGS(x) - ビットフィールド配列の長さをlongで返します。
        x ビット
  BIT_WORD(x) - ビットxの配列のインデックスをlongで返します。
  BIT_MASK(x) - ビットxのインデックスをlongで返します。

1.5 id* と name フィールド
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

入力デバイスを入力によって登録する前に、dev->nameを設定する必要があります。
デバイスドライバ。これは「汎用ボタンデバイス」のような文字列で、
デバイスのユーザーフレンドリな名前。

id*フィールドにはバスID(PCI、USBなど)、ベンダーID、デバイスIDが含まれます。
デバイスのバスIDはinput.hで定義されています。ベンダーIDとデバイスIDは
pci_ids.h、usb_ids.hなどのインクルードファイルで定義されています。これらのフィールド
登録する前に入力デバイス ドライバーによって設定する必要があります。

idtypeフィールドは入力デバイスの特定の情報に使用できます。
ドライバ。

id フィールドと name フィールドは、evdev インターフェースを介してユーザーランドに渡すことができます。

1.6 keycode、keycodemax、keycodesizeフィールド
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

これら 3 つのフィールドは、密なキーマップを持つ入力デバイスで使用する必要があります。
キーコードは、スキャンコードから入力システム キーコードにマッピングするために使用される配列です。
キーコード最大値には配列のサイズが含まれ、キーコードサイズには
各エントリのサイズ(バイト単位)。

ユーザースペースは、現在のスキャンコードとキーコードのマッピングを照会したり変更したりすることができます。
対応する evdev インターフェイス上の EVIOCGKEYCODE および EVIOCSKEYCODE ioctl。
デバイスに前述の3つのフィールドがすべて入力されている場合、ドライバーは
キーコードの設定と照会のカーネルのデフォルト実装に依存する
マッピング。

1.7 dev->getkeycode() および dev->setkeycode()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
getkeycode() および setkeycode() コールバックにより、ドライバーはデフォルトを上書きできます。
入力コアによって提供されるキーコード/キーコードサイズ/キーコード最大マッピングメカニズム
スパース キーコード マップを実装します。

1.8 キーの自動繰り返し
~~~~~~~~~~~~~~~~~~

...は簡単です。input.cモジュールで処理されます。ハードウェアオートリピートは
多くのデバイスに存在せず、また存在する場所でも使用されていないため、
現状では、時々壊れることがあります(キーボード:東芝のノートパソコン)。
デバイスの自動繰り返しを設定するには、dev->evbitでEV_REPを設定するだけです。
入力システムによって処理されます。

1.9 その他のイベントタイプ、出力イベントの処理
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

これまでの他のイベントの種類は次のとおりです。

EV_LED - キーボードの LED に使用されます。
EV_SND - キーボードのビープ音に使用されます。

これらは、例えばキーイベントと非常に似ていますが、他の
方向 - システムから入力デバイスドライバへ。入力デバイスが
ドライバーはこれらのイベントを処理することができ、evbitのそれぞれのビットを設定する必要があります。
*そして* コールバックルーチン:

  button_dev->イベント = button_event;

int button_event(struct input_dev *dev、unsigned int 型、unsigned int コード、int 値);
{
  if (タイプ == EV_SND && コード == SND_BELL) {
    outb(値、BUTTON_BELL);
    0を返します。
  }
  -1 を返します。
}

このコールバックルーチンは割り込みまたはBHから呼び出すことができます(ただし、
(ルールではありません)したがって、スリープ状態になってはならず、終了するまでに時間がかかりすぎてもいけません。

入力プログラミング.txt

この例で提供されるサンプル コードは、ボタン デバイスについて説明しています。生成されたイベントは、BUTTON_PORT ピンを通じて取得されます。押下/解放が発生すると、BUTTON_IRQ がトリガーされます。以下は、ドライバーのソース コードです。

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/io.h>

static struct input_dev *button_dev; /*入力デバイス構造体*/ 
/*割り込み処理関数*/ 
静的 irqreturn_t button_interrupt(int irq, void *dummy)
{
  /*キーイベントを入力サブシステムに報告する*/ 
  input_report_key(button_dev、BTN_0、inb(BUTTON_PORT) & 1);
  /*レポートが送信されたことを受信者に通知する*/ 
  input_sync(ボタンデバイス);
  IRQ_HANDLED を返します。
}
/*関数をロード*/ 
静的 int __init button_init(void)
{
  整数エラー;
  /* 要求割り込み処理関数 */ // 成功した場合は 0 を返し、無効な場合は -INVAL を返します if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
        /*アプリケーションが失敗した場合は、エラーメッセージを出力します*/ 
        printk(KERN_ERR "button.c: irq %d を割り当てることができません\n", button_irq);
        -EBUSY を返します。
    }
  /* デバイス構造体を割り当てる */ 
  //sys/class/input/input-n の下にデバイス属性ファイルを作成します button_dev = input_allocate_device();
  if (!button_dev) { /*割り当てが成功したかどうかを判断します*/ 
    printk(KERN_ERR "button.c: メモリが足りません\n");
    エラー = -ENOMEM;
    err_free_irq に移動します。
  }

  button_dev->evbit[0] = BIT_MASK(EV_KEY); /*キー情報を設定する*/ 
  button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);

  error = input_register_device(button_dev); /*入力デバイスを登録する*/ 
  if (エラー) {
    printk(KERN_ERR "button.c: デバイスの登録に失敗しました\n");
    err_free_dev に移動します。
  }

  0を返します。
 /*以下はエラー処理です*/ 
 エラー_free_dev:
  ボタンデバイスを入力
 err_free_irq:
  free_irq(BUTTON_IRQ、button_interrupt);
  エラーを返します。
}
 /*アンインストール関数*/ 
静的void __exit button_exit(void)
{
  input_unregister_device(button_dev); /*ボタンデバイスの登録を解除する*/ 
  free_irq(BUTTON_IRQ, button_interrupt);/*ボタンによって占有されている割り込みラインを解放する*/ 
}

module_init(ボタン_init);
module_exit(ボタン終了);

これはこの簡単な例で確認できます。

  • 初期化関数 button_init() で割り込み処理関数を登録し、input_allocate_device() 関数を呼び出して input_dev 構造体を割り当て、input_register_device() を呼び出して登録します。
  • 割り込み処理関数 button_interrupt() では、インスタンスは受信したキー情報を入力サブシステムに報告し、入力サブシステムを介してユーザーモード プログラムにキー入力情報を提供します。

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • Windows 10 に Linux サブシステムをインストールする 2 つの方法 (画像とテキスト付き)
  • Linux 時間サブシステムの時間表現例の詳細な説明
  • Win10 の Linux サブシステムを有効にする方法を説明します (詳細な画像とテキスト付き)
  • Linuxシステムにおける重要なサブディレクトリの問題について話す
  • Win10 インストール Linux サブシステム グラフィック チュートリアル
  • Linuxシステムで子プロセスを作成するためのfork()関数の使い方をマスターするだけです

<<:  MySQL 8.0 における非同期レプリケーションの 3 つの方法について簡単に説明します。

>>:  JavaScriptはシンプルな計算機能を実装します

推薦する

uni-app WeChatアプレット認証ログイン実装手順

目次1. appIDの申請と設定1. appidの取得方法2. AppIDの設定2. 基本的なユーザ...

Vue2で配列の変更を検出できない理由と解決策

目次回避策Vue2.0 で 2 つの配列の変更を監視できないのはなぜですか?ソースコード分析ヴュー3...

HTML TextArea でのフォーマット保存の問題の解決方法

textarea の形式は保存時にデータベースに保存できますが、表示時には /n と相互に変換できな...

Vue シングルファイルコンポーネントの実装

最近、vue について読みました。これまで基本的に見落としていた単一ファイル コンポーネントを見つけ...

MySQL の完全なデータベース バックアップからデータベースとテーブルを復元する方法

公式の MySQL ダンプ ツールで、特定のデータベースのみを復元するにはどうすればよいですか?完全...

生年月日を年齢に変換し、グループ化して人数を数えるMySQLの例

データベースのクエリ `学生`から*を選択 クエリ結果id名前誕生日1張三1970-10-01 2李...

PHP スケジュールバックアップ MySQL および mysqldump 構文パラメータの詳細

まず、MySQL バックアップ コマンド mysqldump の一般的な操作例をいくつか紹介します。...

Vueのフィルターとディレクティブの詳細な説明

目次vueカスタムディレクティブグローバル指令ローカル指示使用フック関数(両方ともオプション)使用方...

HTMLは正規表現を使用してテーブルの例をテストします

以下は、HTML で正規表現を使用してテーブルをチェックするサンプル コードです。具体的なコードの内...

MySQL 5.7.24 圧縮パッケージのインストールと設定方法のグラフィックチュートリアル

この記事では、参考までにMySQL 5.7.24圧縮パッケージのインストールチュートリアルを紹介しま...

nginx をシャットダウン/再起動/起動する方法

閉鎖サービス nginx 停止systemctl 停止 nginx起動するサービス nginx 開始...

MyCat を使用して Linux で MySQL マスター/スレーブの読み取り/書き込み分離を実装する方法

目次Linux - MyCat を使用して MySQL マスター スレーブの読み取り書き込み分離を実...

JavaScriptのプリミティブ値とラッパーオブジェクトの詳細な紹介

目次序文文章プリミティブ型プリミティブ値ラッパーオブジェクト物体コンストラクタ通常機能(関数)プリミ...

Ubuntu 20.04 に Xrdp サーバー (リモート デスクトップ) をインストールする方法

Xrdp は、グラフィカル インターフェイスを通じてリモート システムを制御できる Microsof...

デプロイから基本操作までDocker Swarm

Docker SwarmについてDocker Swarm は次の 2 つの部分で構成されます。 D...