ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
デバイスドライバの記述 Oracle Solaris 11.1 Information Library (日本語) |
パート I Oracle Solaris プラットフォーム用デバイスドライバの設計
2. Oracle Solaris カーネルとデバイスツリー
22. ドライバのコンパイル、ロード、パッケージ化、およびテスト
23. デバイスドライバのデバッグ、テスト、およびチューニング
LDI カーネルインタフェースの中には、LDI でカーネルデバイスの使用状態情報を追跡および報告できるものがあります。「階層化識別子 - カーネルデバイスコンシューマ」を参照してください。
その他に、カーネルモジュールから open、read、write などのアクセス操作をターゲットデバイスで実行できる LDI カーネルインタフェースがあります。これらの LDI カーネルインタフェースを使用すると、カーネルデバイスコンシューマから、ターゲットデバイスに関するプロパティーやイベント情報を照会することもできます。「階層化ドライバのハンドル - ターゲットデバイス」を参照してください。
「LDI カーネルインタフェースの例」では、これらの LDI インタフェースの多くを使用するドライバの例を示しています。
LDI は階層化識別子によって、カーネルデバイスの使用状態情報を追跡し、報告できます。階層化識別子 (ldi_ident_t) は、カーネルデバイスコンシューマを識別します。カーネルデバイスコンシューマは LDI を使用してターゲットデバイスを開く前に階層化識別子を取得する必要があります。
階層化ドライバは、唯一サポートされている種類のカーネルデバイスコンシューマです。そのため、階層化ドライバはデバイス番号に関連付けられている階層化識別子、デバイス情報ノード、または階層化ドライバのストリームを取得する必要があります。階層化識別子は階層化ドライバに関連付けられていますが、ターゲットデバイスとは関連付けられていません。
LDI によって収集されたカーネルデバイスの使用状態情報は、libdevinfo(3LIB) インタフェース、fuser(1M) コマンド、または prtconf(1M) コマンドを使用すると取得できます。たとえば、prtconf(1M) コマンドでは、階層化ドライバがアクセスしているターゲットデバイスを表示したり、特定のターゲットデバイスにアクセスしているの階層化ドライバを表示したりできます。デバイス使用状態情報を取得する方法の詳細については、「ユーザーインタフェース」を参照してください。
次に、LDI 階層化識別子インタフェースについて説明します。
階層化識別子。不透明な型です。
dev_t デバイス番号に関連付けられる階層化識別子の割り当てと取得を行います。
dev_info_t デバイス情報ノードに関連付けられる階層化識別子の割り当てと取得を行います。
ストリームに関連付けられる階層化識別子の割り当てと取得を行います。
ldi_ident_from_dev(9F)、ldi_ident_from_dip(9F)、または ldi_ident_from_stream( 9F) に関連付けられていた階層化識別子を解放します。
カーネルデバイスコンシューマは階層化ドライバのハンドル (ldi_handle_t) を使用して、LDI インタフェースを通してターゲットデバイスにアクセスする必要があります。ldi_handle_t 型は LDI インタフェースでのみ有効です。LDI は、デバイスが正常に開かれたときにこのハンドルを割り当てて返します。次にカーネルデバイスコンシューマはこのハンドルを使用することで、LDI インタフェースを通してターゲットデバイスにアクセスできます。LDI はデバイスを閉じるときにハンドルの割り当てを解除します。例については、「LDI カーネルインタフェースの例」を参照してください。
このセクションでは、カーネルデバイスコンシューマからターゲットデバイスにアクセスし、さまざまな種類の情報を取得する方法について説明します。カーネルデバイスコンシューマでターゲットデバイスを開いたり閉じたりする方法については、「ターゲットデバイスを開く操作と閉じる操作」を参照してください。カーネルデバイスコンシューマから、ターゲットデバイスで read、write、strategy、ioctl などの操作を実行する方法については、「ターゲットデバイスへのアクセス」を参照してください。「ターゲットデバイス情報の取得」では、デバイス公開型やデバイスマイナー名などのターゲットデバイスの情報を取得するインタフェースについて説明します。「ターゲットデバイスのプロパティー値の取得」では、ターゲットデバイスのプロパティーの値とアドレスを取得するインタフェースについて説明します。カーネルデバイスコンシューマでターゲットデバイスからイベント通知を受信する方法については、「非同期デバイスイベントの通知の受信」を参照してください。
このセクションでは、ターゲットデバイスを開き、閉じるための LDI カーネルインタフェースについて説明します。open インタフェースは、階層化ドライバのハンドルへのポインタを取得します。open インタフェースは、デバイス番号、デバイス ID、またはパス名によって指定されたターゲットデバイスを開こうとします。開く操作が成功すると、open インタフェースはターゲットデバイスにアクセスするために使用できる階層化ドライバのハンドルを割り当てて返します。close インタフェースは、指定された階層化ドライバのハンドルと関連付けられているターゲットデバイスを閉じ、階層化ドライバのハンドルを解放します。
ターゲットデバイスアクセス用の階層化ドライバのハンドルです。デバイスが正常に開かれたときに返される不透明なデータ構造体です。
dev_t デバイス番号パラメータによって指定されたデバイスを開きます。
ddi_devid_t デバイス ID パラメータによって指定されたデバイスを開きます。開くマイナーノード名も指定する必要があります。
パス名によってデバイスを開きます。パス名は、カーネルアドレス空間内の NULL で終わっている文字列です。パス名はスラッシュ (/) で始まる絶対パスにする必要があります。
ldi_open_by_dev(9F)、ldi_open_by_devid(9F)、または ldi_open_by_name (9F) によって開かれたデバイスを閉じます。ldi_close(9F) が返ったあと、閉じられたデバイスの階層化ドライバのハンドルは有効ではなくなります。
このセクションでは、ターゲットデバイスにアクセスするための LDI カーネルインタフェースについて説明します。これらのインタフェースによって、カーネルデバイスコンシューマは、階層化ドライバのハンドルによって指定されたターゲットデバイスで操作を実行できます。カーネルデバイスコンシューマは、ターゲットデバイスで read、write、strategy、ioctl などの操作を実行できます。
ターゲットデバイスアクセス用の階層化ドライバのハンドルです。不透明なデータ構造体です。
ターゲットデバイスのデバイスエントリポイントに read 要求を渡します。この操作はブロックデバイス、文字デバイス、および STREAMS デバイスでサポートされます。
ターゲットデバイスのデバイスエントリポイントに非同期の read 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。
ターゲットデバイスのデバイスエントリポイントに write 要求を渡します。この操作はブロックデバイス、文字デバイス、および STREAMS デバイスでサポートされます。
ターゲットデバイスのデバイスエントリポイントに非同期の write 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。
ターゲットデバイスのデバイスエントリポイントに strategy 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。
ターゲットデバイスのデバイスエントリポイントに dump 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。
ターゲットデバイスのデバイスエントリポイントに poll 要求を渡します。この操作はブロックデバイス、文字デバイス、および STREAMS デバイスでサポートされます。
ターゲットデバイスのデバイスエントリポイントに ioctl 要求を渡します。この操作はブロックデバイス、文字デバイス、および STREAMS デバイスでサポートされます。LDI は STREAMS リンクと STREAMS ioctl コマンドをサポートしています。ldi_ioctl(9F) のマニュアルページの「STREAM IOCTLS」セクションを参照してください。streamio(7I) のマニュアルページの ioctl コマンドも参照してください。
ターゲットデバイスのデバイスエントリポイントに devmap 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。
ストリームからメッセージブロックを取得します。
メッセージブロックをストリーム上に配置します。
このセクションでは、指定したターゲットデバイスに関するデバイス情報を取得するためにカーネルデバイスコンシューマが使用できる LDI インタフェースについて説明します。ターゲットデバイスは、階層化ドライバのハンドルによって指定します。カーネルデバイスコンシューマは、デバイス番号、デバイス公開型、デバイス ID、デバイスマイナー名、デバイスサイズなどの情報を受け取ることができます。
階層化ドライバのハンドルによって指定されたターゲットデバイスの dev_t デバイス番号を取得します。
階層化ドライバのハンドルによって指定されたターゲットデバイスを開くために使用された open フラグを取得します。このフラグは、ターゲットデバイスが文字デバイスであるかブロックデバイスであるかを通知します。
階層化ドライバのハンドルによって指定されたターゲットデバイスの ddi_devid_t デバイス ID を取得します。デバイス ID の使用を終えて ddi_devid_t を解放するには、ddi_devid_free(9F)を使用します。
ターゲットデバイスのために開かれたマイナーノードの名前を格納しているバッファーを取得します。マイナーノード名の使用を終えてバッファーを解放するには、kmem_free(9F) を使用します。
階層化ドライバのハンドルによって指定されたターゲットデバイスのパーティションサイズを取得します。
このセクションでは、指定したターゲットデバイスに関するプロパティー情報を取得するためにカーネルデバイスコンシューマが使用できる LDI インタフェースについて説明します。ターゲットデバイスは、階層化ドライバのハンドルによって指定します。カーネルデバイスコンシューマはプロパティーの値とアドレスを受け取って、プロパティーが存在するかどうかを判定できます。
階層化ドライバのハンドルによって指定されたターゲットデバイスのプロパティーが存在する場合は 1 を返します。指定されたターゲットデバイスのプロパティーが存在しない場合は 0 を返します。
階層化ドライバのハンドルによって指定されたターゲットデバイスに関連付けられている int 整数プロパティーを検索します。整数プロパティーが見つかった場合はプロパティー値を返します。
階層化ドライバのハンドルによって指定されたターゲットデバイスに関連付けられている int64_t 整数プロパティーを検索します。整数プロパティーが見つかった場合はプロパティー値を返します。
階層化ドライバのハンドルによって指定されたターゲットデバイスの int 整数配列プロパティー値のアドレスを取得します。
階層化ドライバのハンドルによって指定されたターゲットデバイスの int64_t 整数配列プロパティー値のアドレスを取得します。
階層化ドライバのハンドルによって指定されたターゲットデバイスの NULL で終わっている文字列プロパティー値のアドレスを取得します。
文字列の配列のアドレスを取得します。文字列の配列は、階層化ドライバのハンドルによって指定されたターゲットデバイスの NULL で終わっている文字列プロパティー値へのポインタの配列です。
バイトの配列のアドレスを取得します。バイトの配列は、階層化ドライバのハンドルによって指定されたターゲットデバイスのプロパティー値です。
カーネルデバイスコンシューマは LDI によって、イベント通知を登録し、ターゲットデバイスからイベント通知を受信できます。カーネルデバイスコンシューマは、イベントが発生したときに呼び出されるイベントハンドラを登録できます。カーネルデバイスコンシューマで LDI イベント通知インタフェースを使用してイベント通知を登録するには、カーネルデバイスコンシューマでデバイスを開き、階層化ドライバのハンドルを受信する必要があります。
LDI イベント通知インタフェースによって、カーネルデバイスコンシューマはイベント名を指定し、関連付けられているカーネルイベントの cookie を取得できます。カーネルデバイスコンシューマは次に、階層化ドライバのハンドル (ldi_handle_t)、cookie (ddi_eventcookie_t)、および ldi_add_event_handler(9F) へのイベントハンドラを渡して、イベント通知を登録できます。登録が正常に完了すると、カーネルデバイスコンシューマは一意の LDI イベントハンドラ識別子 (ldi_callback_id_t) を受信します。LDI イベントハンドラ識別子は、LDI イベント通知インタフェースでのみ使用できる不透明な型です。
LDI はほかのデバイスによって生成されたイベントを登録するための枠組みを提供します。LDI 自体は、イベントの種類を定義したり、イベントを生成するためのインタフェースを提供したりすることはありません。
次に、LDI 非同期イベント通知インタフェースについて説明します。
イベントハンドラ識別子。不透明な型です。
階層化ドライバのハンドルによって指定されたターゲットデバイスのイベントサービス cookie を取得します。
ldi_callback_id_t 登録識別子によって指定されたコールバックハンドラを追加します。コールバックハンドラは、ddi_eventcookie_t cookie によって指定されたイベントが発生したときに呼び出されます。
ldi_callback_id_t 登録識別子によって指定されたコールバックハンドラを削除します。
このセクションでは、この章の前のセクションで説明した LDI 呼び出しを使用するカーネルデバイスコンシューマの例を示します。このセクションでは、このモジュール例の次の面について説明します。
このカーネルデバイスコンシューマの例は lyr という名前です。lyr モジュールは、LDI 呼び出しを使用してターゲットデバイスにデータを送信する階層化ドライバです。lyr ドライバは open(9E) エントリポイントで、lyr.conf 構成ファイル内の lyr_targ プロパティーによって指定されたデバイスを開きます。lyr ドライバは write(9E) エントリポイントで、lyr_targ プロパティーによって指定されたデバイスに、すべての着信データを書き込みます。
次に示す構成ファイルでは、lyr ドライバの書き込み先ターゲットデバイスはコンソールです。
例 14-1 構成ファイル
# # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" name="lyr" parent="pseudo" instance=1; lyr_targ="/dev/console";
次に示したドライバソースファイルで、lyr_state_t 構造体は lyr ドライバのソフト状態を保持しています。ソフト状態には、lyr_targ デバイスの階層化ドライバのハンドル (lh) と、lyr デバイスの階層化識別子 ( li) が格納されています。ソフト状態の詳細については、「ドライバのソフト状態情報の取得」を参照してください。
lyr_open() エントリポイントで、ddi_prop_lookup_string(9F) は lyr_targ プロパティーから、開く lyr デバイスのターゲットデバイスの名前を取得しています。ldi_ident_from_dev(9F) 関数は lyr デバイスの LDI 階層化識別子を取得します。ldi_open_by_name(9F) 関数は lyr_targ デバイスを開き、lyr_targ デバイスの階層化ドライバのハンドルを取得します。
lyr_open() で何らかのエラーが発生した場合、ldi_close(9F)、ldi_ident_release(9F)、および ddi_prop_free(9F) の呼び出しによって、実行されたすべてのことが取り消されます。ldi_close(9F) 関数は lyr_targ デバイスを閉じます。ldi_ident_release(9F) 関数は lyr 階層化識別子を解放します。ddi_prop_free(9F) 関数は、lyr_targ デバイス名が取得されたときに割り当てられたリソースを解放します。エラーが発生しなければ、lyr_close() エントリポイントで ldi_close(9F) 関数と ldi_ident_release(9F) 関数が呼び出されます。
ドライバモジュールの最終行では、ldi_write(9F) 関数が呼び出されています。ldi_write(9F) 関数は lyr_write() エントリポイントで lyr デバイスに書き込まれたデータを取得し、そのデータを lyr_targ デバイスに書き込みます。ldi_write(9F) 関数は lyr_targ デバイスの階層化ドライバのハンドルを使用して、データを lyr_targ デバイスに書き込みます。
例 14-2 ドライバソースファイル
#include <sys/types.h> #include <sys/file.h> #include <sys/errno.h> #include <sys/open.h> #include <sys/cred.h> #include <sys/cmn_err.h> #include <sys/modctl.h> #include <sys/conf.h> #include <sys/stat.h> #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/sunldi.h> typedef struct lyr_state { ldi_handle_t lh; ldi_ident_t li; dev_info_t *dip; minor_t minor; int flags; kmutex_t lock; } lyr_state_t; #define LYR_OPENED 0x1 /* lh is valid */ #define LYR_IDENTED 0x2 /* li is valid */ static int lyr_info(dev_info_t *, ddi_info_cmd_t, void *, void **); static int lyr_attach(dev_info_t *, ddi_attach_cmd_t); static int lyr_detach(dev_info_t *, ddi_detach_cmd_t); static int lyr_open(dev_t *, int, int, cred_t *); static int lyr_close(dev_t, int, int, cred_t *); static int lyr_write(dev_t, struct uio *, cred_t *); static void *lyr_statep; static struct cb_ops lyr_cb_ops = { lyr_open, /* open */ lyr_close, /* close */ nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ nodev, /* read */ lyr_write, /* write */ nodev, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* prop_op */ NULL, /* streamtab */ D_NEW | D_MP, /* cb_flag */ CB_REV, /* cb_rev */ nodev, /* aread */ nodev /* awrite */ }; static struct dev_ops lyr_dev_ops = { DEVO_REV, /* devo_rev, */ 0, /* refcnt */ lyr_info, /* getinfo */ nulldev, /* identify */ nulldev, /* probe */ lyr_attach, /* attach */ lyr_detach, /* detach */ nodev, /* reset */ &lyr_cb_ops, /* cb_ops */ NULL, /* bus_ops */ NULL, /* power */ ddi_quiesce_not_needed, /* quiesce */ }; static struct modldrv modldrv = { &mod_driverops, "LDI example driver", &lyr_dev_ops }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL }; int _init(void) { int rv; if ((rv = ddi_soft_state_init(&lyr_statep, sizeof (lyr_state_t), 0)) != 0) { cmn_err(CE_WARN, "lyr _init: soft state init failed\n"); return (rv); } if ((rv = mod_install(&modlinkage)) != 0) { cmn_err(CE_WARN, "lyr _init: mod_install failed\n"); goto FAIL; } return (rv); /*NOTEREACHED*/ FAIL: ddi_soft_state_fini(&lyr_statep); return (rv); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } int _fini(void) { int rv; if ((rv = mod_remove(&modlinkage)) != 0) { return(rv); } ddi_soft_state_fini(&lyr_statep); return (rv); } /* * 1:1 mapping between minor number and instance */ static int lyr_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { int inst; minor_t minor; lyr_state_t *statep; char *myname = "lyr_info"; minor = getminor((dev_t)arg); inst = minor; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: statep = ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: get soft state " "failed on inst %d\n", myname, inst); return (DDI_FAILURE); } *result = (void *)statep->dip; break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)inst; break; default: break; } return (DDI_SUCCESS); } static int lyr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int inst; lyr_state_t *statep; char *myname = "lyr_attach"; switch (cmd) { case DDI_ATTACH: inst = ddi_get_instance(dip); if (ddi_soft_state_zalloc(lyr_statep, inst) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: ddi_soft_state_zallac failed " "on inst %d\n", myname, inst); goto FAIL; } statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on " "inst %d\n", myname, inst); goto FAIL; } statep->dip = dip; statep->minor = inst; if (ddi_create_minor_node(dip, "node", S_IFCHR, statep->minor, DDI_PSEUDO, 0) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: ddi_create_minor_node failed on " "inst %d\n", myname, inst); goto FAIL; } mutex_init(&statep->lock, NULL, MUTEX_DRIVER, NULL); return (DDI_SUCCESS); case DDI_RESUME: case DDI_PM_RESUME: default: break; } return (DDI_FAILURE); /*NOTREACHED*/ FAIL: ddi_soft_state_free(lyr_statep, inst); ddi_remove_minor_node(dip, NULL); return (DDI_FAILURE); } static int lyr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int inst; lyr_state_t *statep; char *myname = "lyr_detach"; inst = ddi_get_instance(dip); statep = ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: get soft state failed on " "inst %d\n", myname, inst); return (DDI_FAILURE); } if (statep->dip != dip) { cmn_err(CE_WARN, "%s: soft state does not match devinfo " "on inst %d\n", myname, inst); return (DDI_FAILURE); } switch (cmd) { case DDI_DETACH: mutex_destroy(&statep->lock); ddi_soft_state_free(lyr_statep, inst); ddi_remove_minor_node(dip, NULL); return (DDI_SUCCESS); case DDI_SUSPEND: case DDI_PM_SUSPEND: default: break; } return (DDI_FAILURE); } /* * on this driver's open, we open the target specified by a property and store * the layered handle and ident in our soft state. a good target would be * "/dev/console" or more interestingly, a pseudo terminal as specified by the * tty command */ /*ARGSUSED*/ static int lyr_open(dev_t *devtp, int oflag, int otyp, cred_t *credp) { int rv, inst = getminor(*devtp); lyr_state_t *statep; char *myname = "lyr_open"; dev_info_t *dip; char *lyr_targ = NULL; statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on " "inst %d\n", myname, inst); return (EIO); } dip = statep->dip; /* * our target device to open should be specified by the "lyr_targ" * string property, which should be set in this driver's .conf file */ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_NOTPROM, "lyr_targ", &lyr_targ) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s: ddi_prop_lookup_string failed on " "inst %d\n", myname, inst); return (EIO); } /* * since we only have one pair of lh's and li's available, we don't * allow multiple on the same instance */ mutex_enter(&statep->lock); if (statep->flags & (LYR_OPENED | LYR_IDENTED)) { cmn_err(CE_WARN, "%s: multiple layered opens or idents " "from inst %d not allowed\n", myname, inst); mutex_exit(&statep->lock); ddi_prop_free(lyr_targ); return (EIO); } rv = ldi_ident_from_dev(*devtp, &statep->li); if (rv != 0) { cmn_err(CE_WARN, "%s: ldi_ident_from_dev failed on inst %d\n", myname, inst); goto FAIL; } statep->flags |= LYR_IDENTED; rv = ldi_open_by_name(lyr_targ, FREAD | FWRITE, credp, &statep->lh, statep->li); if (rv != 0) { cmn_err(CE_WARN, "%s: ldi_open_by_name failed on inst %d\n", myname, inst); goto FAIL; } statep->flags |= LYR_OPENED; cmn_err(CE_CONT, "\n%s: opened target '%s' successfully on inst %d\n", myname, lyr_targ, inst); rv = 0; FAIL: /* cleanup on error */ if (rv != 0) { if (statep->flags & LYR_OPENED) (void)ldi_close(statep->lh, FREAD | FWRITE, credp); if (statep->flags & LYR_IDENTED) ldi_ident_release(statep->li); statep->flags &= ~(LYR_OPENED | LYR_IDENTED); } mutex_exit(&statep->lock); if (lyr_targ != NULL) ddi_prop_free(lyr_targ); return (rv); } /* * on this driver's close, we close the target indicated by the lh member * in our soft state and release the ident, li as well. in fact, we MUST do * both of these at all times even if close yields an error because the * device framework effectively closes the device, releasing all data * associated with it and simply returning whatever value the target's * close(9E) returned. therefore, we must as well. */ /*ARGSUSED*/ static int lyr_close(dev_t devt, int oflag, int otyp, cred_t *credp) { int rv, inst = getminor(devt); lyr_state_t *statep; char *myname = "lyr_close"; statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on " "inst %d\n", myname, inst); return (EIO); } mutex_enter(&statep->lock); rv = ldi_close(statep->lh, FREAD | FWRITE, credp); if (rv != 0) { cmn_err(CE_WARN, "%s: ldi_close failed on inst %d, but will ", "continue to release ident\n", myname, inst); } ldi_ident_release(statep->li); if (rv == 0) { cmn_err(CE_CONT, "\n%s: closed target successfully on " "inst %d\n", myname, inst); } statep->flags &= ~(LYR_OPENED | LYR_IDENTED); mutex_exit(&statep->lock); return (rv); } /* * echo the data we receive to the target */ /*ARGSUSED*/ static int lyr_write(dev_t devt, struct uio *uiop, cred_t *credp) { int rv, inst = getminor(devt); lyr_state_t *statep; char *myname = "lyr_write"; statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on " "inst %d\n", myname, inst); return (EIO); } return (ldi_write(statep->lh, uiop, credp)); }
-D_KERNEL オプションを使用して、これがカーネルモジュールであることを示します。
SPARC アーキテクチャー向けにコンパイルする場合は、-xarch=v9 オプションを使用します。
% cc -c -D_KERNEL -xarch=v9 lyr.c
32 ビット x86 アーキテクチャー向けにコンパイルする場合は、次のコマンドを使用します。
% cc -c -D_KERNEL lyr.c
% ld -r -o lyr lyr.o
root ユーザーとして、マシンのカーネルドライバ領域に構成ファイルをコピーします。
# cp lyr.conf /usr/kernel/drv
root ユーザーとして、SPARC アーキテクチャー上の sparcv9 ドライバ領域にドライババイナリをコピーします。
# cp lyr /usr/kernel/drv/sparcv9
root ユーザーとして、32 ビット x86 アーキテクチャー上の drv ドライバ領域にドライババイナリをコピーします。
# cp lyr /usr/kernel/drv
root ユーザーとして、add_drv(1M) コマンドを使用してドライバをロードします。
# add_drv lyr
疑似デバイスを一覧表示して、lyr デバイスが存在するようになったことを確認します。
# ls /devices/pseudo | grep lyr lyr@1 lyr@1:node
lyr ドライバをテストするには、lyr デバイスにメッセージを書き込み、メッセージが lyr_targ デバイスに表示されることを確認します。
例 14-3 階層化デバイスへの短いメッセージの書き込み
この例では、lyr_targ デバイスが、lyr デバイスがインストールされているシステムのコンソールです。
参照している表示が、lyr デバイスがインストールされているシステムのコンソールデバイスの表示でもある場合は、コンソールに書き込むと表示が壊れることに注意してください。コンソールメッセージはウィンドウシステムの外側に表示されます。lyr ドライバのテスト後、表示を再描画またはリフレッシュする必要があります。
参照している表示が、lyr デバイスがインストールされているシステムのコンソールデバイスの表示ではない場合、ログインするなどして、ターゲットコンソールデバイスの表示内容を取得します。
次のコマンドでは、非常に短いメッセージが lyr デバイスに書き込まれます。
# echo "\n\n\t===> Hello World!! <===\n" > /devices/pseudo/lyr@1:node
ターゲットコンソールに次のメッセージが表示されます。
console login: ===> Hello World!! <=== lyr: lyr_open: opened target '/dev/console' successfully on inst 1 lyr: lyr_close: closed target successfully on inst 1
lyr_open() および lyr_close() からのメッセージは、lyr_open() および lyr_close() エントリポイントの cmn_err(9F) 呼び出しからのものです。
例 14-4 階層化デバイスへの長いメッセージの書き込み
次のコマンドでは、長いメッセージが lyr デバイスに書き込まれます。
# cat lyr.conf > /devices/pseudo/lyr@1:node
ターゲットコンソールに次のメッセージが表示されます。
lyr: lyr_open: opened target '/dev/console' successfully on inst 1 # # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" name="lyr" parent="pseudo" instance=1; lyr_targ="/dev/console"; lyr: lyr_close: closed target successfully on inst 1
例 14-5 ターゲットデバイスの変更
ターゲットデバイスを変更するには、/usr/kernel/drv/lyr.conf を編集し、lyr_targ プロパティーの値を、別のターゲットデバイスへのパスに変更します。たとえば、ローカル端末での tty コマンドの出力をターゲットデバイスにできます。/dev/pts/4 はそうしたデバイスパスの例です。
新しいターゲットデバイスを使用するようにドライバを更新する前に、lyr デバイスを使用中でないことを確認してください。
# modinfo -c | grep lyr 174 3 lyr UNLOADED/UNINSTALLED
lyr.conf 構成ファイルを再ロードするには、update_drv(1M) コマンドを使用します。
# update_drv lyr
もう一度 lyr デバイスにメッセージを書き込み、メッセージが新しい lyr_targ デバイスに表示されることを確認します。