Usando a estrutura SPB_TRANSFER_LIST para IOCTLs personalizados

Se o driver do controlador SPB (barramento periférico simples) der suporte a uma ou mais solicitações de ioctl (controle de E/S personalizado), use a estrutura SPB_TRANSFER_LIST para descrever os buffers de leitura e gravação nessas solicitações. Essa estrutura fornece uma maneira uniforme de descrever os buffers em uma solicitação e evita a sobrecarga de cópia de buffer associada a operações de E/S METHOD_BUFFERED.

Se suas solicitações IOCTL personalizadas usarem a estrutura SPB_TRANSFER_LIST , o driver do controlador SPB deverá chamar o método SpbRequestCaptureIoOtherTransferList para capturar esses buffers no contexto de processo do originador da solicitação. Seu driver pode chamar o método SpbRequestGetTransferParameters para acessar esses buffers.

As solicitações IOCTL_SPB_FULL_DUPLEX e IOCTL_SPB_EXECUTE_SEQUENCE , definidas como parte da interface de solicitação de E/S do SPB, usam a estrutura SPB_TRANSFER_LIST para descrever os buffers de leitura e gravação. A estrutura SPB_TRANSFER_LIST para uma solicitação de IOCTL_SPB_FULL_DUPLEX descreve o buffer de gravação e o buffer de leitura (nessa ordem) na solicitação. A estrutura SPB_TRANSFER_LIST para uma solicitação de IOCTL_SPB_EXECUTE_SEQUENCE pode descrever uma sequência arbitrária de buffers de leitura e gravação.

Da mesma forma, você pode definir suas IOCTLs personalizadas para exigir que suas estruturas de SPB_TRANSFER_LIST usem alguma combinação de buffers de leitura e gravação e especificar qualquer ordenação dos buffers na lista que possa ser necessária.

O driver do KMDF (Kernel-Mode Driver Foundation) para um dispositivo periférico SPB chama um método como WdfIoTargetSendIoctlSynchronously para enviar uma solicitação IOCTL a um controlador SPB. Esse método tem parâmetros InputBuffer e OutputBuffer . Drivers para alguns tipos de dispositivos podem usar esses dois parâmetros para apontar para o buffer de gravação e o buffer de leitura, respectivamente, para uma solicitação IOCTL. No entanto, para enviar uma solicitação IOCTL para um controlador SPB, o driver de dispositivo periférico SPB define o parâmetro InputBuffer para apontar para um descritor de memória que aponta para uma estrutura de SPB_TRANSFER_LIST . Essa estrutura descreve os buffers de leitura ou gravação necessários para a operação de controle de E/S. O driver define o parâmetro OutputBuffer como NULL.

Da mesma forma, o driver User-Mode Driver Foundation (UMDF) para um dispositivo periférico SPB chama um método como IWDFIoTarget::FormatRequestForIoctl para formatar uma solicitação de E/S para uma operação de controle de E/S. Esse método tem parâmetros pInputMemory e pOutputMemory . Drivers para alguns tipos de dispositivos podem usar esses dois parâmetros para apontar para o buffer de gravação e o buffer de leitura para uma solicitação IOCTL. No entanto, para enviar uma solicitação IOCTL para um controlador SPB, o driver de dispositivo periférico SPB define o parâmetro pInputMemory para apontar para um objeto de memória que contém uma estrutura SPB_TRANSFER_LIST . Essa estrutura descreve os buffers de leitura ou gravação necessários para a operação de controle de E/S. O driver define o parâmetro pOutputMemory como NULL.

Verificação de parâmetros e captura de buffer

Quando a extensão da estrutura SPB (SpbCx) recebe uma solicitação IOCTL_SPB_EXECUTE_SEQUENCE , o SpbCx passa essa solicitação para o driver do controlador SPB chamando a função EvtSpbControllerIoSequence do driver. Antes dessa chamada, o SpbCx inspeciona a estrutura SPB_TRANSFER_LIST que descreve os buffers na solicitação. O SpbCx captura esses buffers no contexto de processo do originador da solicitação. (Buffers na memória do modo de usuário só podem ser acessados no processo no qual a memória é alocada.) Além disso, o SpbCx verifica se os valores de parâmetro na solicitação são válidos.

