DRIVER(9) NetBSD Kernel Developer's Manual DRIVER(9) 名称 driver -- デバイスドライバの記述 書式 #include #include #include static int foo_match(struct device *parent, struct cfdata *match, void *aux); static void foo_attach(struct device *parent, struct device *self, void *aux); static int foo_detach(struct device *self, int flags); static int foo_activate(struct device *self, enum devact act); (訳註: struct device の宣言は sys/sys/device.h: struct device { enum devclass dv_class; /* this device's classification */ TAILQ_ENTRY(device) dv_list; /* entry on list of all devices */ struct cfdata *dv_cfdata; /* config data that found us (NULL if pseudo-device) */ struct cfdriver *dv_cfdriver; /* our cfdriver */ struct cfattach *dv_cfattach; /* our cfattach */ int dv_unit; /* device unit number */ char dv_xname[16]; /* external name (name + unit) */ struct device *dv_parent; /* pointer to parent device (NULL if pesudo- or root node) */ int dv_flags; /* misc. flags; see below */ int *dv_locators; /* our actual locators (optional) */ }; ) 解説 このマニュアルページでは、デバイスドライバで使用される、NetBSD 自 動コンフィグレーション(autoconfiguration)のインターフェースの概略 を説明します。自動コンフィグレーションのより詳細な概要は、 autoconf(9) を参照してください。 各デバイスドライバはシステムに対して、自動コンフィグレーションの 標準インターフェースを用意しなければなりません。このインターフェー スは cfattach 構造体を用いて提供されます。ドライバに対するインター フェースは、C 言語の定数であり、ドライバ内部に静的に定義されます。 例えばドライバ ``foo'' のインターフェースは次のように定義されます。 CFATTACH_DECL(foo, /* ドライバ名 */ sizeof(struct foo_softc), /* インスタンスデータのサイズ */ foo_match, /* match/probe 関数 */ foo_attach, /* attach 関数 */ foo_detach, /* detach 関数 */ foo_activate); /* activate 関数 */ (訳註: CFATTACH_DECL() の定義は sys/sys/device.h: #define CFATTACH_DECL(name, ddsize, matfn, attfn, detfn, actfn) \ struct cfattach __CONCAT(name,_ca) = { \ ___STRING(name), { 0 }, ddsize, matfn, attfn, detfn, actfn, 0, 0 \ } cfattach 構造体の宣言は sys/sys/device.h: typedef int (*cfmatch_t)(struct device *, struct cfdata *, void *); struct cfattach { const char *ca_name; /* name of attachment */ LIST_ENTRY(cfattach) ca_list; /* link on cfdriver's list */ size_t ca_devsize; /* size of dev data (for malloc) */ cfmatch_t ca_match; /* returns a match level */ void (*ca_attach)(struct device *, struct device *, void *); int (*ca_detach)(struct device *, int); int (*ca_activate)(struct device *, enum devact); /* technically, the next 2 belong into "struct cfdriver" */ int (*ca_rescan)(struct device *, const char *, const int *); /* scan for new children */ void (*ca_childdetached)(struct device *, struct device *); }; struct cfdata { const char *cf_name; /* driver name */ const char *cf_atname; /* attachment name */ short cf_unit; /* unit number */ short cf_fstate; /* finding state (below) */ int *cf_loc; /* locators (machine dependent) */ int cf_flags; /* flags from config */ const struct cfparent *cf_pspec;/* parent specification */ const char * const *cf_locnames;/* locator names (machine dependent) */ }; ) ドライバに制御される個々のデバイスインスタンスに対して、自動コン フィグレーション・フレームワークはブロックメモリを確保し、デバイ スインスタンス固有(device-instance-specific)のドライバ変数を記録 できるようにします。メモリブロックのサイズは CFATTACH_DECL マクロ の第 2 引数に指定します。メモリブロックはドライバの softc 構造体 として使用されます。softc 構造体はドライバ内部でのみアクセスされ るので、その定義はドライバから見てローカルな定義となります。しか し、softc 構造体は、NetBSD のコンフィグレーションとネーミング規約 の標準に従うべきです。例えばドライバ ``foo'' の softc 構造体は次 のように定義しましょう。 struct foo_softc { struct device sc_dev; /* 汎用のデバイス情報 */ /* デバイス固有の情報 */ }; 自動コンフィグレーション・フレームワークでは、softc 構造体の最初 のメンバはドライバ独立(driver-independent)な、device 構造体でな けれはななりません。ドライバにとってこの構造体が最も有用な点は、 おそらくデバイスインスタンス名 dv_xname というメンバを持っている ことでしょう。 ドライバがユーザーランドに対してキャラクタデバイスインターフェー スを提供する場合、ドライバは cdevsw 構造体を定義しなければなりま せん。この構造体は C 言語の定数であり、ドライバ内部で定義されます。 例えばドライバ ``foo'' の cdevsw 構造体は次のように定義されます。 const struct cdevsw foo_cdevsw { int (*d_open)(dev_t, int, int, struct proc *); int (*d_close)(dev_t, int, int, struct proc *); int (*d_read)(dev_t, struct uio *, int); int (*d_write)(dev_t, struct uio *, int); int (*d_ioctl)(dev_t, u_long, caddr_t, int, struct proc *); struct tty *(*d_tty)(dev_t); int (*d_poll)(dev_t, int, struct proc *); paddr_t (*d_mmap)(dev_t, off_t, int); int d_type; }; 構造体変数の名前はドライバのベース名に ``_cdevsw'' を付加した foo_cdevsw でなければなりません。この規約は自動コンフィグレーショ ン・フレームワークにより要請されます。 (訳註: struct proc の宣言は sys/sys/proc.h struct uio の宣言は sys/sys/uio.h: struct uio { struct iovec *uio_iov; /* pointer to array of iovecs */ int uio_iovcnt; /* number of iovecs in array */ off_t uio_offset; /* offset into file this uio corresponds to */ size_t uio_resid; /* residual i/o count */ enum uio_seg uio_segflg; /* see above */ enum uio_rw uio_rw; /* see above */ struct proc *uio_procp;/* process if UIO_USERSPACE */ }; dev_t の定義は sys/types.h: typedef uint32_t dev_t; /* device number */ #define major(x) ((int32_t)((((x) & 0x000fff00) >> 8))) #define minor(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ (((x) & 0x000000ff) >> 0))) #define makedev(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ (((y) << 12) & 0xfff00000) | \ (((y) << 0) & 0x000000ff))) unit32_t の定義は sys/arch/sh3/include/int_types.h typedef unsigned int __uint32_t; caddr_t, daddr_t の定義は sys/types.h: typedef __caddr_t caddr_t; /* core address */ __caddr_t の定義は sys/ansi.h: typedef char * __caddr_t; /* core address */ ) ドライバがブロックデバイスインターフェースも提供する場合、ドライ バは bdevsw 構造体を定義しなければなりません。この構造体は C 言語 の定数であり、ドライバ内部で定義されます。例えばドライバ ``foo'' の bdevsw 構造体は次のように定義されます。 const struct bdevsw foo_bdevsw { int (*d_open)(dev_t, int, int, struct proc *); int (*d_close)(dev_t, int, int, struct proc *); void (*d_strategy)(struct buf *); int (*d_ioctl)(dev_t, u_long, caddr_t, int, struct proc *); int (*d_dump)(dev_t, daddr_t, caddr_t, size_t); int (*d_psize)(dev_t); int d_type; }; 構造体変数の名前はドライバのベース名に ``_bdevsw'' を付加した foo_bdevsw でなければなりません。この規約は自動コンフィグレーショ ン・フレームワークにより要請されます。 static int foo_match(struct device *parent, struct cfdata *match, void *aux); システムのブートストラップの過程で、自動コンフィグレーション・フ レームワークはデバイスを見つけるためにシステムを探します。各デバ イスドライバから見ると、ドライバをデバイスインスタンスにマッチさ せるため、それぞれの match 関数が(cfattach 構造体経由で)呼び出さ れることになります。match 関数は 3 つの引数を持って呼び出されます。 最初の引数 parent はドライバの親の device 構造体へのポインタです。 第 2 引数 match は、データ構造へのポインタで、自動コンフィグレー ション・フレームワークがドライバを理解するための情報が入っていま す。parent と match は、ほとんどのドライバで無視されます。 The third argument aux con- tains a pointer to a structure describing a potential device-instance. 第 3 引数 aux はデータ構造へのポインタで、potential device-instance 情報が入っています。 aux は 親からドライバに渡されます。match 関数は aux 引数を適切な アタッチメント構造体にタイプキャストし、その内容からデバイスをサ ポートするかどうかを決定します。デバイスハードウェアによっては、 アタッチメント構造体に ``ロケータ(locators)'' を含んでいるかも知 れません。ドライバは、デバイスインスタンスの 固有情報をプローブし、 ロケータと照合することで、デバイスインスタンスの位置を決めること ができます。 プローブの過程で、より多くのデバイスプロパティを識別した場合、ア タッチメント構造体のメンバーを変更することができます。NetBSD の規 約では、そのようなデバイスに対して違いを明確にするため、 foo_match() の代わりに match ルーチン foo_probe() を呼び出すこと になっています。どちらの場合も match 関数は、デバイスをサポートす る場合、優先度に応じ 0 でない整数を返し、ドライバがデバイスをサポー トしない場合は 0 を返します。一般には、一つのデバイスに対してはた だ一つのドライバが存在するだけなので、match 関数はマッチした場合 1 を返します。 static void foo_attach(struct device *parent, struct device *self, void *aux); 自動コンフィグレーション・フレームワークは、match 関数が最も大き い値を返したドライバに対して、attach 関数を(cfattach 構造体経由で) 呼び出します。attach 関数は 3 つの引数を持って呼び出されます。 attach 関数はデバイスを動作させる為に必要な初期化作業を行います。 最初の引数 parent はドライバの親の device 構造体へのポインタです。 第 2 引数 self はドライバ自身の device 構造体へのポインタです。 device 構造体は softc 構造体の最初のメンバなので、self はまたドラ イバの softc 構造体へのポインタでもあります。第 3 引数 aux は、 アタッチメント構造体へのポインタです。parent と aux は match 関数 に渡されたものと同じものです。 ドライバの attach 関数は、システム割り込みが有効になる前に呼び出 されます。初期化の過程で割り込みが必要な場合は、attach 関数は config_interrupts() を使用すべきです(autoconf(9) を参照)。 static int foo_detach(struct device *self, int flags); ある種のデバイスは、システムをリブートしなくても取り外しが可能で す。自動コンフィグレーションフレームワークは、デバイスデタッチメ ントの過程で detach 関数を(cfattach 構造体経由で)呼び出します。 デバイスがデタッチメントをサポートしない場合は、ドライバは detach 関数を提供する必要はありません。detach 関数はドライバに割り当てら れた資源のうち、不要になったものを放棄する為に使用されます。 最初の引数 self はドライバ自身の device 構造体です。これは attach 関数に渡されたものと同じものです。第 2 引数 flags にはデタッチメ ントフラグが入っています。有効な値は DETACH_FORCE (強制デタッチ; ハードウェアが無くなった) と DETACH_QUIET (告知を表示しない) です。 static int foo_activate(struct device *self, enum devact act); 自動コンフィグレーションフレームワークは、確保済みの資源に変更が あったことをドライバに通知するため、activate 関数を呼び出します。 例えば、イーサネットドライバは、カーネルにネットワークスタックが 追加・削除されたことを通知されます。最初の引数 self はドライバ自 身の device 構造体です。これは attach 関数に渡されたものと同じも のです。第 2 引数 act はアクションを記述します。有効な値は DVACT_ACTIVATE (デバイスをアクティベートする) と DVACT_DEACTIVATE (デバイスをディアクティベートする) です。activate 関数が action をサポートしない場合は、EOPNOTSUPP を返すべきです。activate 関数 は割り込みコンテキストから呼び出されます。 ほとんどのドライバは割り込み機能を使用したいと考えるでしょう。割 り込みロケータはアタッチメント構造体を通じて供給され、システム内 で割り込みを設定するために使用されます。一般に、割り込みインター フェースは親によって供給されます。このインターフェースでは、割り 込みハンドラと、ドライバ固有の引数を明確にしなければなりません。 固有の引数は多くの場合デバイスインスタンス固有の softc 構造体にな ります。 デバイスのハードウェア割り込みが発生すると、固有の引数を持ってハ ンドラが呼び出されます。割り込みハンドラは ``割り込みは自分のもの ではない'' 場合 0 を、``自分が割り込みを処理した'' 場合 1 を、 ``自分のものかもしれないが、想定していなかった'' 場合 -1 を返すべ きです。 ドライバをカーネル内にコンパイルするためには、config(8) がその存 在を知る必要があります。そのためにはドライバを含むディレクトリに、 files. というファイルを作り、その中にドライバのエントリを記 述しなければなりません。例えばドライバ ``foo'' は、バス ``bar'' にアタッチされ、カーネルモジュール ``baz'' に依存するという場合、 次のように記述します。 device foo: baz attach foo at bar file dev/bar/foo.c foo これにより、機種記述ファイルに次のように追加することができます。 foo* at bar? For device interfaces of a driver to be compiled into the kernel, config(8) must be aware of its existence. ドライバのデバイスインターフェースをカーネル内にコンパイルするためには、 config(8) がその存在を知る必要があります。 そのためには majros. にエントリを記述しなければなりません。 例えば、ドライバ ``foo'' がキャラクタデバイス・インターフェースを 持ち、そのキャラクタ・メジャーデバイス番号が ``cmaj'' で、ブロッ クデバイス・インターフェースを持ち、そのブロックデバイス・メジャー 番号が ``bmaj'' で、カーネルモジュール ``baz'' に依存するという場 合、次のように記述します。 device-major foo char cmaj block bmaj baz 機種記述ファイルと ``デバイス定義'' 言語の詳細な説明は config(9) を参照してください。 関連項目 config(8), autoconf(9), config(9), powerhook_establish(9), shutdownhook_establish(9) NetBSD 3.1 September 6, 2002 NetBSD 3.1 $Id: driver.0,v 1.3 2007/07/12 02:48:28 candy Exp candy $