Отправка запроса IOCTL_ACPI_ENUM_CHILDREN

Драйвер обычно использует последовательность двух IOCTL_ACPI_ENUM_CHILDREN запросов для перечисления объектов, интересующих пространство имен устройства, на которое отправляется запрос. Драйвер отправляет первый запрос, чтобы получить размер выделенного драйвером выходного буфера, который требуется для хранения пути и имени объектов. Драйвер отправляет второй запрос, чтобы вернуть путь и имя объектов в выделенном драйвером выходном буфере.

В следующем примере кода показано, как отправить последовательность двух синхронных IOCTL_ACPI_ENUM_CHILDREN запросов для рекурсивного перечисления всех дочерних устройств родительского устройства, на которые отправляются запросы. Код выполняет следующую последовательность операций для обработки первого запроса:

  1. Задает входной буфер для первого запроса. Входной буфер представляет собой структуру ACPI_ENUM_CHILDREN_INPUT_BUFFER с сигнатуры для ENUM_CHILDREN_INPUT_BUFFER_SIGNATURE и флагов задано значение ENUM_CHILDREN_MULTILEVEL.

  2. Задает выходной буфер для первого запроса. Выходной буфер установлен как структура ACPI_ENUM_CHILDREN_OUTPUT_BUFFER. Этот выходной буфер содержит только одну ACPI_ENUM_CHILD структуру, которая недостаточно велика, чтобы вернуть имя устройства.

  3. Вызывает функцию SendDownStreamIrp, предоставляемую вызывающим абонентом, для синхронной отправки первого запроса на родительское устройство.

  4. Проверяет, если драйвер ACPI установил состояние возврата STATUS_BUFFER_OVERFLOW. Если было возвращено другое состояние, это означает, что произошла ошибка, и код завершается.

  5. Проверяет, что драйвер ACPI задал элементу подписи значение ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE и задает NumberOfChildren на значение больше или равно чем размер(ACPI_ENUM_CHILDREN_OUTPUT_BUFFER). Если оба значения имеют значение true, значение NumberOfChildren — это размер в байтах выходного буфера, который требуется для хранения запрошенных дочерних имен объектов.

После получения требуемого размера выходного буфера код выполняет следующую последовательность операций для обработки второго запроса, который возвращает путь и имя запрошенных дочерних объектов:

  1. Выделяет выходной буфер требуемого размера в байтах.

  2. Вызывает функцию, предоставляемую драйвером, SendDownStreamIrp, чтобы отправить второй запрос синхронно на родительское устройство.

  3. Проверяет, установил ли драйвер ACPI член подписи в значение ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE, устанавливает для NumberOfChildren значение один или несколько (что указывает на то, что путь и имя хотя бы одного объекта было возвращено), и задает член Information в структуре IO_STATUS_BLOCK на выделенный размер выходного буфера.

  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.
 
 
 
    }