Oracle® Solaris 11.2 デバイスドライバの記述

印刷ビューの終了

更新: 2014 年 9 月
 
 

attach() エントリポイント

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

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

  • デバイスインスタンスへのソフト状態構造体の割り当て

  • インスタンスごとの mutex の初期化

  • 条件変数の初期化

  • デバイスの割り込みの登録

  • デバイスインスタンスのレジスタとメモリーのマッピング

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

  • デバイスインスタンスが接続されたことの報告

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

デバイスドライバ作成者が状態構造体を割り当てるのを支援するために、Oracle 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) がどのように関連するかの詳細については、Creating Minor Device Nodesを参照してください。

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

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

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

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

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

ドライバは、1 つのデバイスに対して任意の数のマイナーノードを作成できます。Oracle 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) は次のアクションを実行します。

  • デバイスインスタンスをシリアルポートとして識別する

  • /dev/term ディレクトリ内に名前を作成する

  • /etc/inittab ファイルにエントリを追加する

ベンダーから提供された文字列には、その文字列を一意のものにするために、名前や銘柄記号などの識別値が含まれているはずです。この文字列を 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() を同時に呼び出す可能性もあります。