次の方法で共有


拡張 I/O を使用する安全なデジタル要求

数バイトを超えるデータの読み取りや書き込みを行う Secure Digital (SD) 要求では、拡張 I/O コマンド (SD 仕様では CMD53 と呼ばれます) を使用する必要があります。 拡張 I/O コマンドは、SD カードの DAT ライン経由でデータを送信するようにバス ドライバーに指示します。 データ転送の特性は、SD コントローラーの機能によって異なります。 たとえば、プログラミング可能な I/O (PIO) のみを許可するコントローラーがある一方、ダイレクト メモリ アクセス (DMA) を許可するものもあります。 さまざまなタイプの SD コントローラーの互換性を最大限に高めるために、デバイス ドライバーは、データ バッファーを記述する MDL へのポインターを使用して要求パケットを読み込む必要があります。 上位レイヤーのドライバーが MDL を構築してデバイス ドライバーに渡さない限り、デバイス ドライバーは独自の MDL を構築する必要があります。

次のコード例は、ドライバーが MDL によって記述されたデータ バッファーを使用して拡張 I/O 要求を実行する方法を示しています。 このコード例は、「ダイレクト I/O を使用する安全なデジタル要求」で説明しているダイレクト I/O のコード例に似ています。そのため、拡張 I/O のコード例を見る前に、ダイレクト I/O のコード例を確認すると良いかもしれません。

