JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
デバイスドライバの記述     Oracle Solaris 10 8/11 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

パート I Solaris プラットフォーム用デバイスドライバの設計

1.  Solaris デバイスドライバの概要

2.  Solaris カーネルとデバイスツリー

3.  マルチスレッド

4.  プロパティー

5.  イベントの管理とタスクのキュー

6.  ドライバの自動設定

7.  デバイスアクセス: プログラム式入出力

8.  割り込みハンドラ

9.  ダイレクトメモリーアクセス (DMA)

10.  デバイスメモリーおよびカーネルメモリーのマッピング

11.  デバイスコンテキスト管理

12.  電源管理

13.  Solaris ドライバの強化

14.  階層化ドライバインタフェース (LDI)

LDI の概要

カーネルインタフェース

階層化識別子 - カーネルデバイスコンシューマ

階層化ドライバのハンドル - ターゲットデバイス

ターゲットデバイスを開く操作と閉じる操作

ターゲットデバイスへのアクセス

ターゲットデバイス情報の取得

ターゲットデバイスのプロパティー値の取得

非同期デバイスイベントの通知の受信

LDI カーネルインタフェースの例

デバイス設定ファイル

ドライバソースファイル

階層化ドライバを構築してロードする方法

階層化ドライバのテスト

ユーザーインタフェース

デバイス情報ライブラリインタフェース

システム構成の出力コマンドインタフェース

デバイスユーザーコマンドインタフェース

パート II 特定の種類のデバイスドライバの設計

15.  文字デバイスのドライバ

16.  ブロックデバイスのドライバ

17.  SCSI ターゲットドライバ

18.  SCSI ホストバスアダプタドライバ

19.  ネットワークデバイスのドライバ

20.  USB ドライバ

パート III デバイスドライバの構築

21.  ドライバのコンパイル、ロード、パッケージ化、およびテスト

22.  デバイスドライバのデバッグ、テスト、およびチューニング

23.  推奨されるコーティング方法

パート IV 付録

A.  ハードウェアの概要

B.  Solaris DDI/DKI サービスの概要

C.  64 ビットデバイスドライバの準備

D.  コンソールフレームバッファードライバ

索引

カーネルインタフェース

いくつかの LDI カーネルインタフェースを使用すると、LDI でカーネルデバイスの使用状態情報を追跡し、報告できます。「階層化識別子 - カーネルデバイスコンシューマ」を参照してください。

その他に、カーネルモジュールから openreadwrite などのアクセス操作をターゲットデバイスで実行できる LDI カーネルインタフェースがあります。これらの LDI カーネルインタフェースを使用すると、カーネルデバイスコンシューマから、ターゲットデバイスに関するプロパティーやイベント情報を照会することもできます。「階層化ドライバのハンドル - ターゲットデバイス」を参照してください。

「LDI カーネルインタフェースの例」では、これらの LDI インタフェースの多くを使用するドライバの例を示しています。

階層化識別子 – カーネルデバイスコンシューマ

LDI は階層化識別子によって、カーネルデバイスの使用状態情報を追跡し、報告できます。階層化識別子 (ldi_ident_t) は、カーネルデバイスコンシューマを識別します。カーネルデバイスコンシューマは LDI を使用してターゲットデバイスを開く前に階層化識別子を取得する必要があります。

階層化ドライバは、唯一サポートされている種類のカーネルデバイスコンシューマです。そのため、階層化ドライバはデバイス番号に関連付けられている階層化識別子、デバイス情報ノード、または階層化ドライバのストリームを取得する必要があります。階層化識別子は階層化ドライバに関連付けられていますが、ターゲットデバイスとは関連付けられていません。

LDI によって収集されたカーネルデバイスの使用状態情報は、libdevinfo(3LIB) インタフェース、fuser(1M) コマンド、または prtconf(1M) コマンドを使用すると取得できます。たとえば、prtconf(1M) コマンドでは、階層化ドライバがアクセスしているターゲットデバイスを表示したり、特定のターゲットデバイスにアクセスしているの階層化ドライバを表示したりできます。デバイス使用状態情報を取得する方法の詳細については、「ユーザーインタフェース」を参照してください。

次に、LDI 階層化識別子インタフェースについて説明します。

ldi_ident_t

階層化識別子。不透明な型です。

ldi_ident_from_dev(9F)

dev_t デバイス番号に関連付けられる階層化識別子の割り当てと取得を行います。

ldi_ident_from_dip(9F)

dev_info_t デバイス情報ノードに関連付けられる階層化識別子の割り当てと取得を行います。

ldi_ident_from_stream(9F)

ストリームに関連付けられる階層化識別子の割り当てと取得を行います。

ldi_ident_release(9F)

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 カーネルインタフェースの例」を参照してください。

