发送 IOCTL_ACPI_ENUM_CHILDREN 请求
驱动程序通常使用含两个 IOCTL_ACPI_ENUM_CHILDREN 请求的序列,来枚举将请求发送到的设备命名空间中的相关对象。 驱动程序会发送第一个请求,以获取驱动程序分配的输出缓冲区的大小,该缓冲区是包含对象的路径和名称所必需的。 驱动程序会发送第二个请求,以返回驱动程序分配的输出缓冲区中对象的路径和名称。
以下代码示例演示如何发送含两个同步 IOCTL_ACPI_ENUM_CHILDREN 请求的序列,以递归枚举将请求发送到的父设备的所有子设备。 该代码会执行以下操作序列来处理第一个请求:
设置第一个请求的输入缓冲区。 输入缓冲区是一个 ACPI_ENUM_CHILDREN_INPUT_BUFFER 结构,其中将 Signature 设置为 ENUM_CHILDREN_INPUT_BUFFER_SIGNATURE,并将 Flags 设置为 ENUM_CHILDREN_MULTILEVEL。
设置第一个请求的输出缓冲区。 会将输出缓冲区设置为 ACPI_ENUM_CHILDREN_OUTPUT_BUFFER 结构。 此输出缓冲区仅包含一个 ACPI_ENUM_CHILD 结构,该结构不够大,无法返回设备的名称。
调用调用方提供的 SendDownStreamIrp 函数,以同步方式将第一个请求发送到父设备。
检查 ACPI 驱动程序是否将返回状态设置为 STATUS_BUFFER_OVERFLOW。 如果返回了另一个状态,则表示发生了错误,并且代码将终止。
检查 ACPI 驱动程序是否将 Signature 成员设置为 ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE,并将 NumberOfChildren 设置为大于或等于 sizeof(ACPI_ENUM_CHILDREN_OUTPUT_BUFFER) 的值。 如果两者均为 true,则 NumberOfChildren 的值是包含所请求子对象名称所需的输出缓冲区的大小(以字节为单位)。
在示例代码获取输出缓冲区的所需大小后,将执行以下操作序列来处理第二个请求,该请求将返回所请求子对象的路径和名称:
分配所需大小(以字节为单位)的输出缓冲区。
调用驱动程序提供的 SendDownStreamIrp 函数,以同步方式将第二个请求发送到父设备。
检查 ACPI 驱动程序是否将 Signature 成员设置为 ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE,将 NumberOfChildren 设置为一个或多个(指示返回了至少一个对象的路径和名称),并将 IO_STATUS_BLOCK 的 Information 成员设置为输出缓冲区的所分配大小。
处理输出缓冲区中的子对象名称数组。
#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.
}