newconfig Project 作業例

Last updateed: $Date: 1999/08/11 14:57:29 $
次のような作業があります。

PCI ドライバの変更

PCI ドライバの probe ルーチンと attach ルーチンを書換えます。 probe ルーチンと attach ルーチンの引数などを newconfig に一致するように 書き換えるのです。 ここでは、 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>

probe 関数と attach 関数の名前を変更します。 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 関数の名前も、 慣例としてフロントエンド名から構成します)
probe (match) 関数の引数と返り値を変更します。 newconfig では、PCI であろうが、ISA であろうが、 さらには USB であろうが、SCSI であろうが、 全ての match 関数の引数は同じです。 bus 依存のデータは、 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;

probe (match) 関数の返り値の変更です。 void *aux で指定されたデバイスが自分がサポートしている ものである場合は 1 を、そうでなければ 0 を返します。
-		return ("Cyclades Cyclom-Y Serial Adapter");
-	return NULL;
+		return (1);
+	return (0);

attach 関数の引数を変更します。 これも match 関数と同様に bus 依存のデータは、 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;

デバイス情報の表示は、newconfig では attach 関数で行います。
+	printf (": Cyclades Cyclom-Y Serial Adapter\n")
 	ioport = (u_int32_t) pci_conf_read(config_id, CY_PCI_BASE_ADDR1) & ~0x3;

ISA ドライバの変更

ここでは、 sys/i386/isa/psm.c に対する変更例を参考に、解説します。

書き換え例

書き換え部分の解説


PCI の例と全く同様に、 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,
+};
+#endif
ISA ドライバ互換方式を使うには、 cfattach 構造体の ca_aux メンバーにそのドライバの isa_driver 構造体へのポインタを設定します。 また ca_level メンバーには 0 を設定します。 match 関数には ISA ドライバ互換方式用の共通 match 関数である isa_dev_match を設定し、 attach 関数は使用しないので NULL を設定します。

newconfig 方式の書換えについて

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 */

後述する「softc 構造体について」に従って 既存の softc 構造体の定義を書換え、先頭に 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

psmprobe() と psmattach() は newconfig では使いませんので削除します。
 /* 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

ISA 互換方式のために仮に用意した softc 構造体は この場合不要なので削除します。 cfattach 構造体のオブジェクトを変更し、次のような値にします。
	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

endprobe() は、後述する psm_init() (旧 psmprobe() )で使われるマクロで、 psm_init() への変更に伴なう変更を行っています。
 
 /* 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 */
 }

psmintr() を書換えます。 newconfig 方式では、unit 番号ではなくて softc 構造体が引数として 渡ってきますので、それを書換えます。
+#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 ファイルに設定します。 現状では files ファイルは二つあります。 一つは /sys/conf/files.newconf で もう一つは /sys/i386/conf/files.i386.newconf です。 前者はマシン独立なドライバのためのファイルで、 後者はマシン依存のドライバのためのファイルです。(少くとも本来は) /sys/i386 以下にソースコードがあるデバイスドライバは 後者に、それ以外は前者に登録すればいいでしょう。

デバイスドライバの登録には、次の構文が関係しています。 (ここの説明はかなり省略して端折っています。 詳しくは Chiris Torek 氏の論文を参照して下さい)

softc 構造体について

一般にデバイスドライバには、 ドライバユニット毎に固有の内部作業変数がいくつか、必要になります。 それらは構造体にまとめられ、デバイスドライバの attach の際に初期化されます。 このような構造体のことを、softc 構造体と呼びます。

旧来の FreeBSD の config では、この softc 構造体の初期化と 領域確保はデバイスドライバの仕事でした。 newconfig では softc 構造体の領域確保はデバイスドライバではなく フレームワークの仕事になります。 領域確保するべきサイズをフレームワークに知らせるために、 cfattach 構造体にはそのサイズを記述します。

newconfig では softc 構造体の先頭部分は、 フレームワークが内部的に使用します。 そのため、どんなデバイスドライバでも softc 構造体の先頭部分は device 構造体型の要素で なければなりません。 もちろん、フレームワークに知らせる softc 構造体のサイズは それ以上になるはずです。

newconfig ではフレームワークが softc 構造体を確保する設計ですが、 旧来の FreeBSD のドライバでは、独自に softc 構造体を確保しているので、 移植の初期段階では旧来の softc 構造体を使用し、後にフレームワークが 確保した softc 構造体を使うように変更します。 その場合、newconfig フレームワーク向けの softc として最低でも device 構造体だけは用意します。

newconfig が確保した softc 構造体は、次の方法でアクセスできます。

注意すべきこと

newconfig のもとでは、次のような点に注意しなければなりません。

一般に、match 関数の直後に attach 関数が呼ばれるわけではありません。 bus 側ではさまざまな match 関数を呼び出した後、その結果に応じて 最適な attach 関数を呼び出します。 したがって、match 関数では副作用のある操作を避けるべきです。 match 関数は何度呼び出されても構わないようにコーディングするべきです。 attach 関数は match 関数が呼び出された後の状態に依存してはいけません。 何らかのデバイスの初期化が必要な場合は、attach 関数でも初期化 するようにします。

別のバスのコントローラとなるデバイスでは、 attach 関数の中でそのバスの初期化コードを呼び出さなくてはなりません。 newconfig 化作業では、バスの初期化コードの呼び出し方法も 規定されていますので、変更が必要になります。 SCSI バスや IDE バス(wdc)、パラレルポートバス(lpt)などがその対象になります。 そのようなバスコントローラデバイスのデバイスドライバは 対応するバスコードの初期化呼び出し部分も変更する必要があります。 実際には対応するバスコードが実装されるまで、そのドライバは使えません。


このページに関してのお問い合わせは、
newconfig@jp.FreeBSD.org
までお願いします。