この節では、カーネルデバイスコンシューマからターゲットデバイスにアクセスし、さまざまな種類の情報を取得する方法について説明します。カーネルデバイスコンシューマでターゲットデバイスを開いたり閉じたりする方法については、「ターゲットデバイスを開く操作と閉じる操作」を参照してください。カーネルデバイスコンシューマから、ターゲットデバイスで readwritestrategyioctl などの操作を実行する方法については、「ターゲットデバイスへのアクセス」を参照してください。「ターゲットデバイス情報の取得」では、デバイス公開型やデバイスマイナー名などのターゲットデバイスの情報を取得するインタフェースについて説明します。「ターゲットデバイスのプロパティー値の取得」では、ターゲットデバイスのプロパティーの値とアドレスを取得するインタフェースについて説明します。カーネルデバイスコンシューマでターゲットデバイスからイベント通知を受信する方法については、「非同期デバイスイベントの通知の受信」を参照してください。

ターゲットデバイスを開く操作と閉じる操作

この節では、ターゲットデバイスを開き、閉じるための LDI カーネルインタフェースについて説明します。open インタフェースは、階層化ドライバのハンドルへのポインタを取得します。open インタフェースは、デバイス番号、デバイス ID、またはパス名によって指定されたターゲットデバイスを開こうとします。開く操作が成功すると、open インタフェースはターゲットデバイスにアクセスするために使用できる階層化ドライバのハンドルを割り当てて返します。close インタフェースは、指定された階層化ドライバのハンドルと関連付けられているターゲットデバイスを閉じ、階層化ドライバのハンドルを解放します。

ldi_handle_t

ターゲットデバイスアクセス用の階層化ドライバのハンドルです。デバイスが正常に開かれたときに返される不透明なデータ構造体です。

ldi_open_by_dev(9F)

dev_t デバイス番号パラメータによって指定されたデバイスを開きます。

ldi_open_by_devid(9F)

ddi_devid_t デバイス ID パラメータによって指定されたデバイスを開きます。開くマイナーノード名も指定する必要があります。

ldi_open_by_name(9F)

パス名によってデバイスを開きます。パス名は、カーネルアドレス空間内の NULL で終わっている文字列です。パス名はスラッシュ (/) で始まる絶対パスにする必要があります。

ldi_close(9F)

ldi_open_by_dev(9F)、ldi_open_by_devid(9F)、または ldi_open_by_name (9F) によって開かれたデバイスを閉じます。ldi_close(9F) が返ったあと、閉じられたデバイスの階層化ドライバのハンドルは有効ではなくなります。

ターゲットデバイスへのアクセス

この節では、ターゲットデバイスにアクセスするための LDI カーネルインタフェースについて説明します。これらのインタフェースによって、カーネルデバイスコンシューマは、階層化ドライバのハンドルによって指定されたターゲットデバイスで操作を実行できます。カーネルデバイスコンシューマは、ターゲットデバイスで readwritestrategyioctl などの操作を実行できます。

ldi_handle_t

ターゲットデバイスアクセス用の階層化ドライバのハンドルです。不透明なデータ構造体です。

ldi_read(9F)

ターゲットデバイスのデバイスエントリポイントに read 要求を渡します。この操作はブロックデバイス、文字デバイス、および STREAMS デバイスでサポートされます。

ldi_aread(9F)

ターゲットデバイスのデバイスエントリポイントに非同期の read 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。

ldi_write(9F)

ターゲットデバイスのデバイスエントリポイントに write 要求を渡します。この操作はブロックデバイス、文字デバイス、および STREAMS デバイスでサポートされます。

ldi_awrite(9F)

ターゲットデバイスのデバイスエントリポイントに非同期の write 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。

ldi_strategy(9F)

ターゲットデバイスのデバイスエントリポイントに strategy 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。

ldi_dump(9F)

ターゲットデバイスのデバイスエントリポイントに dump 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。

ldi_poll(9F)

ターゲットデバイスのデバイスエントリポイントに poll 要求を渡します。この操作はブロックデバイス、文字デバイス、および STREAMS デバイスでサポートされます。

ldi_ioctl(9F)

ターゲットデバイスのデバイスエントリポイントに ioctl 要求を渡します。この操作はブロックデバイス、文字デバイス、および STREAMS デバイスでサポートされます。LDI は STREAMS リンクと STREAMS ioctl コマンドをサポートしています。ldi_ioctl(9F) のマニュアルページの「STREAM IOCTLS」セクションを参照してください。streamio(7I) のマニュアルページの ioctl コマンドも参照してください。

ldi_devmap(9F)

ターゲットデバイスのデバイスエントリポイントに devmap 要求を渡します。この操作はブロックデバイスと文字デバイスでサポートされます。

ldi_getmsg(9F)

ストリームからメッセージブロックを取得します。

ldi_putmsg(9F)

メッセージブロックをストリーム上に配置します。

ターゲットデバイス情報の取得

この節では、指定したターゲットデバイスに関するデバイス情報を取得するためにカーネルデバイスコンシューマが使用できる LDI インタフェースについて説明します。ターゲットデバイスは、階層化ドライバのハンドルによって指定します。カーネルデバイスコンシューマは、デバイス番号、デバイス公開型、デバイス ID、デバイスマイナー名、デバイスサイズなどの情報を受け取ることができます。

