Verwenden der SPB_TRANSFER_LIST-Struktur für benutzerdefinierte IOCTLs

Wenn ihr SPB-Controllertreiber (Simple Peripheral Bus) mindestens eine benutzerdefinierte I/O-Steuerungsanforderung (IOCTL) unterstützt, verwenden Sie die SPB_TRANSFER_LIST-Struktur , um die Lese- und Schreibpuffer in diesen Anforderungen zu beschreiben. Diese Struktur bietet eine einheitliche Möglichkeit, die Puffer in einer Anforderung zu beschreiben, und vermeidet den Pufferkopieraufwand, der mit METHOD_BUFFERED E/A-Vorgängen verbunden ist.

Wenn Ihre benutzerdefinierten IOCTL-Anforderungen die SPB_TRANSFER_LIST-Struktur verwenden, muss Ihr SPB-Controllertreiber die SpbRequestCaptureIoOtherTransferList-Methode aufrufen, um diese Puffer im Prozesskontext des Anforderungserstellers zu erfassen. Ihr Treiber kann die SpbRequestGetTransferParameters-Methode aufrufen, um auf diese Puffer zuzugreifen.

Die IOCTL_SPB_FULL_DUPLEX - und IOCTL_SPB_EXECUTE_SEQUENCE-Anforderungen , die als Teil der SPB-E/A-Anforderungsschnittstelle definiert sind, verwenden die SPB_TRANSFER_LIST-Struktur , um ihre Lese- und Schreibpuffer zu beschreiben. Die SPB_TRANSFER_LIST-Struktur für eine IOCTL_SPB_FULL_DUPLEX Anforderung beschreibt sowohl den Schreibpuffer als auch den Lesepuffer (in dieser Reihenfolge) in der Anforderung. Die SPB_TRANSFER_LIST-Struktur für eine IOCTL_SPB_EXECUTE_SEQUENCE Anforderung kann eine beliebige Sequenz von Lese- und Schreibpuffern beschreiben.

Auf ähnliche Weise können Sie Ihre benutzerdefinierten IOCTLs definieren, damit ihre SPB_TRANSFER_LIST Strukturen eine Kombination von Lese- und Schreibpuffern verwenden können. Außerdem können Sie eine beliebige Reihenfolge der Puffer in der Liste angeben, die möglicherweise erforderlich ist.

Der kmDF-Treiber (Kernel-Mode Driver Foundation) für ein SPB-Peripheriegerät ruft eine Methode wie WdfIoTargetSendIoctlSynchronously auf, um eine IOCTL-Anforderung an einen SPB-Controller zu senden. Diese Methode verfügt über die Parameter InputBuffer und OutputBuffer . Treiber für einige Gerätetypen können diese beiden Parameter verwenden, um auf den Schreib- bzw. Lesepuffer für eine IOCTL-Anforderung zu verweisen. Um jedoch eine IOCTL-Anforderung an einen SPB-Controller zu senden, legt der SPB-Peripheriegerätetreiber den InputBuffer-Parameter so fest, dass er auf einen Speicherdeskriptor verweist, der auf eine SPB_TRANSFER_LIST-Struktur verweist. Diese Struktur beschreibt alle Lese- oder Schreibpuffer, die für den E/A-Steuerungsvorgang erforderlich sind. Der Treiber legt den OutputBuffer-Parameter auf NULL fest.

In ähnlicher Weise ruft der UMDF-Treiber (User-Mode Driver Foundation) für ein SPB-Peripheriegerät eine Methode wie IWDFIoTarget::FormatRequestForIoctl auf, um eine E/A-Anforderung für einen E/A-Steuerungsvorgang zu formatieren. Diese Methode verfügt über die Parameter pInputMemory und pOutputMemory . Treiber für einige Gerätetypen können diese beiden Parameter verwenden, um auf den Schreib- und Lesepuffer für eine IOCTL-Anforderung zu verweisen. Um jedoch eine IOCTL-Anforderung an einen SPB-Controller zu senden, legt der SPB-Peripheriegerätetreiber den pInputMemory-Parameter so fest, dass er auf ein Speicherobjekt verweist, das eine SPB_TRANSFER_LIST-Struktur enthält. Diese Struktur beschreibt alle Lese- oder Schreibpuffer, die für den E/A-Steuerungsvorgang erforderlich sind. Der Treiber legt den pOutputMemory-Parameter auf NULL fest.

