使用擴充 I/O 的安全數位要求
安全數位 (SD) 讀取或寫入多個字節的數據要求,必須在 SD 規格) 中使用稱為 CMD53 的擴充 I/O (命令) 。 擴充 I/O 命令會指示總線驅動程式透過 SD 記憶卡的 DAT 線路傳輸數據。 數據傳輸的特性取決於 SD 控制器的功能。 例如,某些控制器只允許可程式化 I/O (PIO) ;其他允許直接記憶體存取 (DMA) 。 為了達到不同 SD 控制器類型的最大相容性,設備驅動器應該使用描述數據緩衝區的 MDL 指標載入要求封包。 除非較高層中的驅動程式建構 MDL,否則設備驅動器必須建構自己的 MDL,並將它傳遞至設備驅動器。
下列程式代碼範例示範驅動程式如何使用 MDL 描述的數據緩衝區來執行擴充 I/O 要求。 此程式代碼範例的格式類似於 使用直接 I/O 的安全數位要求中所述的直接 I/O 程式代碼範例,因此在研究擴充 I/O 程式代碼範例之前,研究直接 I/O 程式代碼範例可能會很有説明。
這兩個範例之間的原則差異在於擴充 I/O 程式代碼範例說明如何搭配 SD 要求使用 MDL。 描述元和要求封包定義的方式也有一些微差異,適用於直接和擴充的 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);
此程式代碼範例包含下列步驟:
初始化描述元
傳送裝置命令要求的第一個步驟是定義 SD 命令描述元 ,SDCMD_DESCRIPTOR。 程式代碼範例中的描述項會使用下列元素定義擴充 I/O 寫入作業:
元素 Description 描述項所定義的作業會執行擴充的 I/O 寫入,因此命令程式代碼的值會SDCMD_IO_RW_DIRECT。
擴充 I/O 寫入作業屬於標準命令集 (命令代碼 0 到 63) ,因此指派給描述元成員的值會SDCC_STANDARD。
寫入作業需要從主機傳輸到裝置,因此指派給描述項成員的值會SDTD_WRITE。
擴充 I/O 寫入作業的描述項必須包含傳輸類型。 程式代碼範例會指定單一區塊寫入SDTT_SINGLE_BLOCK,表示主機會將一個數據區塊寫入裝置。 驅動程式已透過先前的 SET_BLOCKLEN 命令建立區塊的大小 (本程式代碼範例中未說明) 。 如需SET_BLOCKLEN命令和SDTT_SINGLE_BLOCK傳輸類型的說明,請參閱多媒體卡片關聯 (MMCA) 技術委員會所發佈的 多媒體卡片 規格。
描述項會指定SDRT_1的回應類型,它會指定命令的標準 R1 回應,並包含狀態數據。 如需 R1 回應的說明,請參閱 多媒體卡片關聯 規格。
設定 MDL
呼叫 IoAllocateMdl 以配置 MDL,並將指標傳入從非分頁集區配置的緩衝區。 接下來, MmBuildMdlForNonPagedPool 例程會採用新配置的 MDL,以指定非分頁集區中的虛擬記憶體緩衝區,並更新它來描述基礎實體頁面。 MmBuildMdlForNonPagedPool 的呼叫端必須在 IRQL <= DISPATCH_LEVEL執行。
完成下列步驟來初始化要求封包:
定義要求函式:
建立 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要求的裝置時,並將它儲存在裝置擴充功能中。
提交要求
初始化描述元和要求封包之後,此範例會使用同步要求例程 SdBusSubmitRequest 來提交要求。 它會傳入要求封包,以及系統在開啟 SD 介面時提供給驅動程式的介面內容資訊。 因為這是同步要求,所以驅動程式必須在 IRQL 執行小於 DISPATCH_LEVEL。
命令的結果
由於程式代碼範例使用直接 I/O 命令,所以 SD 要求封包中 ResponseData 欄位以外的數據緩衝區除外。
程式代碼範例會從非分頁集區配置數據傳輸緩衝區。 驅動程式可以使用 PagedPool 進行數據傳輸緩衝區,前提是它會鎖定頁面。 不過,當執行SDRF_GET_PROPERTY和SDRF_SET_PROPERTY要求時,驅動程序必須 一律 從非分頁集區配置數據傳輸緩衝區。 驅動程式也必須從非分頁集區配置 SD 要求封包,因為隨附 SD 要求的 IRP 完成例程可能會在延遲的程式調用中執行, (DPC) 。
針對所有類型的要求,當緩衝區很小且驅動程式會短暫保留緩衝區時,從非分頁集區配置緩衝區有效能優點。