ldi_get_dev(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスの dev_t デバイス番号を取得します。

ldi_get_otyp(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスを開くために使用された open フラグを取得します。このフラグは、ターゲットデバイスが文字デバイスであるかブロックデバイスであるかを通知します。

ldi_get_devid(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスの ddi_devid_t デバイス ID を取得します。デバイス ID の使用を終えて ddi_devid_t を解放するには、ddi_devid_free(9F) を使用します。

ldi_get_minor_name(9F)

ターゲットデバイスのために開かれたマイナーノードの名前を格納しているバッファーを取得します。マイナーノード名の使用を終えてバッファーを解放するには、kmem_free(9F) を使用します。

ldi_get_size(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスのパーティションサイズを取得します。

ターゲットデバイスのプロパティー値の取得

この節では、指定したターゲットデバイスに関するプロパティー情報を取得するためにカーネルデバイスコンシューマが使用できる LDI インタフェースについて説明します。ターゲットデバイスは、階層化ドライバのハンドルによって指定します。カーネルデバイスコンシューマはプロパティーの値とアドレスを受け取って、プロパティーが存在するかどうかを判定できます。

ldi_prop_exists(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスのプロパティーが存在する場合は 1 を返します。指定されたターゲットデバイスのプロパティーが存在しない場合は 0 を返します。

ldi_prop_get_int(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスに関連付けられている int 整数プロパティーを検索します。整数プロパティーが見つかった場合はプロパティー値を返します。

ldi_prop_get_int64(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスに関連付けられている int64_t 整数プロパティーを検索します。整数プロパティーが見つかった場合はプロパティー値を返します。

ldi_prop_lookup_int_array(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスの int 整数配列プロパティー値のアドレスを取得します。

ldi_prop_lookup_int64_array(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスの int64_t 整数配列プロパティー値のアドレスを取得します。

ldi_prop_lookup_string(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスの NULL で終わっている文字列プロパティー値のアドレスを取得します。

ldi_prop_lookup_string_array(9F)

文字列の配列のアドレスを取得します。文字列の配列は、階層化ドライバのハンドルによって指定されたターゲットデバイスの NULL で終わっている文字列プロパティー値へのポインタの配列です。

ldi_prop_lookup_byte_array(9F)

バイトの配列のアドレスを取得します。バイトの配列は、階層化ドライバのハンドルによって指定されたターゲットデバイスのプロパティー値です。

非同期デバイスイベントの通知の受信

カーネルデバイスコンシューマは 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 非同期イベント通知インタフェースについて説明します。

ldi_callback_id_t

イベントハンドラ識別子。不透明な型です。

ldi_get_eventcookie(9F)

階層化ドライバのハンドルによって指定されたターゲットデバイスのイベントサービス cookie を取得します。

ldi_add_event_handler(9F)

ldi_callback_id_t 登録識別子によって指定されたコールバックハンドラを追加します。コールバックハンドラは、ddi_eventcookie_t cookie によって指定されたイベントが発生したときに呼び出されます。

ldi_remove_event_handler(9F)

ldi_callback_id_t 登録識別子によって指定されたコールバックハンドラを削除します。

LDI カーネルインタフェースの例

この節では、この章の前の節で説明した LDI 呼び出しを使用するカーネルデバイスコンシューマの例を示します。この節では、このモジュール例の次の面について説明します。

このカーネルデバイスコンシューマの例は lyr という名前です。lyr モジュールは、LDI 呼び出しを使用してターゲットデバイスにデータを送信する階層化ドライバです。lyr ドライバは open(9E) エントリポイントで、lyr.conf 設定ファイル内の lyr_targ プロパティーによって指定されたデバイスを開きます。lyr ドライバは write(9E) エントリポイントで、lyr_targ プロパティーによって指定されたデバイスに、すべての受信データを書き込みます。

デバイス設定ファイル

次に示す設定ファイルでは、lyr ドライバの書き込み先ターゲットデバイスはコンソールです。

例 14-1 設定ファイル

#
# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
# 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 */
};

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));
}

階層化ドライバを構築してロードする方法

  1. ドライバをコンパイルします。

    -D_KERNEL オプションを使用して、これがカーネルモジュールであることを示します。

    • SPARC アーキテクチャー向けにコンパイルする場合は、-xarch=v9 オプションを使用します。

      % cc -c -D_KERNEL -xarch=v9 lyr.c
    • 32 ビット x86 アーキテクチャー向けにコンパイルする場合は、次のコマンドを使用します。

      % cc -c -D_KERNEL lyr.c
  2. ドライバをリンクします。
    % ld -r -o lyr lyr.o
  3. 設定ファイルをインストールします。

    root ユーザーとして、マシンのカーネルドライバ領域に設定ファイルをコピーします。

    # cp lyr.conf /usr/kernel/drv
  4. ドライババイナリをインストールします。
    • root ユーザーとして、SPARC アーキテクチャー上の sparcv9 ドライバ領域にドライババイナリをコピーします。

      # cp lyr /usr/kernel/drv/sparcv9
    • root ユーザーとして、32 ビット x86 アーキテクチャー上の drv ドライバ領域にドライババイナリをコピーします。

      # cp lyr /usr/kernel/drv
  5. ドライバをロードします。

    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
#
# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
# 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 デバイスに表示されることを確認します。