sysfs ノードからデバイスをアクセスする

Linux kernel のデバイスとドライバの管理作法に従ってデバイスを kernel に登録すれば sysfs 上に対応するノードができます。sysfs は多くの Linux で /sys に mount されています。 sysfs のツリーを辿ってデバイスを探してみましょう。

/sys/devices/platform の下を見ていきます。このディレクトリには platform device が並びます。デバイス名の一部または機能名の一部がディレクトリ名になって並んでいます。環境によりデバイスの有無は仮想的な物も含めて違うので、存在するディレクトリは増減します。shell command を操作して /sys/devices 以下のノードと /sys/devices/platform 以下のノードの一覧を出してみます。

~ $ cd /sys/devices
/sys/devices $ ls -la
total 0
drwxr-xr-x 16 root root 0 Jul 24 01:43 .
dr-xr-xr-x 13 root root 0 Jul 24 01:43 ..
drwxr-xr-x 10 root root 0 Jul 24 01:43 LNXSYSTM:00
drwxr-xr-x  3 root root 0 Jul 24 01:43 breakpoint
drwxr-xr-x  5 root root 0 Jul 24 01:43 cpu
drwxr-xr-x  5 root root 0 Jul 24 01:43 cstate_core
drwxr-xr-x  5 root root 0 Jul 24 01:43 cstate_pkg
drwxr-xr-x  3 root root 0 Jul 24 01:43 intel_bts
drwxr-xr-x  5 root root 0 Jul 24 01:43 msr
drwxr-xr-x 16 root root 0 Jul 24 01:43 pci0000:00
drwxr-xr-x 22 root root 0 Jul 24 01:43 platform
drwxr-xr-x 10 root root 0 Jul 24 01:43 pnp0
drwxr-xr-x  3 root root 0 Jul 24 01:43 software
drwxr-xr-x  9 root root 0 Jul 24 01:43 system
drwxr-xr-x  3 root root 0 Jul 24 01:43 tracepoint
drwxr-xr-x 18 root root 0 Jul 24 01:43 virtual
/sys/devices/platform $ cd platform
/sys/devices/platform $ ls -la
total 0
drwxr-xr-x 22 root root    0 Jul 24 01:43 .
drwxr-xr-x 16 root root    0 Jul 24 01:43 ..
drwxr-xr-x  3 root root    0 Jul 24 01:45 ACPI000C:00
drwxr-xr-x  4 root root    0 Jul 24 01:45 Fixed MDIO bus.0
drwxr-xr-x  3 root root    0 Jul 24 01:45 INT33FF:00
drwxr-xr-x  3 root root    0 Jul 24 01:45 INT33FF:01
drwxr-xr-x  3 root root    0 Jul 24 01:45 INT33FF:02
drwxr-xr-x  3 root root    0 Jul 24 01:45 INT33FF:03
drwxr-xr-x  3 root root    0 Jul 24 01:45 PNP0103:00
drwxr-xr-x  3 root root    0 Jul 24 01:45 PNP0C0C:00
drwxr-xr-x  3 root root    0 Jul 24 01:45 PNP0C0E:00
drwxr-xr-x  3 root root    0 Jul 24 01:45 alarmtimer
drwxr-xr-x  4 root root    0 Jul 20 11:14 coretemp.0
drwxr-xr-x  3 root root    0 Jul 24 01:45 efi-framebuffer.0
drwxr-xr-x  5 root root    0 Jul 24 01:45 i8042
drwxr-xr-x  3 root root    0 Jul 24 01:45 microcode
drwxr-xr-x  3 root root    0 Jul 24 01:45 pcspkr
drwxr-xr-x  2 root root    0 Jul 24 01:45 power
drwxr-xr-x  4 root root    0 Jul 24 01:45 reg-dummy
drwxr-xr-x  4 root root    0 Jul 24 01:45 serial8250
drwxr-xr-x  3 root root    0 Jul 24 01:45 snd-soc-dummy
-rw-r--r--  1 root root 4096 Jul 24 01:45 uevent
drwxr-xr-x  3 root root    0 Jul 24 01:45 vboxdrv.0