Quando o SpbCx recebe uma solicitação IOCTL_SPB_FULL_DUPLEX ou uma solicitação IOCTL personalizada, o SpbCx passa essa solicitação para o driver do controlador SPB chamando a função de retorno de chamada EvtSpbControllerIoOther do driver. Antes de fazer essa chamada, o SpbCx não faz nenhuma verificação de validação dos valores de parâmetro na solicitação e não captura os buffers da solicitação no contexto do originador. A verificação de parâmetros e a captura de buffer para essas solicitações são de responsabilidade do driver do controlador SPB.

Se um driver de controlador SPB der suporte à solicitação IOCTL_SPB_FULL_DUPLEX ou oferecer suporte a qualquer solicitação IOCTL personalizada que use a estrutura SPB_TRANSFER_LIST para seus buffers, o driver deverá implementar uma função de retorno de chamada EvtIoInCallerContext . O driver fornece um ponteiro para essa função como um parâmetro de entrada na chamada para o método SpbControllerSetIoOtherCallback que registra a função de retorno de chamada EvtSpbControllerIoOther do driver. Quando o SpbCx recebe uma solicitação IOCTL_SPB_FULL_DUPLEX ou uma solicitação IOCTL personalizada, o SpbCx chama a função EvtIoInCallerContext do driver no contexto do originador. Se a solicitação IOCTL usar a estrutura SPB_TRANSFER_LIST , a função EvtIoInCallerContext chamará o método SpbRequestCaptureIoOtherTransferList para capturar os buffers na solicitação. A função EvtIoInCallerContext também pode executar algum processamento preliminar da solicitação.

O exemplo de código a seguir mostra uma função EvtIoInCallerContext implementada por um driver de controlador SPB.

VOID
EvtIoInCallerContext(
    _In_  WDFDEVICE   SpbController,
    _In_  WDFREQUEST  FxRequest
    ) 
{
    NTSTATUS status = STATUS_SUCCESS;
    WDF_REQUEST_PARAMETERS fxParams;
  
    WDF_REQUEST_PARAMETERS_INIT(&fxParams);
    WdfRequestGetParameters(FxRequest, &fxParams);

    if ((fxParams.Type != WdfRequestTypeDeviceControl) &&
        (fxParams.Type != WdfRequestTypeDeviceControlInternal))
    {
        status = STATUS_NOT_SUPPORTED;
        goto exit;
    }

    //
    // The driver should check for custom IOCTLs that it handles.
    // If the IOCTL is not recognized, complete the request with a
    // status of STATUS_NOT_SUPPORTED.
    //

    switch (fxParams.Parameters.DeviceIoControl.IoControlCode)
    {
        ...

    default:
        status = STATUS_NOT_SUPPORTED;
        goto exit;
    }

    //
    // The IOCTL is recognized. Capture the buffers in the request.
    //

    status = SpbRequestCaptureIoOtherTransferList((SPBREQUEST)FxRequest);

    //
    // If the capture fails, the driver must complete the request instead
    // of placing it in the SPB controller's request queue.
    //

    if (!NT_SUCCESS(status))
    {
        goto exit;
    }

    status = WdfDeviceEnqueueRequest(SpbController, FxRequest);

    if (!NT_SUCCESS(status))
    {
        goto exit;
    }

exit:

    if (!NT_SUCCESS(status))
    {
        WdfRequestComplete(FxRequest, status);
    }
}

No exemplo de código anterior, a switch instrução verifica se a solicitação contém um IOCTL que o driver do controlador SPB reconhece. (Para resumir, o corpo da instrução switch não é mostrado.) Em seguida, a chamada para o método SpbRequestCaptureIoOtherTransferList captura os buffers na solicitação. Se essa chamada for bem-sucedida, a solicitação será adicionada à fila de E/S do controlador SPB. Caso contrário, a solicitação será concluída com um código de status de erro.

Para obter um exemplo de código que mostra a verificação de parâmetro por uma função EvtSpbControllerIoOther , consulte Manipulando solicitações de IOCTL_SPB_FULL_DUPLEX.