#author("2017-09-15T16:56:30+09:00","default:afuruta","afuruta") #author("2017-09-15T21:44:03+09:00","default:afuruta","afuruta") * sysfs ノードからデバイスをアクセスする [#j4096303] Linux kernel のデバイスとドライバの管理作法に従ってデバイスを kernel に登録すれば sysfs 上に対応するノードができます。sysfs は多くの linux で /sys に mount されています。 sysfs のツリーを辿ってデバイスを探してみましょう。 /sys/devices/platform の下を見ていきます。このディレクトリには platform device が並びます。デバイス名の一部または機能名の一部がディレクトリ名になって並んでいます。環境によりデバイスの有無は仮想的な物も含めて違うので、存在するディレクトリは増減します。shell command を操作して /sys/devices 以下のノードと /sys/devices/platform 以下のノードの一覧を出してみます。 #pre(soft,overflow:auto){{ &span(ConsoleOut){~ $ };&span(ConsoleIn){cd /sys/devices}; &span(ConsoleOut){/sys/devices $ };&span(ConsoleIn){ls -la}; &span(ConsoleOut){total 0}; &span(ConsoleOut){drwxr-xr-x 16 root root 0 Jul 24 01:43 .}; &span(ConsoleOut){dr-xr-xr-x 13 root root 0 Jul 24 01:43 ..}; &span(ConsoleOut){drwxr-xr-x 10 root root 0 Jul 24 01:43 LNXSYSTM:00}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:43 breakpoint}; &span(ConsoleOut){drwxr-xr-x 5 root root 0 Jul 24 01:43 cpu}; &span(ConsoleOut){drwxr-xr-x 5 root root 0 Jul 24 01:43 cstate_core}; &span(ConsoleOut){drwxr-xr-x 5 root root 0 Jul 24 01:43 cstate_pkg}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:43 intel_bts}; &span(ConsoleOut){drwxr-xr-x 5 root root 0 Jul 24 01:43 msr}; &span(ConsoleOut){drwxr-xr-x 16 root root 0 Jul 24 01:43 pci0000:00}; &span(ConsoleOut){drwxr-xr-x 22 root root 0 Jul 24 01:43 };&span(ConsoleOut,Focus){platform}; &span(ConsoleOut){drwxr-xr-x 10 root root 0 Jul 24 01:43 pnp0}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:43 software}; &span(ConsoleOut){drwxr-xr-x 9 root root 0 Jul 24 01:43 system}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:43 tracepoint}; &span(ConsoleOut){drwxr-xr-x 18 root root 0 Jul 24 01:43 virtual}; &span(ConsoleOut){/sys/devices/platform $ };&span(ConsoleIn){cd platform}; &span(ConsoleOut){/sys/devices/platform $ };&span(ConsoleIn){ls -la}; &span(ConsoleOut){total 0}; &span(ConsoleOut){drwxr-xr-x 22 root root 0 Jul 24 01:43 .}; &span(ConsoleOut){drwxr-xr-x 16 root root 0 Jul 24 01:43 ..}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 ACPI000C:00}; &span(ConsoleOut){drwxr-xr-x 4 root root 0 Jul 24 01:45 Fixed MDIO bus.0}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 INT33FF:00}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 INT33FF:01}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 INT33FF:02}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 INT33FF:03}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 PNP0103:00}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 PNP0C0C:00}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 PNP0C0E:00}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 alarmtimer}; &span(ConsoleOut){drwxr-xr-x 4 root root 0 Jul 20 11:14 coretemp.0}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 efi-framebuffer.0}; &span(ConsoleOut){drwxr-xr-x 5 root root 0 Jul 24 01:45 };&span(ConsoleOut,Focus){i8042}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 microcode}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 pcspkr}; &span(ConsoleOut){drwxr-xr-x 2 root root 0 Jul 24 01:45 power}; &span(ConsoleOut){drwxr-xr-x 4 root root 0 Jul 24 01:45 reg-dummy}; &span(ConsoleOut){drwxr-xr-x 4 root root 0 Jul 24 01:45 serial8250}; &span(ConsoleOut){drwxr-xr-x 3 root root 0 Jul 24 01:45 snd-soc-dummy}; &span(ConsoleOut){-rw-r--r-- 1 root root 4096 Jul 24 01:45 uevent}; &span(ConsoleOut){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 の中で対応している関数は &ogdefs(serio_set_bind_mode(),serio_set_bind_mode);, &ogdefs(serio_show_bind_mode(),serio_show_bind_mode);) です。description ノードに対応している関数は (&ogdefs(serio_show_description()); と description ノードに文字列 "i8042 KBD port" を設定しているのは &ogdefs(i8042_create_kbd_port(),i8042_create_kbd_port);) です。デバイスを制御、機能確認するためノードです。このようなノードの多くは shell から cat, echo command で読み出し、書き込みできる様に実装されています。手軽にスクリプトで動作確認・制御できる Userland API になっています。続く操作例はノードを読み出しているところです。 i8042 ディレクトリを見てみましょう。このディレクトリには PS/2 keyboard と mouse デバイスが対応します。/sys/devices/platform/i8042/serio0 ディレクトリの下にある bind_mode ノードのために kernel の中で対応している関数は &ogdefs(serio_set_bind_mode(),serio_set_bind_mode);, &ogdefs(serio_show_bind_mode(),serio_show_bind_mode);) です。description ノードに対応している関数は (&ogdefs(serio_show_description()); と description ノードに文字列 "i8042 KBD port" を設定しているのは &ogdefs(i8042_create_kbd_port(),i8042_create_kbd_port);) です。デバイスを制御、機能確認するためノードです。このようなノードの多くは shell から cat, echo command で読み出し、書き込みできる様に実装されています。手軽にスクリプトで動作確認・制御できる User Space API になっています。続く操作例はノードを読み出しているところです。 #pre(soft,overflow:auto){{ &span(ConsoleOut){/sys/devices/platform $ };&span(ConsoleIn){cd i8042}; &span(ConsoleOut){/sys/devices/platform/i8042 $ };&span(ConsoleIn){ls -la}; &span(ConsoleOut){total 0}; &span(ConsoleOut){drwxr-xr-x 5 root root 0 Jul 24 01:45 .}; &span(ConsoleOut){drwxr-xr-x 22 root root 0 Jul 24 01:43 ..}; &span(ConsoleOut){lrwxrwxrwx 1 root root 0 Jul 24 01:53 driver -> ../../../bus/platform/drivers/i8042}; &span(ConsoleOut){-rw-r--r-- 1 root root 4096 Jul 24 01:53 driver_override}; &span(ConsoleOut){-r--r--r-- 1 root root 4096 Jul 24 01:53 modalias}; &span(ConsoleOut){drwxr-xr-x 2 root root 0 Jul 24 01:53 power}; &span(ConsoleOut){drwxr-xr-x 4 root root 0 Jul 24 01:53 };&span(ConsoleOut,Focus){serio0}; &span(ConsoleOut){drwxr-xr-x 4 root root 0 Jul 24 01:53 serio1}; &span(ConsoleOut){lrwxrwxrwx 1 root root 0 Jul 24 01:53 subsystem -> ../../../bus/platform}; &span(ConsoleOut){-rw-r--r-- 1 root root 4096 Jul 24 01:53 uevent}; &span(ConsoleOut){/sys/devices/platform/i8042 $ };&span(ConsoleIn){cd serio0}; &span(ConsoleOut){/sys/devices/platform/i8042/serio0$ };&span(ConsoleIn){ls -la}; &span(ConsoleOut){total 0}; &span(ConsoleOut){drwxr-xr-x 4 root root 0 Jul 24 01:53 .}; &span(ConsoleOut){drwxr-xr-x 5 root root 0 Jul 24 01:45 ..}; &span(ConsoleOut){-rw-r--r-- 1 root root 4096 Jul 24 01:54 };&span(ConsoleOut,Focus){bind_mode}; &span(ConsoleOut){-r--r--r-- 1 root root 4096 Jul 24 01:54 };&span(ConsoleOut,Focus){description}; &span(ConsoleOut){--w------- 1 root root 4096 Jul 24 01:54 drvctl}; &span(ConsoleOut){-r--r--r-- 1 root root 4096 Jul 24 01:54 firmware_id}; &span(ConsoleOut){drwxr-xr-x 2 root root 0 Jul 24 01:54 id}; &span(ConsoleOut){-r--r--r-- 1 root root 4096 Jul 24 01:54 modalias}; &span(ConsoleOut){drwxr-xr-x 2 root root 0 Jul 24 01:54 power}; &span(ConsoleOut){lrwxrwxrwx 1 root root 0 Jul 24 01:54 subsystem -> ../../../../bus/serio}; &span(ConsoleOut){-rw-r--r-- 1 root root 4096 Jul 24 01:54 uevent}; &span(ConsoleOut){/sys/devices/platform/i8042/serio0 $ };&span(ConsoleIn){cat bind_mode}; &span(ConsoleOut){auto}; &span(ConsoleOut){/sys/devices/platform/i8042/serio0 $ };&span(ConsoleIn){cat description}; &span(ConsoleOut){i8042 KBD port}; }} bind_mode ノードの実装を詳しく見ていきましょう。このノードは &ogdefs(DEVICE_ATTR()); マクロでノードの名前、パーミッション、show(), store() 関数が定義されています。 &ogdefs(attribute); 構造体、&ogdefs(attribute_group); 構造体、&ogdefs(attribute_group); 構造体を指すポインタの配列で包み、&ogdefs(serio_init_port()); で &ogdefs(device,device,device.h); 構造体の group メンバに設定しています。 #code(c,/DEVICE_ATTR_RO.*modalias/../serio_id_attr_group/,end+=3,ogfileone:/drivers/input/serio/serio.c); #code(c,/serio_init_port/../^}/,begin-=3,ogfileone:/drivers/input/serio/serio.c); group メンバは &ogdefs(sysfs_create_groups()); が呼び出されるまでそのままです。&ogdefs(sysfs_create_groups()); までを追います。イベント "&ogdefs(serio_queue_event());, &ogrefs(SERIO_REGISTER_PORT);" を経由して &ogdefs(serio_handle_event()); イベントハンドラ関数内で &ogdefs(serio_add_port()); で &ogdefs(serio,serio,serio.h); クラス・デバイスとして追加されます。ドライバとデバイスの基底的な処理&ogfileone(/drivers/base); にある &ogdefs(device_add()); で kernel にデバイスとして追加されます。&ogdefs(device_add()); から &ogdefs(device_add_attrs());, &ogdefs(device_add_groups()); を経て、先の group メンバを &ogdefs(sysfs_create_groups()); に渡して sysfs ノードとして登録しています。 #code(c,/device_add_groups/../^}/,ogfileone:/drivers/base/core.c); sysfs ノードの追加は &ogdefs(serio); クラス・デバイスなので複雑です。簡易に sysfs ノードを設けるのであれば &ogdefs(DEVICE_ATTR()); マクロでノード定義し、&ogdefs(attribute); 構造体、&ogdefs(attribute_group); 構造体で包み、&ogdefs(sysfs_create_group()); (group が単数形であることに注意) でノードを登録、&ogdefs(sysfs_remove_group()); でノードを削除することができます。&ogrefs(DEVICE_ATTR(),DEVICE_ATTR); の沢山の用例を見て使い方を習得できるでしょう。&ogdefs(sysfs_create_group()); の引数 &ogdefs(kobject); 構造体を指すポインタ kobj は &ogdefs(device,device,/include/linux/device.h); 構造体のメンバとして存在している kobj を指すポインタを使って下さい。 #textbox(note, あちこちにある uevent node はどんな機能があるの?){{ 書き込む (&ogdefs(uevent_store());) と uevent を発行する (&ogdefs(kobject_uevent_env());) 機能と、読み込む (&ogdefs(uevent_show());) 機能が実装されています。読み込む機能は一部のノード、例えば /sys/class/input/* の下にある uevent ノードに実装 (&ogdefs(input_dev_uevent(),input_dev_uevent);) されています。読み込む機能は uevent で発行される情報の一部をいつでも取得できる様になっています。 &br;uevent が発行するイベントは socket (&ogdefs(AF_NETLINK);.&ogdefs(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 (&ogsearch(CONFIG_UEVENT_HELPER_PATH, CONFIG_UEVENT_HELPER_PATH);) です。 }}