Go to main content
Oracle® Solaris 11.3 デバイスドライバの記述

印刷ビューの終了

更新: 2016 年 11 月
 
 

HBA ドライバの依存性と構成に関する問題

開発者は、SCSA HBA のエントリポイント、構造体、および関数をドライバに組み込むだけでなく、ドライバの依存性と構成に関する問題にも対処する必要があります。これらの問題は、構成プロパティー、依存性宣言、状態構造体とコマンド別構造体、モジュール初期化用のエントリポイント、および自動構成エントリポイントに関係しています。

宣言と構造体

HBA ドライバには、次のヘッダーファイルを含める必要があります。

#include <sys/scsi/scsi.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

モジュールが SCSA ルーチンに依存していることをシステムに通知するには、次のコマンドを使用してドライバのバイナリを生成する必要があります。SCSA ルーチンの詳細については、SCSA HBA インタフェースを参照してください。

% ld -r xx.o -o xx -N "misc/scsi"

サンプルコードは、QLogic Intelligent SCSI Peripheral デバイス用の簡略化された isp ドライバから入手できます。isp ドライバは、最大 15 台のターゲットデバイスを接続可能で、1 つのターゲットに 8 台までの論理ユニット (LUN) を接続できるワイド SCSI をサポートしています。

コマンド別構造体

HBA ドライバは通常、ターゲットドライバが実行するコマンドごとに状態を保持する構造体を定義する必要があります。このコマンド別構造体のレイアウトは、デバイスドライバの作成者がすべて管理します。レイアウトに必要なのは、ドライバで使用されるハードウェアの機能とソフトウェアのアルゴリズムを反映させることです。

コマンド別構造体の例を次に示します。この章の残りのコードフラグメントは、この構造体を使って HBA インタフェースを示しています。

struct isp_cmd {
     struct isp_request     cmd_isp_request;
     struct isp_response    cmd_isp_response;
     struct scsi_pkt        *cmd_pkt;
     struct isp_cmd         *cmd_forw;
     uint32_t               cmd_dmacount;
     ddi_dma_handle_t       cmd_dmahandle;
     uint_t                 cmd_cookie;
     uint_t                 cmd_ncookies;
     uint_t                 cmd_cookiecnt;
     uint_t                 cmd_nwin;
     uint_t                 cmd_curwin;
     off_t                  cmd_dma_offset;
     uint_t                 cmd_dma_len;
     ddi_dma_cookie_t       cmd_dmacookies[ISP_NDATASEGS];
     u_int                  cmd_flags;
     u_short                cmd_slot;
     u_int                  cmd_cdblen;
     u_int                  cmd_scblen;
 };

モジュール初期化用のエントリポイント

このセクションでは、SCSI HBA ドライバによって実行される操作のエントリポイントについて説明します。

次の SCSI HBA ドライバ用のコードは、代表的なdev_ops(9S) 構造体を示しています。ドライバは、この構造体の devo_bus_ops フィールドを NULL に初期化する必要があります。SCSI HBA ドライバは、特別な目的でリーフドライバインタフェースを提供することがあり、その場合、devo_cb_ops フィールドはcb_ops(9S) 構造体を指すことがあります。この例では、リーフドライバインタフェースはエクスポートされていないため、devo_cb_ops フィールドは NULL に初期化されます。

_init() エントリポイント (SCSI HBA ドライバ)

_init(9E) 関数は、ロード可能なモジュールを初期化します。_init() は、ロード可能なモジュール内のほかのすべてのルーチンの前に呼び出されます。

SCSI HBA では、_init() 関数は、scsi_hba_init(9F) を呼び出す前に、mod_install(9F) を呼び出して、HBA ドライバの存在をフレームワークに通知する必要があります。scsi_hba__init() ルーチンがゼロ以外の値を返す場合、_init() はこの値を返します。それ以外の場合、_init()mod_install(9F) によって返された値を返す必要があります。

ドライバは、mod_install(9F) を呼び出す前に、必要なグローバル状態を初期化します。

mod_install() が失敗した場合、_init() 関数は割り当てられているグローバルリソースをすべて解放する必要があります。_init() は、復帰する前にscsi_hba_fini(9F) を呼び出す必要があります。

次の例では、グローバルな mutex を使用して、ドライバのすべてのインスタンスにグローバルなデータの割り当て方法を示しています。このコードでは、グローバルな mutex とソフト状態構造体の情報を宣言しています。グローバルな mutex とソフト状態は、_init() の実行中に初期化されます。

_fini() エントリポイント (SCSI HBA ドライバ)

