使用扩展 I/O 的安全数字请求

安全数字 (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 代码示例可能会有所帮助。

这两个示例之间的主要区别在于,扩展的 I/O 代码示例演示了如何将 MDL 与 SD 请求配合使用。 为直接和扩展 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. 初始化描述符

    发送设备命令请求的第一步是定义 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),指示主机将一个数据块写入设备。 驱动程序通过前面的 SET_BLOCKLEN 命令建立块的大小, (此代码示例) 未说明。 有关SET_BLOCKLEN命令和SDTT_SINGLE_BLOCK传输类型的说明,请参阅 多媒体卡 协会 (MMCA) 技术委员会发布的多媒体卡规范。

    SD_RESPONSE_TYPE

    描述符指定SDRT_1的响应类型,该类型指定对命令的标准 R1 响应,并包含状态数据。 有关 R1 响应的说明,请参阅 多媒体卡关联 规范。

  2. 设置 MDL

    调用 IoAllocateMdl 以分配 MDL,并将指针传递到从非分页池分配的缓冲区。 接下来, MmBuildMdlForNonPagedPool 例程采用新分配的 MDL,该 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 接口时提供给驱动程序的接口上下文信息。 由于这是同步请求,因此驱动程序在 IRQL 上运行的时间必须小于 DISPATCH_LEVEL。

  5. 命令的结果

    由于代码示例使用直接 I/O 命令,因此 SD 请求数据包中除了 ResponseData 字段之外没有数据缓冲区。

代码示例从非分页池分配数据传输缓冲区。 驱动程序可以使用 PagedPool 作为数据传输缓冲区,前提是该缓冲区锁定页面。 但是,在执行SDRF_GET_PROPERTY和SDRF_SET_PROPERTY请求时,驱动程序必须 始终 从非分页池中分配数据传输缓冲区。 驱动程序还必须从非分页池中分配 SD 请求数据包,因为 SD 请求附带的 IRP 的完成例程可能会在延迟的过程调用中运行 (DPC) 。

对于各种请求,当缓冲区较小且驱动程序短暂保留缓冲区时,从非分页池中分配缓冲区具有性能优势。