发送 IOCTL_ACPI_ENUM_CHILDREN 请求

驱动程序通常使用含两个 IOCTL_ACPI_ENUM_CHILDREN 请求的序列,来枚举将请求发送到的设备命名空间中的相关对象。 驱动程序会发送第一个请求,以获取驱动程序分配的输出缓冲区的大小,该缓冲区是包含对象的路径和名称所必需的。 驱动程序会发送第二个请求,以返回驱动程序分配的输出缓冲区中对象的路径和名称。

以下代码示例演示如何发送含两个同步 IOCTL_ACPI_ENUM_CHILDREN 请求的序列,以递归枚举将请求发送到的父设备的所有子设备。 该代码会执行以下操作序列来处理第一个请求:

  1. 设置第一个请求的输入缓冲区。 输入缓冲区是一个 ACPI_ENUM_CHILDREN_INPUT_BUFFER 结构,其中将 Signature 设置为 ENUM_CHILDREN_INPUT_BUFFER_SIGNATURE,并将 Flags 设置为 ENUM_CHILDREN_MULTILEVEL。

  2. 设置第一个请求的输出缓冲区。 会将输出缓冲区设置为 ACPI_ENUM_CHILDREN_OUTPUT_BUFFER 结构。 此输出缓冲区仅包含一个 ACPI_ENUM_CHILD 结构,该结构不够大,无法返回设备的名称。

  3. 调用调用方提供的 SendDownStreamIrp 函数,以同步方式将第一个请求发送到父设备。

  4. 检查 ACPI 驱动程序是否将返回状态设置为 STATUS_BUFFER_OVERFLOW。 如果返回了另一个状态,则表示发生了错误,并且代码将终止。

  5. 检查 ACPI 驱动程序是否将 Signature 成员设置为 ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE,并将 NumberOfChildren 设置为大于或等于 sizeof(ACPI_ENUM_CHILDREN_OUTPUT_BUFFER) 的值。 如果两者均为 true,则 NumberOfChildren 的值是包含所请求子对象名称所需的输出缓冲区的大小(以字节为单位)。

在示例代码获取输出缓冲区的所需大小后,将执行以下操作序列来处理第二个请求,该请求将返回所请求子对象的路径和名称:

  1. 分配所需大小(以字节为单位)的输出缓冲区。

  2. 调用驱动程序提供的 SendDownStreamIrp 函数,以同步方式将第二个请求发送到父设备。

  3. 检查 ACPI 驱动程序是否将 Signature 成员设置为 ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE,将 NumberOfChildren 设置为一个或多个(指示返回了至少一个对象的路径和名称),并将 IO_STATUS_BLOCKInformation 成员设置为输出缓冲区的所分配大小。

  4. 处理输出缓冲区中的子对象名称数组。

#define MY_TAG 'gTyM'   // Pool tag for memory allocation

 ACPI_ENUM_CHILDREN_INPUT_BUFFER  inputBuffer;
    ACPI_ENUM_CHILDREN_OUTPUT_BUFFER outputSizeBuffer = { 0 };
    ACPI_ENUM_CHILDREN_OUTPUT_BUFFER outputBuffer = { 0 };
    ULONG                            bufferSize;
 PACPI_ENUM_CHILD                 childObject = NULL;
 ULONG                            index;

    NTSTATUS                         status;

    ASSERT( ReturnStatus != NULL );
    *ReturnStatus = 0x0;

    // Fill in the input data
    inputBuffer.Signature = ACPI_ENUM_CHILDREN_INPUT_BUFFER_SIGNATURE;
    inputBuffer.Flags = ENUM_CHILDREN_MULTILEVEL;

    // Send the request along
    status = SendDownStreamIrp(
       Pdo,
 IOCTL_ACPI_ENUM_CHILDREN,
       &inputBuffer,
       sizeof(inputBuffer),
       &outputSizeBuffer,
       sizeof(outputSizeBuffer)
       );

 if (Status != STATUS_BUFFER_OVERFLOW) {
        // There should be at least one child device (that is the device itself)
        // Return error return status
    }

    // Verify the data
    // NOTE: The NumberOfChildren returned by ACPI actually contains the required size
 // when the status returned is STATUS_BUFFER_OVERFLOW 

    if ((outputSizeBuffer.Signature != ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE) ||
       (outputSizeBuffer.NumberOfChildren < sizeof(ACPI_ENUM_CHILDREN_OUTPUT_BUFFER)))
    {
        return STATUS_ACPI_INVALID_DATA;
    }

    //
    // Allocate a buffer to hold all the child devices
    //
    bufferSize = outputSizeBuffer.NumberOfChildren;
    outputBuffer = (PACPI_ENUM_CHILDREN_OUTPUT_BUFFER)
 ExAllocatePoolWithTag(PagedPool, bufferSize, MY_TAG);

    if (outputBuffer == NULL){
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(outputBuffer, bufferSize);

    // Allocate a new IRP with the new output buffer
    // Send another request together with the new output buffer
    status = SendDownStreamIrp(
       Pdo,
 IOCTL_ACPI_ENUM_CHILDREN,
       &inputBuffer,
       sizeof(inputBuffer),
       &outputBuffer,
       bufferSize
       );

    // Verify the data
    if ((outputBuffer->Signature != ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE) ||
        (outputBuffer->NumberOfChildren == 0) ||
        (IoStatusBlock.Information != bufferSize)) {
        return STATUS_ACPI_INVALID_DATA;
    }

    // Skip the first child device because ACPI returns the device itself 
 // as the first child device
    childObject = &(outputBuffer->Children[0]);

    for (index = 1; index < outputBuffer->NumberOfChildren; ++index) {

        // Proceed to the next ACPI child device. 
        childObject = ACPI_ENUM_CHILD_NEXT(childObject);

        //  Process each child device.
 
 
 
    }