デバイスをアクセスするドライバを作ります。Windows そして古くは MS-DOS を実行できる PC ならば備わっていると考えられる i8253 Programmable Interval Timer (PIT) にアクセスします。今時の PC ならばほぼ使われていないはずの Channel 1 (D-RAM Refresh conter あるいは D-RAM Refresh timer) のレジスタにアクセスします。
今時の PC の i8253 Channel1 は D-RAM 制御に使われていないの?
完全な PC の回路が公開されていないので、断言はできません。30pin SIMM, 72pin DIMM を使用するのであれば、i8253-i8237 (PIT-DMAC) の組あるいは i8253(i8254) を timer として容易に Refresh 回路を構成できます。これより機能が増えた DIMM を使用する回路になると i8253 を使用する利点が無くなります。North bridge または CPU に統合された D-RAM controller であれは i8253 を D-RAM controller の一部に組み込むのを止めていると考えられます。i8253, i8237, 440BX North Bridge
おおよその仕様は次の通りです。
項目 | 仕様 |
動作環境 | PC-AT 仕様のパソコン、PentiumIII あるいはこれより新しいプロセッサを使用している |
対象デバイス | i8253 PIT channel1 |
デバイスの形態 | platform device |
ドライバの機能 | read counter, write rate, readback rate (ドライバ内部に保存した値を読み出し) |
ドライバの種類 | platform device driver |
ドライバ実装形態 | Kernel に直接組み込む部分とモジュール部分に分割 |
Userland API | sysfs node |
割り込み処理は実装しません。物足りないかもしれません。
仕様に書かれた内容を見ていきます。
platform device とは実行環境に固定的に接続され、ほかのデバイスの初期化をしなくても使えるデバイスです。多くの場合、プロセッサから出ているバスに直接接続されているか、初期化が不要または boot loader などで初期化が済んでしまっている BUS bridge を通して接続されているデバイスです。platform_device_register() で kernel に登録します。
on board の PCI 接続デバイスはどの様な扱いになるの?
/drivers/pci にある PCI bus ドライバ群で扱います。PCI bus bridge の初期化が必要なため PCI device として扱います。PCI bus のアドレス空間配置などを Linux kernel で都合よく扱えるように初期化し管理しています(pci_assign_resource()、pci_reassign_resource())。PCI デバイスを発見して登録する処理 pci_device_add()、PCI デバイスのためのドライバを登録する処理 pci_register_driver() 辺りを手掛かりにコードを追いかけてみてください。
platform driver は platform device のためのドライバです。platform_driver_register() で kernel に登録します。platform_device_register()、platform_driver_register() それぞれの呼び出しでデバイスとドライバの組み合わせが見つかったならばドライバの probe 処理が呼ばれます。
kernel に静的にリンクするコードと module として構成し動的にリンクするコードに分割して実装します。それぞれは次のように機能します。
実装 | 機能 |
kernel に静的にリンクするコード | i8253 channel 1 を platform device として登録する i8253_ref_setup.c /arch/x86/kernel に配置 |
kernel の Makefile | i8253_ref_setup.c を kernel に静的に結合する /arch/x86/kernel を修正 |
kernel の Kconfig | i8253_ref_setup.c を組み込むかどうか make menuconfig で選択する。*_defconfig チェックをする /arch/x86/Kconfig を修正 |
device header file | i8253 レジスタを定義する i8253_control.h include/linux に配置 |
driver header file | platform device の IO 空間配置と初期設定をドライバ渡す定義 i8253_ref.h include/linux に配置 |
module として構成したドライバ | i8253 channel 1 をアクセスするドライバ i8253_ref.c |
急に大規模な開発になった様に感じるかもしれません。Linux Kernel はデバイスとドライバを分けて扱っています。デバイスのためのコードとドライバのためのコードをそれぞれ書きます。
デバイスとドライバを一つのモジュール(ソースファイル)で同時に登録してはいけないの?
ドライバを作ろうとするデバイスのレジスタアドレスが固定的なのにわざわざファイルを分割してまで書くか? kernel の中を見回すと platform_create_bundle() の様に kernel にデバイスとドライバを同時に登録する処理が用意されています。/drivers/block/floppy.c の様にデバイスとドライバを同一のファイル内で kernel に登録する記述も見られます。ドライバを分割して作るまでもないと判断したら、手を抜いても良いかもしれません。
DEVICE_ATTR() を使い sysfs node を Userland から操作するための API にします。可能な操作は限定的です。単純な open-read-close, または open-write-close の流れに限定されます。一度の read, write で転送できる長さは PAGE_SIZE 以下です。i8253 のカウンタは単純な機能なのでこれで十分です。
汎用性が高い mknod(1)、mknod(2) で作成した major, minor 番号を持ったファイルシステム上のノードを使った API は別の機会で扱おうと考えています。
Linux kernel のデバイスとドライバの管理作法に従ってデバイスを kernel に登録すれば sysfs 上に対応するノードができます。sysfs は多くの linux で /sys に mount されています。 sysfs のツリーを辿ってデバイスを探してみましょう。
platform device は /sys/devices/platform の下にノードが並びます。デバイス名の一部または機能名の一部がディレクトリ名になって並んでいます。環境によりデバイスの有無は仮想的な物も含めて違うので、存在するディレクトリは増減します。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 (serio_set_bind_mode() serio_show_bind_mode()), description (serio_show_description(), description ノードに文字列 "i8042 KBD port" を設定しているのは i8042_create_kbd_port()) ノードを読み出してみます。デバイスを制御、機能確認するためノードです。このようなノードの多くは shell から cat, echo command で読み出し、書き込みできる様に実装されています。手軽にスクリプトで動作確認・制御できる Userland 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
あちこちにある uevent node はどんな機能があるの?
書き込む (uevent_store()) と uevent を発行する (kobject_uevent_env()) 機能と、読み込む (uevent_show()) 機能が実装されています。読み込む機能は一部のノード、例えば /sys/class/input/* の下にある uevent ノードに実装 (input_dev_uevent()) されています。読み込む機能は uevent で発行される情報の一部をいつでも取得できる様になっています。
uevent は Userland の udev 機能が使っている socket 通信 API (AF_NETLINK.NETLINK_KOBJECT_UEVENT)、最近のディストリビューションでは使われなくなりつつある /proc/sys/kernel/hotplug または /sys/kernel/uevent_helper に設定された uevent helper (uevent_helper) 実行ファイルを実行する (call_usermodehelper_setup(), call_usermodehelper_exec()) 機能です。実行ファイルは慣例的に /sbin/hotplug (CONFIG_UEVENT_HELPER_PATH) です。
次の図は i8253 PIT channel1 デバイスとこれを操作するためのドライバに関係するデータ構造です。デバイスとドライバのために最低限扱う必要がある範囲です。構造体のメンバに含まれるポインタ、内包する構造体を追えばもっと範囲は広がります。
うす青 の構造体は Linux Kernel で定義された構造体です。うす橙
の構造体はこのページに書かれた i8253 ドライバのために定義した構造体です。
"Prepared in i8253_ref_setup.c" と書かれた囲みの中が kernel の初期化処理に追加した静的なデータ構造です。ドライバを作る立場から見て platform device の仕様・特性を保持する構造体になります。
"Allocated in i8253_ref.c" と書かれた囲みの中がドライバの状態維持のため i8253_ref.c で動的確保するデータ構造です。ドライバ自身の状態、デバイスの状態のコピーなどを保持します。より多くの機能と処理を行うドライバは排他制御・同期・参照カウンタなどをメンバーとして持ちます。
device 構造体の driver_data メンバ getter/setter
device 構造体の driver_data メンバは getter (dev_get_drvdata()) と setter (dev_set_drvdata()) を使ってアクセスします。珍しく排他制御などの付加的な機能がない getter/setter があるメンバです。
kernel の初期化処理で i8253 を platform device として登録します。/arch/x86/kernel の下に i8253_ref_setup.c を新しく作成し、この中に実装します。
ディレクトリの下に配置したソースをコンパイル対象にする
C 言語で書いたソースをディレクトリに置いただけではコンパイルされません。少なくともそのディレクトリの Makefile を編集してコンパイル対象に加える様にします (/Documentation/kbuild/makefiles.txt)。Makefile の位置は一部例外的に上位ディレクトリに有るかもしれません。多くの場合、Kconfig ファイルを編集して make menuconfig または *_defconfig ファイルの指定にてコンパイル対象にするか選択できる様にします (/Documentation/kbuild/kconfig-language.txt)。この章の後半で詳しく触れます。
次のコード片は i8253_ref_setup.c の初期化処理部分です。
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | - | ! - | | | - | | | | ! | ! |
|
kernel 初期化処理の時に呼ぶ関数を arch_initcall() で指定します。呼び出す順番を気にする場合は loader script (.lds) で厳密に制御する、既に存在する他の初期化処理コードを修正するなどして都合の良さそうな前後順になる様にして下さい。
arch_initcall() で指定した i8253_ref_device_initcall() の処理は platform_device_register() で i8253 デバイスを登録するだけです。エラーチェックをしています。しかし、エラーは発生しないはずです。ここでエラーになる場合は resource が衝突しているか、メモリが kernel 起動時から少ない場合です。
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | - | ! - - | | | ! - | | | ! ! - | ! - | | ! - | | ! - | | | | | - | ! ! - | |
|
25 26 27 28 29 |
|