| ナビゲーションリンクをスキップ | |
| 印刷ビューの終了 | |
|   | デバイスドライバの記述 Oracle Solaris 10 1/13 Information Library (日本語) | 
パート I Oracle Solaris プラットフォーム用デバイスドライバの設計
2. Oracle Solaris カーネルとデバイスツリー
Sun Common SCSI Architecture の概要
probe() エントリポイント (SCSI ターゲットドライバ)
attach() エントリポイント (SCSI ターゲットドライバ)
scsi_alloc_consistent_buf() 関数
22. ドライバのコンパイル、ロード、パッケージ化、およびテスト
SCSI ターゲットドライバには、標準の自動構成ルーチン _init(9E)、_fini(9E)、および _info(9E) を実装する必要があります。詳細については、「ロード可能なドライバインタフェース」を参照してください。
次のルーチンも必要ですが、これらのルーチンは特定の SCSI および SCSA の処理を実行する必要があります。
SCSI ターゲットデバイスは自己識別しないため、ターゲットドライバが probe(9E) ルーチンを備えている必要があります。このルーチンは、予期される種類のデバイスが存在していて、応答しているかどうかを判定する必要があります。
probe(9E) ルーチンの一般的な構造とリターンコードは、ほかのデバイスドライバの構造およびリターンコードと同じです。SCSI ターゲットドライバは、probe(9E) エントリポイントで scsi_probe(9F) ルーチンを使用する必要があります。scsi_probe(9F) はデバイスに SCSI 照会コマンドを送信し、結果を示すコードを返します。SCSI 照会コマンドが成功すると、scsi_probe(9F) は scsi_inquiry(9S) 構造体を割り当てて、構造体にデバイスの照会データを格納します。scsi_probe(9F) から復帰すると、scsi_device(9S) 構造体の sd_inq フィールドが、この scsi_inquiry(9S) 構造体を指します。
probe(9E) はステートレスである必要があるため、ターゲットドライバは、scsi_probe(9F) が失敗した場合でも、 probe(9E) が復帰する前に scsi_unprobe(9F) を呼び出す必要があります。
例 17-1 に、一般的な probe(9E) ルーチンを示します。この例のルーチンは、dev_info 構造体の非公開フィールドから scsi_device(9S) 構造体を取得しています。また、メッセージ内に出力するためのデバイスの SCSI ターゲットと論理ユニット番号を取得しています。probe(9E) ルーチンは次に scsi_probe(9F) を呼び出して、予期されるデバイス (この場合はプリンタ) が存在することを確認しています。
成功すると、scsi_probe(9F) は scsi_inquiry(9S) 構造体内にあるデバイスの SCSI 照会データを、scsi_device(9S) 構造体の sd_inq フィールドに追加します。ドライバはその後、デバイスの種類がプリンタであるかどうかを判定できます。この情報は inq_dtype フィールドで報告されます。デバイスがプリンタである場合、scsi_dname(9F) を使用してデバイスの種類を文字列に変換し、scsi_log(9F) でその種類が報告されます。
例 17-1 SCSI ターゲットドライバの probe (9E) ルーチン
static int
xxprobe(dev_info_t *dip)
{
    struct scsi_device *sdp;
    int rval, target, lun;
    /*
     * Get a pointer to the scsi_device(9S) structure
     */
    sdp = (struct scsi_device *)ddi_get_driver_private(dip);
    target = sdp->sd_address.a_target;
    lun = sdp->sd_address.a_lun;
    /*
     * Call scsi_probe(9F) to send the Inquiry command. It will
     * fill in the sd_inq field of the scsi_device structure.
     */
    switch (scsi_probe(sdp, NULL_FUNC)) {
    case SCSIPROBE_FAILURE:
    case SCSIPROBE_NORESP:
    case SCSIPROBE_NOMEM:
       /*
        * In these cases, device might be powered off,
        * in which case we might be able to successfully
        * probe it at some future time - referred to
        * as `deferred attach'.
        */
        rval = DDI_PROBE_PARTIAL;
        break;
    case SCSIPROBE_NONCCS:
    default:
        /*
         * Device isn't of the type we can deal with,
         * and/or it will never be usable.
         */
        rval = DDI_PROBE_FAILURE;
        break;
    case SCSIPROBE_EXISTS:
        /*
         * There is a device at the target/lun address. Check
         * inq_dtype to make sure that it is the right device
         * type. See scsi_inquiry(9S)for possible device types.
         */
        switch (sdp->sd_inq->inq_dtype) {
        case DTYPE_PRINTER:
        scsi_log(sdp, "xx", SCSI_DEBUG,
           "found %s device at target%d, lun%d\n",
            scsi_dname((int)sdp->sd_inq->inq_dtype),
            target, lun);
        rval = DDI_PROBE_SUCCESS;
        break;
        case DTYPE_NOTPRESENT:
        default:
        rval = DDI_PROBE_FAILURE;
        break;     
        }    
    }
    scsi_unprobe(sdp);
    return (rval);
}
probe(9E) ルーチンをより詳細に記述すると、scsi_inquiry(9S) をチェックして、そのデバイスが特定のドライバで予期されている種類であることを確認できます。
probe(9E) ルーチンで、予期されるデバイスが存在することを確認したら、attach(9E) が呼び出されます。attach() は次のタスクを実行します。
インタンスごとのデータを割り当てて初期化する。
マイナーデバイスノード情報を作成する。
デバイスまたはシステムの一時停止後にハードウェアの状態を復元する。詳細については、「attach() エントリポイント」を参照してください。
SCSI ターゲットドライバは scsi_probe(9F) をもう一度呼び出して、デバイスの照会データを取得する必要があります。ドライバは、SCSI 要求検知パケットを作成する必要もあります。アタッチが成功した場合、attach() 関数は scsi_unprobe(9F) を呼び出してはいけません。
要求検知パケットを作成するには、scsi_alloc_consistent_buf(9F)、scsi_init_pkt(9F)、および scsi_setup_cdb(9F) の 3 つのルーチンが使用されます。scsi_alloc_consistent_buf(9F) は、変化しない DMA に適したバッファーを割り当てます。次に scsi_alloc_consistent_buf () が buf(9S) 構造体へのポインタを返します。変化しないバッファーの利点は、データを明示的に同期する必要がない点です。つまり、ターゲットドライバはコールバック後にデータにアクセスできます。検知バッファーのアドレスを使用して、デバイスの scsi_device(9S) 構造体の sd_sense 要素を初期化する必要があります。scsi_init_pkt(9F) は scsi_pkt(9S) 構造体を作成し、部分的に初期化します。scsi_setup_cdb(9F) は SCSI コマンド記述子ブロックを作成します。この場合は、SCSI 要求検知コマンドを作成することで、それを行っています。
SCSI デバイスは自己識別しないため、reg プロパティーを持っていないことに注意してください。結果として、ドライバで pm-hardware-state プロパティーを設定する必要が生じます。pm-hardware-state を設定することで、このデバイスは一時停止してから再開する必要があることをフレームワークに通知します。
次の例では、SCSI ターゲットドライバの attach() ルーチンを示します。
例 17-2 SCSI ターゲットドライバの attach (9E) ルーチン
static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
    struct xxstate         *xsp;
    struct scsi_pkt        *rqpkt = NULL;
    struct scsi_device     *sdp;
    struct buf         *bp = NULL;
    int            instance;
    instance = ddi_get_instance(dip);
    switch (cmd) {
        case DDI_ATTACH:
        break;
        case DDI_RESUME:
        /* For information, see the "Directory Memory Access (DMA)" */
        /* chapter in this book. */
        default:
        return (DDI_FAILURE);
    }
    /*
     * Allocate a state structure and initialize it.
     */
    xsp = ddi_get_soft_state(statep, instance);
    sdp = (struct scsi_device *)ddi_get_driver_private(dip);
    /*
     * Cross-link the state and scsi_device(9S) structures.
     */
    sdp->sd_private = (caddr_t)xsp;
    xsp->sdp = sdp;
    /*
     * Call scsi_probe(9F) again to get and validate inquiry data.
     * Allocate a request sense buffer. The buf(9S) structure
     * is set to NULL to tell the routine to allocate a new one.
     * The callback function is set to NULL_FUNC to tell the
     * routine to return failure immediately if no
     * resources are available.
     */
    bp = scsi_alloc_consistent_buf(&sdp->sd_address, NULL,
    SENSE_LENGTH, B_READ, NULL_FUNC, NULL);
    if (bp == NULL)
        goto failed;
    /*
     * Create a Request Sense scsi_pkt(9S) structure.
     */
    rqpkt = scsi_init_pkt(&sdp->sd_address, NULL, bp,
    CDB_GROUP0, 1, 0, PKT_CONSISTENT, NULL_FUNC, NULL);
    if (rqpkt == NULL)
        goto failed;
    /*
     * scsi_alloc_consistent_buf(9F) returned a buf(9S) structure.
     * The actual buffer address is in b_un.b_addr.
     */
    sdp->sd_sense = (struct scsi_extended_sense *)bp->b_un.b_addr;
    /*
     * Create a Group0 CDB for the Request Sense command
     */
    if (scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp,
        SCMD_REQUEST_SENSE, 0, SENSE__LENGTH, 0) == 0)
         goto failed;;
    /*
     * Fill in the rest of the scsi_pkt structure.
     * xxcallback() is the private command completion routine.
     */
    rqpkt->pkt_comp = xxcallback;
    rqpkt->pkt_time = 30; /* 30 second command timeout */
    rqpkt->pkt_flags |= FLAG_SENSING;
    xsp->rqs = rqpkt;
    xsp->rqsbuf = bp;
    /*
     * Create minor nodes, report device, and do any other initialization. */
     * Since the device does not have the 'reg' property,
     * cpr will not call its DDI_SUSPEND/DDI_RESUME entries.
     * The following code is to tell cpr that this device
     * needs to be suspended and resumed.
     */
    (void) ddi_prop_update_string(device, dip,
     "pm-hardware-state", "needs-suspend-resume");
    xsp->open = 0;
    return (DDI_SUCCESS);
