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.  ドライバの自動設定

ドライバのロードとアンロード

ドライバに必要なデータ構造体

modlinkage 構造体

modldrv 構造体

dev_ops 構造体

cb_ops 構造体

ロード可能なドライバインタフェース

_init() の例

_fini() の例

_info() の例

デバイス設定の概念

デバイスインスタンスとインスタンス番号

マイナーノードとマイナー番号

probe() エントリポイント

attach() エントリポイント

ドライバのソフト状態管理

ロック変数と条件変数の初期化

マイナーデバイスノードの作成

遅延接続

detach() エントリポイント

getinfo() エントリポイント

デバイス ID の使用

デバイス ID の登録

デバイスによって提供された ID の登録

組み立てられた ID の登録

デバイス ID の登録解除

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

8.  割り込みハンドラ

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

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

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

12.  電源管理

13.  Solaris ドライバの強化

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

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

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

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

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

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

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

20.  USB ドライバ

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

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

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

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

パート IV 付録

A.  ハードウェアの概要

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

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

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

索引

デバイス設定の概念

カーネルデバイスツリー内のノードごとに、システムは、ノード名と compatible プロパティーに基づいてそのノードのドライバを選択します (「ドライバのデバイスへのバインド」を参照)。複数のデバイスノードに同じドライバがバインドされる可能性があります。ドライバは、システムによって割り当てられたインスタンス番号により、異なるノードを区別できます。

デバイスノードに対してドライバが選択されたあと、そのデバイスがシステム上に存在するかどうかを判定するために、そのドライバの probe(9E) エントリポイントが呼び出されます。probe() が成功した場合は、デバイスを設定および管理するために、そのドライバの attach(9E) エントリポイントが呼び出されます。デバイスを開くことができるのは、attach() が成功を返した場合のみです (attach() エントリポイント」を参照)。

デバイスは、システムメモリー資源を節約するため、またはシステムの実行中にそのデバイスを削除できるようにするために、設定解除される可能性があります。デバイスを設定解除できるようにするために、システムはまず、そのデバイスインスタンスが参照されているかどうかをチェックします。このチェックでは、そのドライバのみが認識している情報を取得するために、ドライバの getinfo(9E) エントリポイントを呼び出します (getinfo() エントリポイント」を参照)。そのデバイスインスタンスが参照されていない場合は、そのデバイスを設定解除するために、ドライバの detach(9E) ルーチンが呼び出されます (detach() エントリポイント」を参照)。

まとめると、各ドライバは、デバイス設定のためにカーネルによって使用される次のエントリポイントを定義する必要があります。

attach()detach()、および getinfo() は必須であることに注意してください。probe() は、自身を識別できないデバイスに対してのみ必要です。自身を識別できるデバイスの場合は、明示的な probe() ルーチンを提供するか、または probe() エントリポイントの dev_ops 構造体で nulldev(9F) を指定できます。

デバイスインスタンスとインスタンス番号

システムは、各デバイスにインスタンス番号を割り当てます。ドライバは、特定のデバイスに割り当てられたインスタンス番号の値を確実には予測できない可能性があります。ドライバは ddi_get_instance(9F) を呼び出すことによって、割り当てられた特定のインスタンス番号を取得します。

インスタンス番号は、デバイスに対するシステムの概念を表します。特定のドライバに対する各 dev_info (つまり、デバイスツリー内の各ノード) には、カーネルによってインスタンス番号が割り当てられます。さらに、インスタンス番号は、特定の物理デバイスに固有のデータのインデックスを作成するために便利なメカニズムを提供します。インスタンス番号のもっとも一般的な使用法は、インスタンス番号を使用して特定の物理デバイスのソフト状態データを取得する、ddi_get_soft_state(9F) です。


注意