i8042 ディレクトリを見てみましょう。このディレクトリには PS/2 keyboard と mouse デバイスが対応します。/sys/devices/platform/i8042/serio0 ディレクトリの下にある bind_mode ノードのために kernel の中で対応している関数は serio_set_bind_mode(), serio_show_bind_mode()) です。description ノードに対応している関数は (serio_show_description() と description ノードに文字列 "i8042 KBD port" を設定しているのは i8042_create_kbd_port()) です。デバイスを制御、機能確認するためノードです。このようなノードの多くは shell から cat, echo command で読み出し、書き込みできる様に実装されています。手軽にスクリプトで動作確認・制御できる user space API になっています。続く操作例はノードを読み出しているところです。

/sys/devices/platform $ cd i8042
/sys/devices/platform/i8042 $ ls -la
total 0
drwxr-xr-x  5 root root    0 Jul 24 01:45 .
drwxr-xr-x 22 root root    0 Jul 24 01:43 ..
lrwxrwxrwx  1 root root    0 Jul 24 01:53 driver -> ../../../bus/platform/drivers/i8042
-rw-r--r--  1 root root 4096 Jul 24 01:53 driver_override
-r--r--r--  1 root root 4096 Jul 24 01:53 modalias
drwxr-xr-x  2 root root    0 Jul 24 01:53 power
drwxr-xr-x  4 root root    0 Jul 24 01:53 serio0
drwxr-xr-x  4 root root    0 Jul 24 01:53 serio1
lrwxrwxrwx  1 root root    0 Jul 24 01:53 subsystem -> ../../../bus/platform
-rw-r--r--  1 root root 4096 Jul 24 01:53 uevent
/sys/devices/platform/i8042 $ cd serio0
/sys/devices/platform/i8042/serio0$ ls -la
total 0
drwxr-xr-x 4 root root    0 Jul 24 01:53 .
drwxr-xr-x 5 root root    0 Jul 24 01:45 ..
-rw-r--r-- 1 root root 4096 Jul 24 01:54 bind_mode
-r--r--r-- 1 root root 4096 Jul 24 01:54 description
--w------- 1 root root 4096 Jul 24 01:54 drvctl
-r--r--r-- 1 root root 4096 Jul 24 01:54 firmware_id
drwxr-xr-x 2 root root    0 Jul 24 01:54 id
-r--r--r-- 1 root root 4096 Jul 24 01:54 modalias
drwxr-xr-x 2 root root    0 Jul 24 01:54 power
lrwxrwxrwx 1 root root    0 Jul 24 01:54 subsystem -> ../../../../bus/serio
-rw-r--r-- 1 root root 4096 Jul 24 01:54 uevent
/sys/devices/platform/i8042/serio0 $ cat bind_mode
auto
/sys/devices/platform/i8042/serio0 $ cat description
i8042 KBD port

bind_mode ノードの実装を詳しく見ていきましょう。このノードは DEVICE_ATTR() マクロでノードの名前、パーミッション、show(), store() 関数が定義されています。 attribute 構造体、attribute_group 構造体、attribute_group 構造体を指すポインタの配列で包み、serio_init_port()device 構造体の group メンバに設定しています。

fileOpenGrok(linux-4.1.27/drivers/input/serio/serio.c:479)
Expand allFold all
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
 
 
 
 
 
 
-
|
|
|
|
|
|
!
 
-
|
!
 
-
|
|
|
!
static DEVICE_ATTR_RO(modalias);
static DEVICE_ATTR_WO(drvctl);
static DEVICE_ATTR(description, S_IRUGO, serio_show_description, NULL);
static DEVICE_ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode);
static DEVICE_ATTR_RO(firmware_id);
 
static struct attribute *serio_device_attrs[] = {
        &dev_attr_modalias.attr,
        &dev_attr_description.attr,
        &dev_attr_drvctl.attr,
        &dev_attr_bind_mode.attr,
        &dev_attr_firmware_id.attr,
        NULL
};
 