_fini(9E) 関数は、システムが SCSI HBA ドライバをアンロードしようとするときに呼び出されます。_fini() 関数は、mod_remove(9F) を呼び出して、ドライバがアンロード可能かどうかを判定する必要があります。mod_remove() が 0 を返した場合、このモジュールはアンロード可能です。HBA ドライバは、_init(9E) で割り当てられたグローバルリソースをすべて解放する必要があります。また、HBA ドライバは、scsi_hba_fini(9F) も呼び出す必要があります。

_fini() は、mod_remove() によって返された値を返す必要があります。


注 - HBA ドライバは、scsi_hba_fini(9F) が 0 を返さないかぎり、リソースを解放したり、mod_remove(9F) を呼び出したりすることはできません。

使用例 99 は、SCSI HBA 用のモジュールの初期化を示しています。

使用例 99  SCSI HBA 用のモジュールの初期化
static struct dev_ops isp_dev_ops = {
    DEVO_REV,       /* devo_rev */
    0,              /* refcnt  */
    isp_getinfo,    /* getinfo */
    nulldev,        /* identify */
    nulldev,        /* probe */
    isp_attach,     /* attach */
    isp_detach,     /* detach */
    nodev,          /* reset */
    NULL,           /* driver operations */
    NULL,           /* bus operations */
    isp_power,      /* power management */
    isp_quiesce,    /* quiesce */
};

/*
 * Local static data
 */
static kmutex_t      isp_global_mutex;
static void          *isp_state;

int
_init(void)
{
    int     err;
    
    if ((err = ddi_soft_state_init(&isp_state,
        sizeof (struct isp), 0)) != 0) {
        return (err);
    }
    if ((err = scsi_hba_init(&modlinkage)) == 0) {
        mutex_init(&isp_global_mutex, "isp global mutex",
        MUTEX_DRIVER, NULL);
        if ((err = mod_install(&modlinkage)) != 0) {
            mutex_destroy(&isp_global_mutex);
            scsi_hba_fini(&modlinkage);
            ddi_soft_state_fini(&isp_state);    
        }
    }
    return (err);
}

int
_fini(void)
{
    int     err;
    
    if ((err = mod_remove(&modlinkage)) == 0) {
        mutex_destroy(&isp_global_mutex);
        scsi_hba_fini(&modlinkage);
        ddi_soft_state_fini(&isp_state);
    }
    return (err);
}

自動構成のエントリポイント

各デバイスドライバには dev_ops(9S) 構造体 (これにより、カーネルはドライバの自動構成エントリポイントを見つけることができる) が関連付けられています。これらの自動構成ルーチンについては、ドライバの自動構成に詳しく説明されています。このセクションでは、そのようなエントリポイントのうち、SCSI HBA ドライバで実行される操作に関連付けられたものについてのみ説明します。このようなエントリポイントには、attach(9E)detach(9E) があります。

attach() エントリポイント (SCSI HBA ドライバ)

SCSI HBA ドライバのattach(9E) エントリポイントは、デバイスに対してこのドライバのインスタンスを構成および接続するときにいくつかのタスクを実行します。実際のデバイスの一般的なドライバでは、次のオペレーティングシステムとハードウェアに関する問題に対処する必要があります。

  • ソフト状態構造体

  • DMA

  • トランスポート構造体

  • HBA ドライバの接続

  • レジスタマッピング

  • 割り込み仕様

  • 割り込み処理

  • 電源管理可能なコンポーネントの作成

  • 接続ステータスのレポート

ソフト状態構造体

デバイスインスタンスごとのソフト状態構造体を割り当てる際にエラーが発生した場合、ドライバは慎重にクリーンアップを行う必要があります。

DMA

HBA ドライバは、ddi_dma_attr_t 構造体を正しく初期化することで、その DMA エンジンの属性を記述する必要があります。

static ddi_dma_attr_t isp_dma_attr = {
     DMA_ATTR_V0,        /* ddi_dma_attr version */
     0,                  /* low address */
     0xffffffff,         /* high address */
     0x00ffffff,         /* counter upper bound */
     1,                  /* alignment requirements */
     0x3f,               /* burst sizes */
     1,                  /* minimum DMA access */
     0xffffffff,         /* maximum DMA access */
     (1<<24)-1,          /* segment boundary restrictions */
     1,                  /* scatter-gather list length */
     512,                /* device granularity */
     0                   /* DMA flags */
};

また、DMA を提供する場合は、そのハードウェアが DMA 対応スロットに取り付けられていることも確認するべきです。

if (ddi_slaveonly(dip) == DDI_SUCCESS) {
    return (DDI_FAILURE);
}
トランスポート構造体