注意 - 疑似デバイス (つまり、疑似ネクサスの子) の場合、インスタンス番号は、instance プロパティーを使用して driver.conf(4) ファイルで定義されます。driver.conf ファイルに instance プロパティーが含まれていない場合の動作は未定義です。ハードウェアデバイスノードの場合、システムは、そのデバイスが最初に OS によって認識されたときにインスタンス番号を割り当てます。インスタンス番号は、システムのリブートや OS のアップグレードのあとも持続されます。


マイナーノードとマイナー番号

ドライバは、自身のマイナー番号名前空間の管理を担当します。たとえば、sd ドライバはディスクごとに、8 文字のマイナーノードと 8 ブロックのマイナーノードをファイルシステムにエクスポートする必要があります。各マイナーノードは、ディスクのある部分に対するブロックインタフェースまたは文字インタフェースのどちらかを表します。getinfo(9E) エントリポイントは、マイナー番号からデバイスインスタンスへのマッピングについてシステムに通知します (getinfo() エントリポイント」を参照)。

probe() エントリポイント

自身を識別できないデバイスの場合は、probe(9E) エントリポイントで、そのハードウェアデバイスがシステム上に存在するかどうかを判定します。

probe() でデバイスのインスタンスが存在するかどうかを判定するには、probe() は、一般に attach(9E) によっても実行される多くのタスクを実行する必要があります。特に、probe() によるデバイスレジスタのマッピングが必要になることがあります。

デバイスレジスタのプローブはデバイス固有の操作ですドライバは多くの場合、ハードウェアが実際に存在することを確認するために、そのハードウェアの一連のテストを実行する必要があります。このテストの条件は、デバイスの誤った識別を回避できるほど十分に厳格である必要があります。たとえば、別のデバイスが予期したデバイスと同様に動作しているように見えるため、そのデバイスが実際には使用できないのに、存在しているように見える可能性があります。

このテストは次のフラグを返します。

特定のデバイスインスタンスの場合は、そのデバイスに対して probe(9E) が少なくとも 1 回成功するまで attach(9E) は呼び出されません。

probe() は複数回呼び出される可能性があるため、probe(9E) は、probe() が割り当てたすべての資源を解放する必要があります。ただし、probe(9E) が成功した場合でも、必ずしも attach(9E) が呼び出されるわけではありません。

ドライバの probe(9E) ルーチンで ddi_dev_is_sid(9F) を使用すると、デバイスが自身を識別できるかどうかを判定できます。ddi_dev_is_sid() は、同じデバイスの自身を識別できるバージョンおよび自身を識別できないバージョンのために記述されたドライバで有効です。

次の例は、サンプルの probe() ルーチンです。

例 6-3 probe(9E) ルーチン

static int
xxprobe(dev_info_t *dip)
{
    ddi_acc_handle_t dev_hdl;
    ddi_device_acc_attr_t dev_attr;
    Pio_csr *csrp;
    uint8_t csrval;

    /*
     * if the device is self identifying, no need to probe
     */
    if (ddi_dev_is_sid(dip) == DDI_SUCCESS)
    return (DDI_PROBE_DONTCARE);

    /*
     * Initalize the device access attributes and map in
     * the devices CSR register (register 0)
     */
    dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
    dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
    dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;

    if (ddi_regs_map_setup(dip, 0, (caddr_t *)&csrp, 0, sizeof (Pio_csr),
    &dev_attr, &dev_hdl) != DDI_SUCCESS)
    return (DDI_PROBE_FAILURE);

    /*
     * Reset the device
     * Once the reset completes the CSR should read back
     * (PIO_DEV_READY | PIO_IDLE_INTR)
     */
    ddi_put8(dev_hdl, csrp, PIO_RESET);
    csrval = ddi_get8(dev_hdl, csrp);

    /*
     * tear down the mappings and return probe success/failure
     */
    ddi_regs_map_free(&dev_hdl);
    if ((csrval & 0xff) == (PIO_DEV_READY | PIO_IDLE_INTR))
    return (DDI_PROBE_SUCCESS);
    else
    return (DDI_PROBE_FAILURE);
}

