デバイスをアクセスするドライバを作ります。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_ref_setup.c | i8253 channel 1 を platform device として登録します。 /arch/x86/kernel に配置 |
kernel の Makefile | i8253_ref_setup.c を kernel に静的に結合します。 /arch/x86/kernel/Makefile を修正 |
kernel の Kconfig | i8253_ref_setup.c を組み込むかどうか make menuconfig で選択する。*_defconfig チェックします。 /arch/x86/Kconfig を修正 |
device header file i8253_control.h | i8253 のレジスタを定義します。 include/linux に配置 |
driver header file i8253_ref.h | platform device の IO 空間配置と初期設定をドライバに渡す定義です。 include/linux に配置 |
module として構成したドライバ i8253_ref.c | i8253 channel 1 をアクセスします。 |
上の Makefile | i8253_ref.c を kernel object として構築します。 |
急に大規模な開発になった様に感じるかもしれません。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 (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 で読み出し、書き込みできる様に実装されています。手軽にスクリプトで動作確認・制御できる 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) に kernel から userland に向かう送信をし、最近のディストリビューションでは使われなくなりつつある /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 起動時から少ない場合です。
次のコード片は i8253_ref_device_initcall() で kernel に登録した i8253 device の情報です。Kernel のデータ構造 resource, platform_device とドライバで定義したマクロとデータ構造 i8253_ref.h を参考に読んで見て下さい。
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 | - | ! - - | | | ! - | | | ! ! - | ! - | | ! - | | ! - | | | | | - | ! ! - | |
|
kernel に登録した情報の中に device と driver を結びつけるための情報が含まれています。I8253_REF_DEVICE_NAME です。この文字列をキーにして platform device と platform driver を対応づけます。resource 構造体の配列にも文字列 I8253_REF_RESOURCE_REFRESH_COUNTER, I8253_REF_RESOURCE_CONTROL_WORD が含まれています。これは $ cat /proc/ioports で表示される様になります。文字列をキーにして driver 側で I/O ポート番号を platform_get_resource_byname()で取得できます。デバイス固有のパラメータは platform_device.dev.platform_data で指した先に構造体 i8253_ref_platfrom_data を配置し、driver に渡します。
25 26 27 28 29 |
|
i8253_ref_setup.c を kernel に静的に結合する(スタティック・リンク)する方法を見ていきます。新しく作ったソース・ファイルを kernel にスタティック・リンクするのに最低限必要なことは、ソースを配置したディレクトリまたはその上流に辿る経路で近い場所にある Makefile を修正しコンパイル・リンクする対象に加えることです。加えてソースファイルから上流に辿る経路で近い場所にある Kconfig ファイルに新しく作ったソースをコンパイル・リンク対象にするのかどうか make menuconfig で選択できる様にします。
make menuconfig で新しく加えたソース・ファイルをコンパイル対象にするかどうか選択できる様にしておくと、チーム開発をしている状況での問題の切り分け、開発環境の足並みが揃っていない状況で柔軟に対応することができます。
/arch/x86/kernel/Makefile の変更箇所は次の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
obj-$(CONFIG_I8253_REFRESH) += i8253_ref_setup.o は CONFIG_I8253_REFRESH を展開した結果によって obj- += i8253_ref_setup.o または obj-y += i8253_ref_setup.o と解釈されます。ここでは出てきませんが CONFIG_I8253_REFRESH が tristate (モジュール) の場合は obj-m += i8253_ref_setup.o となる場合が有ります。obj-y += i8253_ref_setup.o となった場合、kernel に静的に結合します。
obj-* += file.o | 構築結果 |
obj- += file.o | file.c はコンパイルされない |
obj-y += file.o | file.c は kernel に静的に結合(スタティックリンクされる) |
obj-m += file.o | file.c はモジュールとして構築され file.ko が作られる (このページでは扱いません) |
arch/x86/Kconfig の変更箇所は次の通りです。この Kconfig の位置は例外的に Makefile と ソースファイル i8253_ref_setup.c が有るディレクトリと違っています。この修正で make menuconfig で i8253_ref_setup.c を kernel に組み込むかどうか前節の Makefile の修正と併せ選択することができる様になります。詳細な書き方は /Documentation/kbuild/kconfig-language.txt を参照して下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
config I8253_REFRESH で選択項目を宣言します。ここでは接頭辞 CONFIG_ を付けずに書くことに注意して下さい。bool は y か n (定義しない)の選択だと言う意味です。default は n (定義しない)、 depends on X86 は X86(CONFIG_X86) が定義されれば選択項目に現れることを意味します。help は make menuconfig で help を要求したときに表示されるテキストです。他の項目で ---help--- と書かれているところもあります。どちらも同様に解釈されます。help の部分はインデントに意味があります。他の部分のインデントも慣例的に合わせて書くことをお勧めします。
追加した config 項目は make menuconfig のどこに現れるの?
make menuconfig を実行して追加した項目がどこに行ったか分からなくなるかもしれません。手っ取り早く知るには make menuconfig で対話的設定を開始したところで / を押してマクロ名を検索します。このページの例では I8253_REFRESH を探す文字列とします。Location: 以下に場所のパスが示されます。丁寧に Kconfig の階層構造を探すより早いです。
他の architecture や platform の場合、どこにソース・コードを追加して、どこの Makefile や Kconfig の修正をしたら良いの?
開発ターゲットの基板に実装されたテバイスを追加するために /arch/processor 以下に配置されたファイルを修正したり、追加するやり方に迷うかもしれません。ディレクトリ構成はプロセッサ(CPU)毎に大きく違います。見回してみて、しっくりする修正方法を考えてください。似たようなデバイスを見つけて並べるように修正するのが良いでしょう。他のプロセッサに比べて派生品種が多い ARM 系では /arch/arm/plat-platform に SoC 毎に派生するコード、/arch/arm/mach-machine に基板品種毎に派生する(付加した周辺回路によって派生する)コードが含まれることが多いです(SoC 毎に派生するコードもあります)。
i8253_ref.c がソースコード全体です。
モジュールを初期化する処理から見ていきましょう。module_init() マクロに初期化処理関数 i8253_ref_init() を指定します。これは HelloWorld モジュールと同じです。printk() で関数が呼び出されたことを表示します。デバッグ目的です。platform_driver_register() で platform driver を登録します。エラー表示は KERN_ERR の方が良かったかもしれません。
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | - | | | ! - | | - | | | | ! | ! |
|
445 446 |
|
登録するドライバ i8253_ref_driver の内容を見てみましょう。platform_driver.device_driver driver.name に文字列 I8253_REF_DEVICE_NAME を指定しています。この文字列は platform_device_register で登録したデバイスの名前と照合され、一致していれば platform_driver の .probe に指定した関数 i8253_ref_probe() が呼ばれます。
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 | - | ! - | | ! - | ! - - | | | | ! | | | ! |
|
platform_driver のメンバのいくつかは関数を指すポインタです。このページに出てきた関数を指すメンバ(メソッド)を次の表に示します。
メンバ変数 | 変数が指した先の関数機能 |
probe | 対応するデバイスが見つかった(kernel に{登録されていたら | 登録されたら})呼び出される。一般的なドライバはレジスタやデバイスのメモリをマップ(bus 直結のデバイス)、デバイスを存在確認、ドライバ状態領域確保、thread または work queue などの worker context 初期化・起動、割り込みハンドラ設置、初期化などをする。 |
remove | 対応するデバイスが外された(kernel から削除された)、または、ドライバが削除されたら呼び出される。一般的なドライバはデバイスを停止する(外された場合はアクセスできないので注意が必要)、デバイスからの割り込みを止めてハンドラを外す、確保した起動した thread, work queue などのコンテキスト停止・終了、ドライバ状態領域解放、メモリマップ解除(bus 直結のデバイス)を行い probe 前の状態にする。 |
shutdown | shutdown の時に呼び出される。一般的なドライバはほぼ remove の時と同じ処理をする。電池動作機器の場合、デバイスに供給されている電源を遮断、クロックを停止などの追加的な停止処理を行う場合もある。 |
driver->pm.suspend | suspend する(standby または hibernate 状態に入る)時に呼び出される。一般的なドライバは電源供給停止、クロック停止または低周波数化などの電力管理、wakeup 準備、不意な割り込みを停止するなどを行う。 |
driver->pm.resume | resume する(standby または hibernate 状態から出る)時に呼び出される。一般的なドライバは電源供給再開、クロック供給または常用周波数に設定するなどの電力管理、wakeup 完了処理、割り込みを再開するなどを行う。 |
ほかにも i8253_ref driver で出てこなかった関数を指すメンバが platform_driver.device_driver driver->dev_pm_ops pm の中に多くあります。これらの中に system call の read(), write(), open(), close()(release) 機能を実装している関数を指すポインタはありません(代表的で原始的な構造体は file_operations)。userland からデバイスをアクセスするための API は別の kernel 内関数を使って登録します。Userland から操作する機能を全く提供しないデバイス・ドライバも作れます。例えばハードウエア・シーケンサの代わりの様な house keeping (実行環境の維持) をするだけのドライバです。
driver->pm.suspend と suspend メンバのどちらを使えば良いの?
platform_driver と device_driver driver->dev_pm_ops pm のメンバを見てみると、それぞれに suspend メンバがあります。 driver->pm.suspend を使った方か良いと考えられます。platform_pm_suspend(), platform_pm_resume() が suspend, resume 処理で通過する関数です。実装を見てみると driver->pm.suspend が NULL でなければ suspend を呼び出す様になっています。resume も同様です。platform_driver の suspend, resume メンバが指している関数を呼ぶ処理はそれぞれ platform_legacy_suspend(), platform_legacy_resume() という名前になっています。旧式だという扱いです。Linux Kernel ではこういった古い方式の処理は書き換えられ、廃止されることがあります。Userland 向けの API を頑なに維持するのとは対照的です。
probe 処理 i8253_ref_probe() を見ていきます。i8253_ref_driver.probe が指している関数です。 platform_driver_register() で対応するデバイスが見つかったならば呼ばれます。kernel に組み込んだ初期化処理 i8253_ref_setup.c にて既に platform_device_register() で name=I8253_REF_DEVICE_NAME となっているデバイスは登録済みです。ですので platform_driver_register() を呼び出すと直ちに probe 関数が呼ばれます。i8253_ref_probe() の back trace を取得すると platform_driver_register() からいくつかの関数を経由して呼ばれた様に見えます。
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | - | | | | ! - | | | | | | | | | | | - | | | ! | |
|
probe 処理の順番はデバイスの挙動、確保するリソース同士の依存関係によって緻密に考える必要があります。i8253_ref ドライバではドライバ状態 i8253_ref を確保する処理から始めました。次に resource から I/O ポート番号を取得して i8253_ref に保持する処理をします。メモリ・マップド・デバイスの場合は、物理メモリ空間を(kernel 内の)仮想アドレスへマップする処理 (例えば devm_ioremap_resource(), devm_request_mem_region(), ioremap(), ioremap_nocache() などを呼び出す処理) をする所です。
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | - | | | | | ! - | ! - | | | | | ! - | ! |
|
platform_device_register() で登録したデバイスのパラメータを platform_data メンバが指す先から取り出します。都度ポインタを辿るのが面倒なのでドライバ状態 i8253_ref のメンバ変数にコピーを保持します。rate 初期値指定がある場合 (rate_default != I8253_REF_RATE_DEFAULT_KEEP) は分周比レジスタ(ここでは rate register と呼びます)へ値を設定します。rate register を読み出すことはできないので、ドライバ状態として rate_saved に書き込んだ値を保持します。
315 316 317 318 319 320 321 322 323 324 325 326 327 | - | | | ! - | ! |
|
Userland 向け API を sysfs node に作ります。DEVICE_ATTR() マクロでノードのデータ構造を作ります。DEVICE_ATTR() は _name 引数(ここでは counter と rate)に接頭辞 dev_attr_ を付けて device_attribute 型の構造体変数を宣言します。このようにいくつかのマクロは接頭辞を付けて変数を宣言するものがあります。Linux Kernel ソース・コードを grep で追いにくくしています。宣言されているはずの変数が見つからない場合、接頭辞部分と思われる部分を除いて検索してみると見つかる可能性が高くなります。
S_IRUGO, S_IWUSR などはノードのパーミッションです。system call の chmod() と同様です。i8253_ref_counter_show, i8253_ref_rate_show, i8253_ref_rate_store は *_show が read() system call に対応する関数、*_store() が write() system call に対応する関数です。_show, _store に付いては後で詳しく触れます。
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | - | | ! - | | ! - | | | ! - | ! - | ! |
|
配列 attribute*[] の要素を DEVICE_ATTR() で作ったデータ構造の attr メンバを指すポインタで構成し NULL 終端します。さらに attribute_group 構造体の attrs メンバで配列 attribute*[] を指します。多段のポインタリンクを構成する理由は、手軽な Kernel 内 API sysfs_create_group() を使うためです。sysfs_create_group() を呼び出すと sysfs の /sys/devices/platform/i8253_ref.0.auto ディレクトリの下に counter, rate ノードが作られます。
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
| - | | | ! ! |
|
ノードを作った直後から Userland のアプリケーションからアクセスされる可能性が有ることに注意して下さい。デバイスの初期化が未完了だったり、定常動作に入るまで時間が掛かったり、ドライバが起動した thread や work queue などの worker が十分に処理可能な状態で無い場合、異常な値をアプリケーションに返したり、デバイスを異常状態に遷移させる可能性が無いようにして下さい。
ドライバにとって必須の機能「デバイスをアクセスする」を見ていきます。レジスタを read, write し「デバイスの状態を取得する」、「デバイスの動作を変える」機能です。PC を構成する i8253 には 3 つのカウンタが収められています。古いデバイスなので、それぞれのカウンタをお互いに干渉無しにアクセスすることはできません。周期割り込み機能を持つ Channel 0 のドライバと BEEP 周波数設定機能を持つ Channel 2 のドライバと互いに排他的にアクセスするために spin lock i8253_lock をロック raw_spin_lock_irqsave()、アンロック raw_spin_unlock_irqrestore() をしてアクセスします。
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | - | | | ! - | | | | | | | | | | | | ! |
|
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | - | | | ! - | | | | | | | | | | | | | | ! |
|
176
177
178
179
180
181
| - | | ! |
|
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | - | | | | | | | ! - | | - | | ! | ! |
|
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | - | | | | | | | ! - | | - | | ! - | | ! | ! |
|
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | - | | | | | | | | ! - | | | | - | | ! | | - | | ! - | | ! - | | ! | | | ! |
|
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | - | | | | ! - | | | - | | | ! | - | | ! | | | | | ! |
|
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | - | | | | ! - | | | - | | | ! | - | | ! | | | | ! |
|
435 436 437 438 439 440 441 442 443 | - | | ! - | | ! |
|