入力サブシステム - カーネルドライバー - Android
android-goldfish-5.4-dev
AOSP > ドキュメント > 主要トピック > 入力
https://www.kernel.org/doc/Documentation/input/input.txt
https://www.kernel.org/doc/Documentation/input/event-codes.txt
https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
1. 入力サブシステム関連定義
1.1 コードの場所
Android_Kernel\goldfish\drivers\input
Android_Kernel\goldfish\include\linux\input.h
標準的なイベント タイプとコードのセットを定義します
1.2 input_dev 構造体: 入力デバイスを表します
Name:デバイスの名前
Phys:システム階層内のデバイスへの物理パス
Uniq:デバイスの一意の識別子 (デバイスに識別子がある場合)
Id:デバイスの ID (struct input_Id
)
Propbit:デバイスのプロパティのビットマップそして癖。
Evbit:デバイスがサポートするイベント タイプのビットマップ (EV_KEY
、EV_REL
など)
Keybit:このデバイスが持つビットマップRelbit:デバイスの相対軸ビットマップkeys/buttons
Absbit :デバイスの絶対軸ビットマップMscbit:デバイスがサポートするさまざまなイベントのビットマップLedbit :デバイス上に存在する LED ビットマップSndbit:デバイスによってサポートされているサウンド効果ビットマップFfbit:デバイスによってサポートされているフォース フィードバック効果ビットマップSwbit:デバイス上に存在するスイッチ ビットマップHint_events_per_packet:データ パケット内でデバイスによって生成されたイベントの平均数 ( /間のイベントの)。イベントに対応するために必要なバッファ サイズを見積もるためにイベント ハンドラーによって使用されます。Keycodemax:キーコードテーブルのサイズKeycodesize:
EV_SYN
SYN_REPORT
キーコード テーブル内の要素のサイズ
Keycode:このデバイスのスキャンコードからキーコードへのマッピング
Getkeycode:現在のキーマップを取得するためのオプションのレガシー メソッド。
Setkeycode:現在のキー マッピングを変更するためのオプションのメソッド。スパース キー マッピングの実装に使用されます。指定しない場合は、デフォルトのメカニズムが使用されます。このメソッドは、event_lock が保持されている間に呼び出されるため、休止状態にすることはできません。
Ff:デバイスがフォース フィードバック効果をサポートする場合、デバイスに関連付けられたフォース フィードバック構造体。
Poller:デバイスがポーリング モードを使用するように設定されている場合、デバイスに関連付けられたポーラー構造体。最後に押されたキーのキー コードを保存します; 自動ソフトウェア監視の実装に使用されますTimer:ソフトウェア自動回復用のタイマーRep:自動再生パラメータの現在値 (遅延、レート) Mt:マルチタッチ状態へのポインタAbsinfo:絶対軸要素が含まれます情報の配列(現在値、最小値、最大値、フラット値、ブラー値、解像度) Key:デバイスのキー/ボタンの現在の状態を反映しますLed:デバイスの現在の状態を反映します Led Snd:の現在の状態を反映します効果音Sw:デバイススイッチの現在の状態を反映しますOpen:最初のユーザーが呼び出したとき
&struct input_Absinfo
input_Open_device()
このメソッドは のときに呼び出されます。ドライバーは、イベント (ポーリング スレッドの開始、IRQ の要求、URB の送信など) の生成を開始するようにデバイスを準備する必要があります。
Close:このメソッドは、最後のユーザーが input_Close_device() を呼び出したときに呼び出されます。
フラッシュ:デバイスをクリアします。最も一般的には、デバイスから切断されたときにデバイスに読み込まれるフォース フィードバック効果を除去するために使用されます。
イベント:デバイスに送信されるイベントのイベント ハンドラー ( や などEV_LED
)EV_SND
。デバイスは、要求されたアクション (LED をオンにする、サウンドを再生するなど) を実行する必要があります。 protected
Event_lock を呼び出します。スリープできません。
Grab:現在、デバイスの入力ハンドルを (EVIOCGRAB 経由でioctl
) 取得しています。ハンドルがデバイスを取得すると、そのデバイスからのすべての入力イベントの唯一の受信者になります。
Event_lock:このスピンロックは、input core
デバイスの新しいイベントが受信されて処理されるときに取得されます (in)。デバイス パラメータ (や、など) にアクセスしたり変更したりするコードは、デバイスが入力コアに登録された後にこのロックを使用する必要がありますinput_Event()
。Mutex:およびメソッドの呼び出しをシリアル化します。Users:このデバイス (入力ハンドラー) を開いたユーザーの数を保存します。これを使用して、最初のユーザーがデバイスを開いたときと、最後のユーザーがデバイスを閉じたときにのみ呼び出されるようにします。keymap
absmin
absmax
absfuzz
open()
close()
flush()
input_open_device()
input_close_device()
dev->open()
dev->close()
Going_away:ログアウト中のデバイスをマークし、失敗を-ENODEV
引き起こすために使用しますinput_open_device*()
。
Dev:このデバイスのドライバー モデル ビュー
H_list:デバイスに関連付けられた入力ハンドルのリスト。リストにアクセスするときは、dev->mutex
Node:デバイスを配置するために使用するinput_dev_list
Num_vals
:現在のフレームでキューに入れられた値の数
Max_vals:フレームでキューに入れられた値の最大数
Vals:値の配列現在のフレームでキューに入れられています。
Devres_managed:Devres框架
管理のためのデバイスの使用を表します。明示的な登録解除や解放は必要ありません。
タイムスタンプ:ドライバーによって呼び出されたinput_set_Timestamp
設定のタイムスタンプを保存します。
include/linux/input.h
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
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)];
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;
struct list_head node;
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
ktime_t timestamp[INPUT_CLK_MAX];
};
1.3 input_handler 構造体:struct input_handler - 入力デバイス用のインターフェイスの 1 つを実装します。
プライベート:ドライバー固有のデータ
イベント:イベント ハンドラー。このメソッドは、割り込みを無効にし、dev->event_lock スピンロックを保持している間に入力コアによって呼び出されるため、
Events:イベント シーケンス ハンドラーをスリープさせない可能性があります。このメソッドは、割り込みを無効にし、dev->event_lock スピンロックを保持している間に入力コアによって呼び出されるため
、スリープしない可能性があります。Match:デバイスとハンドラーの間の詳細なマッチングのために、デバイスとハンドラーの ID を比較した後に呼び出されます。 Connect:ハンドラーを入力デバイスにアタッチするときに呼び出されます。Disconnect:入力デバイスからハンドラーを切断します。* *Start:**指定されたハンドルのハンドラーを開始します。この関数は、メソッドの後、およびデバイスを「取得」したプロセスがデバイスを解放したときに入力コアによって呼び出されます。 Legacy_minors :レガシー マイナー範囲を使用してドライバーによってマイナーに設定されます。 :このドライバーが提供できるデバイスの 32 個の開始レガシー セカンダリ範囲のName:ハンドラーの名前 ( Id_tableに示されている):このドライバーが処理できるテーブルへのポインターH_list:event
Filter
id_table
connect()
%true
/proc/bus/input/handlers
input_device_Id
ノードに関連付けられた入力ハンドラーのリスト:ドライバーの配置、input_handler_list
接続、作成
Input handlers
に使用されます。複数のハンドラーが任意の入力デバイスに同時に接続される場合があります。これらはすべて、デバイスによって生成された入力イベントのコピーを取得します。まったく同じ構造を使用して入力フィルターを実装します。フィルターを最初に実行することが許可されており、いずれかのフィルターがイベントをフィルターする必要があることを示している場合 (そのメソッドから返されることによって)、イベントは通常のハンドラーに渡されません。入力コアはメソッドとメソッドの呼び出しをシリアル化することに注意してください。input devices
input handles
Input core
filter()
%true
connect()
disconnect()
include/linux/input.h
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;
struct list_head node;
};
1.4 input_handle 構造体: 入力デバイスと入力ハンドラーをリンクする
1 つの
input_dev
報告イベントをinput_handler
複数で受信して処理することも、1 人でinput_handler
複数の報告イベントを処理することもできるinput_dev
ため、複数input_dev
と複数がinput_handler
絡み合ったネットワークを形成することができます。この場合、両者の間の接続を構築するためにブリッジが必要となり、両側の関数呼び出しはこの「仲介者」、つまりinput_handle
このブリッジを通じて行うことができます。
プライベート:ハンドラー固有のデータ
オープン:ハンドルが「オープン」であるかどうか、つまりハンドルがイベントを配信するデバイスであるかどうかを示すカウンター 名前
:ハンドルを作成したハンドラーによってハンドルに与えられた名前
Dev:ハンドルが送信される入力デバイスハンドルが接続されています
Handler:ハンドルの接続に使用されるハンドラー
D_node:デバイスの追加ハンドル リストにハンドルを配置するために使用されます
H_node:取得元のハンドラーのハンドル リストにハンドルを配置するために使用されますイベント
include/linux/input.h
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
2. 入力コアの初期化
include/linux/input.h
drivers/input/input.c
sybsys_initcall
起動レベルを登録して設定し、その初期化が入力デバイスと input_handler の登録よりも早くなるようにします。module_init
登録方法input设备
とinput_handler
subsys_initcall(input_init);
module_exit(input_exit);
2.1 input_init 初期化エントリ
drivers/input/input.c
class_register(&input_class)
入力クラスの登録は、/sys/class
input_proc_init();
主にキャラクタデバイスを登録するための情報の参照やinput_handler
ファイルの作成に使用されます。devices
Proc
register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, "input")
static int __init input_init(void)
{
int err;
err = class_register(&input_class);
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init();
if (err)
goto fail1;
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
2.1.1 クラスレジスタ
class_register(&input_class)
入力クラスの登録は/sys/class
Linux カーネル API に配置されます class_register|Geek Notes
drivers/input/input.c
struct class input_class = {
.name = "input",
.devnode = input_devnode,
};
EXPORT_SYMBOL_GPL(input_class);
include/linux/device.h
drivers/base/class.c
/* This is a #define to keep the compiler from merging different
* instances of the __key variable */
#define class_register(class) \
({
\
static struct lock_class_key __key; \
__class_register(class, &__key); \
})
2.1.2 input_proc_init
input_proc_init()
主に情報の閲覧やファイルの作成input_handler
に使用されます。devices
Proc
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;
proc_bus_input_dir = proc_mkdir("bus/input", NULL);
if (!proc_bus_input_dir)
return -ENOMEM;
entry = proc_create("devices", 0, proc_bus_input_dir,
&input_devices_fileops);
if (!entry)
goto fail1;
entry = proc_create("handlers", 0, proc_bus_input_dir,
&input_handlers_fileops);
if (!entry)
goto fail2;
return 0;
fail2: remove_proc_entry("devices", proc_bus_input_dir);
fail1: remove_proc_entry("bus/input", NULL);
return -ENOMEM;
}
2.1.3 register_chrdev_region
register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, "input")
キャラクターデバイスを登録し、プライマリデバイスで「入力」デバイスを作成します 13
include/uapi/linux/major.h
#define INPUT_MAJOR 13
drivers/input/input.c
#define INPUT_MAX_CHAR_DEVICES 1024
fs/char_dev.c
/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
struct char_device_struct *cd;
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
if (next > to)
next = to;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name);
if (IS_ERR(cd))
goto fail;
}
return 0;
fail:
to = n;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
return PTR_ERR(cd);
}
3. input_dev デバイスの登録
3.1 input_allocate_device: input_dev 構造体のメモリを割り当てる
input_allocate_device
- 新しい入力デバイスにメモリを割り当て、
準備された構造体を返します。input_dev
または%NULL
。
注:input_free_device()
未登録デバイスの解放を使用します。input_unregister_device()
登録済みデバイスに適用されます。
drivers/input/input.c
struct input_dev *devm_input_allocate_device(struct device *dev)
{
struct input_dev *input;
struct input_devres *devres;
devres = devres_alloc(devm_input_device_release,
sizeof(*devres), GFP_KERNEL);
if (!devres)
return NULL;
input = input_allocate_device();
if (!input) {
devres_free(devres);
return NULL;
}
input->dev.parent = dev;
input->devres_managed = true;
devres->input = input;
devres_add(dev, devres);
return input;
}
EXPORT_SYMBOL(devm_input_allocate_device);
/**
* input_allocate_device - allocate memory for new input device
*
* Returns prepared struct input_dev or %NULL.
*
* NOTE: Use input_free_device() to free devices that have not been
* registered; input_unregister_device() should be used for already
* registered devices.
*/
struct input_dev *input_allocate_device(void)
{
static atomic_t input_no = ATOMIC_INIT(-1);
struct input_dev *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (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);
timer_setup(&dev->timer, NULL, 0);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
dev_set_name(&dev->dev, "input%lu",
(unsigned long)atomic_inc_return(&input_no));
__module_get(THIS_MODULE);
}
return dev;
}
3.2 input_register_device: デバイスを入力コアに登録します
この関数はデバイスを に登録します
input core
。登録する前に、デバイスを割り当てinput_allocate_device()
、そのすべての機能を含める必要があります。機能が失敗した場合は、リリース デバイスを使用する必要がありますinput_free_device()
。デバイスが正常に登録されたら、をinput_unregister_device()
使用して登録を解除できます。この場合、 を呼び出す必要はありませんinput_free_device()
。devm_input_allocate_device()
この機能は、管理対象入力デバイスの登録(割り当てられたデバイスを使用)にも使用されることに注意してください。このような管理された入力デバイスは、明示的に登録解除または解放する必要はなく、その削除はデバイスのインフラストラクチャによって制御されます。また、管理対象入力デバイスの削除は、内部的に 2 段階のプロセスであることにも注意してください。登録済みの管理対象入力デバイスは、まず登録が解除されますが、メモリ内に残り、呼び出しは引き続き処理できます (ただし、イベントはどこにも配信されません)input_event()
。その後、devres スタックがデバイス割り当てが行われる時点まで巻き戻されると、管理対象入力デバイスが解放されます。
device_add(&dev->dev)
: デバイスを Linux デバイスとして登録しますlist_add_tail(&dev->node, &input_dev_list)
: Linux カーネルのグローバル リストにデバイスを追加しますinput_dev_list
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
:input_handler_list
デバイスを横断して自分のものを見つけますhandler
drivers/input/input.c
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error;
if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
dev_err(&dev->dev,
"Absolute device without dev->absinfo, refusing to register\n");
return -EINVAL;
}
if (dev->devres_managed) {
devres = devres_alloc(devm_input_device_unregister,
sizeof(*devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;
devres->input = dev;
}
/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size;
dev->max_vals = dev->hint_events_per_packet + 2;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
}
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
input_enable_softrepeat(dev, 250, 33);
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
if (dev->poller)
input_dev_poller_finalize(dev->poller);
error = device_add(&dev->dev);
if (error)
goto err_free_vals;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del;
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
if (dev->devres_managed) {
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return 0;
err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
EXPORT_SYMBOL(input_register_device);
3.3 ケース: 「gpio-keys」デバイスの登録
drivers/input/keyboard/gpio_keys.c
「gpio-keys」: その他の場合は、次のディレクトリを参照してください。platform_driver_register(&gpio_keys_device_driver) -> gpio_keys_probe -> devm_input_allocate_device -> input_register_device
drivers/input/gameport
drivers/input/joystick
drivers/input/keyboard
drivers/input/misc
drivers/input/mouse
drivers/input/rmi4
drivers/input/serio
drivers/input/tablet
drivers/input/touchscreen
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.shutdown = gpio_keys_shutdown,
.driver = {
.name = "gpio-keys",
.pm = &gpio_keys_pm_ops,
.of_match_table = gpio_keys_of_match,
.dev_groups = gpio_keys_groups,
}
};
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver);
}
static int gpio_keys_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
struct fwnode_handle *child = NULL;
struct gpio_keys_drvdata *ddata;
struct input_dev *input;
int i, error;
int wakeup = 0;
if (!pdata) {
pdata = gpio_keys_get_devtree_pdata(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
}
ddata = devm_kzalloc(dev, struct_size(ddata, data, pdata->nbuttons),
GFP_KERNEL);
if (!ddata) {
dev_err(dev, "failed to allocate state\n");
return -ENOMEM;
}
ddata->keymap = devm_kcalloc(dev,
pdata->nbuttons, sizeof(ddata->keymap[0]),
GFP_KERNEL);
if (!ddata->keymap)
return -ENOMEM;
input = devm_input_allocate_device(dev);
if (!input) {
dev_err(dev, "failed to allocate input device\n");
return -ENOMEM;
}
ddata->pdata = pdata;
ddata->input = input;
mutex_init(&ddata->disable_lock);
platform_set_drvdata(pdev, ddata);
input_set_drvdata(input, ddata);
input->name = pdata->name ? : pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = dev;
input->open = gpio_keys_open;
input->close = gpio_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->keycode = ddata->keymap;
input->keycodesize = sizeof(ddata->keymap[0]);
input->keycodemax = pdata->nbuttons;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
const struct gpio_keys_button *button = &pdata->buttons[i];
if (!dev_get_platdata(dev)) {
child = device_get_next_child_node(dev, child);
if (!child) {
dev_err(dev,
"missing child device node for entry %d\n",
i);
return -EINVAL;
}
}
error = gpio_keys_setup_key(pdev, input, ddata,
button, i, child);
if (error) {
fwnode_handle_put(child);
return error;
}
if (button->wakeup)
wakeup = 1;
}
fwnode_handle_put(child);
error = input_register_device(input);
if (error) {
dev_err(dev, "Unable to register input device, error: %d\n",
error);
return error;
}
device_init_wakeup(dev, wakeup);
return 0;
}
4.input_handlerの登録
4.1 共通の input_handler
一般的には前、共通に
input_handler注册
なります:input_dev设备注册
input_handler
evdev_handler
: ほとんどのイベント、デフォルトの入力処理イベントに応答します。mousedev_handler
: マウス入力イベントjoydev_handler
:ゲームリモートセンシング入力イベントkbd_handler
:キーボードイベントinput_leds_handler
apmpower_handler
drivers/input/evdev.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",
.id_table = evdev_ids,
};
drivers/tty/vt/keyboard.c
static struct input_handler kbd_handler = {
.event = kbd_event,
.match = kbd_match,
.connect = kbd_connect,
.disconnect = kbd_disconnect,
.start = kbd_start,
.name = "kbd",
.id_table = kbd_ids,
};
drivers/input/mousedev.c
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,
};
drivers/input/joydev.c
static struct input_handler joydev_handler = {
.event = joydev_event,
.match = joydev_match,
.connect = joydev_connect,
.disconnect = joydev_disconnect,
.legacy_minors = true,
.minor = JOYDEV_MINOR_BASE,
.name = "joydev",
.id_table = joydev_ids,
};
drivers/input/input-leds.c
static struct input_handler input_leds_handler = {
.event = input_leds_event,
.connect = input_leds_connect,
.disconnect = input_leds_disconnect,
.name = "leds",
.id_table = input_leds_ids,
};
drivers/input/apm-power.c
static struct input_handler apmpower_handler = {
.event = apmpower_event,
.connect = apmpower_connect,
.disconnect = apmpower_disconnect,
.name = "apm-power",
.id_table = apmpower_ids,
};
4.2 input_register_handler登録関数
input_handler
この関数は、システム上の入力デバイスの新しい (インターフェイス) を登録し、それをこのハンドラーと互換性のあるすべてのデバイスに接続しますinput devices
。
INIT_LIST_HEAD(&handler->h_list)
: Linux でカーネル リンク リストを初期化します。list_add_tail(&handler->node, &input_handler_list)
: Linux カーネルのグローバル リストにハンドラーを追加しますinput_handler_list
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
:input_handler_list
デバイスを横断して自分のものを見つけますhandler
/**
* input_register_handler - register a new input handler
* @handler: handler to be registered
*
* This function registers a new input handler (interface) for input
* devices in the system and attaches it to all input devices that
* are compatible with the handler.
*/
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int error;
error = mutex_lock_interruptible(&input_mutex);
if (error)
return error;
INIT_LIST_HEAD(&handler->h_list);
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
EXPORT_SYMBOL(input_register_handler);
5. input_dev と input_handler は input_handle と一致します
5.1 input_match_device のマッチング
input_dev设备注册
そしてinput_handler注册
呼ばれるだろうinput_attach_handler
input_match_device
: 一致が正常に返されましたhandler->id_table
。つまり、input_device_id
handler->connect(handler, dev, id)
: 正常に呼び出されたconnect
関数 ( など)と一致しますdrivers/input/evdev.c#evdev_connect
。drivers/input/mousedev.c#mousedev_connect
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (input_match_device_id(dev, id) &&
(!handler->match || handler->match(handler, dev))) {
return id;
}
}
return NULL;
}
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
5.2 接続機能
など
drivers/input/evdev.c#evdev_connect
、drivers/input/mousedev.c#mousedev_connect
一般的なイベント処理を表示しますevdev.c
。
.driver_info = 1
:evdev_ids
すべてのデバイスに一致します。evdev_connect
: 登録後evdev的connect
;
1》input_register_handle
新しいものを登録しますinput_handle
。主にとメンバーリンクリストhandle
にそれぞれマウントされます; ( 、) 2》最大 32 個のイベントデバイスを作成できます; 3》最終呼び出し、Linux システムに新しいイベントデバイスを作成しますinput_dev
input_handler
evdev->handle.dev = input_get_device(dev);
evdev->handle.handler = handler;
input_get_new_minor
#define EVDEV_MINORS 32
cdev_device_add
device_add
/dev/input/eventX
drivers/input/evdev.c
struct evdev {
int open;
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
struct cdev cdev;
bool exist;
};
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect.
*/
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
evdev->exist = true;
dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
dev_no -= EVDEV_MINOR_BASE;
dev_set_name(&evdev->dev, "event%d", dev_no);
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops);
error = cdev_device_add(&evdev->cdev, &evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error;
}
static const struct input_device_id evdev_ids[] = {
{
.driver_info = 1 }, /* Matches all devices */
{
}, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
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,
};
5.3 入力レジスタハンドル
input_register_handle
- 新しい入力ハンドル
ハンドルを登録する: ハンドルを登録するためのこの関数は、
新しい入力ハンドルを と のリストに配置しinput_dev
、で開かれたときにイベントがその中に流れることができるようinput_handler
にします。input_open_device()
この関数はハンドラーのconnect()
メソッドから呼び出す必要があります。
/**
* input_register_handle - register a new input handle
* @handle: handle to register
*
* This function puts a new input handle onto device's
* and handler's lists so that events can flow through
* it once it is opened using input_open_device().
*
* This function is supposed to be called from handler's
* connect() method.
*/
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
/*
* Filters go to the head of the list, normal handlers
* to the tail.
*/
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
EXPORT_SYMBOL(input_register_handle);
5.4 input_dev \ input_handler \ input_handle 関数
input_dev
入力デバイスを表すハードウェア ドライバー層
input_handler
イベント プロセッサを表すイベント処理層
input_handle
入力デバイスと入力イベント プロセッサのペアを表すコア層に属します。
input_dev
グローバルにリンクされていますinput_dev_list
。この操作はデバイスの登録時に実行されます。
input_handler
グローバルにリンクされていますinput_handler_list
。この動作はイベントハンドラの登録時に実装されます。
input_hande
グローバルリンクリストは存在せず、登録時にinput_dev
とinput_handler
にそれぞれハングアップしてしまいましたh_list
。
デバイス登録input_dev
とイベント ハンドラーでは、登録時にペアリング作業を行う必要があり、リンクはペアリング後に実装されます。からも見つけることができます。input_handler
input_handle
input_handle
input_dev
input_handler
6. レポート入力イベント
input事件
一般に、中断方式
レポート、関連メソッドinput_report_abs
、input_report_key
などinput_sync
が使用されます。最後にinput_sync
、イベントが報告され、input_event
最終的に処理が呼び出されることを意味します。
6.1 基礎となる入力イベントのレポート
AOSP > ドキュメント > コア トピック > キーボード デバイス、AOSP > ドキュメント > コア トピック > タッチ デバイス
input设备
レポート形式が異なれinput事件
ば、一般的に使用される按键
、または触摸屏
採用される中断方式
レポートも異なります。
たとえば、タッチ スクリーンで報告する場合、input事件
通常は指の ID、x 座標、y 座標、その他の情報を報告する必要があります。
https://www.kernel.org/doc/Documentation/input/input.txt
https://www.kernel.org/doc/Documentation/input/event-codes.txt
https://www.kernel.org/ doc/Documentation/input/multi-touch-protocol.txt
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
input_sync(input);
- | タイプ | コード | 価値 |
---|---|---|---|
ポイント1 | EV_ABS | ABS_MT_SLOT | 0 |
\ | EV_ABS | ABS_MT_TRACKING_ID | ID |
\ | EV_ABS | ABS_MT_POSITION_X | バツ |
\ | EV_ABS | ABS_MT_POSITION_Y | y |
… | … | … | … |
ポイント2 | EV_ABS | ABS_MT_SLOT | n |
\ | EV_ABS | ABS_MT_TRACKING_ID | ID |
\ | EV_ABS | ABS_MT_POSITION_X | バツ |
\ | EV_ABS | ABS_MT_POSITION_Y | y |
6.2 input_event は新しい入力イベントを報告します
さまざまな入力デバイスを実装するドライバーは、この関数を使用して入力イベントを報告する必要があります。も参照してください
input_inject_event()
。
注:入力デバイスを使用した直後、または登録を使用する前でも安全に使用できますinput_event()
が、イベントは入力ハンドラーに到達しません。この初期の の呼び出しは、スイッチの初期状態や絶対軸の初期位置などを「シード」するために使用できます。input_allocate_device()
input_register_device()
input_event()
input_handle_event
: 各イベントのレポートはinput_event
インターフェイスを通じて完了します。イベント タイプがサポートされているかどうかを判断した後、主に呼び出しinput_handle_event
によって完了します。input_get_disposition
:報告された情報に基づいて対応を決定するhandler->events()/handler->event()
: etc( )などのinput_dev
対応するinput_handler
evdev_handler
input_event -> input_handle_event -> input_pass_values -> input_to_handler -> handler->events()/handler->event()
drivers/input/input.c
/*
* Pass event first through all filters and then, if event has not been
* filtered out, through all open handles. This function is called with
* dev->event_lock held and interrupts disabled.
*/
static unsigned int input_to_handler(struct input_handle *handle,
struct input_value *vals, unsigned int count)
{
struct input_handler *handler = handle->handler;
struct input_value *end = vals;
struct input_value *v;
if (handler->filter) {
for (v = vals; v != vals + count; v++) {
if (handler->filter(handle, v->type, v->code, v->value))
continue;
if (end != v)
*end = *v;
end++;
}
count = end - vals;
}
if (!count)
return 0;
if (handler->events)
handler->events(handle, vals, count);
else if (handler->event)
for (v = vals; v != vals + count; v++)
handler->event(handle, v->type, v->code, v->value);
return count;
}
/*
* Pass values first through all filters and then, if event has not been
* filtered out, through all open handles. This function is called with
* dev->event_lock held and interrupts disabled.
*/
static void input_pass_values(struct input_dev *dev,
struct input_value *vals, unsigned int count)
{
struct input_handle *handle;
struct input_value *v;
if (!count)
return;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle) {
count = input_to_handler(handle, vals, count);
} else {
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open) {
count = input_to_handler(handle, vals, count);
if (!count)
break;
}
}
rcu_read_unlock();
/* trigger auto repeat for key events */
if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
for (v = vals; v != vals + count; v++) {
if (v->type == EV_KEY && v->value != 2) {
if (v->value)
input_start_autorepeat(dev, v->code);
else
input_stop_autorepeat(dev);
}
}
}
}
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input
* devices to report input events. See also input_inject_event().
*
* NOTE: input_event() may be safely used right after input device was
* allocated with input_allocate_device(), even before it is registered
* with input_register_device(), but the event will not reach any of the
* input handlers. Such early invocation of input_event() may be used
* to 'seed' initial state of a switch or initial position of absolute
* axis, etc.
*/
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_event);
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = input_get_disposition(dev, type, code, &value);
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
add_input_randomness(type, code, value);
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (!dev->vals)
return;
if (disposition & INPUT_PASS_TO_HANDLERS) {
struct input_value *v;
if (disposition & INPUT_SLOT) {
v = &dev->vals[dev->num_vals++];
v->type = EV_ABS;
v->code = ABS_MT_SLOT;
v->value = dev->mt->slot;
}
v = &dev->vals[dev->num_vals++];
v->type = type;
v->code = code;
v->value = value;
}
if (disposition & INPUT_FLUSH) {
if (dev->num_vals >= 2)
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
/*
* Reset the timestamp on flush so we won't end up
* with a stale one. Note we only need to reset the
* monolithic one as we use its presence when deciding
* whether to generate a synthetic timestamp.
*/
dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 0);
} else if (dev->num_vals >= dev->max_vals - 2) {
dev->vals[dev->num_vals++] = input_value_sync;
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
}
}
6.3 evdev_handler での evdev_events の処理
evdev_events
受信イベントを接続されているすべてのクライアント
evdev_event/evdev_events -> evdev_pass_values -> __pass_event ->
入力イベントに渡し、それらを に保存しますclient->buffer
。通知イベントを送信して、データを読み取ることができることをkill_fasync
上位層に伝えるために使用されます。client->buffer
drivers/input/evdev.c
static void __pass_event(struct evdev_client *client,
const struct input_event *event)
{
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
if (unlikely(client->head == client->tail)) {
/*
* This effectively "drops" all unconsumed events, leaving
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
*/
client->tail = (client->head - 2) & (client->bufsize - 1);
client->buffer[client->tail] = (struct input_event) {
.input_event_sec = event->input_event_sec,
.input_event_usec = event->input_event_usec,
.type = EV_SYN,
.code = SYN_DROPPED,
.value = 0,
};
client->packet_head = client->tail;
}
if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
}
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t *ev_time)
{
struct evdev *evdev = client->evdev;
const struct input_value *v;
struct input_event event;
struct timespec64 ts;
bool wakeup = false;
if (client->revoked)
return;
ts = ktime_to_timespec64(ev_time[client->clk_type]);
event.input_event_sec = ts.tv_sec;
event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
for (v = vals; v != vals + count; v++) {
if (__evdev_is_filtered(client, v->type, v->code))
continue;
if (v->type == EV_SYN && v->code == SYN_REPORT) {
/* drop empty SYN_REPORT */
if (client->packet_head == client->head)
continue;
wakeup = true;
}
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event);
}
spin_unlock(&client->buffer_lock);
if (wakeup)
wake_up_interruptible(&evdev->wait);
}
/*
* Pass incoming events to all connected clients.
*/
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
ktime_t *ev_time = input_get_timestamp(handle->dev);
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_values(client, vals, count, ev_time);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count, ev_time);
rcu_read_unlock();
}
/*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_value vals[] = {
{
type, code, value } };
evdev_events(handle, vals, 1);
}
7. 入力イベントのカーネル空間がユーザー空間に渡されます
EventHub::getEvents -> resd
:input事件
に格納されますclient->buffer
。アプリケーション層またはフレームワーク層がファイルread函数
を読み取るために呼び出すと/dev/input/event*
、たとえば、戻りデータevdev.c
が呼び出されますevdev_read
。event_fetch_next_event
:client->buffer
この循環バッファ内の先頭ポインタと末尾ポインタが等しいかどうかを判断し (等しい場合、バッファ内にデータがありません)、等しくない場合は、イベントのタイプを取り出してイベントに入れますinput_event
。input_event_to_user
: このイベントをアプリケーション層にコピーします。input_event_size
この関数は、input_event
イベントのサイズを取得し、ループ内のイベントをclient->buffer
アプリケーション層のバッファーにコピーするために使用されます。
frameworks/native/services/inputflinger/reader/EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
std::scoped_lock _l(mLock);
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
size_t capacity = bufferSize;
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// Reopen input devices if needed.
if (mNeedToReopenDevices) {
mNeedToReopenDevices = false;
ALOGI("Reopening all input devices due to a configuration change.");
closeAllDevicesLocked();
mNeedToScanDevices = true;
break; // return to the caller before we actually rescan
}
// Report any devices that had last been added/removed.
for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
std::unique_ptr<Device> device = std::move(*it);
ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
event->when = now;
event->deviceId = (device->id == mBuiltInKeyboardId)
? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
: device->id;
event->type = DEVICE_REMOVED;
event += 1;
it = mClosingDevices.erase(it);
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
while (!mOpeningDevices.empty()) {
std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
mOpeningDevices.pop_back();
ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
// Try to find a matching video device by comparing device names
for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
it++) {
std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
if (tryAddVideoDeviceLocked(*device, videoDevice)) {
// videoDevice was transferred to 'device'
it = mUnattachedVideoDevices.erase(it);
break;
}
}
auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
if (!inserted) {
ALOGW("Device id %d exists, replaced.", device->id);
}
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
event->when = now;
event->type = FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0) {
break;
}
}
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
if (eventItem.data.fd == mINotifyFd) {
if (eventItem.events & EPOLLIN) {
mPendingINotify = true;
} else {
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
if (eventItem.data.fd == mWakeReadPipeFd) {
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
char wakeReadBuffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
Device* device = getDeviceByFdLocked(eventItem.data.fd);
if (device == nullptr) {
ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
eventItem.data.fd);
ALOG_ASSERT(!DEBUG);
continue;
}
if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) {
if (eventItem.events & EPOLLIN) {
size_t numFrames = device->videoDevice->readAndQueueFrames();
if (numFrames == 0) {
ALOGE("Received epoll event for video device %s, but could not read frame",
device->videoDevice->getName().c_str());
}
} else if (eventItem.events & EPOLLHUP) {
// TODO(b/121395353) - consider adding EPOLLRDHUP
ALOGI("Removing video device %s due to epoll hang-up event.",
device->videoDevice->getName().c_str());
unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
device->videoDevice = nullptr;
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
device->videoDevice->getName().c_str());
ALOG_ASSERT(!DEBUG);
}
continue;
}
// This must be an input event
if (eventItem.events & EPOLLIN) {
int32_t readSize =
read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// Device was removed before INotify noticed.
ALOGW("could not get event, removed? (fd: %d size: %" PRId32
" bufferSize: %zu capacity: %zu errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(*device);
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event (errno=%d)", errno);
}
} else if ((readSize % sizeof(struct input_event)) != 0) {
ALOGE("could not get event (wrong size: %d)", readSize);
} else {
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
event->when = processEventTimestamp(iev);
event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
mPendingEventIndex -= 1;
break;
}
}
} else if (eventItem.events & EPOLLHUP) {
ALOGI("Removing device %s due to epoll hang-up event.",
device->identifier.name.c_str());
deviceChanged = true;
closeDeviceLocked(*device);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
device->identifier.name.c_str());
}
}
// readNotify() will modify the list of devices so this must be done after
// processing all other events to ensure that we read all remaining events
// before closing the devices.
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
readNotifyLocked();
deviceChanged = true;
}
// Report added or removed devices immediately.
if (deviceChanged) {
continue;
}
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {
break;
}
// Poll for events.
// When a device driver has pending (unread) events, it acquires
// a kernel wake lock. Once the last pending event has been read, the device
// driver will release the kernel wake lock, but the epoll will hold the wakelock,
// since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
// is called again for the same fd that produced the event.
// Thus the system can only sleep if there are no events pending or
// currently being processed.
//
// The timeout is advisory only. If the device is asleep, it will not wake just to
// service the timeout.
mPendingEventIndex = 0;
mLock.unlock(); // release lock before poll
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mLock.lock(); // reacquire lock after poll
if (pollResult == 0) {
// Timed out.
mPendingEventCount = 0;
break;
}
if (pollResult < 0) {
// An error occurred.
mPendingEventCount = 0;
// Sleep after errors to avoid locking up the system.
// Hopefully the error is transient.
if (errno != EINTR) {
ALOGW("poll failed (errno=%d)\n", errno);
usleep(100000);
}
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
}
}
// All done, return the number of events we read.
return event - buffer;
}
drivers/input/evdev.c
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
size_t read = 0;
int error;
if (count != 0 && count < input_event_size())
return -EINVAL;
for (;;) {
if (!evdev->exist || client->revoked)
return -ENODEV;
if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/*
* count == 0 is special - no IO is done but we check
* for error conditions (see above).
*/
if (count == 0)
break;
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + read, &event))
return -EFAULT;
read += input_event_size();
}
if (read)
break;
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked);
if (error)
return error;
}
}
return read;
}
7.1 evdev_fetch_next_event
event_fetch_next_event
:client->buffer
この循環バッファーの先頭ポインターと末尾ポインターが等しいかどうかを判断し (等しい場合はバッファーにデータがありません)、等しくない場合は、イベントのタイプを取り出してイベントに入れますinput_event
。
drivers/input/evdev.c
static int evdev_fetch_next_event(struct evdev_client *client,
struct input_event *event)
{
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->packet_head != client->tail;
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= client->bufsize - 1;
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
7.2 ユーザーへのイベント入力
input_event_to_user
: このイベントをcopy
アプリケーション層に送信します。input_event_size
この関数はinput_event
イベントのサイズを取得し、client->buffer
イベントをアプリケーション層のバッファーにコピーするループに使用されます。
drivers/input/input-compat.c
#ifdef CONFIG_COMPAT
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event;
compat_event.sec = event->input_event_sec;
compat_event.usec = event->input_event_usec;
compat_event.type = event->type;
compat_event.code = event->code;
compat_event.value = event->value;
if (copy_to_user(buffer, &compat_event,
sizeof(struct input_event_compat)))
return -EFAULT;
} else {
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;
}
return 0;
}
#else
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;
return 0;
}
#endif /* CONFIG_COMPAT */
参考文献
https://www.kernel.org/doc/Documentation/input/input.txt
Linux 値入力サブシステム解析(詳細説明)
入力 入力サブシステム