HBA ドライバは、このインスタンスにさらにトランスポート構造体を割り当て、初期化します。tran_hba_private フィールドは、このインスタンスのソフト状態構造体を指すように設定します。特別なプローブカスタマイズが必要ない場合は、tran_tgt_probe フィールドを NULL に設定して、デフォルトの動作を実行できます。

tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);

isp->isp_tran                   = tran;
isp->isp_dip                    = dip;

tran->tran_hba_private          = isp;
tran->tran_tgt_private          = NULL;
tran->tran_tgt_init             = isp_tran_tgt_init;
tran->tran_tgt_probe            = scsi_hba_probe;
tran->tran_tgt_free             = (void (*)())NULL;

tran->tran_start                = isp_scsi_start;
tran->tran_abort                = isp_scsi_abort;
tran->tran_reset                = isp_scsi_reset;
tran->tran_getcap               = isp_scsi_getcap;
tran->tran_setcap               = isp_scsi_setcap;
tran->tran_init_pkt             = isp_scsi_init_pkt;
tran->tran_destroy_pkt          = isp_scsi_destroy_pkt;
tran->tran_dmafree              = isp_scsi_dmafree;
tran->tran_sync_pkt             = isp_scsi_sync_pkt;
tran->tran_reset_notify         = isp_scsi_reset_notify;
tran->tran_bus_quiesce          = isp_tran_bus_quiesce
tran->tran_bus_unquiesce        = isp_tran_bus_unquiesce
tran->tran_bus_reset            = isp_tran_bus_reset
tran->tran_interconnect_type    = isp_tran_interconnect_type
HBA ドライバの接続

HBA ドライバは、デバイスのこのインスタンスを接続し、必要があれば、エラーのクリーンアップを実行します。

i = scsi_hba_attach_setup(dip, &isp_dma_attr, tran, 0);
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);
}
レジスタマッピング

HBA ドライバは、そのデバイスのレジスタをマップします。ドライバでは次の項目を指定する必要があります。

  • レジスタセットインデックス

  • デバイスのデータアクセス特性

  • マップされるレジスタのサイズ

ddi_device_acc_attr_t    dev_attributes;

     dev_attributes.devacc_attr_version = DDI_DEVICE_ATTR_V0;
     dev_attributes.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
     dev_attributes.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;

     if (ddi_regs_map_setup(dip, 0, (caddr_t *)&isp->isp_reg,
     0, sizeof (struct ispregs), &dev_attributes,
     &isp->isp_acc_handle) != DDI_SUCCESS) {
        /* do error recovery */
        return (DDI_FAILURE);
     }
割り込みハンドラの追加

ドライバはまず、iblock cookie を取得して、ドライバハンドラで使用される mutex をすべて初期化する必要があります。それらの mutex の初期化が完了している場合にのみ、割り込みハンドラを追加できます。

i = ddi_get_iblock_cookie(dip, 0, &isp->iblock_cookie};
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);
}

mutex_init(&isp->mutex, "isp_mutex", MUTEX_DRIVER,
(void *)isp->iblock_cookie);
i = ddi_add_intr(dip, 0, &isp->iblock_cookie,
0, isp_intr, (caddr_t)isp);
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);
}

高レベルのハンドラが必要な場合、そのようなハンドラを提供するようにドライバをコーディングします。それ以外の場合、ドライバはその接続に失敗できる必要があります。高レベルの割り込み処理については、高レベルの割り込みの処理を参照してください。

電源管理可能なコンポーネントの作成

電源管理を使用すると、すべてのターゲットアダプタの電源レベルが 0 のときにホストバスアダプタの電源のみを切る必要がある場合、HBA ドライバはpower(9E) エントリポイントを提供するだけで済みます。電源管理を参照してください。HBA ドライバは、デバイスが実装するコンポーネントについて記述するためのpm-components(9P) プロパティーも作成する必要があります。

これ以上は何も必要ありません。コンポーネントがデフォルトでアイドル状態になり、電源管理フレームワークのデフォルトの依存性処理によって、ターゲットアダプタの電源が入ると、ホストバスアダプタの電源も確実に入るようになるためです。自動電源管理が自動的に使用可能になる場合、この処理では、すべてのターゲットアダプタの電源が切れると、ホストバスアダプタの電源も切れます。

接続ステータスのレポート

最後に、HBA ドライバは、デバイスのこのインスタンスが接続され、成功を返すことをレポートします。

ddi_report_dev(dip);
    return (DDI_SUCCESS);

detach() エントリポイント (SCSI HBA ドライバ)

HBA ドライバは、scsi_hba_detach(9F) の呼び出しなど、標準的な切り離し操作を実行します。