failed:
    if (bp)
        scsi_free_consistent_buf(bp);
    if (rqpkt)
        scsi_destroy_pkt(rqpkt);
    sdp->sd_private = (caddr_t)NULL;
    sdp->sd_sense = NULL;
    scsi_unprobe(sdp);
    /* Free any other resources, such as the state structure. */
    return (DDI_FAILURE);
}
detach(9E) エントリポイントは、attach(9E) の逆の操作を行うものです。detach() では、attach () で割り当てられたすべてのリソースを解放する必要があります。成功した場合、detach は scsi_unprobe(9F) を呼び出す必要があります。次の例では、ターゲットドライバの detach() ルーチンを示します。
例 17-3 SCSI ターゲットドライバの detach (9E) ルーチン
static int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
    struct xxstate *xsp;
    switch (cmd) {
    case DDI_DETACH:
      /*
       * Normal detach(9E) operations, such as getting a
       * pointer to the state structure
       */
      scsi_free_consistent_buf(xsp->rqsbuf);
      scsi_destroy_pkt(xsp->rqs);
      xsp->sdp->sd_private = (caddr_t)NULL;
      xsp->sdp->sd_sense = NULL;
      scsi_unprobe(xsp->sdp);
      /*
       * Remove minor nodes.
       * Free resources, such as the state structure and properties.
       */
          return (DDI_SUCCESS);
    case DDI_SUSPEND:
      /* For information, see the "Directory Memory Access (DMA)" */
      /* chapter in this book. */
    default:
      return (DDI_FAILURE);
    }
}
SCSI ターゲットドライバの getinfo(9E) ルーチンは、ほかのドライバとほぼ同じです (DDI_INFO_DEVT2INSTANCE の場合の詳細については 「getinfo() エントリポイント」を参照してください)。ただし、getinfo() ルーチンの DDI_INFO_DEVT2DEVINFO の場合、ターゲットドライバは dev_info ノードへのポインタを返す必要があります。このポインタは、ドライバの状態構造体に保存することも、scsi_device(9S) 構造体の sd_dev フィールドから取得することもできます。次の例は、代替の SCSI ターゲットドライバの getinfo() コードフラグメントを示しています。
例 17-4 代替 SCSI ターゲットドライバの getinfo() コードフラグメント
case DDI_INFO_DEVT2DEVINFO:
    dev = (dev_t)arg;
    instance = getminor(dev);
    xsp = ddi_get_soft_state(statep, instance);
    if (xsp == NULL)
        return (DDI_FAILURE);
    *result = (void *)xsp->sdp->sd_dev;
    return (DDI_SUCCESS);