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.