2 つの例の時基本的な違いは、拡張 I/O コード例では MDL を SD 要求と共に使用する方法を示していることです。 また、ダイレクト I/O と拡張 I/O では、記述子と要求パケットを定義する方法にも若干の違いがあります。

    const SDCMD_DESCRIPTOR WriteIoExtendedDesc =
    {SDCMD_IO_RW_EXTENDED, SDCC_STANDARD,
    SDTD_WRITE, SDTT_SINGLE_BLOCK, SDRT_1};
    
    // first, get an MDL to map the data. Call IoAllocateMdl to
    // allocate an MDL and pass in a pointer to a buffer  
    // allocated from the non-paged pool.
    
    mdl = IoAllocateMdl(Data, Length, FALSE, FALSE, NULL);
    
    if (mdl == NULL) {
      return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    MmBuildMdlForNonPagedPool (mdl);
    
    // next, allocate a request packet for the arguments of the command
     
    sdrp = ExAllocatePool(NonPagedPool, sizeof(SDBUS_REQUEST_PACKET));
    
    if (!sdrp) {
      IoFreeMdl(mdl);
      return STATUS_INSUFFICIENT_RESOURCES;
    }
    RtlZeroMemory(sdrp, sizeof(SDBUS_REQUEST_PACKET));
    sdrp->RequestFunction = SDRF_DEVICE_COMMAND;
    sdrp->Parameters.DeviceCommand.CmdDesc = 
    WriteIoExtendedDesc;
    
    // then, set up the argument and command descriptor
    sdIoArgument.u.AsULONG = 0;
    sdIoArgument.u.bits.Address = Offset;
    
    // retrieve function number, the driver previously initialized 
    // this value with the SdBus GetProperty call
    sdIoArgument.u.bits.Function = pDevExt->FunctionNumber;
    sdIoArgument.u.bits.WriteToDevice = 1;
    
    sdrp->Parameters.DeviceCommand.Argument = 
        sdIoArgument.u.AsULONG;
    
    sdrp->Parameters.DeviceCommand.Mdl = mdl;
    sdrp->Parameters.DeviceCommand.Length = Length;
    // finally, submit the request
    status = SdBusSubmitRequest(pDevExt->BusInterface.Context,sdrp);
    
    IoFreeMdl(mdl);
    ExFreePool(sdrp);

このコード例は次のようなステップで構成されています。

  1. 記述子の初期化

    device-command 要求を送信する最初の手順は、SD コマンド記述子 "SDCMD_DESCRIPTOR" を定義することです。 コード例にある記述子は、次の要素を使用して拡張 I/O 書き込み操作を定義します。

    要素 説明

    SD_COMMAND_CODE

    記述子によって定義された操作は拡張 I/O 書き込みを実行するため、コマンド コードの値は "SDCMD_IO_RW_DIRECT" です。

    SD_COMMAND_CLASS

    拡張 I/O 書き込み操作は標準コマンド セット (コマンド コード 0 から 63) に属しているため、記述子のこのメンバーに割り当てられる値は "SDCC_STANDARD" です。

    SD_TRANSFER_DIRECTION

    書き込み操作ではホストからデバイスへの転送が必要であるため、記述子のこのメンバーに割り当てられる値は "SDTD_WRITE" です。

    SD_TRANSFER_TYPE

    拡張 I/O 書き込み操作の記述子に転送の種類を含める必要があります。 このコード例では、単一のブロック書き込み "SDTT_SINGLE_BLOCK" を指定し、ホストが 1 ブロックのデータをデバイスに書き込むことを示します。 ドライバーは、前の SET_BLOCKLEN コマンドによってブロックのサイズを確立しています (このコード例には含まれていません)。 SET_BLOCKLEN コマンドと SDTT_SINGLE_BLOCK 転送の種類の説明については、マルチメディア・カード・アソシエーション (MMCA) 技術委員会が発行している マルチメディア カード の仕様を参照してください。

    SD_RESPONSE_TYPE

    記述子は応答の種類として "SDRT_1" を指定することで、状態データを含む、コマンドに対する標準の R1 応答を指定します。 R1 応答の説明については、マルチメディア・カード・アソシエーションの仕様を参照してください。

  2. MDL をセットアップする

    IoAllocateMdl を呼び出して MDL を割り当て、非ページ プールが割り当てたバッファーへのポインターを渡します。 次に、MmBuildMdlForNonPagedPool ルーチンは非ページ プール内の仮想メモリ バッファーを指定する、新しく割り当てられた MDL を受け取り、基になる物理ページを記述するように更新します。 MmBuildMdlForNonPagedPool の呼び出し元は、IRQL <= DISPATCH_LEVEL で実行されている必要があります。

  3. 次の手順を実行して、要求パケットを初期化します。

    • 要求関数の定義:

      このコード例では、SD 記述子を作成した後、要求パケット "SDBUS_REQUEST_PACKET" を初期化します。 要求パケットの RequestFunction メンバーは、要求にデバイス コマンド (SDRF_DEVICE_COMMAND の値) またはプロパティ操作 (SDRF_GET_PROPERTY または SDRF_SET_PROPERTY の値) が含まれているかどうかを指定します。 このコード例ではデバイス コマンドを送信しているため、RequestFunction メンバーを "SDRF_DEVICE_COMMAND" に設定します。

    • コマンド記述子を読み込みます。 このコード例では次に、新しく初期化された記述子を要求パケットの Parameters.DeviceCommand.CmdDesc メンバーに格納します。

    • 読み取り/書き込み引数を初期化する:

      要求パケットには、バス ドライバーの書き込み場所を伴う SD_RW_DIRECT_ARGUMENT 構造体が含まれています。 この構造体には、バス ドライバーの読み取り先となる I/O 空間を持つファンクション番号も格納されます。 このコード例では、デバイス拡張機能からファンクション番号を取得します。これは、ドライバーが以前に (おそらくデバイスを起動したときに) SDRF_GET_PROPERTY 要求を使用してカードからこの情報を取得し、デバイス拡張機能に格納したことを意味します。

  4. 要求の送信

    この例では、記述子と要求パケットを初期化した後、同期要求ルーチン "SdBusSubmitRequest" を使用して要求を送信します。 これは、システムが SD インターフェイスを開いたときにドライバーに提供した要求パケットとインターフェイス コンテキスト情報を渡します。 これは同期要求であるため、ドライバーが DISPATCH_LEVEL 未満の IRQL で実行されている必要があります。

  5. コマンドの結果

    このコード例ではダイレクト I/O コマンドを使用するため、SD 要求パケットの ResponseData フィールド以外にデータ バッファーはありません。

このコード例では、非ページ プールからデータ転送バッファーを割り当てます。 ページをロックダウンする場合、ドライバーはデータ転送バッファーに PagedPool を使用できます。 ただし、ドライバーは、SDRF_GET_PROPERTY と SDRF_SET_PROPERTY 要求を実行するときに、必ず非ページ プールからデータ転送バッファーを割り当てる必要があります。 また、ドライバーは、SD 要求に付属する IRP の完了ルーチンが遅延プロシージャ呼び出し (DPC) で実行される可能性があるため、非ページ プールから SD 要求パケットを割り当てる必要もあります。

どのような種類の要求でも、ドライバーが小さなバッファーを短時間保持する場合は、非ページ プールからバッファーを割り当てることでパフォーマンスが向上します。