Parameterüberprüfung und Puffererfassung

Wenn die SPB-Frameworkerweiterung (SpbCx) eine IOCTL_SPB_EXECUTE_SEQUENCE-Anforderung empfängt, übergibt SpbCx diese Anforderung an den SPB-Controllertreiber, indem die EvtSpbControllerIoSequence-Funktion des Treibers aufgerufen wird. Vor diesem Aufruf überprüft SpbCx die SPB_TRANSFER_LIST-Struktur , die die Puffer in der Anforderung beschreibt. SpbCx erfasst diese Puffer im Prozesskontext des Anforderungserstellers. (Auf Puffer im Benutzermodusspeicher kann nur in dem Prozess zugegriffen werden, in dem der Arbeitsspeicher zugeordnet ist.) Darüber hinaus überprüft SpbCx, ob die Parameterwerte in der Anforderung gültig sind.

Wenn SpbCx eine IOCTL_SPB_FULL_DUPLEX-Anforderung oder eine benutzerdefinierte IOCTL-Anforderung empfängt, übergibt SpbCx diese Anforderung an den SPB-Controllertreiber, indem die EvtSpbControllerIoOther-Rückruffunktion des Treibers aufgerufen wird. Vor diesem Aufruf führt SpbCx keine Überprüfung der Parameterwerte in der Anforderung durch und erfasst nicht die Puffer der Anforderung im Kontext des Absenders. Die Parameterüberprüfung und Puffererfassung für diese Anforderungen liegt in der Verantwortung des SPB-Controllertreibers.

Wenn ein SPB-Controllertreiber die IOCTL_SPB_FULL_DUPLEX-Anforderung oder eine benutzerdefinierte IOCTL-Anforderung unterstützt, die die SPB_TRANSFER_LIST-Struktur für seine Puffer verwendet, muss der Treiber eine EvtIoInCallerContext-Rückruffunktion implementieren. Der Treiber stellt einen Zeiger auf diese Funktion als Eingabeparameter im Aufruf der SpbControllerSetIoOtherCallback-Methode bereit, die die EvtSpbControllerIoOther-Rückruffunktion des Treibers registriert. Wenn SpbCx eine IOCTL_SPB_FULL_DUPLEX-Anforderung oder eine benutzerdefinierte IOCTL-Anforderung empfängt, ruft SpbCx die EvtIoInCallerContext-Funktion des Treibers im Kontext des Absenders auf. Wenn die IOCTL-Anforderung die SPB_TRANSFER_LIST-Struktur verwendet, ruft die EvtIoInCallerContext-Funktion die SpbRequestCaptureIoOtherTransferList-Methode auf, um die Puffer in der Anforderung zu erfassen. Die EvtIoInCallerContext-Funktion kann auch eine vorläufige Verarbeitung der Anforderung durchführen.

Das folgende Codebeispiel zeigt eine EvtIoInCallerContext-Funktion , die von einem SPB-Controllertreiber implementiert wird.

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);
    }
}

Im vorherigen Codebeispiel überprüft die switch Anweisung, ob die Anforderung eine IOCTL enthält, die vom SPB-Controllertreiber erkannt wird. (Aus Gründen der Kürze wird der Text der switch Anweisung nicht angezeigt.) Als Nächstes erfasst der Aufruf der SpbRequestCaptureIoOtherTransferList-Methode die Puffer in der Anforderung. Wenn dieser Aufruf erfolgreich ist, wird die Anforderung der E/A-Warteschlange des SPB-Controllers hinzugefügt. Andernfalls wird die Anforderung mit einem Fehlerstatuscode abgeschlossen.

Ein Codebeispiel, das die Parameterüberprüfung durch eine EvtSpbControllerIoOther-Funktion zeigt, finden Sie unter Behandeln IOCTL_SPB_FULL_DUPLEX Anforderungen.