ドライバの probe(9E) ルーチンが呼び出されたとき、そのドライバは、プローブされているデバイスがバス上に存在するかどうかを認識していません。そのため、ドライバは、存在しないデバイスのデバイスレジスタへのアクセスを試みる可能性があります。その結果、一部のバスではバス障害が発生することがあります。

次の例は、ddi_poke8(9F) を使用してデバイスの存在をチェックする probe(9E) ルーチンを示しています。ddi_poke8() は、必要な場合はこのプロセスを支援するために親ネクサスドライバを使用して、指定された仮想アドレスへの値の書き込みを慎重に試みます。アドレスが有効でないか、またはエラーを発生させずに値を書き込むことができない場合は、エラーコードが返されます。ddi_peek(9F)も参照してください。

この例では、デバイスレジスタをマッピングするために ddi_regs_map_setup(9F) が使用されています。

例 6-4 ddi_poke8(9F) を使用した probe(9E) ルーチン

static int
xxprobe(dev_info_t *dip)
{
    ddi_acc_handle_t dev_hdl;
    ddi_device_acc_attr_t dev_attr;
    Pio_csr *csrp;
    uint8_t csrval;

    /*
     * if the device is self-identifying, no need to probe
     */
    if (ddi_dev_is_sid(dip) == DDI_SUCCESS)
    return (DDI_PROBE_DONTCARE);

    /*
     * Initialize the device access attrributes and map in
     * the device's CSR register (register 0)
     */
    dev_attr.devacc_attr_version - DDI_DEVICE_ATTR_V0;
    dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
    dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;

    if (ddi_regs_map_setup(dip, 0, (caddr_t *)&csrp, 0, sizeof (Pio_csr),
    &dev_attr, &dev_hdl) != DDI_SUCCESS)
    return (DDI_PROBE_FAILURE);

    /*
     * The bus can generate a fault when probing for devices that
     * do not exist.  Use ddi_poke8(9f) to handle any faults that
     * might occur.
     *
     * Reset the device.  Once the reset completes the CSR should read
     * back (PIO_DEV_READY | PIO_IDLE_INTR)
     */
    if (ddi_poke8(dip, csrp, PIO_RESET) != DDI_SUCCESS) {
    ddi_regs_map_free(&dev_hdl);
    return (DDI_FAILURE);

    csrval = ddi_get8(dev_hdl, csrp);
    /*
     * tear down the mappings and return probe success/failure
     */
    ddi_regs_map_free(&dev_hdl);
    if ((csrval & 0xff) == (PIO_DEV_READY | PIO_IDLE_INTR))
    return (DDI_PROBE_SUCCESS);
    else
    return (DDI_PROBE_FAILURE);
}

attach() エントリポイント

カーネルは、デバイスのインスタンスを接続するため、または電源管理フレームワークによって中断または停止されたデバイスのインスタンスの操作を再開するために、ドライバの attach(9E) エントリポイントを呼び出します。ここでは、デバイスインスタンスを接続する操作についてのみ説明します。電源管理については、第 12 章電源管理で説明します。

ドライバの attach(9E) エントリポイントは、そのドライバにバインドされたデバイスの各インスタンスを接続するために呼び出されます。このエントリポイントは、接続するデバイスノードのインスタンスと、attach(9E) に対する cmd 引数として指定された DDI_ATTACH を使用して呼び出されます。attach エントリポイントには通常、次のタイプの処理が含まれます。

ドライバのソフト状態管理

デバイスドライバ作成者による状態構造体の割り当てを支援するために、Solaris DDI/DKI には、ソフトウェア状態管理ルーチンと呼ばれる一連のメモリー管理ルーチン (ソフト状態ルーチンとも呼ばれる) が用意されています。これらのルーチンは、指定されたサイズのメモリー項目の動的な割り当て、取得、および破棄を行い、リスト管理の詳細を非表示にします。目的のメモリー項目は、インスタンス番号で識別されます。この番号は通常、システムによって割り当てられるインスタンス番号です。

ドライバは通常、ddi_soft_state_zalloc(9F) を呼び出し、デバイスのインスタンス番号を渡すことによって、そのドライバに接続されたデバイスインスタンスごとにソフト状態構造体を割り当てます。2 つのデバイスノードが同じインスタンス番号を持つことはできないため、指定されたインスタンス番号に対する割り当てがすでに存在する場合は、ddi_soft_state_zalloc(9F) が失敗します。

ドライバの文字またはブロックエントリポイント (cb_ops(9S)) は、まずエントリポイント関数に渡された dev_t 引数からデバイスのインスタンス番号をデコードすることによって、特定のソフト状態構造体を参照します。次に、ドライバが ddi_get_soft_state(9F) を呼び出して、ドライバごとのソフト状態リストと、派生したインスタンス番号を渡します。NULL の戻り値は、そのデバイスが事実上存在しないため、ドライバが該当するコードを返すことを示します。

インスタンス番号とデバイス番号 (つまり、dev_t) がどのように関連するかの詳細については、「マイナーデバイスノードの作成」を参照してください。

ロック変数と条件変数の初期化

ドライバは、接続中にインスタンスごとのロック変数と条件変数をすべて初期化します。ドライバの割り込みハンドラによって取得されたロックはすべて、いずれかの割り込みハンドラを追加する前に初期化する必要があります。ロックの初期化と使用法の説明については、第 3 章マルチスレッドを参照してください。割り込みハンドラとロックの問題の説明については、第 8 章割り込みハンドラを参照してください。

マイナーデバイスノードの作成

接続プロセスの重要な部分として、デバイスインスタンスに対するマイナーノードの作成があります。マイナーノードには、デバイスと DDI フレームワークによってエクスポートされた情報が含まれています。システムはこの情報を使用して、/devices の下にマイナーノードのための特殊ファイルを作成します。

マイナーノードは、ドライバが ddi_create_minor_node(9F) を呼び出したときに作成されます。ドライバは、マイナー番号マイナー名マイナーノードタイプ、およびそのマイナーノードがブロックデバイスまたは文字デバイスのどちらを表すかを指定します。

ドライバは、1 つのデバイスに対して任意の数のマイナーノードを作成できます。Solaris DDI/DKI は、特定のクラスのデバイスには特定の形式で作成されたマイナーノードがあるものと予期します。たとえば、ディスクドライバでは、接続された物理ディスクインスタンスごとに 16 のマイナーノードが作成されると予期しています。a - h のブロックデバイスインタフェースを表す 8 つのマイナーノードに加え、a,raw - h,raw の文字デバイスインタフェースのための 8 つのマイナーノードが追加で作成されます。

ddi_create_minor_node(9F) に渡されるマイナー番号は、完全にドライバによって定義されます。マイナー番号は通常、マイナーノード識別子を含む、デバイスのインスタンス番号のエンコーディングです。前の例でドライバは、デバイスのインスタンス番号を 3 ビット左にシフトし、マイナーノードインデックスにその結果の OR を使用することによって各マイナーノードのマイナー番号を作成します。マイナーノードインデックスの値は 0 - 7 です。マイナーノード aa,raw が同じマイナー番号を共有することに注意してください。これらのマイナーノードは、ddi_create_minor_node() に渡される spec_type 引数によって区別されます。

ddi_create_minor_node(9F) に渡されるマイナーノードタイプによって、ディスク、テープ、ネットワークインタフェース、フレームバッファーなどの、デバイスのタイプが分類されます。

次の表に、作成される可能性のあるノードのタイプを示します。

表 6-1 可能性のあるノードタイプ

定数
説明
DDI_NT_SERIAL
シリアルポート
DDI_NT_SERIAL_DO
ダイヤルアウトポート
DDI_NT_BLOCK
ハードディスク
DDI_NT_BLOCK_CHAN
チャネルまたはターゲット番号を持つハードディスク
DDI_NT_CD
ROM ドライブ (CD-ROM)
DDI_NT_CD_CHAN
チャネルまたはターゲット番号を持つ ROM ドライブ
DDI_NT_FD
フロッピーディスク
DDI_NT_TAPE
テープドライブ
DDI_NT_NET
ネットワークデバイス
DDI_NT_DISPLAY
ディスプレイデバイス
DDI_NT_MOUSE
マウス
DDI_NT_KEYBOARD
キーボード
DDI_NT_AUDIO
オーディオデバイス
DDI_PSEUDO
一般的な疑似デバイス

ノードタイプ DDI_NT_BLOCKDDI_NT_BLOCK_CHANDDI_NT_CD、および DDI_NT_CD_CHAN を指定すると、devfsadm(1M) はデバイスインスタンスをディスクとして識別し、/dev/dsk または /dev/rdsk ディレクトリ内に名前を作成します。

ノードタイプ DDI_NT_TAPE を指定すると、devfsadm(1M) はデバイスインスタンスをテープとして識別し、/dev/rmt ディレクトリ内に名前を作成します。

ノードタイプ DDI_NT_SERIAL および DDI_NT_SERIAL_DO を指定すると、devfsadm(1M) は次のアクションを実行します。

ベンダーから提供された文字列には、その文字列を一意のものにするために、名前や銘柄記号などの識別値が含まれているはずです。この文字列を devfsadm(1M)devlinks.tab ファイル (devlinks(1M) のマニュアルページを参照) と組み合わせて使用することにより、/dev 内に論理名を作成できます。

遅延接続

対応するインスタンスに対して attach(9E) が成功する前に、マイナーデバイスに対して open(9E) が呼び出される可能性があります。その場合、open()ENXIO を返す必要があります。これにより、システムはそのデバイスの接続を試みます。attach() が成功した場合は、open() が自動的に再試行されます。

例 6-5 標準的な attach() エントリポイント

/*
 * Attach an instance of the driver.  We take all the knowledge we
 * have about our board and check it against what has been filled in
 * for us from our FCode or from our driver.conf(4) file.
 */
static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
    int instance;
    Pio *pio_p;
    ddi_device_acc_attr_t   da_attr;
    static int pio_validate_device(dev_info_t *);

    switch (cmd) {
    case DDI_ATTACH:

    /*
     * first validate the device conforms to a configuration this driver
     * supports
     */
    if (pio_validate_device(dip) == 0)
        return (DDI_FAILURE);

    /*
     * Allocate a soft state structure for this device instance
     * Store a pointer to the device node in our soft state structure
     * and a reference to the soft state structure in the device
     * node.
     */
    instance = ddi_get_instance(dip);
    if (ddi_soft_state_zalloc(pio_softstate, instance) != 0)
        return (DDI_FAILURE);
    pio_p = ddi_get_soft_state(pio_softstate, instance);
    ddi_set_driver_private(dip, (caddr_t)pio_p);
    pio_p->dip = dip;

    /*
     * Before adding the interrupt, get the interrupt block
     * cookie associated with the interrupt specification to
     * initialize the mutex used by the interrupt handler.
     */
    if (ddi_get_iblock_cookie(dip, 0, &pio_p->iblock_cookie) !=
      DDI_SUCCESS) {
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    mutex_init(&pio_p->mutex, NULL, MUTEX_DRIVER, pio_p->iblock_cookie);

    /*
     * Now that the mutex is initialized, add the interrupt itself.
     */
    if (ddi_add_intr(dip, 0, NULL, NULL, pio_intr, (caddr_t)instance) !=
      DDI_SUCCESS) {
        mutex_destroy(&pio_p>mutex);
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    /*
     * Initialize the device access attributes for the register mapping
     */
    dev_acc_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
    dev_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
    dev_acc_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;

    /*
     * Map in the csr register (register 0)
     */
    if (ddi_regs_map_setup(dip, 0, (caddr_t *)&(pio_p->csr), 0,
        sizeof (Pio_csr), &dev_acc_attr, &pio_p->csr_handle) !=
        DDI_SUCCESS) {
        ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
        mutex_destroy(&pio_p->mutex);
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    /*
     * Map in the data register (register 1)
     */
    if (ddi_regs_map_setup(dip, 1, (caddr_t *)&(pio_p->data), 0,
        sizeof (uchar_t), &dev_acc_attr, &pio_p->data_handle) !=
        DDI_SUCCESS) {
        ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
        ddi_regs_map_free(&pio_p->csr_handle);
        mutex_destroy(&pio_p->mutex);
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    /*
     * Create an entry in /devices for user processes to open(2)
     * This driver will create a minor node entry in /devices
     * of the form:  /devices/..../pio@X,Y:pio
     */
    if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR,
        instance, DDI_PSEUDO, 0) == DDI_FAILURE) {
        ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
        ddi_regs_map_free(&pio_p->csr_handle);
        ddi_regs_map_free(&pio_p->data_handle);
        mutex_destroy(&pio_p->mutex);
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    /*
     * reset device (including disabling interrupts)
     */
    ddi_put8(pio_p->csr_handle, pio_p->csr, PIO_RESET);

    /*
     * report the name of the device instance which has attached
     */
    ddi_report_dev(dip);
    return (DDI_SUCCESS);

    case DDI_RESUME:
    return (DDI_SUCCESS);

    default:
    return (DDI_FAILURE);
    }
}

注 - attach() ルーチンでは、異なるデバイスインスタンスに対する呼び出しの順序に関して、どのような想定もしてはいけません。システムは、異なるデバイスインスタンスに対して attach() を同時に呼び出す可能性があります。システムはまた、異なるデバイスインスタンスに対して attach()detach() を同時に呼び出す可能性もあります。


detach() エントリポイント

カーネルは、デバイスのインスタンスを切り離すため、または電源管理によるデバイスのインスタンスに対する操作を中断するために、ドライバの detach(9E) エントリポイントを呼び出します。ここでは、デバイスインスタンスを切り離す操作について説明します。電源管理の問題の説明については、第 12 章電源管理を参照してください。

ドライバの detach() エントリポイントは、そのドライバにバインドされたデバイスのインスタンスを切り離すために呼び出されます。このエントリポイントは、切り離されるデバイスノードのインスタンスと、このエントリポイントに対する cmd 引数として指定された DDI_DETACH を使用して呼び出されます。

ドライバは、すべてのタイムアウトまたはコールバックを取り消すか、または待機して完了したあと、戻る前にデバイスインスタンスに割り当てられた資源をすべて解放する必要があります。ドライバが何らかの理由で、使用されていない資源の未処理のコールバックを取り消すことができない場合、ドライバはデバイスを元の状態に戻し、エントリポイントから DDI_FAILURE を返す必要があります。これにより、デバイスインスタンスは接続された状態のままになります。

コールバックルーチンには、取り消すことができるコールバックと、取り消すことができないコールバックの 2 つのタイプがあります。timeout(9F)bufcall(9F) のコールバックは、detach(9E) 中にドライバが原子的に取り消すことができます。scsi_init_pkt(9F)ddi_dma_buf_bind_handle(9F) などのほかのタイプのコールバックは、取り消すことができません。ドライバは、コールバックが完了するまで detach() 内でブロックするか、または切り離しの要求を失敗させるかのどちらかを行う必要があります。

例 6-6 標準的な detach() エントリポイント

/*
 * detach(9e)
 * free the resources that were allocated in attach(9e)
 */
static int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
    Pio     *pio_p;
    int     instance;

    switch (cmd) {
    case DDI_DETACH:

    instance = ddi_get_instance(dip);
    pio_p = ddi_get_soft_state(pio_softstate, instance);

    /*
     * turn off the device
     * free any resources allocated in attach
     */
    ddi_put8(pio_p->csr_handle, pio_p->csr, PIO_RESET);
    ddi_remove_minor_node(dip, NULL);
    ddi_regs_map_free(&pio_p->csr_handle);
    ddi_regs_map_free(&pio_p->data_handle);
    ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
    mutex_destroy(&pio_p->mutex);
    ddi_soft_state_free(pio_softstate, instance);
    return (DDI_SUCCESS);

    case DDI_SUSPEND:
    default:
    return (DDI_FAILURE);
    }
}

getinfo() エントリポイント

システムは、ドライバのみが認識している設定情報を取得するために getinfo(9E) を呼び出します。デバイスインスタンスへのマイナー番号のマッピングは、ドライバによって完全に制御されます。システムは、特定の dev_t がどのデバイスを表すかをドライバに問い合わせることが必要になる場合があります。

getinfo() 関数は、infocmd 引数として DDI_INFO_DEVT2INSTANCE または DDI_INFO_DEVT2DEVINFO のどちらかを取ることができます。DDI_INFO_DEVT2INSTANCE コマンドは、デバイスのインスタンス番号を要求します。DDI_INFO_DEVT2DEVINFO コマンドは、デバイスの dev_info 構造体へのポインタを要求します。

DDI_INFO_DEVT2INSTANCE の場合は、argdev_tであり、getinfo()dev_t 内のマイナー番号をインスタンス番号に変換する必要があります。次の例では、マイナー番号がインスタンス番号と同じであるため、getinfo() はマイナー番号を戻すのみで済みます。この場合は、getinfo()attach() の前に呼び出される可能性があるため、ドライバでは状態構造体が使用できることを前提にしてはいけません。ドライバによって定義されるマイナーデバイス番号とインスタンス番号の間のマッピングは、必ずしも例に示したマッピングに従う必要はありません。ただし、いずれの場合も、マッピングは静的である必要があります。

DDI_INFO_DEVT2DEVINFO の場合も、argdev_t であるため、getinfo() は最初にデバイスのインスタンス番号をデコードします。getinfo() は次に、次の例に示すように、該当するデバイスに対するドライバのソフト状態構造体に保存された dev_info ポインタを戻します。

例 6-7 標準的な getinfo() エントリポイント

/*
 * getinfo(9e)
 * Return the instance number or device node given a dev_t
 */
static int
xxgetinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
    int error;
    Pio *pio_p;
    int instance = getminor((dev_t)arg);

    switch (infocmd) {

    /*
     * return the device node if the driver has attached the
     * device instance identified by the dev_t value which was passed
     */
    case DDI_INFO_DEVT2DEVINFO:
    pio_p = ddi_get_soft_state(pio_softstate, instance);
    if (pio_p == NULL) {
        *result = NULL;
        error = DDI_FAILURE;
    } else {
        mutex_enter(&pio_p->mutex);
        *result = pio_p->dip;
        mutex_exit(&pio_p->mutex);
        error = DDI_SUCCESS;
    }
    break;

    /*
     * the driver can always return the instance number given a dev_t
     * value, even if the instance is not attached.
     */
    case DDI_INFO_DEVT2INSTANCE:
    *result = (void *)instance;
    error = DDI_SUCCESS;
    break;
    default:
    *result = NULL;
    error = DDI_FAILURE;
    }
    return (error);
}

注 - getinfo() ルーチンは、ドライバが作成するマイナーノードとの同期が維持される必要があります。マイナーノードが同期から外れた場合は、ホットプラグ操作がすべて失敗し、システムパニックが発生することがあります。