summaryrefslogtreecommitdiff
path: root/spb/SpbTestTool/sys/peripheral.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'spb/SpbTestTool/sys/peripheral.cpp')
-rw-r--r--spb/SpbTestTool/sys/peripheral.cpp1683
1 files changed, 1683 insertions, 0 deletions
diff --git a/spb/SpbTestTool/sys/peripheral.cpp b/spb/SpbTestTool/sys/peripheral.cpp
new file mode 100644
index 00000000..c6966ada
--- /dev/null
+++ b/spb/SpbTestTool/sys/peripheral.cpp
@@ -0,0 +1,1683 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ peripheral.cpp
+
+Abstract:
+
+ This module contains the function for interaction
+ with the SPB API.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "peripheral.h"
+
+#include "peripheral.tmh"
+
+NTSTATUS
+SpbPeripheralOpen(
+ _In_ PDEVICE_CONTEXT pDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine opens a handle to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ WDF_IO_TARGET_OPEN_PARAMS openParams;
+ NTSTATUS status;
+
+ //
+ // Create the device path using the connection ID.
+ //
+
+ DECLARE_UNICODE_STRING_SIZE(DevicePath, RESOURCE_HUB_PATH_SIZE);
+
+ RESOURCE_HUB_CREATE_PATH_FROM_ID(
+ &DevicePath,
+ pDevice->PeripheralId.LowPart,
+ pDevice->PeripheralId.HighPart);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Opening handle to SPB target via %wZ",
+ &DevicePath);
+
+ //
+ // Open a handle to the SPB controller.
+ //
+
+ WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
+ &openParams,
+ &DevicePath,
+ (GENERIC_READ | GENERIC_WRITE));
+
+ openParams.ShareAccess = 0;
+ openParams.CreateDisposition = FILE_OPEN;
+ openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+
+ status = WdfIoTargetOpen(
+ pDevice->SpbController,
+ &openParams);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to open SPB target - %!STATUS!",
+ status);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return status;
+}
+
+NTSTATUS
+SpbPeripheralClose(
+ _In_ PDEVICE_CONTEXT pDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine closes a handle to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Closing handle to SPB target");
+
+ WdfIoTargetClose(pDevice->SpbController);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+SpbPeripheralLock(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a lock command to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_LOCK_CONTROLLER",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for lock and send.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_LOCK_CONTROLLER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_LOCK_CONTROLLER - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralUnlock(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends an unlock command to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_UNLOCK_CONTROLLER",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for unlock and send.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_UNLOCK_CONTROLLER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_UNLOCK_CONTROLLER - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralLockConnection(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a lock connection command to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_LOCK_CONNECTION",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for lock and send.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_LOCK_CONNECTION,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_LOCK_CONNECTION - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralUnlockConnection(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends an unlock connection command to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_UNLOCK_CONNECTION",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for unlock and send.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_UNLOCK_CONNECTION,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_UNLOCK_CONNECTION - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralRead(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine reads from the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ WDFMEMORY memory = nullptr;
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for read",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for read and send.
+ //
+
+ status = WdfRequestRetrieveOutputMemory(
+ FxRequest,
+ &memory);
+
+ if (NT_SUCCESS(status))
+ {
+ status = WdfIoTargetFormatRequestForRead(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ memory,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "read - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralWrite(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine writes to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ WDFMEMORY memory = nullptr;
+ NTSTATUS status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for write",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Initialize the SPB request for write and send.
+ //
+
+ status = WdfRequestRetrieveInputMemory(
+ FxRequest,
+ &memory);
+
+ if (NT_SUCCESS(status))
+ {
+ status = WdfIoTargetFormatRequestForWrite(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ memory,
+ nullptr,
+ nullptr);
+
+ if (NT_SUCCESS(status))
+ {
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "write - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralWriteRead(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a write-read sequence to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ PVOID pInputBuffer = nullptr;
+ PVOID pOutputBuffer = nullptr;
+ size_t inputBufferLength = 0;
+ size_t outputBufferLength = 0;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ PREQUEST_CONTEXT pRequest;
+ NTSTATUS status;
+
+ pRequest = GetRequestContext(pDevice->SpbRequest);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_EXECUTE_SEQUENCE",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Get input and output buffers.
+ //
+
+ status = WdfRequestRetrieveInputBuffer(
+ FxRequest,
+ 0,
+ &pInputBuffer,
+ &inputBufferLength);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to retrieve input buffer - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ status = WdfRequestRetrieveOutputBuffer(
+ FxRequest,
+ 0,
+ &pOutputBuffer,
+ &outputBufferLength);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to retrieve output buffer - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ //
+ // Build SPB sequence.
+ //
+
+ const ULONG transfers = 2;
+
+ SPB_TRANSFER_LIST_AND_ENTRIES(transfers) seq;
+ SPB_TRANSFER_LIST_INIT(&(seq.List), transfers);
+
+ {
+ //
+ // PreFAST cannot figure out the SPB_TRANSFER_LIST_ENTRY
+ // "struct hack" size but using an index variable quiets
+ // the warning. This is a false positive from OACR.
+ //
+
+ ULONG index = 0;
+ seq.List.Transfers[index] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionToDevice,
+ 0,
+ pInputBuffer,
+ (ULONG)inputBufferLength);
+
+ seq.List.Transfers[index + 1] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionFromDevice,
+ 0,
+ pOutputBuffer,
+ (ULONG)outputBufferLength);
+ }
+
+ //
+ // Create preallocated WDFMEMORY. The IOCTL is METHOD_BUFFERED,
+ // so the memory doesn't have to persist until the request is
+ // completed.
+ //
+
+ NT_ASSERT(pDevice->InputMemory == WDF_NO_HANDLE);
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+
+ status = WdfMemoryCreatePreallocated(
+ &attributes,
+ (PVOID)&seq,
+ sizeof(seq),
+ &pDevice->InputMemory);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to create WDFMEMORY - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Built write-read sequence %p with byte length=%lu",
+ &seq,
+ (ULONG)(inputBufferLength + outputBufferLength));
+
+ //
+ // Send sequence IOCTL.
+ //
+
+ //
+ // Mark SPB request as sequence and save length.
+ // These will be used in the completion callback
+ // to complete the client request with the correct
+ // number of bytes
+ //
+
+ pRequest->IsSpbSequenceRequest = TRUE;
+ pRequest->SequenceWriteLength = (ULONG_PTR)inputBufferLength;
+
+ //
+ // Format and send the SPB sequence request.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_EXECUTE_SEQUENCE,
+ pDevice->InputMemory,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to format request - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_EXECUTE_SEQUENCE - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ goto Done;
+ }
+
+Done:
+
+ if (!NT_SUCCESS(status))
+ {
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralFullDuplex(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a full duplex transfer to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ FxRequest - the framework request object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxRequest);
+
+ PVOID pInputBuffer = nullptr;
+ PVOID pOutputBuffer = nullptr;
+ size_t inputBufferLength = 0;
+ size_t outputBufferLength = 0;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ PREQUEST_CONTEXT pRequest;
+ NTSTATUS status;
+
+ pRequest = GetRequestContext(pDevice->SpbRequest);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Formatting SPB request %p for IOCTL_SPB_FULL_DUPLEX",
+ pDevice->SpbRequest);
+
+ //
+ // Save the client request.
+ //
+
+ pDevice->ClientRequest = FxRequest;
+
+ //
+ // Get input and output buffers.
+ //
+
+ status = WdfRequestRetrieveInputBuffer(
+ FxRequest,
+ 0,
+ &pInputBuffer,
+ &inputBufferLength);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to retrieve input buffer - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ status = WdfRequestRetrieveOutputBuffer(
+ FxRequest,
+ 0,
+ &pOutputBuffer,
+ &outputBufferLength);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to retrieve output buffer - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ //
+ // Build full duplex transfer using SPB transfer list.
+ //
+
+ const ULONG transfers = 2;
+
+ SPB_TRANSFER_LIST_AND_ENTRIES(transfers) seq;
+ SPB_TRANSFER_LIST_INIT(&(seq.List), transfers);
+
+ {
+ //
+ // PreFAST cannot figure out the SPB_TRANSFER_LIST_ENTRY
+ // "struct hack" size but using an index variable quiets
+ // the warning. This is a false positive from OACR.
+ //
+
+ ULONG index = 0;
+ seq.List.Transfers[index] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionToDevice,
+ 0,
+ pInputBuffer,
+ (ULONG)inputBufferLength);
+
+ seq.List.Transfers[index + 1] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionFromDevice,
+ 0,
+ pOutputBuffer,
+ (ULONG)outputBufferLength);
+ }
+
+ //
+ // Create preallocated WDFMEMORY. The IOCTL is METHOD_BUFFERED,
+ // so the memory doesn't have to persist until the request is
+ // completed.
+ //
+
+ NT_ASSERT(pDevice->InputMemory == WDF_NO_HANDLE);
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+
+ status = WdfMemoryCreatePreallocated(
+ &attributes,
+ (PVOID)&seq,
+ sizeof(seq),
+ &pDevice->InputMemory);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to create WDFMEMORY - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Built full duplex transfer %p with byte length=%lu",
+ &seq,
+ (ULONG)(inputBufferLength + outputBufferLength));
+
+ //
+ // Send full duplex IOCTL.
+ //
+
+ //
+ // Mark SPB request as full duplex (sequence format)
+ // and save length. These will be used in the completion
+ // callback to complete the client request with the correct
+ // number of bytes
+ //
+
+ pRequest->IsSpbSequenceRequest = TRUE;
+ pRequest->SequenceWriteLength = (ULONG_PTR)inputBufferLength;
+
+ //
+ // Format and send the full duplex request.
+ //
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ pDevice->SpbController,
+ pDevice->SpbRequest,
+ IOCTL_SPB_FULL_DUPLEX,
+ pDevice->InputMemory,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to format request - %!STATUS!",
+ status);
+
+ goto Done;
+ }
+
+ status = SpbPeripheralSendRequest(
+ pDevice,
+ pDevice->SpbRequest,
+ FxRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p for "
+ "IOCTL_SPB_FULL_DUPLEX - %!STATUS!",
+ pDevice->SpbRequest,
+ status);
+
+ goto Done;
+ }
+
+Done:
+
+ if (!NT_SUCCESS(status))
+ {
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ 0);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralSignalInterrupt(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+Routine Description:
+
+ This routine signals the interrupt service routine
+ to continue.
+
+Arguments:
+
+ pDevice - the device context
+ FxRequest - the framework request object
+
+Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if (pDevice->Interrupt != nullptr)
+ {
+ //
+ // Signal ISR to continue.
+ //
+
+ KeSetEvent(
+ &pDevice->IsrWaitEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Setting ISR wait event");
+ }
+ else
+ {
+ status = STATUS_NOT_FOUND;
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "No interrupt object found, ignoring - %!STATUS!",
+ status);
+ }
+
+ WdfRequestComplete(FxRequest, status);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralWaitOnInterrupt(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+Routine Description:
+
+ This routine pends the WaitOnInterrupt request.
+
+Arguments:
+
+ pDevice - the device context
+ FxRequest - the framework request object
+
+Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest = GetRequestContext(FxRequest);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if (pDevice->WaitOnInterruptRequest == nullptr)
+ {
+ //
+ // Mark request cancellable.
+ //
+
+ pRequest->FxDevice = pDevice->FxDevice;
+
+ status = WdfRequestMarkCancelableEx(
+ FxRequest,
+ SpbPeripheralOnWaitOnInterruptCancel);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to mark WaitOnInterrupt request %p cancellable - %!STATUS!",
+ FxRequest,
+ status);
+ }
+
+ //
+ // Pend the WaitOnInterrupt request.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "WaitOnInterrupt request %p pended",
+ FxRequest);
+
+ pDevice->WaitOnInterruptRequest = FxRequest;
+ }
+ }
+ else
+ {
+ status = STATUS_INVALID_DEVICE_STATE;
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Cannont pend multiple WaitOnInterrupt requests, ignoring - %!STATUS!",
+ status);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ WdfRequestComplete(FxRequest, status);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralOnWaitOnInterruptCancel(
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+Routine Description:
+
+ This event is called when the WaitOnInterrupt request is cancelled.
+
+Arguments:
+
+ FxRequest - the framework request object
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest;
+ PDEVICE_CONTEXT pDevice;
+
+ pRequest = GetRequestContext(FxRequest);
+ pDevice = GetDeviceContext(pRequest->FxDevice);
+
+ //
+ // Complete the request as cancelled
+ //
+
+ if (FxRequest == pDevice->WaitOnInterruptRequest)
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "WaitOnInterrupt request %p cancelled",
+ FxRequest);
+
+ pDevice->WaitOnInterruptRequest = nullptr;
+ WdfRequestComplete(FxRequest, STATUS_CANCELLED);
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Cancel for WDFREQUEST %p without WaitOnInterrupt request pended,"
+ "will complete as cancelled anyway",
+ FxRequest);
+
+ WdfRequestComplete(FxRequest, STATUS_CANCELLED);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+BOOLEAN
+SpbPeripheralInterruptNotify(
+ _In_ PDEVICE_CONTEXT pDevice
+ )
+/*++
+Routine Description:
+
+ This routine completes the pending WaitOnInterrupt request.
+
+Arguments:
+
+ pDevice - the device context
+
+Return Value:
+
+ TRUE if notification sent, false otherwise.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ WDFREQUEST request;
+ BOOLEAN fNotificationSent = FALSE;
+ NTSTATUS status;
+
+ if (pDevice->WaitOnInterruptRequest != nullptr)
+ {
+ //
+ // Complete the WaitOnInterrupt request.
+ //
+
+ request = pDevice->WaitOnInterruptRequest;
+ pDevice->WaitOnInterruptRequest = nullptr;
+
+ status = WdfRequestUnmarkCancelable(request);
+
+ if (NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected, WaitOnInterrupt request %p completed",
+ pDevice->WaitOnInterruptRequest);
+
+ WdfRequestComplete(request, STATUS_SUCCESS);
+ fNotificationSent = TRUE;
+ }
+ else if (status == STATUS_CANCELLED)
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected with WaitOnInterrupt request %p "
+ "already completed - %!STATUS!",
+ pDevice->WaitOnInterruptRequest,
+ status);
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected but failed to unmark WaitOnInterrupt "
+ "request %p as cancellable - %!STATUS!",
+ pDevice->WaitOnInterruptRequest,
+ status);
+ }
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected without a pended WaitOnInterrupt request");
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return fNotificationSent;
+}
+
+NTSTATUS
+SpbPeripheralSendRequest(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST SpbRequest,
+ _In_ WDFREQUEST ClientRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine sends a write-read sequence to the SPB controller.
+
+ Arguments:
+
+ pDevice - a pointer to the device context
+ SpbRequest - the SPB request object
+ ClientRequest - the client request object
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest = GetRequestContext(ClientRequest);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Saving client request %p, and "
+ "sending SPB request %p",
+ ClientRequest,
+ SpbRequest);
+
+ //
+ // Init client request context.
+ //
+
+ pRequest->FxDevice = pDevice->FxDevice;
+ pRequest->IsSpbSequenceRequest = FALSE;
+ pRequest->SequenceWriteLength = 0;
+
+ //
+ // Mark the client request as cancellable.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ status = WdfRequestMarkCancelableEx(
+ ClientRequest,
+ SpbPeripheralOnCancel);
+ }
+
+ //
+ // Send the SPB request.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ WdfRequestSetCompletionRoutine(
+ SpbRequest,
+ SpbPeripheralOnCompletion,
+ GetRequestContext(SpbRequest));
+
+ BOOLEAN fSent = WdfRequestSend(
+ SpbRequest,
+ pDevice->SpbController,
+ WDF_NO_SEND_OPTIONS);
+
+ if (!fSent)
+ {
+ status = WdfRequestGetStatus(SpbRequest);
+
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to send SPB request %p - %!STATUS!",
+ SpbRequest,
+ status);
+
+ NTSTATUS cancelStatus;
+ cancelStatus = WdfRequestUnmarkCancelable(ClientRequest);
+
+ if (!NT_SUCCESS(cancelStatus))
+ {
+ NT_ASSERTMSG("WdfRequestUnmarkCancelable should only fail if request has already been cancelled",
+ cancelStatus == STATUS_CANCELLED);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Client request %p has already been cancelled - "
+ "%!STATUS!",
+ ClientRequest,
+ cancelStatus);
+ }
+ }
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return status;
+}
+
+VOID
+SpbPeripheralOnCompletion(
+ _In_ WDFREQUEST FxRequest,
+ _In_ WDFIOTARGET FxTarget,
+ _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
+ _In_ WDFCONTEXT Context
+ )
+/*++
+
+ Routine Description:
+
+ This routine is called when a request completes.
+
+ Arguments:
+
+ FxRequest - the framework request object
+ FxTarget - the framework IO target object
+ Params - a pointer to the request completion parameters
+ Context - the request context
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxTarget);
+ UNREFERENCED_PARAMETER(Context);
+
+ PREQUEST_CONTEXT pRequest;
+ PDEVICE_CONTEXT pDevice;
+ NTSTATUS status;
+ NTSTATUS cancelStatus;
+ ULONG_PTR bytesCompleted;
+
+ pRequest = GetRequestContext(FxRequest);
+ pDevice = GetDeviceContext(pRequest->FxDevice);
+
+ status = Params->IoStatus.Status;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Completion callback received for SPB request %p with %!STATUS!",
+ FxRequest,
+ status);
+
+ //
+ // Unmark the client request as cancellable
+ //
+
+ cancelStatus = WdfRequestUnmarkCancelable(pDevice->ClientRequest);
+
+ if (!NT_SUCCESS(cancelStatus))
+ {
+ NT_ASSERTMSG("WdfRequestUnmarkCancelable should only fail if request has already been cancelled",
+ cancelStatus == STATUS_CANCELLED);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Client request %p has already been cancelled - %!STATUS!",
+ pDevice->ClientRequest,
+ cancelStatus);
+ }
+
+ //
+ // Complete the request pair
+ //
+
+ if (pRequest->IsSpbSequenceRequest == TRUE)
+ {
+ //
+ // The client DeviceIoControl should only be
+ // completed with bytesReturned and not total
+ // bytes transferred. Here we infer the number
+ // of bytes read by substracting the write
+ // length from the total.
+ //
+
+ bytesCompleted =
+ Params->IoStatus.Information < pRequest->SequenceWriteLength ?
+ 0 : Params->IoStatus.Information - pRequest->SequenceWriteLength;
+ }
+ else
+ {
+ bytesCompleted = Params->IoStatus.Information;
+ }
+
+ SpbPeripheralCompleteRequestPair(
+ pDevice,
+ status,
+ bytesCompleted);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralOnCancel(
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+Routine Description:
+
+ This event is called when the client request is cancelled.
+
+Arguments:
+
+ FxRequest - the framework request object
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest;
+ PDEVICE_CONTEXT pDevice;
+
+ pRequest = GetRequestContext(FxRequest);
+ pDevice = GetDeviceContext(pRequest->FxDevice);
+
+ //
+ // Attempt to cancel the SPB request
+ //
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Cancel received for client request %p, "
+ "attempting to cancel SPB request %p",
+ FxRequest,
+ pDevice->SpbRequest);
+
+ WdfRequestCancelSentRequest(pDevice->SpbRequest);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+SpbPeripheralCompleteRequestPair(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ NTSTATUS status,
+ _In_ ULONG_PTR bytesCompleted
+ )
+/*++
+Routine Description:
+
+ This routine marks the SpbRequest as reuse
+ and completes the client request.
+
+Arguments:
+
+ pDevice - the device context
+ status - the client completion status
+ bytesCompleted - the number of bytes completed
+ for the client request
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ PREQUEST_CONTEXT pRequest;
+ pRequest = GetRequestContext(pDevice->SpbRequest);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Marking SPB request %p for reuse, and completing "
+ "client request %p with %!STATUS! and bytes=%lu",
+ pDevice->SpbRequest,
+ pDevice->ClientRequest,
+ status,
+ (ULONG)bytesCompleted);
+
+ //
+ // Mark the SPB request as reuse
+ //
+
+ pRequest->IsSpbSequenceRequest = FALSE;
+ pRequest->SequenceWriteLength = 0;
+
+ WDF_REQUEST_REUSE_PARAMS params;
+ WDF_REQUEST_REUSE_PARAMS_INIT(
+ &params,
+ WDF_REQUEST_REUSE_NO_FLAGS,
+ STATUS_SUCCESS);
+
+ WdfRequestReuse(pDevice->SpbRequest, &params);
+
+ if (pDevice->InputMemory != WDF_NO_HANDLE)
+ {
+ WdfObjectDelete(pDevice->InputMemory);
+ pDevice->InputMemory = WDF_NO_HANDLE;
+ }
+
+ //
+ // Complete the client request
+ //
+
+ if (pDevice->ClientRequest != nullptr)
+ {
+ WDFREQUEST clientRequest = pDevice->ClientRequest;
+ pDevice->ClientRequest = nullptr;
+
+ //
+ // In order to satisfy SDV, assume clientRequest
+ // is equal to pDevice->ClientRequest. This suppresses
+ // a warning in the driver's cancellation path.
+ //
+ // Typically when WdfRequestUnmarkCancelable returns
+ // STATUS_CANCELLED a driver does not go on to complete
+ // the request in that context. This sample, however,
+ // driver has handled this condition appropriately by
+ // not completing the cancelled request in its
+ // EvtRequestCancel callback. Developers should be
+ // cautious when copying code from this sample, paying
+ // close attention to the cancellation logic.
+ //
+ _Analysis_assume_(clientRequest == pDevice->ClientRequest);
+
+ WdfRequestCompleteWithInformation(
+ clientRequest,
+ status,
+ bytesCompleted);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}