static struct attribute_group serio_device_attr_group = {
        .attrs  = serio_device_attrs,
};
 
static const struct attribute_group *serio_device_attr_groups[] = {
        &serio_id_attr_group,
        &serio_device_attr_group,
        NULL
};
fileOpenGrok(linux-4.1.27/drivers/input/serio/serio.c:512)
Expand allFold all
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
-
|
!
 
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
!
|
|
!
/*
 * Prepare serio port for registration.
 */
static void serio_init_port(struct serio *serio)
{
        static atomic_t serio_no = ATOMIC_INIT(-1);
 
        __module_get(THIS_MODULE);
 
        INIT_LIST_HEAD(&serio->node);
        INIT_LIST_HEAD(&serio->child_node);
        INIT_LIST_HEAD(&serio->children);
        spin_lock_init(&serio->lock);
        mutex_init(&serio->drv_mutex);
        device_initialize(&serio->dev);
        dev_set_name(&serio->dev, "serio%lu",
                     (unsigned long)atomic_inc_return(&serio_no));
        serio->dev.bus = &serio_bus;
        serio->dev.release = serio_release_port;
        serio->dev.groups = serio_device_attr_groups;
        if (serio->parent) {
                serio->dev.parent = &serio->parent->dev;
                serio->depth = serio->parent->depth + 1;
        } else
                serio->depth = 0;
        lockdep_set_subclass(&serio->lock, serio->depth);
}

group メンバは sysfs_create_groups() が呼び出されるまでそのままです。sysfs_create_groups() までを追います。イベント "serio_queue_event(), SERIO_REGISTER_PORT" を経由して serio_handle_event() イベントハンドラ関数内で serio_add_port()serio クラス・デバイスとして追加されます。ドライバとデバイスの基底的な処理/drivers/base にある device_add() で kernel にデバイスとして追加されます。device_add() から device_add_attrs(), device_add_groups() を経て、先の group メンバを sysfs_create_groups() に渡して sysfs ノードとして登録しています。

fileOpenGrok(linux-4.1.27/drivers/base/core.c:458)
Expand allFold all
458
459
460
461
 
-
|
!
int device_add_groups(struct device *dev, const struct attribute_group **groups)
{
        return sysfs_create_groups(&dev->kobj, groups);
}

sysfs ノードの追加は serio クラス・デバイスなので複雑です。簡易に sysfs ノードを設けるのであれば DEVICE_ATTR() マクロでノード定義し、attribute 構造体、attribute_group 構造体で包み、sysfs_create_group() (group が単数形であることに注意) でノードを登録、sysfs_remove_group() でノードを削除することができます。DEVICE_ATTR() の沢山の用例を見て使い方を習得できるでしょう。sysfs_create_group() の引数 kobject 構造体を指すポインタ kobj は device 構造体のメンバとして存在している kobj を指すポインタを使って下さい。

あちこちにある uevent node はどんな機能があるの?

書き込む (uevent_store()) と uevent を発行する (kobject_uevent_env()) 機能と、読み込む (uevent_show()) 機能が実装されています。読み込む機能は一部のノード、例えば /sys/class/input/* の下にある uevent ノードに実装 (input_dev_uevent()) されています。読み込む機能は uevent で発行される情報の一部をいつでも取得できる様になっています。
uevent が発行するイベントは socket (AF_NETLINK.NETLINK_KOBJECT_UEVENT) から配信されるか、イベント発生時に /proc/sys/kernel/hotplug または /sys/kernel/uevent_helper に設定した path のファイルを user space の process として起動することで配信されます。多くのディストリビューションでは user space に udev あるいはこれに類似する名前の daemon が socket を開いて kernel から配信されるイベントを受信しています。uevent で起動される実行ファイルの path は慣例的に /sbin/hotplug (CONFIG_UEVENT_HELPER_PATH) です。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-09-27 (水) 11:03:48 (2406d)