Last updateed: $Date: 1999/08/11 14:57:29 $次のような作業があります。
sys/pci/cy_pci.c
に対する変更例を参考に、解説します。
sys/device.h
をインクルードするようにします。
#include <sys/systm.h> #include <sys/kernel.h> +#ifdef NEW_CONFIG +#include <sys/device.h> +#endif #include <vm/vm.h> #include <vm/pmap.h>
foo_probe
を
foo_pci_match
に変更します。
foo_attach
を
foo_pci_attach
に変更します。
ただし、PCI 専用のデバイスで、ISA 等とドライバを全く共有しないものは
混乱の恐れがないので、現状のような _pci
の部分を省略する
こともできます。
+#ifndef NEW_CONFIG static char *cy_probe __P((pcici_t, pcidi_t)); static void cy_attach __P((pcici_t, int)); +#else +static int cy_pci_match __P((struct device *, struct cfdata *, void *)); +static void cy_pci_attach __P((struct device *, struct device *, void *)); +#endif
cfattach
構造体のオブジェクトを定義します。
これは旧来の pci_device
構造体とおよそ同様の働きをしますが、
どのバスのドライバでも共通なのが違います。
cy_pci_softc
構造体は後述します。
+#ifdef NEW_CONFIG +struct cy_pci_softc { + struct device cy_pci_dev; +}; + +struct cfattach cy_pci_ca = { + sizeof(struct cy_pci_softc), 1, NULL, cy_pci_match, cy_pci_attach, +}; +#else
pci_device
構造体と、それをシステムに格納する
DATA_SET
マクロの部分は、不要なので
#ifdef
で削除します。
cfattach
構造体の
cy_pci
の部分の名前は、
config.new
コマンドが読み込む
files ファイルで指定したフロントエンド名でなければなりません。
フロントエンド名の定義は後述します。
(softc 構造体や match 関数、attach 関数の名前も、
慣例としてフロントエンド名から構成します)
void *aux
経由で
渡され、これを bus 依存の構造体(PCI の場合は
pci_attach_args
構造体) にキャストして
使うことになります。
-static char * -cy_probe(config_id, device_id) - pcici_t config_id; - pcidi_t device_id; +static int +cy_match (struct device *parent, struct cfdata *match, void *aux) { + struct pci_attach_args *pa = aux; + pcici_t config_id = pa->pa_tag; + pcidi_t device_id = pa->pa_type;
void *aux
で指定されたデバイスが自分がサポートしている
ものである場合は 1 を、そうでなければ 0 を返します。
- return ("Cyclades Cyclom-Y Serial Adapter"); - return NULL; + return (1); + return (0);
void *aux
経由で渡され、
これを bus 依存の構造体
(PCI の場合は pci_attach_args
構造体) にキャストして
使うことになります。
static void -cy_attach(config_id, unit) - pcici_t config_id; - int unit; +cy_attach (struct device *parent, struct device *self, void *aux) { + struct pci_attach_args *pa = aux; + pcici_t config_id = pa->pa_tag; + int unit = self->dv_unit;
+ printf (": Cyclades Cyclom-Y Serial Adapter\n") ioport = (u_int32_t) pci_conf_read(config_id, CY_PCI_BASE_ADDR1) & ~0x3;
sys/i386/isa/psm.c
に対する変更例を参考に、解説します。
sys/device.h
をインクルードするようにします。
#include <sys/select.h> #include <sys/uio.h> +#ifdef NEW_CONFIG +#include <sys/device.h> +#endif
cfattach
構造体を定義します。
これも PCI の例と似ていますが、若干違いがあります。
旧来の ISA デバイスドライバからの変更を最小限に済ますため、
ISA のバスコードは二通りのコンフィギュレーション方法を
用意しています。
一つは newconfig 方式の方法です。newconfig 方式では
PCI デバイスの変更と同様に match 関数と attach 関数を定義します。
もう一つは ISA ドライバ互換方式の方法です。
ISA ドライバ互換方式では、ISA ドライバの probe 関数と attach 関数を
変更することなしにそのまま使います。
この psm.c
の変更例では、ISA ドライバ互換方式を使います。
+#ifdef NEW_CONFIG +struct psm_softc_new { + struct device psm_dev; +}; + +struct cfattach psm_ca = { + sizeof(struct psm_softc_new), 0, &psmdriver, isa_dev_match, NULL, +}; +#endifISA ドライバ互換方式を使うには、
cfattach
構造体の
ca_aux
メンバーにそのドライバの
isa_driver
構造体へのポインタを設定します。
また ca_level
メンバーには 0 を設定します。
match 関数には ISA ドライバ互換方式用の共通 match 関数である
isa_dev_match
を設定し、
attach 関数は使用しないので NULL を設定します。
ISA ドライバ互換方式はあくまで互換性のための応急処置です。
ISA ドライバは、 newconfig 方式で書換えることが期待されます。
ISA 互換方式から newconfig 方式への書換え例を、
sys/i386/isa/psm.c
を
例に取って解説します。
なお、この書き換えは softc 構造体も newconfig が確保するものを使うように 書換えています。そのため、softc 構造体にアクセスする場所は全て書き換えが 必要です。この softc 構造体へのアクセスのための書き換えは多量になるため ここでは逐一解説することなく省略します。 詳しくは後述する「softc 構造体について」をごらん下さい。
ioconf.h
をインクルードするようにします。
+#ifdef NEW_CONFIG +#include "ioconf.h" +#endif /* NEW_CONFIG */
struct device
型の
メンバーを加えます。
また、 psm.c
の例では softc 構造体へのポインタの
(長さ NPSM
の)配列を
static に確保していますが、これは newconfig 的には好ましくありませんので、
削除します。
詳しくは、後述する「softc 構造体について」をごらんください。
/* driver control block */ -static struct psm_softc { /* Driver status information */ +struct psm_softc { /* Driver status information */ +#ifdef NEW_CONFIG + struct device psm_dev; /* device generic information */ +#endif : -} *psm_softc[NPSM]; +}; +#ifndef NEW_CONFIG +static struct psm_softc *psm_softc[NPSM]; +#endif
/* function prototypes */ +#ifndef NEW_CONFIG static int psmprobe __P((struct isa_device *)); static int psmattach __P((struct isa_device *)); +#endif
isa_driver
構造体も newconfig 方式では使いませんので削除します。
+#ifndef NEW_CONFIG /* device driver declarateion */ struct isa_driver psmdriver = { psmprobe, psmattach, "psm", FALSE }; +#endif
cf_level ISA 互換方式なら 0, newconfig 方式なら 1 ca_aux newconfig 方式ではNULL
ca_match newconfig 方式の match 関数 ca_attach newconfig 方式の attach 関数 ca_detach 通常NULL
ca_activate 通常NULL
#ifdef NEW_CONFIG -struct psm_softc_new { - struct device psm_dev; -}; +static int psm_match __P((struct device *, struct cfdata *, void *)); +static void psm_attach __P((struct device *, struct device *, void *)); +static int psm_init __P((struct psm_softc *, int, int)); struct cfattach psm_ca = { - sizeof(struct psm_softc_new), 0, &psmdriver, isa_dev_match, NULL, NULL, + sizeof(struct psm_softc), 1, NULL, psm_match, psm_attach, NULL,NULL, }; #endif
/* psm driver entry points */ +#ifdef NEW_CONFIG + +#define endprobe(v) { if (bootverbose) \ + --verbose; \ + kbdc_set_device_mask(sc->kbdc, mask); \ + kbdc_lock(sc->kbdc, FALSE); \ + return (v); \ + } +#else + #define endprobe(v) { if (bootverbose) \ --verbose; \ kbdc_set_device_mask(sc->kbdc, mask); \ @@ -728,17 +761,28 @@ free(sc, M_DEVBUF); \ return (v); \ } +#endif
psmprobe() と psmattach() を書換えて psm_match() と psm_attach() を 作成します。 ただし、旧 psmprobe() は単にデバイス検出だけではなく、 デバイスや softc 構造体の初期化を行っていて、 psmattach() はその初期化に依存しています。 後述する「注意すべきこと」にあるように、 これは好ましくないので、psmprobe() を psm_init() という初期化関数に 変更し、psm_match() と psm_attach() 双方から pms_init() を呼びだして、 必要に応じてデバイスや softc 構造体の初期化を行うようにします。
softc 構造体は newconfig で確保されたものを使いますので、
自分で malloc() しているところは削除します。
また malloc() に関連する処理も変更します。
また static に softc 構造体を保持する配列を削除しましたので、
NPSM
定数による検査も削除します。
+#ifdef NEW_CONFIG +static int +psm_init(struct psm_softc *sc, int iobase, int flags) +#else static int psmprobe(struct isa_device *dvp) +#endif { +#ifdef NEW_CONFIG + int unit = sc->psm_dev.dv_unit; /* XXX */ +#else int unit = dvp->id_unit; struct psm_softc *sc; +#endif int stat[3]; int command_byte; int mask; int i; +#ifndef NEW_CONFIG /* validate unit number */ if (unit >= NPSM) return (0); @@ -749,13 +793,20 @@ if (sc == NULL) return (0); bzero(sc, sizeof *sc); +#endif #if 0 kbdc_debug(TRUE); #endif +#ifdef NEW_CONFIG + sc->addr = iobase; + sc->kbdc = kbdc_open(sc->addr); + sc->config = flags & PSM_CONFIG_FLAGS; +#else sc->addr = dvp->id_iobase; sc->kbdc = kbdc_open(sc->addr); sc->config = dvp->id_flags & PSM_CONFIG_FLAGS; +#endif sc->flags = 0; if (bootverbose) ++verbose; @@ -764,7 +815,9 @@ printf("psm%d: unable to lock the controller.\n", unit); if (bootverbose) --verbose; +#ifndef NEW_CONFIG free(sc, M_DEVBUF); +#endif return (0); } @@ -978,20 +1031,56 @@ } /* done */ +#ifdef NEW_CONFIG + kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); + kbdc_lock(sc->kbdc, FALSE); + return (1); +#else psm_softc[unit] = sc; kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); kbdc_lock(sc->kbdc, FALSE); return (IO_PSMSIZE); +#endif +}
psm_init() を呼出す psm_match() を作成します。
match 関数には softc 構造体が渡されてきません。 したがって以前とは違い match 関数の中で softc 構造体にアクセスできないので それを工夫します。
parent の isa_softc 構造体にある sc_sensitive
フィールドは
ISA の probe 順序を制御します。sensitive hardware である
デバイスは、
if (!isa_sc->sc_sensitive) return (0);さもなくば
if (isa_sc->sc_sensitive) return (0);とします。
+#ifdef NEW_CONFIG +static int +psm_match(struct device *parent, struct cfdata *cf, void *aux) +{ + struct psm_softc dummy; + struct isa_softc *isa_sc = (struct isa_softc *)parent; + struct isa_attach_args *ia = (struct isa_attach_args *)aux; + int v; + + if (isa_sc->sc_sensitive) + return (0); /* psm is not sensitive device */ + bzero(&dummy, sizeof dummy); + ia->ia_compat.id_alive = IO_PSMSIZE; + + v = psm_init(&dummy, ia->ia_iobase, ia->ia_flags); + return (v); }
psmattach() を psm_attach() に書換えます。
newconfig 方式では、割り込みの登録を attach() 関数の中で行います。
割り込みの登録は isa_register_intr()
という関数を呼びだして行います。
isa_register_intr()
は sys/i386/isa/isa.c
で定義されています。
isa_register_intr()
の引数は次の通りです。
int
irq; 割り込み番号です。(ビットベクトルではないので注意)int
*mp; 割り込みマスクへのポインタです。旧 config ファイル中で のデバイス行の imask 指定 {tty,bio,net,cam} に応じて、 次のいずれかの変数へのポインタを渡します。 tty_imask bio_imask net_imask cam_imask デバイス行に imask 指定が無ければ、NULL を渡します。u_int
flags; FAST 割込みフラグがあれば、RI_FAST を指定します。これは 旧 struct isa_device の id_ri_flags フィールドに相当し、 主として RS232C ドライバ(sio 等)で使われます。それ以外の 場合は単に 0 を渡します。inthand2_t
handler; 割込みハンドラ (psm_intr()) への関数ポインタを渡します。void*
arg; 割込みハンドラが呼ばれた時の引数です。softc 構造体への ポインタを渡します。
+static void +psm_attach(struct device *parent, struct device *self, void *aux) +#else static int psmattach(struct isa_device *dvp) +#endif { +#ifdef NEW_CONFIG + struct psm_softc *sc = (struct psm_softc *)self; + struct isa_attach_args *ia = (struct isa_attach_args *)aux; + + psm_init(sc, ia->ia_iobase, ia->ia_flags); + isa_register_intr(ia->ia_irq, &tty_imask, 0, psm_intr, (void *)sc); +#else int unit = dvp->id_unit; struct psm_softc *sc = psm_softc[unit]; if (sc == NULL) /* shouldn't happen */ return (0); +#endif /* Setup initial state */ sc->state = PSM_VALID; @@ -1007,7 +1096,7 @@ #endif /* DEVFS */ #ifdef NEW_CONFIG - printf(": "); + printf(": "); #endif /* NEW_CONFIG */ #ifdef PSM_HOOKAPM @@ -1053,7 +1142,9 @@ if (bootverbose) --verbose; +#ifndef NEW_CONFIG return (1); +#endif /* NEW_CONFIG */ }
+#ifdef NEW_CONFIG +void +psm_intr(void *arg) +#else /* NEW_CONFIG */ void psmintr(int unit) +#endif /* NEW_CONFIG */ { /* * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN) @@ -1714,7 +1826,11 @@ MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN }; +#ifdef NEW_CONFIG + struct psm_softc *sc = (struct psm_softc *)arg; +#else /* NEW_CONFIG */ register struct psm_softc *sc = psm_softc[unit]; +#endif /* NEW_CONFIG */ mousestatus_t ms; int x, y, z; int c;
ドライバを追加したら、そのドライバのソースコードのあるファイルを
files ファイルに設定します。
現状では files ファイルは二つあります。
一つは /sys/conf/files.newconf
で
もう一つは /sys/i386/conf/files.i386.newconf
です。
前者はマシン独立なドライバのためのファイルで、
後者はマシン依存のドライバのためのファイルです。(少くとも本来は)
/sys/i386
以下にソースコードがあるデバイスドライバは
後者に、それ以外は前者に登録すればいいでしょう。
デバイスドライバの登録には、次の構文が関係しています。 (ここの説明はかなり省略して端折っています。 詳しくは Chiris Torek 氏の論文を参照して下さい)
device
構文。登録するデバイスドライバを記述します。
複数のバスで同じデバイスドライバを共有する場合、
一つだけ記述します。構文は次の通りです。
device device [{locators}] [: attrs]
device はデバイス名です。
locators はデバイスが制御するバスに関するロケータを
記述するリストです。
isa
などのバス型デバイスで使われ、
それ以外の多くのドライバでは通常使用されません。
attrs はそのデバイスの属性です。
attach
構文。デバイスドライバが接続されるバスに
ついて記述します。
複数のバスで同じデバイスドライバを共有する場合、
必要なだけ記述します。構文は次の通りです。
attach device at parent [with name] [: attrs]
device はデバイス名です。
parent は親デバイスの名前です。
name は、そのデバイスのフロントエンド名です。
フロントエンド名は通常、複数のバスで同じデバイスドライバを
共有する場合、おたがいを区別するのに使います。
デバイスドライバの cfattach
構造体の名前は
フロントエンド名をもとに構成されます。
一方cfdriver
構造体の名前は
デバイス名をもとに構成されます。
そのデバイスが単一のバスでしか使われない場合は、
[with name]
の部分を省略できます。
この場合フロントエンド名としてはデバイス名自身が使われます。
attrs はそのデバイスの属性です。
file
構文。デバイスドライバのソースファイルについて
記述します。構文は次の通りです。
file path fopts [needs-flag | needs-count]
path はソースファイルの相対パス名です。
fopts はコンパイル条件です。
この条件を満す場合にコンパイルされます。
コンパイル条件としては、デバイス名やオプションなどがあります。
コンパイル条件は、論理演算することができます。
例えば、pci/hoge.c
は foo デバイスと
bar デバイスから使われる共有モジュールだったとすると、
file pci/hoge.c foo | bar
などと書けます。
needs-count
は
デバイス数ヘッダファイル
(ドライバ ed
に対して、
実際のドライバ数を
定数NED
で#define
するもの)
をドライバがインクルードしている場合に記述します。
needs-count
を使用した場合、そのデバイスをたとえば
ed* at pci? ...
などと記述することはできなくなります。
この記述では、実際のドライバ数はコンパイル時には不明で、
OS が起動するまで確定しないからです。
needs-flag
は needs-count
と
似ていますが、実際のドライバ数ではなくて、
config ファイルに存在しているかどうかで、定数1か0を
#define
する、というところが違います。
needs-flag
を指定した場合には、"*" 指定を
config ファイルに記述することが可能です。
一般にデバイスドライバには、 ドライバユニット毎に固有の内部作業変数がいくつか、必要になります。 それらは構造体にまとめられ、デバイスドライバの attach の際に初期化されます。 このような構造体のことを、softc 構造体と呼びます。
旧来の FreeBSD の config では、この softc 構造体の初期化と 領域確保はデバイスドライバの仕事でした。 newconfig では softc 構造体の領域確保はデバイスドライバではなく フレームワークの仕事になります。 領域確保するべきサイズをフレームワークに知らせるために、 cfattach 構造体にはそのサイズを記述します。
newconfig では softc 構造体の先頭部分は、
フレームワークが内部的に使用します。
そのため、どんなデバイスドライバでも
softc 構造体の先頭部分は device
構造体型の要素で
なければなりません。
もちろん、フレームワークに知らせる softc 構造体のサイズは
それ以上になるはずです。
newconfig ではフレームワークが softc 構造体を確保する設計ですが、
旧来の FreeBSD のドライバでは、独自に softc 構造体を確保しているので、
移植の初期段階では旧来の softc 構造体を使用し、後にフレームワークが
確保した softc 構造体を使うように変更します。
その場合、newconfig フレームワーク向けの softc として最低でも
device
構造体だけは用意します。
newconfig が確保した softc 構造体は、次の方法でアクセスできます。
foo_cd.cd_devs[unit]
でアクセスできます。ここで、foo_cd
は cfdriver
構造体の大域変数で、
config.new
が自動生成するものです。
foo_cd.
にアクセスする為には
これが宣言されている "ioconf.h
" というファイルを
#include
する必要があります。
self
を、ドライバ毎の
softc 構造体へのポインタにキャストして使います。
softc 構造体の先頭は常にフレームワークが使用する
device
構造体で、これに対するポインタは
つまり softc 構造体へのポインタなのです。
newconfig のもとでは、次のような点に注意しなければなりません。
一般に、match 関数の直後に attach 関数が呼ばれるわけではありません。 bus 側ではさまざまな match 関数を呼び出した後、その結果に応じて 最適な attach 関数を呼び出します。 したがって、match 関数では副作用のある操作を避けるべきです。 match 関数は何度呼び出されても構わないようにコーディングするべきです。 attach 関数は match 関数が呼び出された後の状態に依存してはいけません。 何らかのデバイスの初期化が必要な場合は、attach 関数でも初期化 するようにします。
別のバスのコントローラとなるデバイスでは、 attach 関数の中でそのバスの初期化コードを呼び出さなくてはなりません。 newconfig 化作業では、バスの初期化コードの呼び出し方法も 規定されていますので、変更が必要になります。 SCSI バスや IDE バス(wdc)、パラレルポートバス(lpt)などがその対象になります。 そのようなバスコントローラデバイスのデバイスドライバは 対応するバスコードの初期化呼び出し部分も変更する必要があります。 実際には対応するバスコードが実装されるまで、そのドライバは使えません。